/* TextureXList::readTEXTURESData * Reads in a ZDoom-format TEXTURES entry. Returns true on success, * false otherwise *******************************************************************/ bool TextureXList::readTEXTURESData(ArchiveEntry* entry) { // Check for empty entry if (!entry) { Global::error = "Attempt to read texture data from NULL entry"; return false; } if (entry->getSize() == 0) { txformat = TXF_TEXTURES; return true; } // Get text to parse Tokenizer tz; tz.openMem(&(entry->getMCData()), entry->getName()); // Parsing gogo string token = tz.getToken(); while (!token.IsEmpty()) { // Texture definition if (S_CMPNOCASE(token, "Texture")) { CTexture* tex = new CTexture(); if (tex->parse(tz, "Texture")) addTexture(tex); } // Sprite definition if (S_CMPNOCASE(token, "Sprite")) { CTexture* tex = new CTexture(); if (tex->parse(tz, "Sprite")) addTexture(tex); } // Graphic definition if (S_CMPNOCASE(token, "Graphic")) { CTexture* tex = new CTexture(); if (tex->parse(tz, "Graphic")) addTexture(tex); } // WallTexture definition if (S_CMPNOCASE(token, "WallTexture")) { CTexture* tex = new CTexture(); if (tex->parse(tz, "WallTexture")) addTexture(tex); } // Flat definition if (S_CMPNOCASE(token, "Flat")) { CTexture* tex = new CTexture(); if (tex->parse(tz, "Flat")) addTexture(tex); } token = tz.getToken(); } txformat = TXF_TEXTURES; return true; }
// ----------------------------------------------------------------------------- // Parses all statements/blocks in [entry], adding them to [parsed] // ----------------------------------------------------------------------------- void parseBlocks(ArchiveEntry* entry, vector<ParsedStatement>& parsed) { Tokenizer tz; tz.setSpecialCharacters(CHR(Tokenizer::DEFAULT_SPECIAL_CHARACTERS + "()+-[]&!?.")); tz.enableDecorate(true); tz.setCommentTypes(Tokenizer::CommentTypes::CPPStyle | Tokenizer::CommentTypes::CStyle); tz.openMem(entry->getMCData(), "ZScript"); // Log::info(2, S_FMT("Parsing ZScript entry \"%s\"", entry->getPath(true))); while (!tz.atEnd()) { // Preprocessor if (tz.current().text.StartsWith("#")) { if (tz.checkNC("#include")) { auto inc_entry = entry->relativeEntry(tz.next().text); // Check #include path could be resolved if (!inc_entry) { Log::warning(S_FMT( "Warning parsing ZScript entry %s: " "Unable to find #included entry \"%s\" at line %u, skipping", CHR(entry->getName()), CHR(tz.current().text), tz.current().line_no)); } else parseBlocks(inc_entry, parsed); } tz.advToNextLine(); continue; } // Version else if (tz.checkNC("version")) { tz.advToNextLine(); continue; } // ZScript parsed.push_back({}); parsed.back().entry = entry; if (!parsed.back().parse(tz)) parsed.pop_back(); } // Set entry type if (etype_zscript && entry->getType() != etype_zscript) entry->setType(etype_zscript); }
/* Parser::parseText * Parses the given text data to build a tree of ParseTreeNodes. * Example: * base { * child1 = value1; * child2 = value2, value3, value4; * child3 { * grandchild1 = value5; * grandchild2 = value6; * } * child4 { * grandchild3 = value7, value8; * } * } * * will generate this tree (represented in xml-like format, node names within <>): * <root> * <base> * <child1>value1</child1> * <child2>value2, value3, value4</child2> * <child3> * <grandchild1>value5</grandchild1> * <grandchild2>value6</grandchild2> * </child3> * <child4> * <grandchild3>value7, value8</grandchild3> * </child4> * </base> * </root> *******************************************************************/ bool Parser::parseText(MemChunk& mc, string source) { Tokenizer tz; // Open the given text data if (!tz.openMem(&mc, source)) { wxLogMessage("Unable to open text data for parsing"); return false; } // Do parsing return pt_root->parse(tz); }
// ---------------------------------------------------------------------------- // Parser::parseText // // Parses the given text data to build a tree of ParseTreeNodes. // Example: // base // { // child1 = value1; // child2 = value2, value3, value4; // child3 // { // grandchild1 = value5; // grandchild2 = value6; // } // child4 // { // grandchild3 = value7, value8; // } // } // // will generate this tree // (represented in xml-like format, node names within <>): // <root> // <base> // <child1>value1</child1> // <child2>value2, value3, value4</child2> // <child3> // <grandchild1>value5</grandchild1> // <grandchild2>value6</grandchild2> // </child3> // <child4> // <grandchild3>value7, value8</grandchild3> // </child4> // </base> // </root> // ---------------------------------------------------------------------------- bool Parser::parseText(MemChunk& mc, string source) { Tokenizer tz; // Open the given text data tz.setReadLowerCase(!case_sensitive_); if (!tz.openMem(mc, source)) { LOG_MESSAGE(1, "Unable to open text data for parsing"); return false; } // Do parsing return pt_root_->parse(tz); }
// ----------------------------------------------------------------------------- // Parses the given text data to build a tree of ParseTreeNodes. // Example: // base // { // child1 = value1; // child2 = value2, value3, value4; // child3 // { // grandchild1 = value5; // grandchild2 = value6; // } // child4 // { // grandchild3 = value7, value8; // } // } // // will generate this tree // (represented in xml-like format, node names within <>): // <root> // <base> // <child1>value1</child1> // <child2>value2, value3, value4</child2> // <child3> // <grandchild1>value5</grandchild1> // <grandchild2>value6</grandchild2> // </child3> // <child4> // <grandchild3>value7, value8</grandchild3> // </child4> // </base> // </root> // ----------------------------------------------------------------------------- bool Parser::parseText(MemChunk& mc, std::string_view source) const { Tokenizer tz; // Open the given text data tz.setReadLowerCase(!case_sensitive_); if (!tz.openMem(mc, source)) { Log::error("Unable to open text data for parsing"); return false; } // Do parsing return pt_root_->parse(tz); }
/* WadArchive::detectIncludes * Parses the DECORATE, GLDEFS, etc. lumps for included files, and * mark them as being of the same type *******************************************************************/ void WadArchive::detectIncludes() { // DECORATE: #include "lumpname" // GLDEFS: #include "lumpname" // SBARINFO: #include "lumpname" // ZMAPINFO: translator = "lumpname" // EMAPINFO: extradata = lumpname // EDFROOT: lumpinclude("lumpname") const char * lumptypes[6] = { "DECORATE", "GLDEFS", "SBARINFO", "ZMAPINFO", "EMAPINFO", "EDFROOT" }; const char * entrytypes[6] = { "decorate", "gldefslump", "sbarinfo", "xlat", "extradata", "edf" }; const char * tokens[6] = { "#include", "#include", "#include", "translator", "extradata", "lumpinclude" }; Archive::search_options_t opt; opt.ignore_ext = true; Tokenizer tz; tz.setSpecialCharacters(";,:|={}/()"); for (int i = 0; i < 6; ++i) { opt.match_name = lumptypes[i]; vector<ArchiveEntry*> entries = findAll(opt); if (entries.size()) { for (size_t j = 0; j < entries.size(); ++j) { tz.openMem(&entries[j]->getMCData(), lumptypes[i]); string token = tz.getToken(); while (token.length()) { if (token.Lower() == tokens[i]) { if (i >= 3) // skip '=' or '(' tz.getToken(); string name = tz.getToken(); if (i == 5) // skip ')' tz.getToken(); opt.match_name = name; ArchiveEntry * entry = findFirst(opt); if (entry) entry->setType(EntryType::getType(entrytypes[i])); } else tz.skipLineComment(); token = tz.getToken(); } } } } }
// ---------------------------------------------------------------------------- // TextLanguage::readLanguageDefinition // // Reads in a text definition of a language. See slade.pk3 for // formatting examples // ---------------------------------------------------------------------------- bool TextLanguage::readLanguageDefinition(MemChunk& mc, string source) { Tokenizer tz; // Open the given text data if (!tz.openMem(mc, source)) { Log::warning(1, S_FMT("Warning: Unable to open %s", source)); return false; } // Parse the definition text ParseTreeNode root; if (!root.parse(tz)) return false; // Get parsed data for (unsigned a = 0; a < root.nChildren(); a++) { auto node = root.getChildPTN(a); // Create language TextLanguage* lang = new TextLanguage(node->getName()); // Check for inheritance if (!node->inherit().IsEmpty()) { TextLanguage* inherit = fromId(node->inherit()); if (inherit) inherit->copyTo(lang); else Log::warning( 1, S_FMT("Warning: Language %s inherits from undefined language %s", node->getName(), node->inherit()) ); } // Parse language info for (unsigned c = 0; c < node->nChildren(); c++) { auto child = node->getChildPTN(c); // Language name if (S_CMPNOCASE(child->getName(), "name")) lang->setName(child->stringValue()); // Comment begin else if (S_CMPNOCASE(child->getName(), "comment_begin")) { lang->setCommentBeginList(child->stringValues()); } // Comment end else if (S_CMPNOCASE(child->getName(), "comment_end")) { lang->setCommentEndList(child->stringValues()); } // Line comment else if (S_CMPNOCASE(child->getName(), "comment_line")) { lang->setLineCommentList(child->stringValues()); } // Preprocessor else if (S_CMPNOCASE(child->getName(), "preprocessor")) lang->setPreprocessor(child->stringValue()); // Case sensitive else if (S_CMPNOCASE(child->getName(), "case_sensitive")) lang->setCaseSensitive(child->boolValue()); // Doc comment else if (S_CMPNOCASE(child->getName(), "comment_doc")) lang->setDocComment(child->stringValue()); // Keyword lookup link else if (S_CMPNOCASE(child->getName(), "keyword_link")) lang->word_lists_[WordType::Keyword].lookup_url = child->stringValue(); // Constant lookup link else if (S_CMPNOCASE(child->getName(), "constant_link")) lang->word_lists_[WordType::Constant].lookup_url = child->stringValue(); // Function lookup link else if (S_CMPNOCASE(child->getName(), "function_link")) lang->f_lookup_url_ = child->stringValue(); // Jump blocks else if (S_CMPNOCASE(child->getName(), "blocks")) { for (unsigned v = 0; v < child->nValues(); v++) lang->jump_blocks_.push_back(child->stringValue(v)); } else if (S_CMPNOCASE(child->getName(), "blocks_ignore")) { for (unsigned v = 0; v < child->nValues(); v++) lang->jb_ignore_.push_back(child->stringValue(v)); } // Block begin else if (S_CMPNOCASE(child->getName(), "block_begin")) lang->block_begin_ = child->stringValue(); // Block end else if (S_CMPNOCASE(child->getName(), "block_end")) lang->block_end_ = child->stringValue(); // Preprocessor block begin else if (S_CMPNOCASE(child->getName(), "pp_block_begin")) { for (unsigned v = 0; v < child->nValues(); v++) lang->pp_block_begin_.push_back(child->stringValue(v)); } // Preprocessor block end else if (S_CMPNOCASE(child->getName(), "pp_block_end")) { for (unsigned v = 0; v < child->nValues(); v++) lang->pp_block_end_.push_back(child->stringValue(v)); } // Word block begin else if (S_CMPNOCASE(child->getName(), "word_block_begin")) { for (unsigned v = 0; v < child->nValues(); v++) lang->word_block_begin_.push_back(child->stringValue(v)); } // Word block end else if (S_CMPNOCASE(child->getName(), "word_block_end")) { for (unsigned v = 0; v < child->nValues(); v++) lang->word_block_end_.push_back(child->stringValue(v)); } // Keywords else if (S_CMPNOCASE(child->getName(), "keywords")) { // Go through values for (unsigned v = 0; v < child->nValues(); v++) { string val = child->stringValue(v); // Check for '$override' if (S_CMPNOCASE(val, "$override")) { // Clear any inherited keywords lang->clearWordList(WordType::Keyword); } // Not a special symbol, add as keyword else lang->addWord(WordType::Keyword, val); } } // Constants else if (S_CMPNOCASE(child->getName(), "constants")) { // Go through values for (unsigned v = 0; v < child->nValues(); v++) { string val = child->stringValue(v); // Check for '$override' if (S_CMPNOCASE(val, "$override")) { // Clear any inherited constants lang->clearWordList(WordType::Constant); } // Not a special symbol, add as constant else lang->addWord(WordType::Constant, val); } } // Types else if (S_CMPNOCASE(child->getName(), "types")) { // Go through values for (unsigned v = 0; v < child->nValues(); v++) { string val = child->stringValue(v); // Check for '$override' if (S_CMPNOCASE(val, "$override")) { // Clear any inherited constants lang->clearWordList(WordType::Type); } // Not a special symbol, add as constant else lang->addWord(WordType::Type, val); } } // Properties else if (S_CMPNOCASE(child->getName(), "properties")) { // Go through values for (unsigned v = 0; v < child->nValues(); v++) { string val = child->stringValue(v); // Check for '$override' if (S_CMPNOCASE(val, "$override")) { // Clear any inherited constants lang->clearWordList(WordType::Property); } // Not a special symbol, add as constant else lang->addWord(WordType::Property, val); } } // Functions else if (S_CMPNOCASE(child->getName(), "functions")) { bool lang_has_void = lang->isWord(Keyword, "void") || lang->isWord(Type, "void"); if (lang->id_ != "zscript") { // Go through children (functions) for (unsigned f = 0; f < child->nChildren(); f++) { auto child_func = child->getChildPTN(f); string params; // Simple definition if (child_func->nChildren() == 0) { if (child_func->stringValue(0).empty()) { if (lang_has_void) params = "void"; else params = ""; } else { params = child_func->stringValue(0); } // Add function lang->addFunction( child_func->getName(), params, "", "", !child_func->getName().Contains("."), child_func->type()); // Add args for (unsigned v = 1; v < child_func->nValues(); v++) lang->addFunction(child_func->getName(), child_func->stringValue(v)); } // Full definition else { string name = child_func->getName(); vector<string> args; string desc = ""; string deprecated = ""; for (unsigned p = 0; p < child_func->nChildren(); p++) { auto child_prop = child_func->getChildPTN(p); if (child_prop->getName() == "args") { for (unsigned v = 0; v < child_prop->nValues(); v++) args.push_back(child_prop->stringValue(v)); } else if (child_prop->getName() == "description") desc = child_prop->stringValue(); else if (child_prop->getName() == "deprecated") deprecated = child_prop->stringValue(); } if (args.empty() && lang_has_void) args.push_back("void"); for (unsigned as = 0; as < args.size(); as++) lang->addFunction(name, args[as], desc, deprecated, as == 0, child_func->type()); } } } // ZScript function info which cannot be parsed from (g)zdoom.pk3 else { zfunc_ex_prop ex_prop; for (unsigned f = 0; f < child->nChildren(); f++) { auto child_func = child->getChildPTN(f); for (unsigned p = 0; p < child_func->nChildren(); ++p) { auto child_prop = child_func->getChildPTN(p); if (child_prop->getName() == "description") ex_prop.description = child_prop->stringValue(); else if (child_prop->getName() == "deprecated_f") ex_prop.deprecated_f = child_prop->stringValue(); } lang->zfuncs_ex_props_.emplace(child_func->getName(), ex_prop); } } } } } return true; }
/* MainWindow::createStartPage * Builds the HTML start page and loads it into the html viewer * (start page tab) *******************************************************************/ void MainWindow::createStartPage(bool newtip) { // Get relevant resource entries Archive* res_archive = theArchiveManager->programResourceArchive(); if (!res_archive) return; // Get entries to export vector<ArchiveEntry*> export_entries; ArchiveEntry* entry_html = res_archive->entryAtPath("html/startpage.htm"); ArchiveEntry* entry_tips = res_archive->entryAtPath("tips.txt"); export_entries.push_back(res_archive->entryAtPath("logo.png")); export_entries.push_back(res_archive->entryAtPath("html/box-title-back.png")); //export_entries.push_back(res_archive->entryAtPath("icons/e_archive.png")); //export_entries.push_back(res_archive->entryAtPath("icons/e_wad.png")); //export_entries.push_back(res_archive->entryAtPath("icons/e_zip.png")); //export_entries.push_back(res_archive->entryAtPath("icons/e_folder.png")); // Can't do anything without html entry if (!entry_html) { LOG_MESSAGE(1, "No start page resource found"); html_startpage->SetPage("<html><head><title>SLADE</title></head><body><center><h1>Something is wrong with slade.pk3 :(</h1><center></body></html>", wxEmptyString); return; } // Get html as string string html = wxString::FromAscii((const char*)(entry_html->getData()), entry_html->getSize()); // Generate tip of the day string string tip = "It seems tips.txt is missing from your slade.pk3"; if (entry_tips) { Tokenizer tz; tz.openMem((const char*)entry_tips->getData(), entry_tips->getSize(), entry_tips->getName()); srand(wxGetLocalTime()); int numtips = tz.getInteger(); if (numtips < 2) // Needs at least two choices or it's kinda pointless. tip = "Did you know? Something is wrong with the tips.txt file in your slade.pk3."; else { int tipindex = 0; if (newtip || lasttipindex == 0) { // Don't show same tip twice in a row do { tipindex = 1 + (rand() % numtips); } while (tipindex == lasttipindex); } else tipindex = lasttipindex; lasttipindex = tipindex; for (int a = 0; a < tipindex; a++) tip = tz.getToken(); } } // Generate recent files string string recent; recent += "<table class=\"box\">"; if (theArchiveManager->numRecentFiles() > 0) { for (unsigned a = 0; a < 12; a++) { if (a >= theArchiveManager->numRecentFiles()) break; // No more recent files recent += "<tr><td valign=\"middle\" class=\"box\">"; // Determine icon string fn = theArchiveManager->recentFile(a); string icon = "archive"; if (fn.EndsWith(".wad")) icon = "wad"; else if (fn.EndsWith(".zip") || fn.EndsWith(".pk3") || fn.EndsWith(".pke")) icon = "zip"; else if (wxDirExists(fn)) icon = "folder"; // Add recent file link recent += S_FMT("<img src=\"%s.png\"></td><td valign=\"top\" class=\"box\">", icon); recent += S_FMT("<a href=\"recent://%d\">%s</a></td></tr>", a, fn); } } else recent += "<tr><td valign=\"top\" class=\"box\">No recently opened files</td></tr>"; recent += "</table>"; // Insert tip and recent files into html html.Replace("#recent#", recent); html.Replace("#totd#", tip); // Write html and images to temp folder for (unsigned a = 0; a < export_entries.size(); a++) export_entries[a]->exportFile(appPath(export_entries[a]->getName(), DIR_TEMP)); Icons::exportIconPNG(Icons::ENTRY, "archive", appPath("archive.png", DIR_TEMP)); Icons::exportIconPNG(Icons::ENTRY, "wad", appPath("wad.png", DIR_TEMP)); Icons::exportIconPNG(Icons::ENTRY, "zip", appPath("zip.png", DIR_TEMP)); Icons::exportIconPNG(Icons::ENTRY, "folder", appPath("folder.png", DIR_TEMP)); string html_file = appPath("startpage.htm", DIR_TEMP); wxFile outfile(html_file, wxFile::write); outfile.Write(html); outfile.Close(); #ifdef __WXGTK__ html_file = "file://" + html_file; #endif // Load page html_startpage->ClearHistory(); html_startpage->LoadURL(html_file); #ifdef __WXMSW__ html_startpage->Reload(); #endif // Clean up //wxRemoveFile(html_file); //wxRemoveFile(appPath("logo.png", DIR_TEMP)); }
/* MapSpecials::processACSScripts * Process 'OPEN' ACS scripts for various specials - sector colours, * slopes, etc. *******************************************************************/ void MapSpecials::processACSScripts(ArchiveEntry* entry) { sector_colours.clear(); sector_fadecolours.clear(); if (!entry || entry->getSize() == 0) return; Tokenizer tz; tz.setSpecialCharacters(";,:|={}/()"); tz.openMem(entry->getMCData(), "ACS Scripts"); while (!tz.atEnd()) { if (tz.checkNC("script")) { LOG_MESSAGE(3, "script found"); tz.adv(2); // Skip script # // Check for open script if (tz.checkNC("OPEN")) { LOG_MESSAGE(3, "script is OPEN"); // Skip to opening brace while (!tz.check("{")) tz.adv(); // Parse script while (!tz.checkOrEnd("}")) { // --- Sector_SetColor --- if (tz.checkNC("Sector_SetColor")) { // Get parameters auto parameters = tz.getTokensUntil(")"); // Parse parameters long val; int tag = -1; int r = -1; int g = -1; int b = -1; for (unsigned a = 0; a < parameters.size(); a++) { if (parameters[a].text.ToLong(&val)) { if (tag < 0) tag = val; else if (r < 0) r = val; else if (g < 0) g = val; else if (b < 0) b = val; } } // Check everything is set if (b < 0) { LOG_MESSAGE(2, "Invalid Sector_SetColor parameters"); } else { sector_colour_t sc; sc.tag = tag; sc.colour.set(r, g, b, 255); LOG_MESSAGE(3, "Sector tag %d, colour %d,%d,%d", tag, r, g, b); sector_colours.push_back(sc); } } // --- Sector_SetFade --- else if (tz.checkNC("Sector_SetFade")) { // Get parameters auto parameters = tz.getTokensUntil(")"); // Parse parameters long val; int tag = -1; int r = -1; int g = -1; int b = -1; for (unsigned a = 0; a < parameters.size(); a++) { if (parameters[a].text.ToLong(&val)) { if (tag < 0) tag = val; else if (r < 0) r = val; else if (g < 0) g = val; else if (b < 0) b = val; } } // Check everything is set if (b < 0) { LOG_MESSAGE(2, "Invalid Sector_SetFade parameters"); } else { sector_colour_t sc; sc.tag = tag; sc.colour.set(r, g, b, 0); LOG_MESSAGE(3, "Sector tag %d, fade colour %d,%d,%d", tag, r, g, b); sector_fadecolours.push_back(sc); } } tz.adv(); } } } tz.adv(); } }
void ArchiveOperations::removeUnusedTextures(Archive* archive) { // Check archive was given if (!archive) return; // --- Build list of used textures --- TexUsedMap used_textures; int total_maps = 0; // Get all SIDEDEFS entries Archive::SearchOptions opt; opt.match_type = EntryType::fromId("map_sidedefs"); auto sidedefs = archive->findAll(opt); total_maps += sidedefs.size(); // Go through and add used textures to list DoomMapFormat::SideDef sdef; wxString tex_lower, tex_middle, tex_upper; for (auto& sidedef : sidedefs) { int nsides = sidedef->size() / 30; sidedef->seek(0, SEEK_SET); for (int s = 0; s < nsides; s++) { // Read side data sidedef->read(&sdef, 30); // Get textures tex_lower = wxString::FromAscii(sdef.tex_lower, 8); tex_middle = wxString::FromAscii(sdef.tex_middle, 8); tex_upper = wxString::FromAscii(sdef.tex_upper, 8); // Add to used textures list used_textures[tex_lower].used = true; used_textures[tex_middle].used = true; used_textures[tex_upper].used = true; } } // Get all TEXTMAP entries opt.match_name = "TEXTMAP"; opt.match_type = EntryType::fromId("udmf_textmap"); auto udmfmaps = archive->findAll(opt); total_maps += udmfmaps.size(); // Go through and add used textures to list Tokenizer tz; tz.setSpecialCharacters("{};="); for (auto& udmfmap : udmfmaps) { // Open in tokenizer tz.openMem(udmfmap->data(), "UDMF TEXTMAP"); // Go through text tokens wxString token = tz.getToken(); while (!token.IsEmpty()) { // Check for sidedef definition if (token == "sidedef") { tz.getToken(); // Skip { token = tz.getToken(); while (token != "}") { // Check for texture property if (token == "texturetop" || token == "texturemiddle" || token == "texturebottom") { tz.getToken(); // Skip = used_textures[tz.getToken()].used = true; } token = tz.getToken(); } } // Next token token = tz.getToken(); } } // Check if any maps were found if (total_maps == 0) return; // Find all TEXTUREx entries opt.match_name = ""; opt.match_type = EntryType::fromId("texturex"); auto tx_entries = archive->findAll(opt); // Go through texture lists PatchTable ptable; // Dummy patch table, patch info not needed here wxArrayString unused_tex; for (auto& tx_entrie : tx_entries) { TextureXList txlist; txlist.readTEXTUREXData(tx_entrie, ptable); // Go through textures bool anim = false; for (unsigned t = 1; t < txlist.size(); t++) { wxString texname = txlist.texture(t)->name(); // Check for animation start for (int b = 0; b < n_tex_anim; b++) { if (texname == tex_anim_start[b]) { anim = true; break; } } // Check for animation end bool thisend = false; for (int b = 0; b < n_tex_anim; b++) { if (texname == tex_anim_end[b]) { anim = false; thisend = true; break; } } // Mark if unused and not part of an animation if (!used_textures[texname].used && !anim && !thisend) unused_tex.Add(txlist.texture(t)->name()); } } // Pop up a dialog with a checkbox list of unused textures wxMultiChoiceDialog dialog( theMainWindow, "The following textures are not used in any map,\nselect which textures to delete", "Delete Unused Textures", unused_tex); // Get base resource textures (if any) auto base_resource = App::archiveManager().baseResourceArchive(); vector<ArchiveEntry*> base_tx_entries; if (base_resource) base_tx_entries = base_resource->findAll(opt); PatchTable pt_temp; TextureXList tx; for (auto& texturex : base_tx_entries) tx.readTEXTUREXData(texturex, pt_temp, true); vector<wxString> base_resource_textures; for (unsigned a = 0; a < tx.size(); a++) base_resource_textures.push_back(tx.texture(a)->name()); // Determine which textures to check initially wxArrayInt selection; for (unsigned a = 0; a < unused_tex.size(); a++) { bool swtex = false; // Check for switch texture if (unused_tex[a].StartsWith("SW1")) { // Get counterpart switch name wxString swname = unused_tex[a]; swname.Replace("SW1", "SW2", false); // Check if its counterpart is used if (used_textures[swname].used) swtex = true; } else if (unused_tex[a].StartsWith("SW2")) { // Get counterpart switch name wxString swname = unused_tex[a]; swname.Replace("SW2", "SW1", false); // Check if its counterpart is used if (used_textures[swname].used) swtex = true; } // Check for base resource texture bool br_tex = false; for (auto& texture : base_resource_textures) { if (texture.CmpNoCase(unused_tex[a]) == 0) { Log::info(3, "Texture " + texture + " is in base resource"); br_tex = true; break; } } if (!swtex && !br_tex) selection.Add(a); } dialog.SetSelections(selection); int n_removed = 0; if (dialog.ShowModal() == wxID_OK) { // Get selected textures selection = dialog.GetSelections(); // Go through texture lists for (auto& entry : tx_entries) { TextureXList txlist; txlist.readTEXTUREXData(entry, ptable); // Go through selected textures to delete for (int i : selection) { // Get texture index int index = txlist.textureIndex(WxUtils::strToView(unused_tex[i])); // Delete it from the list (if found) if (index >= 0) { txlist.removeTexture(index); n_removed++; } } // Write texture list data back to entry txlist.writeTEXTUREXData(entry, ptable); } } wxMessageBox(wxString::Format("Removed %d unused textures", n_removed)); }
/* 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; }
// ----------------------------------------------------------------------------- // Parses ZMAPINFO-format definitions in [entry] // ----------------------------------------------------------------------------- bool MapInfo::parseZMapInfo(ArchiveEntry* entry) { Tokenizer tz; tz.setReadLowerCase(true); tz.openMem(entry->data(), entry->name()); while (!tz.atEnd()) { // Include if (tz.check("include")) { // Get entry at include path auto include_entry = entry->parent()->entryAtPath(tz.next().text); if (!include_entry) { Log::warning( "Warning - Parsing ZMapInfo \"{}\": Unable to include \"{}\" at line {}", entry->name(), tz.current().text, tz.lineNo()); } else if (!parseZMapInfo(include_entry)) return false; } // Map else if (tz.check("map") || tz.check("defaultmap") || tz.check("adddefaultmap")) { if (!parseZMap(tz, tz.current().text)) return false; } // DoomEdNums else if (tz.check("doomednums")) { if (!parseDoomEdNums(tz)) return false; } // Unknown block (skip it) else if (tz.check("{")) { Log::warning(2, "Warning - Parsing ZMapInfo \"{}\": Skipping {{}} block", entry->name()); tz.adv(); tz.skipSection("{", "}"); continue; } // Unknown else { Log::warning(2, R"(Warning - Parsing ZMapInfo "{}": Unknown token "{}")", entry->name(), tz.current().text); } tz.adv(); } Log::info(2, "Parsed ZMapInfo entry {} successfully", entry->name()); return true; } // ----------------------------------------------------------------------------- // Parses a ZMAPINFO map definition of [type] beginning at the current token in // tokenizer [tz] // ----------------------------------------------------------------------------- bool MapInfo::parseZMap(Tokenizer& tz, std::string_view type) { // TODO: Handle adddefaultmap auto map = default_map_; // Normal map, get lump/name/etc tz.adv(); if (type == "map") { // Entry name should be just after map keyword map.entry_name = tz.current().text; // Parse map name tz.adv(); if (tz.check("lookup")) { map.lookup_name = true; map.name = tz.next().text; } else { map.lookup_name = false; map.name = tz.current().text; } tz.adv(); } if (!tz.advIf("{")) { Log::error(R"(Error Parsing ZMapInfo: Expecting "{{", got "{}" at line {})", tz.current().text, tz.lineNo()); return false; } while (!tz.checkOrEnd("}")) { // Block (skip it) if (tz.advIf("{")) tz.skipSection("{", "}"); // LevelNum else if (tz.check("levelnum")) { if (!checkEqualsToken(tz, "ZMapInfo")) return false; // Parse number // TODO: Checks tz.next().toInt(map.level_num); } // Sky1 else if (tz.check("sky1")) { if (!checkEqualsToken(tz, "ZMapInfo")) return false; map.sky1 = tz.next().text; // Scroll speed // TODO: Checks if (tz.advIfNext(",")) tz.next().toFloat(map.sky1_scroll_speed); } // Sky2 else if (tz.check("sky2")) { if (!checkEqualsToken(tz, "ZMapInfo")) return false; map.sky2 = tz.next().text; // Scroll speed // TODO: Checks if (tz.advIfNext(",")) tz.next().toFloat(map.sky2_scroll_speed); } // Skybox else if (tz.check("skybox")) { if (!checkEqualsToken(tz, "ZMapInfo")) return false; map.sky1 = tz.next().text; } // DoubleSky else if (tz.check("doublesky")) map.sky_double = true; // ForceNoSkyStretch else if (tz.check("forcenoskystretch")) map.sky_force_no_stretch = true; // SkyStretch else if (tz.check("skystretch")) map.sky_stretch = true; // Fade else if (tz.check("fade")) { if (!checkEqualsToken(tz, "ZMapInfo")) return false; if (!strToCol(tz.next().text, map.fade)) return false; } // OutsideFog else if (tz.check("outsidefog")) { if (!checkEqualsToken(tz, "ZMapInfo")) return false; if (!strToCol(tz.next().text, map.fade_outside)) return false; } // EvenLighting else if (tz.check("evenlighting")) { map.lighting_wallshade_h = 0; map.lighting_wallshade_v = 0; } // SmoothLighting else if (tz.check("smoothlighting")) map.lighting_smooth = true; // VertWallShade else if (tz.check("vertwallshade")) { if (!checkEqualsToken(tz, "ZMapInfo")) return false; // TODO: Checks tz.next().toInt(map.lighting_wallshade_v); } // HorzWallShade else if (tz.check("horzwallshade")) { if (!checkEqualsToken(tz, "ZMapInfo")) return false; // TODO: Checks tz.next().toInt(map.lighting_wallshade_h); } // ForceFakeContrast else if (tz.check("forcefakecontrast")) map.force_fake_contrast = true; tz.adv(); }
/* 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; }
/* 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; }
// ----------------------------------------------------------------------------- // Initialises the start page // ----------------------------------------------------------------------------- void SStartPage::init() { // wxWebView #ifdef USE_WEBVIEW_STARTPAGE html_startpage_ = wxWebView::New( this, -1, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxWebViewBackendDefault, wxBORDER_NONE); html_startpage_->SetZoomType(App::platform() == App::MacOS ? wxWEBVIEW_ZOOM_TYPE_TEXT : wxWEBVIEW_ZOOM_TYPE_LAYOUT); // wxHtmlWindow #else html_startpage_ = new wxHtmlWindow(this, -1, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_NEVER, "startpage"); #endif // Add to sizer GetSizer()->Add(html_startpage_, 1, wxEXPAND); // Bind events #ifdef USE_WEBVIEW_STARTPAGE html_startpage_->Bind(wxEVT_WEBVIEW_NAVIGATING, &SStartPage::onHTMLLinkClicked, this); html_startpage_->Bind( wxEVT_WEBVIEW_ERROR, [&](wxWebViewEvent& e) { Log::error(S_FMT("wxWebView Error: %s", CHR(e.GetString()))); }); if (App::platform() == App::Platform::Windows) { html_startpage_->Bind(wxEVT_WEBVIEW_LOADED, [&](wxWebViewEvent& e) { html_startpage_->Reload(); }); } Bind(wxEVT_THREAD_WEBGET_COMPLETED, [&](wxThreadEvent& e) { latest_news_ = e.GetString(); latest_news_.Trim(); if (latest_news_ == "connect_failed" || latest_news_.empty()) latest_news_ = "<center>Unable to load latest SLADE news</center>"; load(false); }); #else html_startpage_->Bind(wxEVT_COMMAND_HTML_LINK_CLICKED, &SStartPage::onHTMLLinkClicked, this); #endif // Get data used to build the page auto res_archive = App::archiveManager().programResourceArchive(); if (res_archive) { entry_base_html_ = res_archive->entryAtPath(App::useWebView() ? "html/startpage.htm" : "html/startpage_basic.htm"); entry_css_ = res_archive->entryAtPath(web_dark_theme ? "html/theme-dark.css" : "html/theme-light.css"); entry_export_.push_back(res_archive->entryAtPath("html/base.css")); entry_export_.push_back(res_archive->entryAtPath("fonts/FiraSans-Regular.woff")); entry_export_.push_back(res_archive->entryAtPath("fonts/FiraSans-Italic.woff")); entry_export_.push_back(res_archive->entryAtPath("fonts/FiraSans-Medium.woff")); entry_export_.push_back(res_archive->entryAtPath("fonts/FiraSans-Bold.woff")); entry_export_.push_back(res_archive->entryAtPath("fonts/FiraSans-Heavy.woff")); entry_export_.push_back(res_archive->entryAtPath("logo_icon.png")); entry_export_.push_back(res_archive->entryAtPath("icons/entry_list/Rounded/archive.png")); entry_export_.push_back(res_archive->entryAtPath("icons/entry_list/Rounded/wad.png")); entry_export_.push_back(res_archive->entryAtPath("icons/entry_list/Rounded/zip.png")); entry_export_.push_back(res_archive->entryAtPath("icons/entry_list/Rounded/folder.png")); entry_export_.push_back(res_archive->entryAtPath("icons/general/open.png")); entry_export_.push_back(res_archive->entryAtPath("icons/general/newarchive.png")); entry_export_.push_back(res_archive->entryAtPath("icons/general/newzip.png")); entry_export_.push_back(res_archive->entryAtPath("icons/general/mapeditor.png")); entry_export_.push_back(res_archive->entryAtPath("icons/general/wiki.png")); // Load tips auto entry_tips = res_archive->entryAtPath("tips.txt"); if (entry_tips) { Tokenizer tz; tz.openMem((const char*)entry_tips->getData(), entry_tips->getSize(), entry_tips->getName()); while (!tz.atEnd() && tz.peekToken() != "") tips_.push_back(tz.getToken()); } } }
/* MainWindow::createStartPage * Builds the HTML start page and loads it into the html viewer * (start page tab) *******************************************************************/ void MainWindow::createStartPage() { // Get relevant resource entries Archive* res_archive = theArchiveManager->programResourceArchive(); if (!res_archive) return; ArchiveEntry* entry_html = res_archive->entryAtPath("html/startpage.htm"); ArchiveEntry* entry_logo = res_archive->entryAtPath("logo.png"); ArchiveEntry* entry_tips = res_archive->entryAtPath("tips.txt"); // Can't do anything without html entry if (!entry_html) { html_startpage->SetPage("<html><head><title>SLADE</title></head><body><center><h1>Something is wrong with slade.pk3 :(</h1><center></body></html>"); return; } // Get html as string string html = wxString::FromAscii((const char*)(entry_html->getData()), entry_html->getSize()); // Generate tip of the day string string tip = "It seems tips.txt is missing from your slade.pk3"; if (entry_tips) { Tokenizer tz; tz.openMem((const char*)entry_tips->getData(), entry_tips->getSize(), entry_tips->getName()); srand(wxGetLocalTime()); int numtips = tz.getInteger(); if (numtips < 2) // Needs at least two choices or it's kinda pointless. tip = "Did you know? Something is wrong with the tips.txt file in your slade.pk3."; else { int tipindex = 0; // Don't show same tip twice in a row do { tipindex = 1 + (rand() % numtips); } while (tipindex == lasttipindex); lasttipindex = tipindex; for (int a = 0; a < tipindex; a++) tip = tz.getToken(); } } // Generate recent files string string recent; for (unsigned a = 0; a < 4; a++) { if (a >= theArchiveManager->numRecentFiles()) break; // No more recent files // Add line break if needed if (a > 0) recent += "<br/>\n"; // Add recent file link recent += S_FMT("<a href=\"recent://%d\">%s</a>", a, theArchiveManager->recentFile(a)); } // Insert tip and recent files into html html.Replace("#recent#", recent); html.Replace("#totd#", tip); // Write html and images to temp folder if (entry_logo) entry_logo->exportFile(appPath("logo.png", DIR_TEMP)); string html_file = appPath("startpage.htm", DIR_TEMP); wxFile outfile(html_file, wxFile::write); outfile.Write(html); outfile.Close(); // Load page html_startpage->LoadPage(html_file); // Clean up wxRemoveFile(html_file); wxRemoveFile(appPath("logo.png", DIR_TEMP)); }
void ArchiveOperations::removeUnusedFlats(Archive* archive) { // Check archive was given if (!archive) return; // --- Build list of used flats --- TexUsedMap used_textures; int total_maps = 0; // Get all SECTORS entries Archive::SearchOptions opt; opt.match_type = EntryType::fromId("map_sectors"); auto sectors = archive->findAll(opt); total_maps += sectors.size(); // Go through and add used flats to list DoomMapFormat::Sector sec; wxString tex_floor, tex_ceil; for (auto& sector : sectors) { int nsec = sector->size() / 26; sector->seek(0, SEEK_SET); for (int s = 0; s < nsec; s++) { // Read sector data sector->read(&sec, 26); // Get textures tex_floor = wxString::FromAscii(sec.f_tex, 8); tex_ceil = wxString::FromAscii(sec.c_tex, 8); // Add to used textures list used_textures[tex_floor].used = true; used_textures[tex_ceil].used = true; } } // Get all TEXTMAP entries opt.match_name = "TEXTMAP"; opt.match_type = EntryType::fromId("udmf_textmap"); auto udmfmaps = archive->findAll(opt); total_maps += udmfmaps.size(); // Go through and add used flats to list Tokenizer tz; tz.setSpecialCharacters("{};="); for (auto& udmfmap : udmfmaps) { // Open in tokenizer tz.openMem(udmfmap->data(), "UDMF TEXTMAP"); // Go through text tokens wxString token = tz.getToken(); while (!token.IsEmpty()) { // Check for sector definition if (token == "sector") { tz.getToken(); // Skip { token = tz.getToken(); while (token != "}") { // Check for texture property if (token == "texturefloor" || token == "textureceiling") { tz.getToken(); // Skip = used_textures[tz.getToken()].used = true; } token = tz.getToken(); } } // Next token token = tz.getToken(); } } // Check if any maps were found if (total_maps == 0) return; // Find all flats opt.match_name = ""; opt.match_namespace = "flats"; opt.match_type = nullptr; auto flats = archive->findAll(opt); // Create list of all unused flats wxArrayString unused_tex; bool anim = false; for (auto& flat : flats) { // Skip markers if (flat->size() == 0) continue; // Check for animation start string flatname{ flat->nameNoExt() }; for (int b = 0; b < n_flat_anim; b++) { if (flatname == flat_anim_start[b]) { anim = true; Log::info(wxString::Format("%s anim start", flatname)); break; } } // Check for animation end bool thisend = false; for (int b = 0; b < n_flat_anim; b++) { if (flatname == flat_anim_end[b]) { anim = false; thisend = true; Log::info(wxString::Format("%s anim end", flatname)); break; } } // Add if not animated if (!used_textures[flatname].used && !anim && !thisend) unused_tex.Add(flatname); } // Pop up a dialog with a checkbox list of unused textures wxMultiChoiceDialog dialog( theMainWindow, "The following textures are not used in any map,\nselect which textures to delete", "Delete Unused Textures", unused_tex); // Select all flats initially wxArrayInt selection; for (unsigned a = 0; a < unused_tex.size(); a++) selection.push_back(a); dialog.SetSelections(selection); int n_removed = 0; if (dialog.ShowModal() == wxID_OK) { // Go through selected flats selection = dialog.GetSelections(); opt.match_namespace = "flats"; for (int i : selection) { opt.match_name = unused_tex[i]; ArchiveEntry* entry = archive->findFirst(opt); archive->removeEntry(entry); n_removed++; } } wxMessageBox(wxString::Format("Removed %d unused flats", n_removed)); }
// ----------------------------------------------------------------------------- // Reads in a ZDoom-format TEXTURES entry. // Returns true on success, false otherwise // ----------------------------------------------------------------------------- bool TextureXList::readTEXTURESData(ArchiveEntry* textures) { // Check for empty entry if (!textures) { Global::error = "Attempt to read texture data from NULL entry"; return false; } if (textures->size() == 0) { txformat_ = Format::Textures; return true; } // Get text to parse Tokenizer tz; tz.openMem(textures->data(), textures->name()); // Parsing gogo while (!tz.atEnd()) { // Texture definition if (tz.checkNC("Texture")) { auto tex = std::make_unique<CTexture>(); if (tex->parse(tz, "Texture")) addTexture(std::move(tex)); } // Sprite definition else if (tz.checkNC("Sprite")) { auto tex = std::make_unique<CTexture>(); if (tex->parse(tz, "Sprite")) addTexture(std::move(tex)); } // Graphic definition else if (tz.checkNC("Graphic")) { auto tex = std::make_unique<CTexture>(); if (tex->parse(tz, "Graphic")) addTexture(std::move(tex)); } // WallTexture definition else if (tz.checkNC("WallTexture")) { auto tex = std::make_unique<CTexture>(); if (tex->parse(tz, "WallTexture")) addTexture(std::move(tex)); } // Flat definition else if (tz.checkNC("Flat")) { auto tex = std::make_unique<CTexture>(); if (tex->parse(tz, "Flat")) addTexture(std::move(tex)); } // Old HIRESTEX "Define" else if (tz.checkNC("Define")) { auto tex = std::make_unique<CTexture>(); if (tex->parseDefine(tz)) addTexture(std::move(tex)); } tz.adv(); } txformat_ = Format::Textures; return true; }