/* Wad2Archive::write * Writes the wad archive to a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool Wad2Archive::write(MemChunk& mc, bool update) { // Determine directory offset & individual lump offsets uint32_t dir_offset = 12; ArchiveEntry* entry = NULL; for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); entry->exProp("Offset") = (int)dir_offset; dir_offset += entry->getSize(); } // Clear/init MemChunk mc.clear(); mc.seek(0, SEEK_SET); mc.reSize(dir_offset + numEntries() * 32); // Setup wad type char wad_type[4] = { 'W', 'A', 'D', '2' }; if (wad3) wad_type[3] = '3'; // Write the header uint32_t num_lumps = numEntries(); mc.write(wad_type, 4); mc.write(&num_lumps, 4); mc.write(&dir_offset, 4); // Write the lumps for (uint32_t l = 0; l < num_lumps; l++) { entry = getEntry(l); mc.write(entry->getData(), entry->getSize()); } // Write the directory for (uint32_t l = 0; l < num_lumps; l++) { entry = getEntry(l); // Setup directory entry wad2entry_t info; memset(info.name, 0, 16); memcpy(info.name, CHR(entry->getName()), entry->getName().Len()); info.cmprs = (bool)entry->exProp("W2Comp"); info.dsize = entry->getSize(); info.size = entry->getSize(); info.offset = (int)entry->exProp("Offset"); info.type = (int)entry->exProp("W2Type"); // Write it mc.write(&info, 32); if (update) entry->setState(0); } return true; }
/* MapEditorWindow::loadMapScripts * Loads any scripts from [map] into the script editor *******************************************************************/ void MapEditorWindow::loadMapScripts(Archive::mapdesc_t map) { // Don't bother if no scripting language specified if (theGameConfiguration->scriptLanguage().IsEmpty()) { // Hide script editor wxAuiManager* m_mgr = wxAuiManager::GetManager(this); wxAuiPaneInfo& p_inf = m_mgr->GetPane("script_editor"); p_inf.Show(false); m_mgr->Update(); return; } // Don't bother if new map if (!map.head) { panel_script_editor->openScripts(NULL, NULL); return; } // Check for pk3 map if (map.archive) { WadArchive* wad = new WadArchive(); wad->open(map.head->getMCData()); vector<Archive::mapdesc_t> maps = wad->detectMaps(); if (!maps.empty()) { loadMapScripts(maps[0]); wad->close(); delete wad; return; } } // Go through map entries ArchiveEntry* entry = map.head->nextEntry(); ArchiveEntry* scripts = NULL; ArchiveEntry* compiled = NULL; while (entry && entry != map.end->nextEntry()) { // Check for SCRIPTS/BEHAVIOR if (theGameConfiguration->scriptLanguage() == "acs_hexen" || theGameConfiguration->scriptLanguage() == "acs_zdoom") { if (S_CMPNOCASE(entry->getName(), "SCRIPTS")) scripts = entry; if (S_CMPNOCASE(entry->getName(), "BEHAVIOR")) compiled = entry; } // Next entry entry = entry->nextEntry(); } // Open scripts/compiled if found panel_script_editor->openScripts(scripts, compiled); }
/* GobArchive::write * Writes the gob archive to a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool GobArchive::write(MemChunk& mc, bool update) { // Determine directory offset & individual lump offsets uint32_t dir_offset = 8; ArchiveEntry* entry = NULL; for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); setEntryOffset(entry, dir_offset); dir_offset += entry->getSize(); } // Clear/init MemChunk mc.clear(); mc.seek(0, SEEK_SET); mc.reSize(dir_offset + 4 + numEntries() * 21); // Write the header uint32_t num_lumps = wxINT32_SWAP_ON_BE(numEntries()); dir_offset = wxINT32_SWAP_ON_BE(dir_offset); char header[4] = { 'G', 'O', 'B', 0xA }; mc.write(header, 4); mc.write(&dir_offset, 4); // Write the lumps for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); mc.write(entry->getData(), entry->getSize()); } // Write the directory mc.write(&num_lumps, 4); for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); char name[13] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; long offset = wxINT32_SWAP_ON_BE(getEntryOffset(entry)); long size = wxINT32_SWAP_ON_BE(entry->getSize()); for (size_t c = 0; c < entry->getName().length() && c < 13; c++) name[c] = entry->getName()[c]; mc.write(&offset, 4); mc.write(&size, 4); mc.write(name, 13); if (update) { entry->setState(0); entry->exProp("Offset") = (int)offset; } } return true; }
// ---------------------------------------------------------------------------- // 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 }
/* HogArchive::write * Writes the hog archive to a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool HogArchive::write(MemChunk& mc, bool update) { // Determine individual lump offsets uint32_t offset = 3; ArchiveEntry* entry = NULL; for (uint32_t l = 0; l < numEntries(); l++) { offset += 17; entry = getEntry(l); setEntryOffset(entry, offset); if (update) { entry->setState(0); entry->exProp("Offset") = (int)offset; } offset += entry->getSize(); } // Clear/init MemChunk mc.clear(); mc.seek(0, SEEK_SET); mc.reSize(offset); // Write the header char header[3] = { 'D', 'H', 'F' }; mc.write(header, 3); // Write the lumps for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); mc.write(entry->getData(), entry->getSize()); } // Write the directory for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); char name[13] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; long size = wxINT32_SWAP_ON_BE(entry->getSize()); for (size_t c = 0; c < entry->getName().length() && c < 13; c++) name[c] = entry->getName()[c]; mc.write(name, 13); mc.write(&size, 4); mc.write(entry->getData(), entry->getSize()); } return true; }
// ---------------------------------------------------------------------------- // ArchiveEntryList::getSelectedDirectories // // Returns a vector of all currently selected directories // ---------------------------------------------------------------------------- vector<ArchiveTreeNode*> ArchiveEntryList::getSelectedDirectories() { vector<ArchiveTreeNode*> ret; // Get all selected items vector<long> selection = getSelection(); // Go through the selection for (size_t a = 0; a < selection.size(); a++) { ArchiveEntry* entry = getEntry(selection[a]); // If the selected entry is the 'back folder', ignore it if (entry == entry_dir_back) continue; else if (entry->getType() == EntryType::folderType()) { // If the entry is a folder type, get its ArchiveTreeNode counterpart ArchiveTreeNode* dir = archive->getDir(entry->getName(), current_dir); // Add it to the return list if (dir) ret.push_back(dir); } } return ret; }
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() + "/"); } }
/* TextureClipboardItem::TextureClipboardItem * TextureClipboardItem class constructor *******************************************************************/ TextureClipboardItem::TextureClipboardItem(CTexture* texture, Archive* parent) : ClipboardItem(CLIPBOARD_COMPOSITE_TEXTURE) { // Create/copy texture this->texture = new CTexture(); this->texture->copyTexture(texture); // Copy patch entries if possible for (unsigned a = 0; a < texture->nPatches(); a++) { ArchiveEntry* entry = texture->getPatch(a)->getPatchEntry(parent); // FIXME/TODO: Do something to handle patches that are defined // in TEXTURES rather than a discrete entry! if (entry == NULL) continue; // Don't copy patch if it has been already bool there = false; for (unsigned b = 0; b < patch_entries.size(); b++) { if (patch_entries[b]->getName() == entry->getName()) { there = true; break; } } if (there) continue; // Copy patch entry if (entry) patch_entries.push_back(new ArchiveEntry(*entry)); } }
// ---------------------------------------------------------------------------- // ArchiveEntryList::onListItemActivated // // Called when a list item is 'activated' (double-click or enter) // ---------------------------------------------------------------------------- void ArchiveEntryList::onListItemActivated(wxListEvent& e) { // Get item entry ArchiveEntry* entry = getEntry(e.GetIndex()); // Do nothing if NULL (shouldn't be) if (!entry) return; // If it's a folder, open it if (entry->getType() == EntryType::folderType()) { // Get directory to open ArchiveTreeNode* dir = nullptr; if (entry == entry_dir_back) dir = (ArchiveTreeNode*)current_dir->getParent(); // 'Back directory' entry, open current dir's parent else dir = archive->getDir(entry->getName(), current_dir); // Check it exists (really should) if (!dir) { LOG_MESSAGE(1, "Error: Trying to open nonexistant directory"); return; } // Set current dir setDir(dir); } else e.Skip(); }
/* DatArchive::updateNamespaces * Updates the namespace list *******************************************************************/ void DatArchive::updateNamespaces() { // Clear current namespace info sprites[0] = sprites[1] = flats[0] = flats[1] = walls[0] = walls[1] = -1; // Go through all entries for (unsigned a = 0; a < numEntries(); a++) { ArchiveEntry* entry = getRoot()->getEntry(a); // Check for markers if (!entry->getName().Cmp("startflats")) flats[0] = a; if (!entry->getName().Cmp("endflats")) flats[1] = a; if (!entry->getName().Cmp("startsprites")) sprites[0] = a; if (!entry->getName().Cmp("endmonsters")) sprites[1] = a; if (!entry->getName().Cmp("startwalls")) walls[0] = a; if (!entry->getName().Cmp("endwalls")) walls[1] = a; } }
// ---------------------------------------------------------------------------- // 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(); }
/* 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; }
/* GZipArchive::findFirst * Returns the entry if it matches the search criteria in [options], * or NULL otherwise *******************************************************************/ ArchiveEntry* GZipArchive::findFirst(search_options_t& options) { // Init search variables options.match_name = options.match_name.Lower(); ArchiveEntry* entry = getEntry(0); if (entry == NULL) return entry; // Check type if (options.match_type) { if (entry->getType() == EntryType::unknownType()) { if (!options.match_type->isThisType(entry)) { return NULL; } } else if (options.match_type != entry->getType()) { return NULL; } } // Check name if (!options.match_name.IsEmpty()) { if (!options.match_name.Matches(entry->getName().Lower())) { return NULL; } } // Entry passed all checks so far, so we found a match return entry; }
/* loadIcons * Loads all icons from slade.pk3 (in the icons/ dir) *******************************************************************/ bool loadIcons() { string tempfile = appPath("sladetemp", DIR_TEMP); // Get slade.pk3 Archive* res_archive = theArchiveManager->programResourceArchive(); // Do nothing if it doesn't exist if (!res_archive) return false; // Get the icons directory of the archive ArchiveTreeNode* dir_icons = res_archive->getDir("icons/"); // Go through each entry in the directory for (size_t a = 0; a < dir_icons->numEntries(false); a++) { ArchiveEntry* entry = dir_icons->getEntry(a); // Export entry data to a temporary file entry->exportFile(tempfile); // Create / setup icon icon_t n_icon; n_icon.image.LoadFile(tempfile); // Load image from temp file n_icon.name = entry->getName(true); // Set icon name // Add the icon icons.push_back(n_icon); // Delete the temporary file wxRemoveFile(tempfile); } return true; }
/* WadArchive::getMapInfo * Returns the mapdesc_t information about the map beginning at * [maphead]. If [maphead] is not really a map header entry, an * invalid mapdesc_t will be returned (mapdesc_t::head == NULL) *******************************************************************/ Archive::mapdesc_t WadArchive::getMapInfo(ArchiveEntry* maphead) { mapdesc_t map; if (!maphead) return map; // Check for embedded wads (e.g., Doom 64 maps) if (maphead->getType()->getFormat() == "archive_wad") { map.archive = true; map.head = maphead; map.end = maphead; map.name = maphead->getName(); return map; } // Check for UDMF format map if (S_CMPNOCASE(maphead->nextEntry()->getName(), "TEXTMAP")) { // Get map info map.head = maphead; map.name = maphead->getName(); map.format = MAP_UDMF; // All entries until we find ENDMAP ArchiveEntry* entry = maphead->nextEntry(); while (true) { if (!entry || S_CMPNOCASE(entry->getName(), "ENDMAP")) break; // Check for unknown map lumps bool known = false; for (unsigned a = 0; a < NUMMAPLUMPS; a++) { if (S_CMPNOCASE(entry->getName(), map_lumps[a])) { known = true; a = NUMMAPLUMPS; } } if (!known) map.unk.push_back(entry); // Next entry entry = entry->nextEntry(); } // If we got to the end before we found ENDMAP, something is wrong if (!entry) return mapdesc_t(); // Set end entry map.end = entry; return map; } // Check for doom/hexen format map uint8_t existing_map_lumps[NUMMAPLUMPS]; memset(existing_map_lumps, 0, NUMMAPLUMPS); ArchiveEntry* entry = maphead->nextEntry(); while (entry) { // Check that the entry is a valid map-related entry bool mapentry = false; for (unsigned a = 0; a < NUMMAPLUMPS; a++) { if (S_CMPNOCASE(entry->getName(), map_lumps[a])) { mapentry = true; existing_map_lumps[a] = 1; break; } else if (a == LUMP_GL_HEADER) { string name = maphead->getName(true); name.Prepend("GL_"); if (S_CMPNOCASE(entry->getName(), name)) { mapentry = true; existing_map_lumps[a] = 1; break; } } } // If it wasn't a map entry, exit this loop if (!mapentry) { entry = entry->prevEntry(); break; } // If we've reached the end of the archive, exit this loop if (!entry->nextEntry()) break; // Go to next entry entry = entry->nextEntry(); } // Check for the required map entries for (unsigned a = 0; a < 5; a++) { if (existing_map_lumps[a] == 0) return mapdesc_t(); } // Setup map info map.head = maphead; map.end = entry; map.name = maphead->getName(); // If BEHAVIOR lump exists, it's a hexen format map if (existing_map_lumps[LUMP_BEHAVIOR]) map.format = MAP_HEXEN; // If LEAFS, LIGHTS and MACROS exist, it's a doom 64 format map else if (existing_map_lumps[LUMP_LEAFS] && existing_map_lumps[LUMP_LIGHTS] && existing_map_lumps[LUMP_MACROS]) map.format = MAP_DOOM64; // Otherwise it's doom format else map.format = MAP_DOOM; return map; }
/* LfdArchive::write * Writes the lfd archive to a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool LfdArchive::write(MemChunk& mc, bool update) { // Determine total size uint32_t dir_size = (numEntries() + 1)<<4; uint32_t total_size = dir_size; ArchiveEntry* entry = NULL; for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); total_size += 16; setEntryOffset(entry, total_size); if (update) { entry->setState(0); entry->exProp("Offset") = (int)total_size; } total_size += entry->getSize(); } // Clear/init MemChunk mc.clear(); mc.seek(0, SEEK_SET); mc.reSize(total_size); // Variables char type[5] = "RMAP"; char name[9] = "resource"; size_t size = wxINT32_SWAP_ON_BE(numEntries()<<4); // Write the resource map first mc.write(type, 4); mc.write(name, 8); mc.write(&size,4); for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); for (int t = 0; t < 5; ++t) type[t] = 0; for (int n = 0; n < 9; ++n) name[n] = 0; size = wxINT32_SWAP_ON_BE(entry->getSize()); wxFileName fn(entry->getName()); for (size_t c = 0; c < fn.GetName().length() && c < 9; c++) name[c] = fn.GetName()[c]; for (size_t c = 0; c < fn.GetExt().length() && c < 5; c++) type[c] = fn.GetExt()[c]; mc.write(type, 4); mc.write(name, 8); mc.write(&size,4); } // Write the lumps for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); for (int t = 0; t < 5; ++t) type[t] = 0; for (int n = 0; n < 9; ++n) name[n] = 0; size = wxINT32_SWAP_ON_BE(entry->getSize()); wxFileName fn(entry->getName()); for (size_t c = 0; c < fn.GetName().length() && c < 9; c++) name[c] = fn.GetName()[c]; for (size_t c = 0; c < fn.GetExt().length() && c < 5; c++) type[c] = fn.GetExt()[c]; mc.write(type, 4); mc.write(name, 8); mc.write(&size,4); mc.write(entry->getData(), entry->getSize()); } 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; }
/* EntryOperations::compileACS * Attempts to compile [entry] as an ACS script. If the entry is * named SCRIPTS, the compiled data is imported to the BEHAVIOR * entry previous to it, otherwise it is imported to a same-name * compiled library entry in the acs namespace *******************************************************************/ bool EntryOperations::compileACS(ArchiveEntry* entry, bool hexen, ArchiveEntry* target, wxFrame* parent) { // Check entry was given if (!entry) return false; // Check entry has a parent (this is useless otherwise) if (!target && !entry->getParent()) return false; // Check entry is text if (!EntryDataFormat::getFormat("text")->isThisFormat(entry->getMCData())) { wxMessageBox("Error: Entry does not appear to be text", "Error", wxOK|wxCENTRE|wxICON_ERROR); return false; } // Check if the ACC path is set up string accpath = path_acc; if (accpath.IsEmpty() || !wxFileExists(accpath)) { wxMessageBox("Error: ACC path not defined, please configure in SLADE preferences", "Error", wxOK|wxCENTRE|wxICON_ERROR); PreferencesDialog::openPreferences(parent, "ACS"); return false; } // Setup some path strings string srcfile = appPath(entry->getName(true) + ".acs", DIR_TEMP); string ofile = appPath(entry->getName(true) + ".o", DIR_TEMP); wxArrayString include_paths = wxSplit(path_acc_libs, ';'); // Setup command options string opt; if (hexen) opt += " -h"; if (!include_paths.IsEmpty()) { for (unsigned a = 0; a < include_paths.size(); a++) opt += S_FMT(" -i \"%s\"", include_paths[a]); } // Find/export any resource libraries Archive::search_options_t sopt; sopt.match_type = EntryType::getType("acs"); sopt.search_subdirs = true; vector<ArchiveEntry*> entries = theArchiveManager->findAllResourceEntries(sopt); wxArrayString lib_paths; for (unsigned a = 0; a < entries.size(); a++) { // Ignore SCRIPTS if (S_CMPNOCASE(entries[a]->getName(true), "SCRIPTS")) continue; // Ignore entries from other archives if (entry->getParent() && (entry->getParent()->getFilename(true) != entries[a]->getParent()->getFilename(true))) continue; string path = appPath(entries[a]->getName(true) + ".acs", DIR_TEMP); entries[a]->exportFile(path); lib_paths.Add(path); LOG_MESSAGE(2, "Exporting ACS library %s", entries[a]->getName()); } // Export script to file entry->exportFile(srcfile); // Execute acc string command = path_acc + " " + opt + " \"" + srcfile + "\" \"" + ofile + "\""; wxArrayString output; wxArrayString errout; theApp->SetTopWindow(parent); wxExecute(command, output, errout, wxEXEC_SYNC); theApp->SetTopWindow(theMainWindow); // Log output theConsole->logMessage("ACS compiler output:"); string output_log; if (!output.IsEmpty()) { const char *title1 = "=== Log: ===\n"; theConsole->logMessage(title1); output_log += title1; for (unsigned a = 0; a < output.size(); a++) { theConsole->logMessage(output[a]); output_log += output[a]; } } if (!errout.IsEmpty()) { const char *title2 = "\n=== Error log: ===\n"; theConsole->logMessage(title2); output_log += title2; for (unsigned a = 0; a < errout.size(); a++) { theConsole->logMessage(errout[a]); output_log += errout[a]; } } // Delete source file wxRemoveFile(srcfile); // Delete library files for (unsigned a = 0; a < lib_paths.size(); a++) wxRemoveFile(lib_paths[a]); // Check it compiled successfully if (wxFileExists(ofile)) { // If no target entry was given, find one if (!target) { // Check if the script is a map script (BEHAVIOR) if (S_CMPNOCASE(entry->getName(), "SCRIPTS")) { // Get entry before SCRIPTS ArchiveEntry* prev = entry->prevEntry(); // Create a new entry there if it isn't BEHAVIOR if (!prev || !(S_CMPNOCASE(prev->getName(), "BEHAVIOR"))) prev = entry->getParent()->addNewEntry("BEHAVIOR", entry->getParent()->entryIndex(entry)); // Import compiled script prev->importFile(ofile); } else { // Otherwise, treat it as a library // See if the compiled library already exists as an entry Archive::search_options_t opt; opt.match_namespace = "acs"; opt.match_name = entry->getName(true); if (entry->getParent()->getDesc().names_extensions) { opt.match_name += ".o"; opt.ignore_ext = false; } ArchiveEntry* lib = entry->getParent()->findLast(opt); // If it doesn't exist, create it if (!lib) lib = entry->getParent()->addEntry(new ArchiveEntry(entry->getName(true) + ".o"), "acs"); // Import compiled script lib->importFile(ofile); } } else target->importFile(ofile); // Delete compiled script file wxRemoveFile(ofile); } else { string errors; if (wxFileExists(appPath("acs.err", DIR_TEMP))) { // Read acs.err to string wxFile file(appPath("acs.err", DIR_TEMP)); char* buf = new char[file.Length()]; file.Read(buf, file.Length()); errors = wxString::From8BitData(buf, file.Length()); delete[] buf; } else errors = output_log; ExtMessageDialog dlg(NULL, "Error Compiling"); dlg.setMessage("The following errors were encountered while compiling, please fix them and recompile:"); dlg.setExt(errors); dlg.ShowModal(); return false; } 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; }
/* WadArchive::findLast * Returns the last entry matching the search criteria in [options], * or NULL if no matching entry was found *******************************************************************/ ArchiveEntry* WadArchive::findLast(search_options_t& options) { // Init search variables ArchiveEntry* start = getEntry(numEntries()-1); ArchiveEntry* end = NULL; options.match_name = options.match_name.Lower(); // "graphics" namespace is the global namespace in a wad if (options.match_namespace == "graphics") options.match_namespace = ""; // "global" namespace has no name, by the way if (options.match_namespace == "global") options.match_namespace = ""; // Check for namespace to search if (!options.match_namespace.IsEmpty()) { // Find matching namespace bool ns_found = false; for (unsigned a = 0; a < namespaces.size(); a++) { if (namespaces[a].name == options.match_namespace) { start = namespaces[a].end->prevEntry(); end = namespaces[a].start; ns_found = true; break; } } // Return none if namespace not found if (!ns_found) return NULL; } // Begin search ArchiveEntry* entry = start; while (entry != end) { // Check type if (options.match_type) { if (entry->getType() == EntryType::unknownType()) { if (!options.match_type->isThisType(entry)) { entry = entry->prevEntry(); continue; } } else if (options.match_type != entry->getType()) { entry = entry->prevEntry(); continue; } } // Check name if (!options.match_name.IsEmpty()) { if (!options.match_name.Matches(entry->getName().Lower())) { entry = entry->prevEntry(); continue; } } // Entry passed all checks so far, so we found a match return entry; } // No match found return NULL; }
bool loadIconsDir(int type, ArchiveTreeNode* dir) { if (!dir) return false; // Check for icon set dirs for (unsigned a = 0; a < dir->nChildren(); a++) { if (dir->getChild(a)->getName() != "large") { if (type == GENERAL) iconsets_general.push_back(dir->getChild(a)->getName()); else if (type == ENTRY) iconsets_entry.push_back(dir->getChild(a)->getName()); } } // Get icon set dir string icon_set_dir = "Default"; if (type == ENTRY) icon_set_dir = iconset_entry_list; if (type == GENERAL) icon_set_dir = iconset_general; if (icon_set_dir != "Default" && dir->getChild(icon_set_dir)) dir = (ArchiveTreeNode*)dir->getChild(icon_set_dir); vector<icon_t>& icons = iconList(type); string tempfile = appPath("sladetemp", DIR_TEMP); // Go through each entry in the directory for (size_t a = 0; a < dir->numEntries(false); a++) { ArchiveEntry* entry = dir->getEntry(a); // Ignore anything not png format if (!entry->getName().EndsWith("png")) continue; // Export entry data to a temporary file entry->exportFile(tempfile); // Create / setup icon icon_t n_icon; n_icon.image.LoadFile(tempfile); // Load image from temp file n_icon.name = entry->getName(true); // Set icon name n_icon.resource_entry = entry; // Add the icon icons.push_back(n_icon); // Delete the temporary file wxRemoveFile(tempfile); } // Go through large icons ArchiveTreeNode* dir_large = (ArchiveTreeNode*)dir->getChild("large"); if (dir_large) { for (size_t a = 0; a < dir_large->numEntries(false); a++) { ArchiveEntry* entry = dir_large->getEntry(a); // Ignore anything not png format if (!entry->getName().EndsWith("png")) continue; // Export entry data to a temporary file entry->exportFile(tempfile); // Create / setup icon bool found = false; string name = entry->getName(true); for (unsigned i = 0; i < icons.size(); i++) { if (icons[i].name == name) { icons[i].image_large.LoadFile(tempfile); found = true; break; } } if (!found) { icon_t n_icon; n_icon.image_large.LoadFile(tempfile); // Load image from temp file n_icon.name = entry->getName(true); // Set icon name n_icon.resource_entry = entry; // Add the icon icons.push_back(n_icon); } // Delete the temporary file wxRemoveFile(tempfile); } } return true; }
/* WadArchive::updateNamespaces * Updates the namespace list *******************************************************************/ void WadArchive::updateNamespaces() { // Clear current namespace info while (namespaces.size() > 0) namespaces.pop_back(); // Go through all entries for (unsigned a = 0; a < numEntries(); a++) { ArchiveEntry* entry = getRoot()->getEntry(a); // Check for namespace begin if (entry->getName().Matches("*_START")) { // Create new namespace wad_ns_pair_t ns(entry, NULL); string name = entry->getName(); ns.name = name.Left(name.Length() - 6).Lower(); ns.start_index = entryIndex(ns.start); // Convert some special cases (because technically PP_START->P_END is a valid namespace) if (ns.name == "pp") ns.name = "p"; if (ns.name == "ff") ns.name = "f"; if (ns.name == "ss") ns.name = "s"; if (ns.name == "tt") ns.name = "t"; // Add to namespace list namespaces.push_back(ns); } // Check for namespace end else if (entry->getName().Matches("?_END") || entry->getName().Matches("??_END")) { // Get namespace 'name' int len = entry->getName().Length() - 4; string ns_name = entry->getName().Left(len).Lower(); // Convert some special cases (because technically P_START->PP_END is a valid namespace) if (ns_name == "pp") ns_name = "p"; if (ns_name == "ff") ns_name = "f"; if (ns_name == "ss") ns_name = "s"; if (ns_name == "tt") ns_name = "t"; // Check if it's the end of an existing namespace // Remember entry is getEntry(a)? index is 'a' //size_t index = entryIndex(entry); bool found = false; for (unsigned b = 0; b < namespaces.size(); b++) { // Can't close a namespace that starts afterwards if (namespaces[b].start_index > a) break; // Can't close an already-closed namespace if (namespaces[b].end != NULL) continue; if (S_CMP(ns_name, namespaces[b].name)) { found = true; namespaces[b].end = entry; namespaces[b].end_index = a; break; } } // Flat hack: closing the flat namespace without opening it if (found == false && ns_name == "f") { wad_ns_pair_t ns(getRoot()->getEntry(0), entry); ns.start_index = 0; ns.end_index = a; ns.name = "f"; namespaces.push_back(ns); } } } // ROTT stuff. The first lump in the archive is always WALLSTRT, the last lump is either // LICENSE (darkwar.wad) or VENDOR (huntbgin.wad), with TABLES just before in both cases. // The shareware version has 2091 lumps, the complete version has about 50% more. if (numEntries() > 2090 && getRoot()->getEntry(0)->getName().Matches("WALLSTRT") && getRoot()->getEntry(numEntries()-2)->getName().Matches("TABLES")) { wad_ns_pair_t ns(getRoot()->getEntry(0), getRoot()->getEntry(numEntries()-1)); ns.name = "rott"; ns.start_index = 0; ns.end_index = entryIndex(ns.end); namespaces.push_back(ns); } // Check namespaces for (unsigned a = 0; a < namespaces.size(); a++) { wad_ns_pair_t& ns = namespaces[a]; // Check the namespace has an end if (!ns.end) { // If not, remove the namespace as it is invalid namespaces.erase(namespaces.begin() + a); a--; continue; } // Check namespace name for special cases for (int n = 0; n < n_special_namespaces; n++) { if (S_CMP(ns.name, special_namespaces[n].letter)) ns.name = special_namespaces[n].name; } ns.start_index = entryIndex(ns.start); ns.end_index = entryIndex(ns.end); // Testing //wxLogMessage("Namespace %s from %s (%d) to %s (%d)", ns.name, // ns.start->getName(), ns.start_index, ns.end->getName(), ns.end_index); } }
/* WadArchive::findAll * Returns all entries matching the search criteria in [options] *******************************************************************/ vector<ArchiveEntry*> WadArchive::findAll(search_options_t& options) { // Init search variables ArchiveEntry* start = getEntry(0); ArchiveEntry* end = NULL; options.match_name = options.match_name.Lower(); vector<ArchiveEntry*> ret; // "graphics" namespace is the global namespace in a wad if (options.match_namespace == "graphics") options.match_namespace = ""; // Check for namespace to search if (!options.match_namespace.IsEmpty()) { // Find matching namespace bool ns_found = false; for (unsigned a = 0; a < namespaces.size(); a++) { if (namespaces[a].name == options.match_namespace) { start = namespaces[a].start->nextEntry(); end = namespaces[a].end; ns_found = true; break; } } // Return none if namespace not found if (!ns_found) return ret; } ArchiveEntry* entry = start; while (entry != end) { // Check type if (options.match_type) { if (entry->getType() == EntryType::unknownType()) { if (!options.match_type->isThisType(entry)) { entry = entry->nextEntry(); continue; } } else if (options.match_type != entry->getType()) { entry = entry->nextEntry(); continue; } } // Check name if (!options.match_name.IsEmpty()) { // Force case insensitivity options.match_name.MakeLower(); if (!options.match_name.Matches(entry->getName().Lower())) { entry = entry->nextEntry(); continue; } } // Entry passed all checks so far, so we found a match ret.push_back(entry); entry = entry->nextEntry(); } // Return search result return ret; }
// ---------------------------------------------------------------------------- // ArchiveEntryList::applyFilter // // Applies the current filter(s) to the list // ---------------------------------------------------------------------------- void ArchiveEntryList::applyFilter() { // Clear current filter list items.clear(); // Check if any filters were given if (filter_text.IsEmpty() && filter_category.IsEmpty()) { // No filter, just refresh the list unsigned count = current_dir->numEntries() + current_dir->nChildren(); for (unsigned a = 0; a < count; a++) items.push_back(a); updateList(); return; } // Filter by category unsigned index = 0; ArchiveEntry* entry = getEntry(index, false); while (entry) { if (filter_category.IsEmpty() || entry->getType() == EntryType::folderType()) items.push_back(index); // If no category specified, just add all entries to the filter else { // Check for category match if (S_CMPNOCASE(entry->getType()->getCategory(), filter_category)) items.push_back(index); } entry = getEntry(++index, false); } // Now filter by name if needed if (!filter_text.IsEmpty()) { // Split filter by , wxArrayString terms = wxSplit(filter_text, ','); // Process filter strings for (unsigned a = 0; a < terms.size(); a++) { // Remove spaces terms[a].Replace(" ", ""); // Set to lowercase and add * to the end if (!terms[a].IsEmpty()) terms[a] = terms[a].Lower() + "*"; } // Go through filtered list for (unsigned a = 0; a < items.size(); a++) { entry = getEntry(items[a], false); // Don't filter folders if !elist_filter_dirs if (!elist_filter_dirs && entry->getType() == EntryType::folderType()) continue; // Check for name match with filter bool match = false; for (unsigned b = 0; b < terms.size(); b++) { if (entry == entry_dir_back || entry->getName().Lower().Matches(terms[b])) { match = true; continue; } } if (match) continue; // No match, remove from filtered list items.erase(items.begin() + a); a--; } } // Update the list updateList(); }
/* WadArchive::open * Reads wad format data from a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool WadArchive::open(MemChunk& mc) { // Check data was given if (!mc.hasData()) return false; // Read wad header uint32_t num_lumps = 0; uint32_t dir_offset = 0; char wad_type[4] = ""; mc.seek(0, SEEK_SET); mc.read(&wad_type, 4); // Wad type mc.read(&num_lumps, 4); // No. of lumps in wad mc.read(&dir_offset, 4); // Offset to directory // Byteswap values for big endian if needed num_lumps = wxINT32_SWAP_ON_BE(num_lumps); dir_offset = wxINT32_SWAP_ON_BE(dir_offset); // Check the header if (wad_type[1] != 'W' || wad_type[2] != 'A' || wad_type[3] != 'D') { wxLogMessage("WadArchive::openFile: File %s has invalid header", filename); Global::error = "Invalid wad header"; return false; } // Check for iwad if (wad_type[0] == 'I') iwad = true; // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); vector<uint32_t> offsets; // Read the directory mc.seek(dir_offset, SEEK_SET); theSplashWindow->setProgressMessage("Reading wad archive data"); for (uint32_t d = 0; d < num_lumps; d++) { // Update splash window progress theSplashWindow->setProgress(((float)d / (float)num_lumps)); // Read lump info char name[9] = ""; uint32_t offset = 0; uint32_t size = 0; mc.read(&offset, 4); // Offset mc.read(&size, 4); // Size mc.read(name, 8); // Name name[8] = '\0'; // Byteswap values for big endian if needed offset = wxINT32_SWAP_ON_BE(offset); size = wxINT32_SWAP_ON_BE(size); // Check to catch stupid shit if (size > 0) { if (offset == 0) { LOG_MESSAGE(2, "No."); continue; } if (VECTOR_EXISTS(offsets, offset)) { LOG_MESSAGE(1, "Ignoring entry %d: %s, is a clone of a previous entry", d, name); continue; } offsets.push_back(offset); } // Hack to open Operation: Rheingold WAD files if (size == 0 && offset > mc.getSize()) offset = 0; // Is there a compression/encryption thing going on? bool jaguarencrypt = !!(name[0] & 0x80); // look at high bit name[0] = name[0] & 0x7F; // then strip it away // Look for encryption shenanigans size_t actualsize = size; if (jaguarencrypt) { if (d < num_lumps - 1) { size_t pos = mc.currentPos(); uint32_t nextoffset = 0; for (int i = 0; i + d < num_lumps; ++i) { mc.read(&nextoffset, 4); if (nextoffset != 0) break; mc.seek(12, SEEK_CUR); } nextoffset = wxINT32_SWAP_ON_BE(nextoffset); if (nextoffset == 0) nextoffset = dir_offset; mc.seek(pos, SEEK_SET); actualsize = nextoffset - offset; } else { if (offset > dir_offset) { actualsize = mc.getSize() - offset; } else { actualsize = dir_offset - offset; } } } // If the lump data goes past the end of the file, // the wadfile is invalid if (offset + actualsize > mc.getSize()) { wxLogMessage("WadArchive::open: Wad archive is invalid or corrupt"); Global::error = S_FMT("Archive is invalid and/or corrupt (lump %d: %s data goes past end of file)", d, name); 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); if (jaguarencrypt) { nlump->setEncryption(ENC_JAGUAR); nlump->exProp("FullSize") = (int)size; } // Add to entry list getRoot()->addEntry(nlump); } // Detect namespaces (needs to be done before type detection as some types // rely on being within certain namespaces) updateNamespaces(); // Detect all entry types MemChunk edata; theSplashWindow->setProgressMessage("Detecting entry types"); for (size_t a = 0; a < numEntries(); a++) { // Update splash window progress theSplashWindow->setProgress((((float)a / (float)numEntries()))); // Get entry ArchiveEntry* entry = getEntry(a); // Read entry data if it isn't zero-sized if (entry->getSize() > 0) { // Read the entry data mc.exportMemChunk(edata, getEntryOffset(entry), entry->getSize()); if (entry->isEncrypted()) { if (entry->exProps().propertyExists("FullSize") && (unsigned)(int)(entry->exProp("FullSize")) > entry->getSize()) edata.reSize((int)(entry->exProp("FullSize")), true); if (!JaguarDecode(edata)) wxLogMessage("%i: %s (following %s), did not decode properly", a, entry->getName(), a>0?getEntry(a-1)->getName():"nothing"); } 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); } // Identify #included lumps (DECORATE, GLDEFS, etc.) detectIncludes(); // Detect maps (will detect map entry types) theSplashWindow->setProgressMessage("Detecting maps"); detectMaps(); // Setup variables setMuted(false); setModified(false); //if (iwad && iwad_lock) read_only = true; announce("opened"); theSplashWindow->setProgressMessage(""); return true; }
/* WadArchive::write * Writes the wad archive to a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool WadArchive::write(MemChunk& mc, bool update) { // Don't write if iwad if (iwad && iwad_lock) { Global::error = "IWAD saving disabled"; return false; } // Determine directory offset & individual lump offsets uint32_t dir_offset = 12; ArchiveEntry* entry = NULL; for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); setEntryOffset(entry, dir_offset); dir_offset += entry->getSize(); } // Clear/init MemChunk mc.clear(); mc.seek(0, SEEK_SET); mc.reSize(dir_offset + numEntries() * 16); // Setup wad type char wad_type[4] = { 'P', 'W', 'A', 'D' }; if (iwad) wad_type[0] = 'I'; // Write the header uint32_t num_lumps = numEntries(); mc.write(wad_type, 4); mc.write(&num_lumps, 4); mc.write(&dir_offset, 4); // Write the lumps for (uint32_t l = 0; l < num_lumps; l++) { entry = getEntry(l); mc.write(entry->getData(), entry->getSize()); } // Write the directory for (uint32_t l = 0; l < num_lumps; l++) { entry = getEntry(l); char name[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; long offset = getEntryOffset(entry); long size = entry->getSize(); for (size_t c = 0; c < entry->getName().length() && c < 8; c++) name[c] = entry->getName()[c]; mc.write(&offset, 4); mc.write(&size, 4); mc.write(name, 8); if (update) { entry->setState(0); entry->exProp("Offset") = (int)offset; } } return true; }
/* Archive::findAll * Returns a list of entries matching the search criteria in * [options] *******************************************************************/ vector<ArchiveEntry*> Archive::findAll(search_options_t& options) { // Init search variables ArchiveTreeNode* dir = options.dir; if (!dir) dir = dir_root; vector<ArchiveEntry*> ret; options.match_name.MakeLower(); // Force case-insensitive // Begin search // Search entries for (unsigned a = 0; a < dir->numEntries(); a++) { ArchiveEntry* entry = dir->getEntry(a); // Check type if (options.match_type) { if (entry->getType() == EntryType::unknownType()) { if (!options.match_type->isThisType(entry)) continue; } else if (options.match_type != entry->getType()) continue; } // Check name if (!options.match_name.IsEmpty()) { // Cut extension if ignoring wxFileName fn(entry->getName()); if (options.ignore_ext) { if (!fn.GetName().MakeLower().Matches(options.match_name)) continue; } else if (!fn.GetFullName().MakeLower().Matches(options.match_name)) continue; } // Check namespace if (!options.match_namespace.IsEmpty()) { if (!(S_CMPNOCASE(detectNamespace(entry), options.match_namespace))) continue; } // Entry passed all checks so far, so we found a match ret.push_back(entry); } // Search subdirectories (if needed) if (options.search_subdirs) { for (unsigned a = 0; a < dir->nChildren(); a++) { search_options_t opt = options; opt.dir = (ArchiveTreeNode*)dir->getChild(a); // Add any matches to the list vector<ArchiveEntry*> vec = findAll(opt); ret.insert(ret.end(), vec.begin(), vec.end()); } } // Return matches return ret; }
/* DatArchive::write * Writes the dat archive to a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool DatArchive::write(MemChunk& mc, bool update) { // Only two bytes are used for storing entry amount, // so abort for excessively large files: if (numEntries() > 65535) return false; // Determine directory offset, name offsets & individual lump offsets uint32_t dir_offset = 10; uint16_t name_offset = numEntries() * 12; uint32_t name_size = 0; string previousname = ""; uint16_t* nameoffsets = new uint16_t[numEntries()]; ArchiveEntry* entry = NULL; for (uint16_t l = 0; l < numEntries(); l++) { entry = getEntry(l); setEntryOffset(entry, dir_offset); dir_offset += entry->getSize(); // Does the entry has a name? string name = entry->getName(); if (l > 0 && previousname.length() > 0 && name.length() > previousname.length() && !previousname.compare(0, previousname.length(), name, 0, previousname.length()) && name.at(previousname.length()) == '+') { // This is a fake name name = ""; nameoffsets[l] = 0; } else { // This is a true name previousname = name; nameoffsets[l] = uint16_t(name_offset + name_size); name_size += name.length() + 1; } } // Clear/init MemChunk mc.clear(); mc.seek(0, SEEK_SET); mc.reSize(dir_offset + name_size + numEntries() * 12); // Write the header uint16_t num_lumps = wxINT16_SWAP_ON_BE(numEntries()); dir_offset = wxINT32_SWAP_ON_BE(dir_offset); uint32_t unknown = 0; mc.write(&num_lumps, 2); mc.write(&dir_offset, 4); mc.write(&unknown, 4); // Write the lumps for (uint16_t l = 0; l < numEntries(); l++) { entry = getEntry(l); mc.write(entry->getData(), entry->getSize()); } // Write the directory for (uint16_t l = 0; l < num_lumps; l++) { entry = getEntry(l); uint32_t offset = wxINT32_SWAP_ON_BE(getEntryOffset(entry)); uint32_t size = wxINT32_SWAP_ON_BE(entry->getSize()); uint16_t nameofs = wxINT16_SWAP_ON_BE(nameoffsets[l]); uint16_t flags = wxINT16_SWAP_ON_BE((entry->isEncrypted() == ENC_SCRLE0) ? 1 : 0); mc.write(&offset, 4); // Offset mc.write(&size, 4); // Size mc.write(&nameofs, 2); // Name offset mc.write(&flags, 2); // Flags if (update) { entry->setState(0); entry->exProp("Offset") = (int)wxINT32_SWAP_ON_BE(offset); } } // Write the names for (uint16_t l = 0; l < num_lumps; l++) { uint8_t zero = 0; entry = getEntry(l); if (nameoffsets[l]) { mc.write(CHR(entry->getName()), entry->getName().length()); mc.write(&zero, 1); } } // Clean-up delete[] nameoffsets; // Finished! return true; }
/* DatArchive::open * Reads wad format data from a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool DatArchive::open(MemChunk& mc) { // Check data was given if (!mc.hasData()) return false; const uint8_t* mcdata = mc.getData(); // Read dat header mc.seek(0, SEEK_SET); uint16_t num_lumps; uint32_t dir_offset, unknown; mc.read(&num_lumps, 2); // Size mc.read(&dir_offset, 4); // Directory offset mc.read(&unknown, 4); // Unknown value num_lumps = wxINT16_SWAP_ON_BE(num_lumps); dir_offset = wxINT32_SWAP_ON_BE(dir_offset); unknown = wxINT32_SWAP_ON_BE(unknown); string lastname(wxString::FromAscii("-noname-")); size_t namecount = 0; // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); // Read the directory mc.seek(dir_offset, SEEK_SET); theSplashWindow->setProgressMessage("Reading dat archive data"); for (uint32_t d = 0; d < num_lumps; d++) { // Update splash window progress theSplashWindow->setProgress(((float)d / (float)num_lumps)); // Read lump info uint32_t offset = 0; uint32_t size = 0; uint16_t nameofs = 0; uint16_t flags = 0; mc.read(&offset, 4); // Offset mc.read(&size, 4); // Size mc.read(&nameofs, 2); // Name offset mc.read(&flags, 2); // Flags (only one: RLE encoded) // Byteswap values for big endian if needed offset = wxINT32_SWAP_ON_BE(offset); size = wxINT32_SWAP_ON_BE(size); nameofs = wxINT16_SWAP_ON_BE(nameofs); flags = wxINT16_SWAP_ON_BE(flags); // If the lump data goes past the directory, // the data file is invalid if (offset + size > mc.getSize()) { wxLogMessage("DatArchive::open: Dat archive is invalid or corrupt at entry %i", d); Global::error = "Archive is invalid and/or corrupt"; setMuted(false); return false; } string myname; if (nameofs != 0) { size_t len = 1; size_t start = nameofs+dir_offset; for (size_t i = start; mcdata[i] != 0; ++i) { ++len; } lastname = myname = wxString::FromAscii(mcdata+start, len); namecount = 0; } else { myname = S_FMT("%s+%d", lastname, ++namecount); } // Create & setup lump ArchiveEntry* nlump = new ArchiveEntry(myname, size); nlump->setLoaded(false); nlump->exProp("Offset") = (int)offset; nlump->setState(0); if (flags & 1) nlump->setEncryption(ENC_SCRLE0); // Check for markers if (!nlump->getName().Cmp("startflats")) flats[0] = d; if (!nlump->getName().Cmp("endflats")) flats[1] = d; if (!nlump->getName().Cmp("startsprites")) sprites[0] = d; if (!nlump->getName().Cmp("endmonsters")) sprites[1] = d; if (!nlump->getName().Cmp("startwalls")) walls[0] = d; if (!nlump->getName().Cmp("endwalls")) walls[1] = d; // Add to entry list getRoot()->addEntry(nlump); } // Detect all entry types MemChunk edata; theSplashWindow->setProgressMessage("Detecting entry types"); for (size_t a = 0; a < numEntries(); a++) { // Update splash window progress theSplashWindow->setProgress((((float)a / (float)num_lumps))); // Get entry ArchiveEntry* entry = getEntry(a); // Read entry data if it isn't zero-sized if (entry->getSize() > 0) { // Read the entry data mc.exportMemChunk(edata, getEntryOffset(entry), entry->getSize()); entry->importMemChunk(edata); } // Detect entry type EntryType::detectEntryType(entry); // Set entry to unchanged entry->setState(0); } // Detect maps (will detect map entry types) //theSplashWindow->setProgressMessage("Detecting maps"); //detectMaps(); // Setup variables setMuted(false); setModified(false); announce("opened"); theSplashWindow->setProgressMessage(""); return true; }
/* WadArchive::detectMaps * Searches for any maps in the wad and adds them to the map list *******************************************************************/ vector<Archive::mapdesc_t> WadArchive::detectMaps() { vector<mapdesc_t> maps; // Go through all lumps ArchiveEntry* entry = getEntry(0); bool lastentryismapentry = false; while (entry) { // UDMF format map check ******************************************************** // Check for UDMF format map lump (TEXTMAP lump) if (entry->getName() == "TEXTMAP" && entry->prevEntry()) { // Get map info mapdesc_t md = getMapInfo(entry->prevEntry()); // Add to map list if (md.head != NULL) { entry = md.end; maps.push_back(md); } // Current index is ENDMAP, we don't want to check for a doom/hexen format // map so just go to the next index and continue the loop entry = entry->nextEntry(); continue; } // Doom/Hexen format map check ************************************************** // TODO maybe get rid of code duplication by calling getMapInfo() here too? // Array to keep track of what doom/hexen map lumps have been found uint8_t existing_map_lumps[NUMMAPLUMPS]; memset(existing_map_lumps, 0, NUMMAPLUMPS); // Check if the current lump is a doom/hexen map lump bool maplump_found = false; for (int a = 0; a < 5; a++) { // Compare with all base map lump names if (S_CMP(entry->getName(), map_lumps[a])) { maplump_found = true; existing_map_lumps[a] = 1; break; } } // If we've found what might be a map if (maplump_found && entry->prevEntry()) { // Save map header entry ArchiveEntry* header_entry = entry->prevEntry(); // Check off map lumps until we find a non-map lump bool done = false; while (!done) { // Loop will end if no map lump is found done = true; // Compare with all map lump names for (int a = 0; a < NUMMAPLUMPS; a++) { // Compare with all base map lump names if (S_CMP(entry->getName(), map_lumps[a])) { existing_map_lumps[a] = 1; done = false; break; } } // If we're at the end of the wad, exit the loop if (!entry->nextEntry()) { lastentryismapentry = true; break; } // Go to next lump if there is one if (!lastentryismapentry) entry = entry->nextEntry(); } // Go back to the lump just after the last map lump found, but only if we actually moved if (!lastentryismapentry) entry = entry->prevEntry(); // Check that we have all the required map lumps: VERTEXES, LINEDEFS, SIDEDEFS, THINGS & SECTORS if (!memchr(existing_map_lumps, 0, 5)) { // Get map info mapdesc_t md; md.head = header_entry; // Header lump md.name = header_entry->getName(); // Map title md.end = lastentryismapentry ? // End lump entry : entry->prevEntry(); // If BEHAVIOR lump exists, it's a hexen format map if (existing_map_lumps[LUMP_BEHAVIOR]) md.format = MAP_HEXEN; // If LEAFS, LIGHTS and MACROS exist, it's a doom 64 format map else if (existing_map_lumps[LUMP_LEAFS] && existing_map_lumps[LUMP_LIGHTS] && existing_map_lumps[LUMP_MACROS]) md.format = MAP_DOOM64; // Otherwise it's doom format else md.format = MAP_DOOM; // Add map info to the maps list maps.push_back(md); } } // Embedded WAD check (for Doom 64) if (entry->getType()->getFormat() == "archive_wad") { // Detect map format (probably kinda slow but whatever, no better way to do it really) Archive* tempwad = new WadArchive(); tempwad->open(entry); vector<mapdesc_t> emaps = tempwad->detectMaps(); if (emaps.size() > 0) { mapdesc_t md; md.head = entry; md.end = entry; md.archive = true; md.name = entry->getName(true).Upper(); md.format = emaps[0].format; maps.push_back(md); } delete tempwad; entry->unlock(); } // Not a UDMF or Doom/Hexen map lump, go to next lump entry = entry->nextEntry(); } // Set all map header entries to ETYPE_MAP type for (size_t a = 0; a < maps.size(); a++) if (!maps[a].archive) maps[a].head->setType(EntryType::mapMarkerType()); // Update entry map format hints for (unsigned a = 0; a < maps.size(); a++) { string format; if (maps[a].format == MAP_DOOM) format = "doom"; else if (maps[a].format == MAP_DOOM64) format = "doom64"; else if (maps[a].format == MAP_HEXEN) format = "hexen"; else format = "udmf"; ArchiveEntry* entry = maps[a].head; while (entry && entry != maps[a].end->nextEntry()) { entry->exProp("MapFormat") = format; entry = entry->nextEntry(); } } return maps; }