/* SAction::initActions * Loads and parses all SActions configured in actions.cfg in the * program resource archive *******************************************************************/ bool SAction::initActions() { // Get actions.cfg from slade.pk3 auto cfg_entry = theArchiveManager->programResourceArchive()->entryAtPath("actions.cfg"); if (!cfg_entry) return false; Parser parser(cfg_entry->getParentDir()); if (parser.parseText(cfg_entry->getMCData(), "actions.cfg")) { auto root = parser.parseTreeRoot(); for (unsigned a = 0; a < root->nChildren(); a++) { auto node = root->getChildPTN(a); // Single action if (S_CMPNOCASE(node->type(), "action")) { auto action = new SAction(node->getName(), node->getName()); if (action->parse(node)) actions.push_back(action); else delete action; } // Group of actions else if (S_CMPNOCASE(node->getName(), "group")) { int group = newGroup(); for (unsigned b = 0; b < node->nChildren(); b++) { auto group_node = node->getChildPTN(b); if (S_CMPNOCASE(group_node->type(), "action")) { auto action = new SAction(group_node->getName(), group_node->getName()); if (action->parse(group_node)) { action->group = group; actions.push_back(action); } else delete action; } } } } } return true; }
vguard<TreeNodeIndex> Tree::rerootedChildren (TreeNodeIndex node, TreeNodeIndex parent) const { vguard<TreeNodeIndex> children; for (int c = 0; c < nChildren(node); ++c) if (getChild(node,c) != parent) children.push_back (getChild(node,c)); if (parentNode(node) >= 0 && parentNode(node) != parent) children.push_back (parentNode(node)); return children; }
/* Executables::parse * Parses an executables configuration from [p] *******************************************************************/ void Executables::parse(Parser* p, bool custom) { auto n = p->parseTreeRoot()->getChildPTN("executables"); if (!n) return; for (unsigned a = 0; a < n->nChildren(); a++) { auto exe_node = n->getChildPTN(a); string type = exe_node->type(); // Game Executable (if type is blank it's a game executable in old config format) if (type == "game_exe" || type.IsEmpty()) parseGameExe(exe_node, custom); // External Executable else if (type == "external_exe") parseExternalExe(exe_node); } }
/* EntryDataFormat::readDataFormatDefinition * Parses a user data format definition (unimplemented, currently) *******************************************************************/ bool EntryDataFormat::readDataFormatDefinition(MemChunk& mc) { // Parse the definition Parser p; p.parseText(mc); // Get data_formats tree auto pt_formats = p.parseTreeRoot()->getChildPTN("data_formats"); // Check it exists if (!pt_formats) return false; // Go through all parsed types for (unsigned a = 0; a < pt_formats->nChildren(); a++) { // Get child as ParseTreeNode auto formatnode = pt_formats->getChildPTN(a); // Create+add new data format EntryDataFormat* edf = new EntryDataFormat(formatnode->getName().Lower()); /* // Copy from existing type if inherited if (!formatnode->getInherit().IsEmpty()) { EntryType* parent_type = EntryType::getType(formatnode->getInherit()); if (parent_type != EntryType::unknownType()) parent_type->copyToType(ntype); else LOG_MESSAGE(1, "Warning: Entry type %s inherits from unknown type %s", ntype->getId(), typenode->getInherit()); } */ } return true; }
// ---------------------------------------------------------------------------- // 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; }
void Tree::assertBinary() const { for (TreeNodeIndex node = 0; node < nodes(); ++node) if (!isLeaf(node)) Assert (nChildren(node) == 2, "Tree is not binary: node %d has %s\nSubtree rooted at %d: %s\n", node, plural(nChildren(node),"child","children").c_str(), node, toString(node).c_str()); }
TreeNodeIndex Tree::getSibling (TreeNodeIndex node) const { const TreeNodeIndex parent = parentNode(node); Assert (parent >= 0, "Attempt to find sibling of root node"); Assert (nChildren(parent) == 2, "Attempt to find sibling in non-binary tree"); return getChild(parent,0) == node ? getChild(parent,1) : getChild(parent,0); }
bool Tree::isBinary() const { for (TreeNodeIndex node = 0; node < nodes(); ++node) if (!isLeaf(node) && nChildren(node) != 2) return false; return true; }
// ----------------------------------------------------------------------------- // Automatically seek IWADs to populate the list // ----------------------------------------------------------------------------- void BaseResourceArchivesPanel::autodetect() { // List of known IWADs and common aliases ArchiveEntry* iwadlist = App::archiveManager().programResourceArchive()->entryAtPath("config/iwads.cfg"); if (!iwadlist) return; Parser p; p.parseText(iwadlist->getMCData(), "slade.pk3:config/iwads.cfg"); // Find IWADs from DOOMWADDIR and DOOMWADPATH // See http://doomwiki.org/wiki/Environment_variables string doomwaddir, doomwadpath, envvar; envvar = "DOOMWADDIR"; wxGetEnv(envvar, &doomwaddir); envvar = "DOOMWADPATH"; wxGetEnv(envvar, &doomwadpath); if (doomwaddir.length() || doomwadpath.length()) { #ifdef WIN32 char separator = ';'; doomwadpath.Replace("\\", "/", true); doomwaddir.Replace("\\", "/", true); #else char separator = ':'; #endif wxArrayString paths = wxSplit(doomwadpath, separator); paths.Add(doomwaddir); wxArrayString iwadnames; auto list = p.parseTreeRoot()->getChildPTN("iwads"); for (size_t i = 0; i < list->nChildren(); ++i) iwadnames.Add(list->getChild(i)->getName()); // Look for every known IWAD in every known IWAD directory for (size_t i = 0; i < paths.size(); ++i) { string folder = paths[i]; if (folder.Last() != '/') folder += '/'; for (size_t j = 0; j < iwadnames.size(); ++j) { string iwad = folder + iwadnames[j]; #ifndef WIN32 // Try a couple variants before throwing the towel about a name if (!wxFileExists(iwad)) iwad = folder + iwadnames[j].Capitalize(); if (!wxFileExists(iwad)) iwad = folder + iwadnames[j].Upper(); #endif // If a valid combo is found, add it to the list unless already present if (wxFileExists(iwad)) { // Verify existence before adding it to the list if (list_base_archive_paths_->FindString(iwad) == wxNOT_FOUND) { App::archiveManager().addBaseResourcePath(iwad); list_base_archive_paths_->Append(iwad); } } } } } // Let's take a look at the registry wxArrayString paths; string path; string gamepath; // Now query GOG.com paths -- Windows only for now #ifdef __WXMSW__ #ifdef _WIN64 string gogregistrypath = "Software\\Wow6432Node\\GOG.com"; #else // If a 32-bit ZDoom runs on a 64-bit Windows, this will be transparently and // automatically redirected to the Wow6432Node address instead, so this address // should be safe to use in all cases. string gogregistrypath = "Software\\GOG.com"; #endif if (QueryPathKey(wxRegKey::HKLM, gogregistrypath, "DefaultPackPath", path)) { auto list = p.parseTreeRoot()->getChildPTN("gog"); for (size_t i = 0; i < list->nChildren(); ++i) { auto child = list->getChildPTN(i); gamepath = gogregistrypath + (child->getChildPTN("id"))->stringValue(); if (QueryPathKey(wxRegKey::HKLM, gamepath, "Path", path)) paths.Add(path + (child->getChildPTN("path"))->stringValue()); } } #endif // Now query Steam paths -- Windows only for now as well #ifdef __WXMSW__ if (QueryPathKey(wxRegKey::HKCU, "Software\\Valve\\Steam", "SteamPath", gamepath) || QueryPathKey(wxRegKey::HKLM, "Software\\Valve\\Steam", "InstallPath", gamepath)) { gamepath += "/SteamApps/common/"; auto list = p.parseTreeRoot()->getChildPTN("steam"); for (size_t i = 0; i < list->nChildren(); ++i) paths.Add(gamepath + (list->getChildPTN(i))->stringValue()); } #else // TODO: Querying Steam registry on Linux and OSX. This involves parsing Steam's config.vdf file, which is found in // FSFindFolder(kUserDomain, kApplicationSupportFolderType, kCreateFolder, &folder) + "/Steam/config/config.vdf" on // OSX and in ~home/.local/share/Steam/config/config.vdf on Linux/BSD systems. There's also default install dirs in // appSupportPath + "/Steam/SteamApps/common" for OSX, ~home/.local/share/Steam/SteamApps/common for Linux/BSD. #endif // Add GOG & Steam paths for (size_t i = 0; i < paths.size(); ++i) { string iwad = paths[i]; iwad.Replace("\\", "/", true); if (wxFileExists(iwad)) { // Verify existence before adding it to the list if (list_base_archive_paths_->FindString(iwad) == wxNOT_FOUND) { App::archiveManager().addBaseResourcePath(iwad); list_base_archive_paths_->Append(iwad); } } } }
// ----------------------------------------------------------------------------- // Reads archive formats configuration file from [mc] // ----------------------------------------------------------------------------- bool Archive::loadFormats(MemChunk& mc) { Parser parser; if (!parser.parseText(mc)) return false; auto root = parser.parseTreeRoot(); auto formats_node = root->child("archive_formats"); for (unsigned a = 0; a < formats_node->nChildren(); a++) { auto fmt_desc = (ParseTreeNode*)formats_node->child(a); ArchiveFormat fmt{ fmt_desc->name() }; for (unsigned p = 0; p < fmt_desc->nChildren(); p++) { auto prop = (ParseTreeNode*)fmt_desc->child(p); // Format name if (StrUtil::equalCI(prop->name(), "name")) fmt.name = prop->stringValue(); // Supports dirs else if (StrUtil::equalCI(prop->name(), "supports_dirs")) fmt.supports_dirs = prop->boolValue(); // Entry names have extensions else if (StrUtil::equalCI(prop->name(), "names_extensions")) fmt.names_extensions = prop->boolValue(); // Max entry name length else if (StrUtil::equalCI(prop->name(), "max_name_length")) fmt.max_name_length = prop->intValue(); // Entry format (id) else if (StrUtil::equalCI(prop->name(), "entry_format")) fmt.entry_format = prop->stringValue(); // Extensions else if (StrUtil::equalCI(prop->name(), "extensions")) { for (unsigned e = 0; e < prop->nChildren(); e++) { auto ext = (ParseTreeNode*)prop->child(e); fmt.extensions.emplace_back(ext->name(), ext->stringValue()); } } // Prefer uppercase entry names else if (StrUtil::equalCI(prop->name(), "prefer_uppercase")) fmt.prefer_uppercase = prop->boolValue(); } Log::info(3, wxString::Format("Read archive format %s: \"%s\"", fmt.id, fmt.name)); if (fmt.supports_dirs) Log::info(3, " Supports folders"); if (fmt.names_extensions) Log::info(3, " Entry names have extensions"); if (fmt.max_name_length >= 0) Log::info(3, wxString::Format(" Max entry name length: %d", fmt.max_name_length)); for (auto ext : fmt.extensions) Log::info(3, wxString::Format(" Extension \"%s\" = \"%s\"", ext.first, ext.second)); formats.push_back(fmt); } // Add builtin 'folder' format ArchiveFormat fmt_folder("folder"); fmt_folder.name = "Folder"; fmt_folder.names_extensions = true; fmt_folder.supports_dirs = true; formats.push_back(fmt_folder); return true; }
// ----------------------------------------------------------------------------- // Returns a list of entries matching the search criteria in [options] // ----------------------------------------------------------------------------- vector<ArchiveEntry*> Archive::findAll(SearchOptions& options) { // Init search variables auto dir = options.dir; if (!dir) dir = &dir_root_; vector<ArchiveEntry*> ret; StrUtil::upperIP(options.match_name); // Force case-insensitive // Begin search // Search entries for (unsigned a = 0; a < dir->numEntries(); a++) { auto entry = dir->entryAt(a); // Check type if (options.match_type) { if (entry->type() == EntryType::unknownType()) { if (!options.match_type->isThisType(entry)) continue; } else if (options.match_type != entry->type()) continue; } // Check name if (!options.match_name.empty()) { // Cut extension if ignoring auto check_name = options.ignore_ext ? entry->upperNameNoExt() : entry->upperName(); if (!StrUtil::matches(check_name, options.match_name)) continue; } // Check namespace if (!options.match_namespace.empty()) { if (!StrUtil::equalCI(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++) { auto opt = options; opt.dir = (ArchiveTreeNode*)dir->child(a); // Add any matches to the list auto vec = findAll(opt); ret.insert(ret.end(), vec.begin(), vec.end()); } } // Return matches return ret; }
// ----------------------------------------------------------------------------- // Returns the last entry matching the search criteria in [options], or null if // no matching entry was found // ----------------------------------------------------------------------------- ArchiveEntry* Archive::findLast(SearchOptions& options) { // Init search variables auto dir = options.dir; if (!dir) dir = &dir_root_; StrUtil::upperIP(options.match_name); // Force case-insensitive // Begin search // Search entries (bottom-up) for (int a = dir->numEntries() - 1; a >= 0; a--) { auto entry = dir->entryAt(a); // Check type if (options.match_type) { if (entry->type() == EntryType::unknownType()) { if (!options.match_type->isThisType(entry)) continue; } else if (options.match_type != entry->type()) continue; } // Check name if (!options.match_name.empty()) { // Cut extension if ignoring auto check_name = options.ignore_ext ? entry->upperNameNoExt() : entry->upperName(); if (!StrUtil::matches(check_name, options.match_name)) continue; } // Check namespace if (!options.match_namespace.empty()) { if (!StrUtil::equalCI(detectNamespace(entry), options.match_namespace)) continue; } // Entry passed all checks so far, so we found a match return entry; } // Search subdirectories (if needed) (bottom-up) if (options.search_subdirs) { for (int a = dir->nChildren() - 1; a >= 0; a--) { auto opt = options; opt.dir = (ArchiveTreeNode*)dir->child(a); auto match = findLast(opt); // If a match was found in this subdir, return it if (match) return match; } } // No matches found return nullptr; }
// ----------------------------------------------------------------------------- // Reads a colour configuration from text data [mc] // ----------------------------------------------------------------------------- bool ColourConfiguration::readConfiguration(MemChunk& mc) { // Parse text Parser parser; parser.parseText(mc); // Get 'colours' block auto colours = parser.parseTreeRoot()->childPTN("colours"); if (colours) { // Read all colour definitions for (unsigned a = 0; a < colours->nChildren(); a++) { auto def = colours->childPTN(a); // Read properties for (unsigned b = 0; b < def->nChildren(); b++) { auto prop = def->childPTN(b); auto& col = cc_colours[def->name()]; col.exists = true; // Colour name if (prop->name() == "name") col.name = prop->stringValue(); // Colour group (for config ui) else if (prop->name() == "group") col.group = prop->stringValue(); // Colour else if (prop->name() == "rgb") col.colour.set(prop->intValue(0), prop->intValue(1), prop->intValue(2)); // Alpha else if (prop->name() == "alpha") col.colour.a = prop->intValue(); // Additive else if (prop->name() == "additive") col.blend_additive = prop->boolValue(); else Log::warning(fmt::format("Unknown colour definition property \"{}\"", prop->name())); } } } // Get 'theme' block auto theme = parser.parseTreeRoot()->childPTN("theme"); if (theme) { // Read all theme definitions for (unsigned a = 0; a < theme->nChildren(); a++) { auto prop = theme->childPTN(a); if (prop->name() == "line_hilight_width") line_hilight_width = prop->floatValue(); else if (prop->name() == "line_selection_width") line_selection_width = prop->floatValue(); else if (prop->name() == "flat_alpha") flat_alpha = prop->floatValue(); else Log::warning(fmt::format("Unknown theme property \"{}\"", prop->name())); } } return true; }