/* ArchiveTreeNode::merge * Merges [node] with this node. Entries within [node] are added * at [position] within this node. Returns false if [node] is invalid, * true otherwise *******************************************************************/ bool ArchiveTreeNode::merge(ArchiveTreeNode* node, unsigned position, int state) { // Check node was given to merge if (!node) return false; // Merge entries for (unsigned a = 0; a < node->numEntries(); a++) { if (node->getEntry(a)) { string name = Misc::lumpNameToFileName(node->getEntry(a)->getName()); node->getEntry(a)->setName(name); } ArchiveEntry* nentry = new ArchiveEntry(*(node->getEntry(a))); addEntry(nentry, position); nentry->setState(state); if (position < entries.size()) position++; } // Merge subdirectories for (unsigned a = 0; a < node->nChildren(); a++) { ArchiveTreeNode* child = (ArchiveTreeNode*)STreeNode::addChild(node->getChild(a)->getName()); child->merge((ArchiveTreeNode*)node->getChild(a)); child->getDirEntry()->setState(state); } return true; }
/* Archive::removeDir * Deletes the directory matching [path], starting from [base]. If * [base] is NULL, the root directory is used. Returns false if * the directory does not exist, true otherwise *******************************************************************/ bool Archive::removeDir(string path, ArchiveTreeNode* base) { // Abort if read only if (read_only) return false; // Get the dir to remove ArchiveTreeNode* dir = getDir(path, base); // Check it exists (and that it isn't the root dir) if (!dir || dir == getRoot()) return false; // Record undo step if (UndoRedo::currentlyRecording()) UndoRedo::currentManager()->recordUndoStep(new DirCreateDeleteUS(false, dir)); // Remove the directory from its parent if (dir->getParent()) dir->getParent()->removeChild(dir); // Delete the directory delete dir; // Set the archive state to modified setModified(true); return true; }
/* Archive::importDir * Imports all files (including subdirectories) from [directory] into * the archive *******************************************************************/ bool Archive::importDir(string directory) { // Get a list of all files in the directory wxArrayString files; wxDir::GetAllFiles(directory, &files); // Go through files for (unsigned a = 0; a < files.size(); a++) { string name = files[a]; name.Replace(directory, "", false); // Remove directory from entry name // Split filename into dir+name wxFileName fn(name); string ename = fn.GetFullName(); string edir = fn.GetPath(); // Remove beginning \ or / from dir if (edir.StartsWith("\\") || edir.StartsWith("/")) edir.Remove(0, 1); // Add the entry ArchiveTreeNode* dir = createDir(edir); ArchiveEntry* entry = addNewEntry(ename, dir->numEntries()+1, dir); // Load data entry->importFile(files[a]); // Set unmodified entry->setState(0); dir->getDirEntry()->setState(0); } return true; }
void importEditorImages(MapTexHashMap& map, ArchiveTreeNode* dir, string path) { SImage image; // Go through entries for (unsigned a = 0; a < dir->numEntries(); a++) { ArchiveEntry* entry = dir->getEntry(a); // Load entry to image if (image.open(entry->getMCData())) { // Create texture in hashmap string name = path + entry->getName(true); //wxLogMessage("Loading editor texture %s", CHR(name)); map_tex_t& mtex = map[name]; mtex.texture = new GLTexture(false); mtex.texture->setFilter(GLTexture::MIPMAP); mtex.texture->loadImage(&image); } } // Go through subdirs for (unsigned a = 0; a < dir->nChildren(); a++) { ArchiveTreeNode* subdir = (ArchiveTreeNode*)dir->getChild(a); importEditorImages(map, subdir, path + subdir->getName() + "/"); } }
// ---------------------------------------------------------------------------- // TextLanguage::loadLanguages // // Loads all text language definitions from slade.pk3 // ---------------------------------------------------------------------------- bool TextLanguage::loadLanguages() { // Get slade resource archive Archive* res_archive = App::archiveManager().programResourceArchive(); // Read language definitions from resource archive if (res_archive) { // Get 'config/languages' directly ArchiveTreeNode* dir = res_archive->getDir("config/languages"); if (dir) { // Read all entries in this dir for (unsigned a = 0; a < dir->numEntries(); a++) readLanguageDefinition(dir->entryAt(a)->getMCData(), dir->entryAt(a)->getName()); } else Log::warning( 1, "Warning: 'config/languages' not found in slade.pk3, no builtin text language definitions loaded" ); } return true; }
void swapNames() { ArchiveTreeNode* dir = archive->getDir(path); archive->renameDir(dir, old_name); old_name = new_name; new_name = dir->getName(); path = dir->getPath(); }
bool doSwap() { // Get parent dir ArchiveTreeNode* dir = archive->getDir(path); if (dir) return dir->swapEntries(index1, index2); return false; }
bool deleteEntry() { // Get parent dir ArchiveTreeNode* dir = archive->getDir(path); if (dir) return archive->removeEntry(dir->getEntry(index)); else return false; }
/* ColourConfiguration::getConfigurationNames * Adds all available colour configuration names to [names] *******************************************************************/ void ColourConfiguration::getConfigurationNames(vector<string>& names) { // TODO: search custom folder // Search resource pk3 Archive* res = theArchiveManager->programResourceArchive(); ArchiveTreeNode* dir = res->getDir("config/colours"); for (unsigned a = 0; a < dir->numEntries(); a++) names.push_back(dir->getEntry(a)->getName(true)); }
/* Archive::removeEntry * Removes [entry] from the archive. If [delete_entry] is true, the * entry will also be deleted. Returns true if the removal succeeded *******************************************************************/ bool Archive::removeEntry(ArchiveEntry* entry, bool delete_entry) { // Abort if read only if (read_only) return false; // Check entry if (!checkEntry(entry)) return false; // Check if entry is locked if (entry->isLocked()) return false; // Get its directory ArchiveTreeNode* dir = entry->getParentDir(); // Error if entry has no parent directory if (!dir) return false; // Create undo step if (UndoRedo::currentlyRecording()) UndoRedo::currentManager()->recordUndoStep(new EntryCreateDeleteUS(false, entry)); // Get the entry index int index = dir->entryIndex(entry); // Announce (before actually removing in case entry is still needed) MemChunk mc; wxUIntPtr ptr = wxPtrToUInt(entry); mc.write(&index, sizeof(int)); mc.write(&ptr, sizeof(wxUIntPtr)); announce("entry_removing", mc); // Remove it from its directory bool ok = dir->removeEntry(index); // If it was removed ok if (ok) { // Announce removed announce("entry_removed", mc); // Delete if necessary if (delete_entry) delete entry; // Update variables etc setModified(true); } return ok; }
/* Archive::swapEntries * Swaps [entry1] and [entry2]. Returns false if either entry is * invalid or if both entries are not in the same directory, true * otherwise *******************************************************************/ bool Archive::swapEntries(ArchiveEntry* entry1, ArchiveEntry* entry2) { // Abort if read only if (read_only) return false; // Check both entries if (!checkEntry(entry1) || !checkEntry(entry2)) return false; // Check neither entry is locked if (entry1->isLocked() || entry2->isLocked()) return false; // Get their directory ArchiveTreeNode* dir = entry1->getParentDir(); // Error if no dir if (!dir) return false; // Check they are both in the same directory if (entry2->getParentDir() != dir) { wxLogMessage("Error: Can't swap two entries in different directories"); return false; } // Get entry indices int i1 = dir->entryIndex(entry1); int i2 = dir->entryIndex(entry2); // Check indices if (i1 < 0 || i2 < 0) return false; // Create undo step if (UndoRedo::currentlyRecording()) UndoRedo::currentManager()->recordUndoStep(new EntrySwapUS(dir, i1, i2)); // Swap entries dir->swapEntries(i1, i2); // Announce the swap announce("entries_swapped"); // Set modified setModified(true); // Return success return true; }
bool doRedo() { // Get entry parent dir ArchiveTreeNode* dir = archive->getDir(entry_path); if (dir) { // Rename entry ArchiveEntry* entry = dir->getEntry(entry_index); return archive->renameEntry(entry, new_name); } return false; }
// ---------------------------------------------------------------------------- // ArchiveEntryList::getItemText // // Called when the widget requests the text for [item] at [column] // ---------------------------------------------------------------------------- string ArchiveEntryList::getItemText(long item, long column, long index) const { // Get entry ArchiveEntry* entry = getEntry(index, false); // Check entry if (!entry) return "INVALID INDEX"; // Determine what column we want int col = columnType(column); if (col == 0) return entry->getName(); // Name column else if (col == 1) { // Size column if (entry->getType() == EntryType::folderType()) { // Entry is a folder, return the number of entries+subdirectories in it ArchiveTreeNode* dir = nullptr; // Get selected directory if (entry == entry_dir_back) dir = (ArchiveTreeNode*)current_dir->getParent(); // If it's the 'back directory', get the current dir's parent else dir = archive->getDir(entry->getName(), current_dir); // If it's null, return error if (!dir) return "INVALID DIRECTORY"; // Return the number of items in the directory return S_FMT("%d entries", dir->numEntries() + dir->nChildren()); } else return entry->getSizeString(); // Not a folder, just return the normal size string } else if (col == 2) return entry->getTypeString(); // Type column else if (col == 3) { // Index column if (entry->getType() == EntryType::folderType()) return ""; else return S_FMT("%d", entry->getParentDir()->entryIndex(entry)); } else return "INVALID COLUMN"; // Invalid column }
// ---------------------------------------------------------------------------- // ArchiveEntryList::entrySize // // Returns either the size of the entry at [index], or if it is a folder, the // number of entries+subfolders within it // ---------------------------------------------------------------------------- int ArchiveEntryList::entrySize(long index) { ArchiveEntry* entry = getEntry(index, false); if (entry->getType() == EntryType::folderType()) { ArchiveTreeNode* dir = archive->getDir(entry->getName(), current_dir); if (dir) return dir->numEntries() + dir->nChildren(); else return 0; } else return entry->getSize(); }
/* ColourConfiguration::readConfiguration * Reads saved colour configuration [name] *******************************************************************/ bool ColourConfiguration::readConfiguration(string name) { // TODO: search custom folder // Search resource pk3 Archive* res = theArchiveManager->programResourceArchive(); ArchiveTreeNode* dir = res->getDir("config/colours"); for (unsigned a = 0; a < dir->numEntries(); a++) { if (S_CMPNOCASE(dir->getEntry(a)->getName(true), name)) return readConfiguration(dir->getEntry(a)->getMCData()); } return false; }
/* ArchiveTreeNode::clone * Returns a clone of this node *******************************************************************/ ArchiveTreeNode* ArchiveTreeNode::clone() { // Create copy ArchiveTreeNode* copy = new ArchiveTreeNode(); copy->setName(dir_entry->getName()); // Copy entries for (unsigned a = 0; a < entries.size(); a++) copy->addEntry(new ArchiveEntry(*(entries[a]))); // Copy subdirectories for (unsigned a = 0; a < children.size(); a++) copy->addChild(((ArchiveTreeNode*)children[a])->clone()); return copy; }
/* ArchiveManager::addArchive * Adds an archive to the archive list *******************************************************************/ bool ArchiveManager::addArchive(Archive* archive) { // Only add if archive is a valid pointer if (archive) { // Add to the list archive_t n_archive; n_archive.archive = archive; n_archive.resource = true; open_archives.push_back(n_archive); // Listen to the archive listenTo(archive); // Announce the addition announce("archive_added"); // Add to resource manager theResourceManager->addArchive(archive); // ZDoom also loads any WADs found in the root of a PK3 or directory if ((archive->getType() == ARCHIVE_ZIP || archive->getType() == ARCHIVE_FOLDER) && auto_open_wads_root) { ArchiveTreeNode* root = archive->getRoot(); ArchiveEntry* entry; EntryType* type; for (unsigned a = 0; a < root->numEntries(); a++) { entry = root->getEntry(a); if (entry->getType() == EntryType::unknownType()) EntryType::detectEntryType(entry); type = entry->getType(); if (type->getId() == "wad") // First true: yes, manage this // Second true: open silently, don't open a tab for it openArchive(entry, true, true); } } return true; } else return false; }
bool doUndo() { if (created) return archive->removeDir(path); else { // Create directory ArchiveTreeNode* dir = archive->createDir(path); // Restore entries/subdirs if needed if (dir && cb_tree) dir->merge(cb_tree->getTree(), 0, 0); dir->getDirEntry()->setState(0); return !!dir; } }
// ----------------------------------------------------------------------------- // Returns the entry at the given path in the archive, or null if it doesn't // exist // ----------------------------------------------------------------------------- shared_ptr<ArchiveEntry> Archive::entryAtPathShared(string_view path) { // Get path as wxFileName for processing StrUtil::Path fn(StrUtil::startsWith(path, '/') ? path.substr(1) : path); // Get directory from path ArchiveTreeNode* dir; if (fn.path(false).empty()) dir = &dir_root_; else dir = this->dir(fn.path(true)); // If dir doesn't exist, return nullptr if (!dir) return nullptr; // Return entry return dir->sharedEntry(fn.fileName()); }
/* ADatArchive::detectNamespace * Returns the namespace that [entry] is within *******************************************************************/ string ADatArchive::detectNamespace(ArchiveEntry* entry) { // Check entry if (!checkEntry(entry)) return "global"; // If the entry is in the root dir, it's in the global namespace if (entry->getParentDir() == getRoot()) return "global"; // Get the entry's *first* parent directory after root (ie <root>/namespace/) ArchiveTreeNode* dir = entry->getParentDir(); while (dir && dir->getParent() != getRoot()) dir = (ArchiveTreeNode*)dir->getParent(); // Namespace is the directory's name (in lowercase) if (dir) return dir->getName().Lower(); else return "global"; // Error, just return global }
/* ZipArchive::detectMaps * Detects all the maps in the archive and returns a vector of * information about them. *******************************************************************/ vector<Archive::mapdesc_t> ZipArchive::detectMaps() { vector<mapdesc_t> ret; // Get the maps directory ArchiveTreeNode* mapdir = getDir("maps"); if (!mapdir) return ret; // Go through entries in map dir for (unsigned a = 0; a < mapdir->numEntries(); a++) { ArchiveEntry* entry = mapdir->getEntry(a); // Maps can only be wad archives if (entry->getType()->getFormat() != "archive_wad") continue; // Detect map format (probably kinda slow but whatever, no better way to do it really) int format = MAP_UNKNOWN; Archive* tempwad = new WadArchive(); tempwad->open(entry); vector<mapdesc_t> emaps = tempwad->detectMaps(); if (emaps.size() > 0) format = emaps[0].format; delete tempwad; // Add map description mapdesc_t md; md.head = entry; md.end = entry; md.archive = true; md.name = entry->getName(true).Upper(); md.format = format; ret.push_back(md); } return ret; }
/* ArchiveManager::deleteBookmarksInDir * Removes any bookmarked entries in [node] from the list *******************************************************************/ bool ArchiveManager::deleteBookmarksInDir(ArchiveTreeNode* node) { // Go through bookmarks Archive * archive = node->getArchive(); bool removed = deleteBookmark(node->getDirEntry()); for (unsigned a = 0; a < bookmarks.size(); ++a) { // Check bookmarked entry's parent archive if (bookmarks[a]->getParent() == archive) { // Now check if the bookmarked entry is within // the removed dir or one of its descendants ArchiveTreeNode* anode = bookmarks[a]->getParentDir(); bool remove = false; while (anode != archive->getRoot() && !remove) { if (anode == node) remove = true; else anode = (ArchiveTreeNode*)anode->getParent(); } if (remove) { bookmarks.erase(bookmarks.begin() + a); --a; removed = true; } } } if (removed) { // Announce announce("bookmarks_changed"); return true; } else return false; }
/* Archive::entryAtPath * Returns the entry at the given path in the archive, or NULL if it * doesn't exist *******************************************************************/ ArchiveEntry* Archive::entryAtPath(string path) { // Remove leading / from path if needed if (path.StartsWith("/")) path.Remove(0, 1); // Get path as wxFileName for processing wxFileName fn(path); // Get directory from path ArchiveTreeNode* dir; if (fn.GetPath(false, wxPATH_UNIX).IsEmpty()) dir = getRoot(); else dir = getDir(fn.GetPath(true, wxPATH_UNIX)); // If dir doesn't exist, return null if (!dir) return NULL; // Return entry return dir->getEntry(fn.GetFullName()); }
/* MapBackupPanel::updateMapPreview * Updates the map preview with the currently selected backup *******************************************************************/ void MapBackupPanel::updateMapPreview() { // Clear current preview canvas_map->clearMap(); // Check for selection if (list_backups->selectedItems().IsEmpty()) return; int selection = (list_backups->GetItemCount()-1) - list_backups->selectedItems()[0]; // Load map data to temporary wad if (archive_mapdata) delete archive_mapdata; archive_mapdata = new WadArchive(); ArchiveTreeNode* dir = (ArchiveTreeNode*)dir_current->getChild(selection); for (unsigned a = 0; a < dir->numEntries(); a++) archive_mapdata->addEntry(dir->getEntry(a), "", true); // Open map preview vector<Archive::mapdesc_t> maps = archive_mapdata->detectMaps(); if (!maps.empty()) canvas_map->openMap(maps[0]); }
/* ZipArchive::open * Reads zip data from a file * Returns true if successful, false otherwise *******************************************************************/ bool ZipArchive::open(string filename) { // Open the file wxFFileInputStream in(filename); if (!in.IsOk()) { Global::error = "Unable to open file"; return false; } // Create zip stream wxZipInputStream zip(in); if (!zip.IsOk()) { Global::error = "Invalid zip file"; return false; } // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); // Go through all zip entries int entry_index = 0; wxZipEntry* entry = zip.GetNextEntry(); theSplashWindow->setProgressMessage("Reading zip data"); while (entry) { theSplashWindow->setProgress(-1.0f); if (entry->GetMethod() != wxZIP_METHOD_DEFLATE && entry->GetMethod() != wxZIP_METHOD_STORE) { Global::error = "Unsupported zip compression method"; setMuted(false); return false; } if (!entry->IsDir()) { // Get the entry name as a wxFileName (so we can break it up) wxFileName fn(entry->GetName(wxPATH_UNIX), wxPATH_UNIX); // Create entry ArchiveEntry* new_entry = new ArchiveEntry(fn.GetFullName(), entry->GetSize()); // Setup entry info new_entry->setLoaded(false); new_entry->exProp("ZipIndex") = entry_index; // Add entry and directory to directory tree ArchiveTreeNode* ndir = createDir(fn.GetPath(true, wxPATH_UNIX)); ndir->addEntry(new_entry); //zipdir_t* ndir = addDirectory(fn.GetPath(true, wxPATH_UNIX)); //ndir->entries.push_back(new_entry); // Read the data, if possible if (entry->GetSize() < 250 * 1024 * 1024) { uint8_t* data = new uint8_t[entry->GetSize()]; zip.Read(data, entry->GetSize()); // Note: this is where exceedingly large files cause an exception. new_entry->importMem(data, entry->GetSize()); new_entry->setLoaded(true); // Determine its type EntryType::detectEntryType(new_entry); // Unload data if needed if (!archive_load_data) new_entry->unloadData(); // Clean up delete[] data; } else { Global::error = S_FMT("Entry too large: %s is %u mb", entry->GetName(wxPATH_UNIX), entry->GetSize() / (1<<20)); setMuted(false); return false; } } else { // Zip entry is a directory, add it to the directory tree wxFileName fn(entry->GetName(wxPATH_UNIX), wxPATH_UNIX); createDir(fn.GetPath(true, wxPATH_UNIX)); //addDirectory(fn.GetPath(true, wxPATH_UNIX)); } // Go to next entry in the zip file delete entry; entry = zip.GetNextEntry(); entry_index++; } theSplashWindow->forceRedraw(); // Set all entries/directories to unmodified vector<ArchiveEntry*> entry_list; getEntryTreeAsList(entry_list); for (size_t a = 0; a < entry_list.size(); a++) entry_list[a]->setState(0); // Enable announcements setMuted(false); // Setup variables this->filename = filename; setModified(false); on_disk = true; theSplashWindow->setProgressMessage(""); return true; }
/* ADatArchive::open * Reads dat format data from a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool ADatArchive::open(MemChunk& mc) { // Check given data is valid if (mc.getSize() < 16) return false; // Read dat header char magic[4]; long dir_offset; long dir_size; mc.seek(0, SEEK_SET); mc.read(magic, 4); mc.read(&dir_offset, 4); mc.read(&dir_size, 4); // Check it if (magic[0] != 'A' || magic[1] != 'D' || magic[2] != 'A' || magic[3] != 'T') { wxLogMessage("ADatArchive::open: Opening failed, invalid header"); Global::error = "Invalid dat header"; return false; } // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); // Read the directory size_t num_entries = dir_size / DIRENTRY; mc.seek(dir_offset, SEEK_SET); theSplashWindow->setProgressMessage("Reading dat archive data"); for (uint32_t d = 0; d < num_entries; d++) { // Update splash window progress theSplashWindow->setProgress(((float)d / (float)num_entries)); // Read entry info char name[128]; long offset; long decsize; long compsize; long whatever; // No idea what this could be mc.read(name, 128); mc.read(&offset, 4); mc.read(&decsize, 4); mc.read(&compsize, 4); mc.read(&whatever, 4); // Byteswap if needed offset = wxINT32_SWAP_ON_BE(offset); decsize = wxINT32_SWAP_ON_BE(decsize); compsize = wxINT32_SWAP_ON_BE(compsize); // Check offset+size if ((unsigned)(offset + compsize) > mc.getSize()) { wxLogMessage("ADatArchive::open: dat archive is invalid or corrupt (entry goes past end of file)"); Global::error = "Archive is invalid and/or corrupt"; setMuted(false); return false; } // Parse name wxFileName fn(wxString::FromAscii(name, 128)); // Create directory if needed ArchiveTreeNode* dir = createDir(fn.GetPath(true, wxPATH_UNIX)); // Create entry ArchiveEntry* entry = new ArchiveEntry(fn.GetFullName(), compsize); entry->exProp("Offset") = (int)offset; entry->exProp("FullSize") = (int)decsize; entry->setLoaded(false); entry->setState(0); // Add to directory dir->addEntry(entry); } // Detect all entry types MemChunk edata; vector<ArchiveEntry*> all_entries; getEntryTreeAsList(all_entries); theSplashWindow->setProgressMessage("Detecting entry types"); for (size_t a = 0; a < all_entries.size(); a++) { // Update splash window progress theSplashWindow->setProgress((((float)a / (float)num_entries))); // Get entry ArchiveEntry* entry = all_entries[a]; // Read entry data if it isn't zero-sized if (entry->getSize() > 0) { // Read the entry data mc.exportMemChunk(edata, (int)entry->exProp("Offset"), entry->getSize()); MemChunk xdata; if (Compression::ZlibInflate(edata, xdata, (int)entry->exProp("FullSize"))) entry->importMemChunk(xdata); else { wxLogMessage("Entry %s couldn't be inflated", CHR(entry->getName())); entry->importMemChunk(edata); } } // Detect entry type EntryType::detectEntryType(entry); // Unload entry data if needed if (!archive_load_data) entry->unloadData(); // Set entry to unchanged entry->setState(0); } // Setup variables setMuted(false); setModified(false); announce("opened"); theSplashWindow->setProgressMessage(""); return true; }
/* PakArchive::open * Reads pak format data from a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool PakArchive::open(MemChunk& mc) { // Check given data is valid if (mc.getSize() < 12) return false; // Read pak header char pack[4]; long dir_offset; long dir_size; mc.seek(0, SEEK_SET); mc.read(pack, 4); mc.read(&dir_offset, 4); mc.read(&dir_size, 4); // Check it if (pack[0] != 'P' || pack[1] != 'A' || pack[2] != 'C' || pack[3] != 'K') { wxLogMessage("PakArchive::open: Opening failed, invalid header"); Global::error = "Invalid pak header"; return false; } // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); // Read the directory size_t num_entries = dir_size / 64; mc.seek(dir_offset, SEEK_SET); theSplashWindow->setProgressMessage("Reading pak archive data"); for (uint32_t d = 0; d < num_entries; d++) { // Update splash window progress theSplashWindow->setProgress(((float)d / (float)num_entries)); // Read entry info char name[56]; long offset; long size; mc.read(name, 56); mc.read(&offset, 4); mc.read(&size, 4); // Byteswap if needed offset = wxINT32_SWAP_ON_BE(offset); size = wxINT32_SWAP_ON_BE(size); // Check offset+size if ((unsigned)(offset + size) > mc.getSize()) { wxLogMessage("PakArchive::open: Pak archive is invalid or corrupt (entry goes past end of file)"); Global::error = "Archive is invalid and/or corrupt"; setMuted(false); return false; } // Parse name wxFileName fn(wxString::FromAscii(name, 56)); // Create directory if needed ArchiveTreeNode* dir = createDir(fn.GetPath(true, wxPATH_UNIX)); // Create entry ArchiveEntry* entry = new ArchiveEntry(fn.GetFullName(), size); entry->exProp("Offset") = (int)offset; entry->setLoaded(false); entry->setState(0); // Add to directory dir->addEntry(entry); } // Detect all entry types MemChunk edata; vector<ArchiveEntry*> all_entries; getEntryTreeAsList(all_entries); theSplashWindow->setProgressMessage("Detecting entry types"); for (size_t a = 0; a < all_entries.size(); a++) { // Update splash window progress theSplashWindow->setProgress((((float)a / (float)num_entries))); // Get entry ArchiveEntry* entry = all_entries[a]; // Read entry data if it isn't zero-sized if (entry->getSize() > 0) { // Read the entry data mc.exportMemChunk(edata, (int)entry->exProp("Offset"), entry->getSize()); entry->importMemChunk(edata); } // Detect entry type EntryType::detectEntryType(entry); // Unload entry data if needed if (!archive_load_data) entry->unloadData(); // Set entry to unchanged entry->setState(0); } // Setup variables setMuted(false); setModified(false); announce("opened"); theSplashWindow->setProgressMessage(""); return true; }
/* StyleSet::loadResourceStyles * Loads all text styles from the slade resource archive (slade.pk3) *******************************************************************/ bool StyleSet::loadResourceStyles() { // Get 'config/text_styles' directory in slade.pk3 ArchiveTreeNode* dir = theArchiveManager->programResourceArchive()->getDir("config/text_styles"); // Check it exists if (!dir) { wxLogMessage("Warning: No 'config/text_styles' directory exists in slade.pk3"); return false; } // Read default style set first ArchiveEntry* default_style = dir->getEntry("default.sss"); if (default_style) { // Read entry data into tokenizer Tokenizer tz; tz.openMem(&default_style->getMCData(), default_style->getName()); // Parse it ParseTreeNode root; root.allowDup(true); root.parse(tz); // Read any styleset definitions vector<STreeNode*> nodes = root.getChildren("styleset"); for (unsigned b = 0; b < nodes.size(); b++) { StyleSet* newset = new StyleSet(); if (newset->parseSet((ParseTreeNode*)nodes[b])) style_sets.push_back(newset); else delete newset; } } // Go through all entries within it for (unsigned a = 0; a < dir->numEntries(); a++) { ArchiveEntry* entry = dir->getEntry(a); // Skip default if (entry->getName(true) == "default") continue; // Read entry data into tokenizer Tokenizer tz; tz.openMem(&entry->getMCData(), entry->getName()); // Parse it ParseTreeNode root; root.allowDup(true); root.parse(tz); // Read any styleset definitions vector<STreeNode*> nodes = root.getChildren("styleset"); for (unsigned b = 0; b < nodes.size(); b++) { StyleSet* newset = new StyleSet(); if (newset->parseSet((ParseTreeNode*)nodes[b])) style_sets.push_back(newset); else delete newset; } } return true; }
/* ResArchive::readDirectory * Reads a res directory from a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool ResArchive::readDirectory(MemChunk& mc, size_t dir_offset, size_t num_lumps, ArchiveTreeNode* parent) { if (!parent) { LOG_MESSAGE(1, "ReadDir: No parent node"); Global::error = "Archive is invalid and/or corrupt"; return false; } mc.seek(dir_offset, SEEK_SET); for (uint32_t d = 0; d < num_lumps; d++) { // Update splash window progress UI::setSplashProgress(((float)d / (float)num_lumps)); // Read lump info char magic[4] = ""; char name[15] = ""; uint32_t dumzero1, dumzero2; uint16_t dumff, dumze; uint8_t flags = 0; uint32_t offset = 0; uint32_t size = 0; mc.read(magic, 4); // ReS\0 mc.read(name, 14); // Name mc.read(&offset, 4); // Offset mc.read(&size, 4); // Size // Check the identifier if (magic[0] != 'R' || magic[1] != 'e' || magic[2] != 'S' || magic[3] != 0) { LOG_MESSAGE(1, "ResArchive::readDir: Entry %s (%i@0x%x) has invalid directory entry", name, size, offset); Global::error = "Archive is invalid and/or corrupt"; return false; } // Byteswap values for big endian if needed offset = wxINT32_SWAP_ON_BE(offset); size = wxINT32_SWAP_ON_BE(size); name[14] = '\0'; mc.read(&dumze, 2); if (dumze) LOG_MESSAGE(1, "Flag guard not null for entry %s", name); mc.read(&flags, 1); if (flags != 1 && flags != 17) LOG_MESSAGE(1, "Unknown flag value for entry %s", name); mc.read(&dumzero1, 4); if (dumzero1) LOG_MESSAGE(1, "Near-end values not set to zero for entry %s", name); mc.read(&dumff, 2); if (dumff != 0xFFFF) LOG_MESSAGE(1, "Dummy set to a non-FF value for entry %s", name); mc.read(&dumzero2, 4); if (dumzero2) LOG_MESSAGE(1, "Trailing values not set to zero for entry %s", name); // If the lump data goes past the end of the file, // the resfile is invalid if (offset + size > mc.getSize()) { LOG_MESSAGE(1, "ResArchive::readDirectory: Res archive is invalid or corrupt, offset overflow"); Global::error = "Archive is invalid and/or corrupt"; setMuted(false); return false; } // Create & setup lump ArchiveEntry* nlump = new ArchiveEntry(wxString::FromAscii(name), size); nlump->setLoaded(false); nlump->exProp("Offset") = (int)offset; nlump->setState(0); // Read entry data if it isn't zero-sized if (nlump->getSize() > 0) { // Read the entry data MemChunk edata; mc.exportMemChunk(edata, offset, size); nlump->importMemChunk(edata); } // What if the entry is a directory? size_t d_o, n_l; if (isResArchive(nlump->getMCData(), d_o, n_l)) { ArchiveTreeNode* ndir = createDir(name, parent); if (ndir) { UI::setSplashProgressMessage(S_FMT("Reading res archive data: %s directory", name)); // Save offset to restore it once the recursion is done size_t myoffset = mc.currentPos(); readDirectory(mc, d_o, n_l, ndir); ndir->getDirEntry()->setState(0); // Restore offset and clean out the entry mc.seek(myoffset, SEEK_SET); delete nlump; } else { delete nlump; return false; } // Not a directory, then add to entry list } else { parent->addEntry(nlump); // Detect entry type EntryType::detectEntryType(nlump); // Unload entry data if needed if (!archive_load_data) nlump->unloadData(); // Set entry to unchanged nlump->setState(0); } } return true; }
/* DirArchive::open * Reads files from the directory [filename] into the archive * Returns true if successful, false otherwise *******************************************************************/ bool DirArchive::open(string filename) { theSplashWindow->setProgressMessage("Reading directory structure"); theSplashWindow->setProgress(0); //wxArrayString files; //wxDir::GetAllFiles(filename, &files, wxEmptyString, wxDIR_FILES|wxDIR_DIRS); vector<string> files, dirs; DirArchiveTraverser traverser(files, dirs); wxDir dir(filename); dir.Traverse(traverser, "", wxDIR_FILES | wxDIR_DIRS); theSplashWindow->setProgressMessage("Reading files"); for (unsigned a = 0; a < files.size(); a++) { theSplashWindow->setProgress((float)a / (float)files.size()); // Cut off directory to get entry name + relative path string name = files[a]; name.Remove(0, filename.Length()); if (name.StartsWith(separator)) name.Remove(0, 1); //LOG_MESSAGE(3, fn.GetPath(true, wxPATH_UNIX)); // Create entry wxFileName fn(name); ArchiveEntry* new_entry = new ArchiveEntry(fn.GetFullName()); // Setup entry info new_entry->setLoaded(false); new_entry->exProp("filePath") = files[a]; // Add entry and directory to directory tree ArchiveTreeNode* ndir = createDir(fn.GetPath(true, wxPATH_UNIX)); ndir->addEntry(new_entry); ndir->getDirEntry()->exProp("filePath") = filename + fn.GetPath(true, wxPATH_UNIX); // Read entry data new_entry->importFile(files[a]); new_entry->setLoaded(true); time_t modtime = wxFileModificationTime(files[a]); file_modification_times[new_entry] = modtime; // Detect entry type EntryType::detectEntryType(new_entry); // Unload data if needed if (!archive_load_data) new_entry->unloadData(); } // Add empty directories for (unsigned a = 0; a < dirs.size(); a++) { string name = dirs[a]; name.Remove(0, filename.Length()); if (name.StartsWith(separator)) name.Remove(0, 1); name.Replace("\\", "/"); ArchiveTreeNode* ndir = createDir(name); ndir->getDirEntry()->exProp("filePath") = dirs[a]; } // Set all entries/directories to unmodified vector<ArchiveEntry*> entry_list; getEntryTreeAsList(entry_list); for (size_t a = 0; a < entry_list.size(); a++) entry_list[a]->setState(0); // Enable announcements setMuted(false); // Setup variables this->filename = filename; setModified(false); on_disk = true; theSplashWindow->setProgressMessage(""); return true; }