// ---------------------------------------------------------------------------- // 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::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; }
// ---------------------------------------------------------------------------- // 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(); }
/* 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; }
// ---------------------------------------------------------------------------- // ArchiveEntryList::updateItemAttr // // Called when widget requests the attributes // (text colour / background colour / font) for [item] // ---------------------------------------------------------------------------- void ArchiveEntryList::updateItemAttr(long item, long column, long index) const { // Get associated entry ArchiveEntry* entry = getEntry(item); // Init attributes wxColour col_bg = wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX); item_attr->SetTextColour(WXCOL(ColourConfiguration::getColour("error"))); item_attr->SetBackgroundColour(col_bg); // If entry doesn't exist, return error colour if (!entry) return; // Set font if (elist_name_monospace && !list_font_monospace) item_attr->SetFont((column == 0) ? *font_monospace : *font_normal); else item_attr->SetFont(list_font_monospace ? *font_monospace : *font_normal); // Set background colour defined in entry type (if any) rgba_t col = entry->getType()->getColour(); if ((col.r != 255 || col.g != 255 || col.b != 255) && elist_type_bgcol) { rgba_t bcol; bcol.r = (col.r * elist_type_bgcol_intensity) + (col_bg.Red() * (1.0 - elist_type_bgcol_intensity)); bcol.g = (col.g * elist_type_bgcol_intensity) + (col_bg.Green() * (1.0 - elist_type_bgcol_intensity)); bcol.b = (col.b * elist_type_bgcol_intensity) + (col_bg.Blue() * (1.0 - elist_type_bgcol_intensity)); item_attr->SetBackgroundColour(WXCOL(bcol)); } // Alternating row colour if (elist_alt_row_colour && item % 2 > 0) { wxColour dark = item_attr->GetBackgroundColour().ChangeLightness(95); item_attr->SetBackgroundColour(dark); } // Set colour depending on entry state switch (entry->getState()) { case 1: item_attr->SetTextColour(WXCOL(ColourConfiguration::getColour("modified"))); break; case 2: item_attr->SetTextColour(WXCOL(ColourConfiguration::getColour("new"))); break; default: item_attr->SetTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOXTEXT)); break; }; // Locked state overrides others if (entry->isLocked()) item_attr->SetTextColour(WXCOL(ColourConfiguration::getColour("locked"))); }
// ---------------------------------------------------------------------------- // 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(); }
// ---------------------------------------------------------------------------- // ArchiveEntryList::getItemIcon // // Called when the widget requests the icon for [item] // ---------------------------------------------------------------------------- int ArchiveEntryList::getItemIcon(long item, long column, long index) const { if (column > 0) return -1; // Get associated entry ArchiveEntry* entry = getEntry(item); // If entry doesn't exist, return invalid image if (!entry) return -1; return entry->getType()->getIndex(); }
/* 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; }
/* 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; }
// ---------------------------------------------------------------------------- // ArchiveEntryList::getSelectedEntries // // Returns a vector of all selected archive entries // ---------------------------------------------------------------------------- vector<ArchiveEntry*> ArchiveEntryList::getSelectedEntries() { // Init vector vector<ArchiveEntry*> ret; // Return empty if no archive open if (!archive) return ret; // Get selection vector<long> selection = getSelection(); // Go through selection and add associated entries to the return vector ArchiveEntry* entry = nullptr; for (size_t a = 0; a < selection.size(); a++) { entry = getEntry(selection[a]); if (entry && entry->getType() != EntryType::folderType()) ret.push_back(entry); } return ret; }
/* TextureXPanel::newTextureFromFile * Creates a new texture from an image file. The file will be * imported and added to the patch table if needed *******************************************************************/ void TextureXPanel::newTextureFromFile() { // Get all entry types vector<EntryType*> etypes = EntryType::allTypes(); // Go through types string ext_filter = "All files (*.*)|*.*|"; for (unsigned a = 0; a < etypes.size(); a++) { // If the type is a valid image type, add its extension filter if (etypes[a]->extraProps().propertyExists("image")) { ext_filter += etypes[a]->getFileFilterString(); ext_filter += "|"; } } // Create open file dialog wxFileDialog dialog_open(this, "Choose file(s) to open", dir_last, wxEmptyString, ext_filter, wxFD_OPEN|wxFD_MULTIPLE|wxFD_FILE_MUST_EXIST, wxDefaultPosition); // Run the dialog & check that the user didn't cancel if (dialog_open.ShowModal() == wxID_OK) { // Get file selection wxArrayString files; dialog_open.GetPaths(files); // Save 'dir_last' dir_last = dialog_open.GetDirectory(); // Go through file selection for (unsigned a = 0; a < files.size(); a++) { // Load the file into a temporary ArchiveEntry ArchiveEntry* entry = new ArchiveEntry(); entry->importFile(files[a]); // Determine type EntryType::detectEntryType(entry); // If it's not a valid image type, ignore this file if (!entry->getType()->extraProps().propertyExists("image")) { wxLogMessage("%s is not a valid image file", files[a]); continue; } // Ask for name for texture wxFileName fn(files[a]); string name = fn.GetName().Upper().Truncate(8); name = wxGetTextFromUser(S_FMT("Enter a texture name for %s:", fn.GetFullName()), "New Texture", name); name = name.Truncate(8); // Add patch to archive entry->setName(name); entry->setExtensionByType(); tx_entry->getParent()->addEntry(entry, "patches"); // Add patch to patch table if needed if (texturex.getFormat() != TXF_TEXTURES) tx_editor->patchTable().addPatch(name); // Create new texture from patch CTexture* tex = newTextureFromPatch(name, name); // Add texture after the last selected item int selected = list_textures->getLastSelected(); if (selected == -1) selected = texturex.nTextures() - 1; // Add to end of the list if nothing selected texturex.addTexture(tex, selected + 1); // Record undo level undo_manager->beginRecord("New Texture from File"); undo_manager->recordUndoStep(new TextureCreateDeleteUS(this, tex, true)); undo_manager->endRecord(true); // Update texture list list_textures->updateList(); // Select the new texture list_textures->clearSelection(); list_textures->selectItem(selected + 1); list_textures->EnsureVisible(selected + 1); // Update patch table counts tx_editor->patchTable().updatePatchUsage(tex); } } }
/* PatchTablePanel::onBtnPatchFromFile * Called when the 'New Patch from File' button is clicked *******************************************************************/ void PatchTablePanel::onBtnPatchFromFile(wxCommandEvent& e) { // Get all entry types vector<EntryType*> etypes = EntryType::allTypes(); // Go through types string ext_filter = "All files (*.*)|*.*|"; for (unsigned a = 0; a < etypes.size(); a++) { // If the type is a valid image type, add its extension filter if (etypes[a]->extraProps().propertyExists("image")) { ext_filter += etypes[a]->getFileFilterString(); ext_filter += "|"; } } // Create open file dialog wxFileDialog dialog_open(this, "Choose file(s) to open", dir_last, wxEmptyString, ext_filter, wxFD_OPEN|wxFD_MULTIPLE|wxFD_FILE_MUST_EXIST, wxDefaultPosition); // Run the dialog & check that the user didn't cancel if (dialog_open.ShowModal() == wxID_OK) { // Get file selection wxArrayString files; dialog_open.GetPaths(files); // Save 'dir_last' dir_last = dialog_open.GetDirectory(); // Go through file selection for (unsigned a = 0; a < files.size(); a++) { // Load the file into a temporary ArchiveEntry ArchiveEntry* entry = new ArchiveEntry(); entry->importFile(files[a]); // Determine type EntryType::detectEntryType(entry); // If it's not a valid image type, ignore this file if (!entry->getType()->extraProps().propertyExists("image")) { wxLogMessage("%s is not a valid image file", files[a]); continue; } // Ask for name for patch wxFileName fn(files[a]); string name = fn.GetName().Upper().Truncate(8); name = wxGetTextFromUser(S_FMT("Enter a patch name for %s:", fn.GetFullName()), "New Patch", name); name = name.Truncate(8); // Add patch to archive entry->setName(name); entry->setExtensionByType(); parent->getArchive()->addEntry(entry, "patches"); // Add patch to patch table patch_table->addPatch(name); } // Refresh patch list list_patches->updateList(); parent->pnamesModified(true); } }
/* Archive::findLast * Returns the last entry matching the search criteria in [options], * or NULL if no matching entry was found *******************************************************************/ ArchiveEntry* Archive::findLast(search_options_t& options) { // Init search variables ArchiveTreeNode* dir = options.dir; if (!dir) dir = dir_root; options.match_name.MakeLower(); // Force case-insensitive // Begin search // Search subdirectories (if needed) (bottom-up) if (options.search_subdirs) { for (int a = dir->nChildren() - 1; a >= 0; a--) { search_options_t opt = options; opt.dir = (ArchiveTreeNode*)dir->getChild(a); ArchiveEntry* match = findLast(opt); // If a match was found in this subdir, return it if (match) return match; } } // Search entries (bottom-up) for (int a = dir->numEntries() - 1; a >= 0; 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 (!options.match_name.Matches(fn.GetName().MakeLower())) continue; } else if (!options.match_name.Matches(fn.GetFullName().MakeLower())) 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 return entry; } // No matches found return NULL; }
// ---------------------------------------------------------------------------- // 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(); }
/* MapPreviewCanvas::openMap * Opens a map from a mapdesc_t *******************************************************************/ bool MapPreviewCanvas::openMap(Archive::mapdesc_t map) { // All errors = invalid map Global::error = "Invalid map"; // Check if this map is a pk3 map bool map_archive = false; if (map.archive) { map_archive = true; // Attempt to open entry as wad archive temp_archive = new WadArchive(); if (!temp_archive->open(map.head)) { delete temp_archive; return false; } // Detect maps vector<Archive::mapdesc_t> maps = temp_archive->detectMaps(); // Set map if there are any in the archive if (maps.size() > 0) map = maps[0]; else return false; } // Parse UDMF map if (map.format == MAP_UDMF) { ArchiveEntry* udmfdata = NULL; for (ArchiveEntry* mapentry = map.head; mapentry != map.end; mapentry = mapentry->nextEntry()) { // Check entry type if (mapentry->getType() == EntryType::getType("udmf_textmap")) { udmfdata = mapentry; break; } } if (udmfdata == NULL) return false; // Start parsing Tokenizer tz; tz.openMem(udmfdata->getData(), udmfdata->getSize(), map.head->getName()); // Get first token string token = tz.getToken(); size_t vertcounter = 0, linecounter = 0; while (!token.IsEmpty()) { if (!token.CmpNoCase("namespace")) { // skip till we reach the ';' do { token = tz.getToken(); } while (token.Cmp(";")); } else if (!token.CmpNoCase("vertex")) { // Get X and Y properties bool gotx = false; bool goty = false; double x = 0.; double y = 0.; do { token = tz.getToken(); if (!token.CmpNoCase("x") || !token.CmpNoCase("y")) { bool isx = !token.CmpNoCase("x"); token = tz.getToken(); if (token.Cmp("=")) { wxLogMessage("Bad syntax for vertex %i in UDMF map data", vertcounter); return false; } if (isx) x = tz.getDouble(), gotx = true; else y = tz.getDouble(), goty = true; // skip to end of declaration after each key do { token = tz.getToken(); } while (token.Cmp(";")); } } while (token.Cmp("}")); if (gotx && goty) addVertex(x, y); else { wxLogMessage("Wrong vertex %i in UDMF map data", vertcounter); return false; } vertcounter++; } else if (!token.CmpNoCase("linedef")) { bool special = false; bool twosided = false; bool gotv1 = false, gotv2 = false; size_t v1 = 0, v2 = 0; do { token = tz.getToken(); if (!token.CmpNoCase("v1") || !token.CmpNoCase("v2")) { bool isv1 = !token.CmpNoCase("v1"); token = tz.getToken(); if (token.Cmp("=")) { wxLogMessage("Bad syntax for linedef %i in UDMF map data", linecounter); return false; } if (isv1) v1 = tz.getInteger(), gotv1 = true; else v2 = tz.getInteger(), gotv2 = true; // skip to end of declaration after each key do { token = tz.getToken(); } while (token.Cmp(";")); } else if (!token.CmpNoCase("special")) { special = true; // skip to end of declaration after each key do { token = tz.getToken(); } while (token.Cmp(";")); } else if (!token.CmpNoCase("sideback")) { twosided = true; // skip to end of declaration after each key do { token = tz.getToken(); } while (token.Cmp(";")); } } while (token.Cmp("}")); if (gotv1 && gotv2) addLine(v1, v2, twosided, special); else { wxLogMessage("Wrong line %i in UDMF map data", linecounter); return false; } linecounter++; } else { // map preview ignores things, sidedefs, sectors, comments, // unknown fields, etc. so skip to end of block do { token = tz.getToken(); } while (token.Cmp("}")); } // Iterate to next token token = tz.getToken(); } } // Read vertices if (map.format == MAP_DOOM || map.format == MAP_HEXEN || map.format == MAP_DOOM64) { // Find VERTEXES entry ArchiveEntry* mapentry = map.head; ArchiveEntry* vertexes = NULL; while (mapentry) { // Check entry type if (mapentry->getType() == EntryType::getType("map_vertexes")) { vertexes = mapentry; break; } // Exit loop if we've reached the end of the map entries if (mapentry == map.end) break; else mapentry = mapentry->nextEntry(); } // Can't open a map without vertices if (!vertexes) return false; // Read vertex data MemChunk& mc = vertexes->getMCData(); mc.seek(0, SEEK_SET); if (map.format == MAP_DOOM64) { doom64vertex_t v; while (1) { // Read vertex if (!mc.read(&v, 8)) break; // Add vertex addVertex((double)v.x/65536, (double)v.y/65536); } } else { doomvertex_t v; while (1) { // Read vertex if (!mc.read(&v, 4)) break; // Add vertex addVertex((double)v.x, (double)v.y); } } } // Read linedefs if (map.format == MAP_DOOM || map.format == MAP_HEXEN || map.format == MAP_DOOM64) { // Find LINEDEFS entry ArchiveEntry* mapentry = map.head; ArchiveEntry* linedefs = NULL; while (mapentry) { // Check entry type if (mapentry->getType() == EntryType::getType("map_linedefs")) { linedefs = mapentry; break; } // Exit loop if we've reached the end of the map entries if (mapentry == map.end) break; else mapentry = mapentry->nextEntry(); } // Can't open a map without linedefs if (!linedefs) return false; // Read line data MemChunk& mc = linedefs->getMCData(); mc.seek(0, SEEK_SET); if (map.format == MAP_DOOM) { while (1) { // Read line doomline_t l; if (!mc.read(&l, sizeof(doomline_t))) break; // Check properties bool special = false; bool twosided = false; if (l.side2 != 0xFFFF) twosided = true; if (l.type > 0) special = true; // Add line addLine(l.vertex1, l.vertex2, twosided, special); } } else if (map.format == MAP_DOOM64) { while (1) { // Read line doom64line_t l; if (!mc.read(&l, sizeof(doom64line_t))) break; // Check properties bool macro = false; bool special = false; bool twosided = false; if (l.side2 != 0xFFFF) twosided = true; if (l.type > 0) { if (l.type & 0x100) macro = true; else special = true; } // Add line addLine(l.vertex1, l.vertex2, twosided, special, macro); } } else if (map.format == MAP_HEXEN) { while (1) { // Read line hexenline_t l; if (!mc.read(&l, sizeof(hexenline_t))) break; // Check properties bool special = false; bool twosided = false; if (l.side2 != 0xFFFF) twosided = true; if (l.type > 0) special = true; // Add line addLine(l.vertex1, l.vertex2, twosided, special); } } } // Clean up if (map_archive) { temp_archive->close(); delete temp_archive; temp_archive = NULL; } // Refresh map Refresh(); 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; }
/* 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; }
/* 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; }
/* 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; }
/* MapPreviewCanvas::openMap * Opens a map from a mapdesc_t *******************************************************************/ bool MapPreviewCanvas::openMap(Archive::mapdesc_t map) { // All errors = invalid map Global::error = "Invalid map"; // Check if this map is a pk3 map bool map_archive = false; if (map.archive) { map_archive = true; // Attempt to open entry as wad archive temp_archive = new WadArchive(); if (!temp_archive->open(map.head)) { delete temp_archive; return false; } // Detect maps vector<Archive::mapdesc_t> maps = temp_archive->detectMaps(); // Set map if there are any in the archive if (maps.size() > 0) map = maps[0]; else return false; } // Parse UDMF map if (map.format == MAP_UDMF) { ArchiveEntry* udmfdata = NULL; for (ArchiveEntry* mapentry = map.head; mapentry != map.end; mapentry = mapentry->nextEntry()) { // Check entry type if (mapentry->getType() == EntryType::getType("udmf_textmap")) { udmfdata = mapentry; break; } } if (udmfdata == NULL) return false; // Start parsing Tokenizer tz; tz.openMem(udmfdata->getData(), udmfdata->getSize(), map.head->getName()); // Get first token string token = tz.getToken(); size_t vertcounter = 0, linecounter = 0, thingcounter = 0; while (!token.IsEmpty()) { if (!token.CmpNoCase("namespace")) { // skip till we reach the ';' do { token = tz.getToken(); } while (token.Cmp(";")); } else if (!token.CmpNoCase("vertex")) { // Get X and Y properties bool gotx = false; bool goty = false; double x = 0.; double y = 0.; do { token = tz.getToken(); if (!token.CmpNoCase("x") || !token.CmpNoCase("y")) { bool isx = !token.CmpNoCase("x"); token = tz.getToken(); if (token.Cmp("=")) { wxLogMessage("Bad syntax for vertex %i in UDMF map data", vertcounter); return false; } if (isx) x = tz.getDouble(), gotx = true; else y = tz.getDouble(), goty = true; // skip to end of declaration after each key do { token = tz.getToken(); } while (token.Cmp(";")); } } while (token.Cmp("}")); if (gotx && goty) addVertex(x, y); else { wxLogMessage("Wrong vertex %i in UDMF map data", vertcounter); return false; } vertcounter++; } else if (!token.CmpNoCase("linedef")) { bool special = false; bool twosided = false; bool gotv1 = false, gotv2 = false; size_t v1 = 0, v2 = 0; do { token = tz.getToken(); if (!token.CmpNoCase("v1") || !token.CmpNoCase("v2")) { bool isv1 = !token.CmpNoCase("v1"); token = tz.getToken(); if (token.Cmp("=")) { wxLogMessage("Bad syntax for linedef %i in UDMF map data", linecounter); return false; } if (isv1) v1 = tz.getInteger(), gotv1 = true; else v2 = tz.getInteger(), gotv2 = true; // skip to end of declaration after each key do { token = tz.getToken(); } while (token.Cmp(";")); } else if (!token.CmpNoCase("special")) { special = true; // skip to end of declaration after each key do { token = tz.getToken(); } while (token.Cmp(";")); } else if (!token.CmpNoCase("sideback")) { twosided = true; // skip to end of declaration after each key do { token = tz.getToken(); } while (token.Cmp(";")); } } while (token.Cmp("}")); if (gotv1 && gotv2) addLine(v1, v2, twosided, special); else { wxLogMessage("Wrong line %i in UDMF map data", linecounter); return false; } linecounter++; } else if (S_CMPNOCASE(token, "thing")) { // Get X and Y properties bool gotx = false; bool goty = false; double x = 0.; double y = 0.; do { token = tz.getToken(); if (!token.CmpNoCase("x") || !token.CmpNoCase("y")) { bool isx = !token.CmpNoCase("x"); token = tz.getToken(); if (token.Cmp("=")) { wxLogMessage("Bad syntax for thing %i in UDMF map data", vertcounter); return false; } if (isx) x = tz.getDouble(), gotx = true; else y = tz.getDouble(), goty = true; // skip to end of declaration after each key do { token = tz.getToken(); } while (token.Cmp(";")); } } while (token.Cmp("}")); if (gotx && goty) addThing(x, y); else { wxLogMessage("Wrong thing %i in UDMF map data", vertcounter); return false; } vertcounter++; } else { // Check for side or sector definition (increase counts) if (S_CMPNOCASE(token, "sidedef")) n_sides++; else if (S_CMPNOCASE(token, "sector")) n_sectors++; // map preview ignores sidedefs, sectors, comments, // unknown fields, etc. so skip to end of block do { token = tz.getToken(); } while (token.Cmp("}")); } // Iterate to next token token = tz.getToken(); } } // Non-UDMF map if (map.format != MAP_UDMF) { // Read vertices (required) if (!readVertices(map.head, map.end, map.format)) return false; // Read linedefs (required) if (!readLines(map.head, map.end, map.format)) return false; // Read things if (map.format != MAP_UDMF) readThings(map.head, map.end, map.format); // Read sides & sectors (count only) ArchiveEntry* sidedefs = NULL; ArchiveEntry* sectors = NULL; while (map.head) { // Check entry type if (map.head->getType() == EntryType::getType("map_sidedefs")) sidedefs = map.head; if (map.head->getType() == EntryType::getType("map_sectors")) sectors = map.head; // Exit loop if we've reached the end of the map entries if (map.head == map.end) break; else map.head = map.head->nextEntry(); } if (sidedefs && sectors) { // Doom64 map if (map.format != MAP_DOOM64) { n_sides = sidedefs->getSize() / 30; n_sectors = sectors->getSize() / 26; } // Doom/Hexen map else { n_sides = sidedefs->getSize() / 12; n_sectors = sectors->getSize() / 16; } } } // Clean up if (map_archive) { temp_archive->close(); delete temp_archive; temp_archive = NULL; } // Refresh map Refresh(); return true; }