/* CTexture::removePatch * Removes all instances of [patch] from the texture. Returns true if * any were removed, false otherwise *******************************************************************/ bool CTexture::removePatch(string patch) { // Go through patches bool removed = false; vector<CTPatch*>::iterator i = patches.begin(); while (i != patches.end()) { if (S_CMP((*i)->getName(), patch)) { delete (*i); patches.erase(i); removed = true; } else i++; } // Cannot be a simple define anymore this->defined = false; if (removed) announce("patches_modified"); return removed; }
/* PatchTable::patch * Returns the patch matching [name], or an 'invalid' patch if no * match is found *******************************************************************/ patch_t& PatchTable::patch(string name) { // Go through list for (unsigned a = 0; a < patches.size(); a++) { if (S_CMP(patches[a].name, name)) return patches[a]; } return patch_invalid; }
/* MainApp::getAction * Returns the SAction matching [id] *******************************************************************/ SAction* MainApp::getAction(string id) { // Find matching action for (unsigned a = 0; a < actions.size(); a++) { if (S_CMP(actions[a]->getId(), id)) return actions[a]; } // Not found return action_invalid; }
// ----------------------------------------------------------------------------- // Checks if the file at [filename] is a valid DN3D grp archive // ----------------------------------------------------------------------------- bool GrpArchive::isGrpArchive(const string& filename) { // Open file for reading wxFile file(filename); // Check it opened ok if (!file.IsOpened()) return false; // Check size if (file.Length() < 16) return false; // Get number of lumps uint32_t num_lumps = 0; char ken_magic[13] = ""; file.Seek(0, wxFromStart); file.Read(ken_magic, 12); // "KenSilverman" file.Read(&num_lumps, 4); // No. of lumps in grp // Byteswap values for big endian if needed num_lumps = wxINT32_SWAP_ON_BE(num_lumps); // Null-terminate the magic header ken_magic[12] = 0; // Check the header if (!(S_CMP(wxString::From8BitData(ken_magic), "KenSilverman"))) return false; // Compute total size uint32_t totalsize = (1 + num_lumps) * 16; uint32_t size = 0; for (uint32_t a = 0; a < num_lumps; ++a) { file.Read(ken_magic, 12); file.Read(&size, 4); totalsize += size; } // Check if total size is correct if (totalsize > file.Length()) return false; // If it's passed to here it's probably a grp file return true; }
bool SAction::addToMenu(wxMenu* menu, bool show_shortcut, string text_override, string icon_override, int wx_id_offset) { // Can't add to nonexistant menu if (!menu) return false; // Determine shortcut key string sc = shortcut; bool is_bind = false; bool sc_control = shortcut.Contains("Ctrl") || shortcut.Contains("Alt"); if (shortcut.StartsWith("kb:")) { is_bind = true; keypress_t kp = KeyBind::getBind(shortcut.Mid(3)).getKey(0); if (kp.key != "") sc = kp.as_string(); else sc = "None"; sc_control = (kp.ctrl || kp.alt); } // Setup menu item string string item_text = text; if (!(S_CMP(text_override, "NO"))) item_text = text_override; if (!sc.IsEmpty() && sc_control) item_text = S_FMT("%s\t%s", item_text, sc); // Append this action to the menu string help = helptext; int wid = wx_id + wx_id_offset; string real_icon = (icon_override == "NO") ? icon : icon_override; if (!sc.IsEmpty()) help += S_FMT(" (Shortcut: %s)", sc); if (type == NORMAL) menu->Append(createMenuItem(menu, wid, item_text, help, real_icon)); else if (type == CHECK) { wxMenuItem* item = menu->AppendCheckItem(wid, item_text, help); item->Check(toggled); } else if (type == RADIO) menu->AppendRadioItem(wid, item_text, help); return true; }
/* ArchiveManager::addBaseResourcePath * Adds [path] to the list of base resource paths *******************************************************************/ bool ArchiveManager::addBaseResourcePath(string path) { // Firstly, check the file exists if (!wxFileExists(path)) return false;; // Second, check the path doesn't already exist for (unsigned a = 0; a < base_resource_paths.size(); a++) { if (S_CMP(base_resource_paths[a], path)) return false; } // Add it base_resource_paths.push_back(path); // Announce announce("base_resource_path_added"); return true; }
/* SAction::addToToolbar * Adds this action to [toolbar]. If [icon_override] is not "NO", it * will be used instead of the action's icon as the tool icon *******************************************************************/ bool SAction::addToToolbar(wxToolBar* toolbar, string icon_override) { // Can't add to nonexistant toolbar if (!toolbar) return false; // Setup icon string useicon = icon; if (!(S_CMP(icon_override, "NO"))) useicon = icon_override; // Append this action to the toolbar if (type == NORMAL) toolbar->AddTool(wx_id, "", getIcon(useicon), helptext); else if (type == CHECK) toolbar->AddTool(wx_id, "", getIcon(useicon), helptext, wxITEM_CHECK); else if (type == RADIO) toolbar->AddTool(wx_id, "", getIcon(useicon), helptext, wxITEM_RADIO); return true; }
// ----------------------------------------------------------------------------- // Adds [path] to the list of base resource paths // ----------------------------------------------------------------------------- bool ArchiveManager::addBaseResourcePath(const string& path) { // Firstly, check the file exists if (!wxFileExists(path)) return false; // Second, check the path doesn't already exist for (auto& base_resource_path : base_resource_paths_) { if (S_CMP(base_resource_path, path)) return false; } // Add it base_resource_paths_.push_back(path); // Announce announce("base_resource_path_added"); return true; }
// ----------------------------------------------------------------------------- // Checks if the given data is a valid Duke Nukem 3D grp archive // ----------------------------------------------------------------------------- bool GrpArchive::isGrpArchive(MemChunk& mc) { // Check size if (mc.size() < 16) return false; // Get number of lumps uint32_t num_lumps = 0; char ken_magic[13] = ""; mc.seek(0, SEEK_SET); mc.read(ken_magic, 12); // "KenSilverman" mc.read(&num_lumps, 4); // No. of lumps in grp // Byteswap values for big endian if needed num_lumps = wxINT32_SWAP_ON_BE(num_lumps); // Null-terminate the magic header ken_magic[12] = 0; // Check the header if (!(S_CMP(wxString::From8BitData(ken_magic), "KenSilverman"))) return false; // Compute total size uint32_t totalsize = (1 + num_lumps) * 16; uint32_t size = 0; for (uint32_t a = 0; a < num_lumps; ++a) { mc.read(ken_magic, 12); mc.read(&size, 4); totalsize += size; } // Check if total size is correct if (totalsize > mc.size()) return false; // If it's passed to here it's probably a grp file return true; }
/* SAction::addToToolbar * Adds this action to [toolbar]. If [icon_override] is not "NO", it * will be used instead of the action's icon as the tool icon *******************************************************************/ bool SAction::addToToolbar(wxToolBar* toolbar, string icon_override, int wx_id_offset) { // Can't add to nonexistant toolbar if (!toolbar) return false; // Setup icon string useicon = icon; if (!(S_CMP(icon_override, "NO"))) useicon = icon_override; // Append this action to the toolbar int wid = wx_id + wx_id_offset; if (type == Type::Normal) toolbar->AddTool(wid, "", Icons::getIcon(Icons::GENERAL, useicon), helptext); else if (type == Type::Check) toolbar->AddTool(wid, "", Icons::getIcon(Icons::GENERAL, useicon), helptext, wxITEM_CHECK); else if (type == Type::Radio) toolbar->AddTool(wid, "", Icons::getIcon(Icons::GENERAL, useicon), helptext, wxITEM_RADIO); return true; }
bool SAction::addToMenu(wxMenu* menu, bool menubar, string text_override) { // Can't add to nonexistant menu if (!menu) return false; // Determine shortcut key string sc = shortcut; bool is_bind = false; if (shortcut.StartsWith("kb:")) { is_bind = true; keypress_t kp = KeyBind::getBind(shortcut.Mid(3)).getKey(0); if (kp.key != "") sc = kp.as_string(); } // Setup menu item string string item_text = text; if (!(S_CMP(text_override, "NO"))) item_text = text_override; if (!sc.IsEmpty() && (!is_bind || !menubar)) item_text = S_FMT("%s\t%s", item_text, sc); // Append this action to the menu string help = helptext; if (!sc.IsEmpty()) help += S_FMT(" (Shortcut: %s)", sc); if (type == NORMAL) menu->Append(createMenuItem(menu, wx_id, item_text, help, icon)); else if (type == CHECK) { wxMenuItem* item = menu->AppendCheckItem(wx_id, item_text, help); item->Check(toggled); } else if (type == RADIO) menu->AppendRadioItem(wx_id, item_text, help); return true; }
/* SAction::addToMenu * Adds this action to [menu]. If [text_override] is not "NO", it * will be used instead of the action's text as the menu item label *******************************************************************/ bool SAction::addToMenu(wxMenu* menu, string text_override) { // Can't add to nonexistant menu if (!menu) return false; // Setup menu item string string item_text = text; if (!(S_CMP(text_override, "NO"))) item_text = text_override; if (!shortcut.IsEmpty()) item_text = S_FMT("%s\t%s", CHR(item_text), CHR(shortcut)); // Append this action to the menu if (type == NORMAL) menu->Append(createMenuItem(menu, wx_id, item_text, helptext, icon)); else if (type == CHECK) menu->AppendCheckItem(wx_id, item_text, helptext); else if (type == RADIO) menu->AppendRadioItem(wx_id, item_text, helptext); return true; }
/* SAction::addToToolbar * Adds this action to [toolbar]. If [icon_override] is not "NO", it * will be used instead of the action's icon as the tool icon *******************************************************************/ bool SAction::addToToolbar(wxAuiToolBar* toolbar, string icon_override, int wx_id_offset) { // Can't add to nonexistant toolbar if (!toolbar) return false; // Setup icon string useicon = icon; if (!(S_CMP(icon_override, "NO"))) useicon = icon_override; // Append this action to the toolbar wxAuiToolBarItem* item = NULL; int wid = wx_id + wx_id_offset; if (type == NORMAL) item = toolbar->AddTool(wid, text, Icons::getIcon(Icons::GENERAL, useicon), helptext); else if (type == CHECK) item = toolbar->AddTool(wid, text, Icons::getIcon(Icons::GENERAL, useicon), helptext, wxITEM_CHECK); else if (type == RADIO) item = toolbar->AddTool(wid, text, Icons::getIcon(Icons::GENERAL, useicon), helptext, wxITEM_RADIO); return true; }
/* PatchTable::addPatch * Adds a new patch with [name] to the end of the list *******************************************************************/ bool PatchTable::addPatch(string name, bool allow_dup) { // Check patch doesn't already exist if (!allow_dup) { for (unsigned a = 0; a < patches.size(); a++) { if (S_CMP(name, patches[a].name)) return false; } } // Create/init new patch patch_t patch; patch.name = name; // Add the patch patches.push_back(patch); // Announce announce("modified"); return true; }
/* WadArchive::detectMaps * Searches for any maps in the wad and adds them to the map list *******************************************************************/ vector<Archive::mapdesc_t> WadArchive::detectMaps() { vector<mapdesc_t> maps; // Go through all lumps ArchiveEntry* entry = getEntry(0); bool lastentryismapentry = false; while (entry) { // UDMF format map check ******************************************************** // Check for UDMF format map lump (TEXTMAP lump) if (entry->getName() == "TEXTMAP" && entry->prevEntry()) { // Get map info mapdesc_t md = getMapInfo(entry->prevEntry()); // Add to map list if (md.head != NULL) { entry = md.end; maps.push_back(md); } // Current index is ENDMAP, we don't want to check for a doom/hexen format // map so just go to the next index and continue the loop entry = entry->nextEntry(); continue; } // Doom/Hexen format map check ************************************************** // TODO maybe get rid of code duplication by calling getMapInfo() here too? // Array to keep track of what doom/hexen map lumps have been found uint8_t existing_map_lumps[NUMMAPLUMPS]; memset(existing_map_lumps, 0, NUMMAPLUMPS); // Check if the current lump is a doom/hexen map lump bool maplump_found = false; for (int a = 0; a < 5; a++) { // Compare with all base map lump names if (S_CMP(entry->getName(), map_lumps[a])) { maplump_found = true; existing_map_lumps[a] = 1; break; } } // If we've found what might be a map if (maplump_found && entry->prevEntry()) { // Save map header entry ArchiveEntry* header_entry = entry->prevEntry(); // Check off map lumps until we find a non-map lump bool done = false; while (!done) { // Loop will end if no map lump is found done = true; // Compare with all map lump names for (int a = 0; a < NUMMAPLUMPS; a++) { // Compare with all base map lump names if (S_CMP(entry->getName(), map_lumps[a])) { existing_map_lumps[a] = 1; done = false; break; } } // If we're at the end of the wad, exit the loop if (!entry->nextEntry()) { lastentryismapentry = true; break; } // Go to next lump if there is one if (!lastentryismapentry) entry = entry->nextEntry(); } // Go back to the lump just after the last map lump found, but only if we actually moved if (!lastentryismapentry) entry = entry->prevEntry(); // Check that we have all the required map lumps: VERTEXES, LINEDEFS, SIDEDEFS, THINGS & SECTORS if (!memchr(existing_map_lumps, 0, 5)) { // Get map info mapdesc_t md; md.head = header_entry; // Header lump md.name = header_entry->getName(); // Map title md.end = lastentryismapentry ? // End lump entry : entry->prevEntry(); // If BEHAVIOR lump exists, it's a hexen format map if (existing_map_lumps[LUMP_BEHAVIOR]) md.format = MAP_HEXEN; // If LEAFS, LIGHTS and MACROS exist, it's a doom 64 format map else if (existing_map_lumps[LUMP_LEAFS] && existing_map_lumps[LUMP_LIGHTS] && existing_map_lumps[LUMP_MACROS]) md.format = MAP_DOOM64; // Otherwise it's doom format else md.format = MAP_DOOM; // Add map info to the maps list maps.push_back(md); } } // Embedded WAD check (for Doom 64) if (entry->getType()->getFormat() == "archive_wad") { // Detect map format (probably kinda slow but whatever, no better way to do it really) Archive* tempwad = new WadArchive(); tempwad->open(entry); vector<mapdesc_t> emaps = tempwad->detectMaps(); if (emaps.size() > 0) { mapdesc_t md; md.head = entry; md.end = entry; md.archive = true; md.name = entry->getName(true).Upper(); md.format = emaps[0].format; maps.push_back(md); } delete tempwad; entry->unlock(); } // Not a UDMF or Doom/Hexen map lump, go to next lump entry = entry->nextEntry(); } // Set all map header entries to ETYPE_MAP type for (size_t a = 0; a < maps.size(); a++) if (!maps[a].archive) maps[a].head->setType(EntryType::mapMarkerType()); // Update entry map format hints for (unsigned a = 0; a < maps.size(); a++) { string format; if (maps[a].format == MAP_DOOM) format = "doom"; else if (maps[a].format == MAP_DOOM64) format = "doom64"; else if (maps[a].format == MAP_HEXEN) format = "hexen"; else format = "udmf"; ArchiveEntry* entry = maps[a].head; while (entry && entry != maps[a].end->nextEntry()) { entry->exProp("MapFormat") = format; entry = entry->nextEntry(); } } return maps; }
/* WadArchive::updateNamespaces * Updates the namespace list *******************************************************************/ void WadArchive::updateNamespaces() { // Clear current namespace info while (namespaces.size() > 0) namespaces.pop_back(); // Go through all entries for (unsigned a = 0; a < numEntries(); a++) { ArchiveEntry* entry = getRoot()->getEntry(a); // Check for namespace begin if (entry->getName().Matches("*_START")) { // Create new namespace wad_ns_pair_t ns(entry, NULL); string name = entry->getName(); ns.name = name.Left(name.Length() - 6).Lower(); ns.start_index = entryIndex(ns.start); // Convert some special cases (because technically PP_START->P_END is a valid namespace) if (ns.name == "pp") ns.name = "p"; if (ns.name == "ff") ns.name = "f"; if (ns.name == "ss") ns.name = "s"; if (ns.name == "tt") ns.name = "t"; // Add to namespace list namespaces.push_back(ns); } // Check for namespace end else if (entry->getName().Matches("?_END") || entry->getName().Matches("??_END")) { // Get namespace 'name' int len = entry->getName().Length() - 4; string ns_name = entry->getName().Left(len).Lower(); // Convert some special cases (because technically P_START->PP_END is a valid namespace) if (ns_name == "pp") ns_name = "p"; if (ns_name == "ff") ns_name = "f"; if (ns_name == "ss") ns_name = "s"; if (ns_name == "tt") ns_name = "t"; // Check if it's the end of an existing namespace // Remember entry is getEntry(a)? index is 'a' //size_t index = entryIndex(entry); bool found = false; for (unsigned b = 0; b < namespaces.size(); b++) { // Can't close a namespace that starts afterwards if (namespaces[b].start_index > a) break; // Can't close an already-closed namespace if (namespaces[b].end != NULL) continue; if (S_CMP(ns_name, namespaces[b].name)) { found = true; namespaces[b].end = entry; namespaces[b].end_index = a; break; } } // Flat hack: closing the flat namespace without opening it if (found == false && ns_name == "f") { wad_ns_pair_t ns(getRoot()->getEntry(0), entry); ns.start_index = 0; ns.end_index = a; ns.name = "f"; namespaces.push_back(ns); } } } // ROTT stuff. The first lump in the archive is always WALLSTRT, the last lump is either // LICENSE (darkwar.wad) or VENDOR (huntbgin.wad), with TABLES just before in both cases. // The shareware version has 2091 lumps, the complete version has about 50% more. if (numEntries() > 2090 && getRoot()->getEntry(0)->getName().Matches("WALLSTRT") && getRoot()->getEntry(numEntries()-2)->getName().Matches("TABLES")) { wad_ns_pair_t ns(getRoot()->getEntry(0), getRoot()->getEntry(numEntries()-1)); ns.name = "rott"; ns.start_index = 0; ns.end_index = entryIndex(ns.end); namespaces.push_back(ns); } // Check namespaces for (unsigned a = 0; a < namespaces.size(); a++) { wad_ns_pair_t& ns = namespaces[a]; // Check the namespace has an end if (!ns.end) { // If not, remove the namespace as it is invalid namespaces.erase(namespaces.begin() + a); a--; continue; } // Check namespace name for special cases for (int n = 0; n < n_special_namespaces; n++) { if (S_CMP(ns.name, special_namespaces[n].letter)) ns.name = special_namespaces[n].name; } ns.start_index = entryIndex(ns.start); ns.end_index = entryIndex(ns.end); // Testing //wxLogMessage("Namespace %s from %s (%d) to %s (%d)", ns.name, // ns.start->getName(), ns.start_index, ns.end->getName(), ns.end_index); } }
/* ParseTreeNode::parse * Parses formatted text data. Current valid formatting is: * (type) child = value; * (type) child = value1, value2, ...; * (type) child = { value1, value2, ... } * (type) child { grandchild = value; etc... } * (type) child : inherited { ... } * All values are read as strings, but can be retrieved as string, * int, bool or float. *******************************************************************/ bool ParseTreeNode::parse(Tokenizer& tz) { // Get first token string token = tz.getToken(); // Keep parsing until final } is reached (or end of file) while (!(S_CMP(token, "}")) && !token.IsEmpty()) { // Check for preprocessor stuff if (parser) { // #define if (S_CMPNOCASE(token, "#define")) { parser->define(tz.getToken()); token = tz.getToken(); continue; } // #if(n)def if (S_CMPNOCASE(token, "#ifdef") || S_CMPNOCASE(token, "#ifndef")) { bool test = true; if (S_CMPNOCASE(token, "#ifndef")) test = false; string define = tz.getToken(); if (parser->defined(define) == test) { // Continue token = tz.getToken(); continue; } else { // Skip section int skip = 0; while (true) { token = tz.getToken(); if (S_CMPNOCASE(token, "#endif")) skip--; else if (S_CMPNOCASE(token, "#ifdef")) skip++; else if (S_CMPNOCASE(token, "#ifndef")) skip++; if (skip < 0) break; if (token.IsEmpty()) { wxLogMessage("Error: found end of file within #if(n)def block"); break; } } continue; } } // #include (ignore) if (S_CMPNOCASE(token, "#include")) { tz.skipToken(); // Skip include path token = tz.getToken(); continue; } // #endif (ignore) if (S_CMPNOCASE(token, "#endif")) { token = tz.getToken(); continue; } } // If it's a special character (ie not a valid name), parsing fails if (tz.isSpecialCharacter(token.at(0))) { wxLogMessage("Parsing error: Unexpected special character '%s' in %s (line %d)", CHR(token), CHR(tz.getName()), tz.lineNo()); return false; } // So we have either a node or property name string name = token; // Check next token to determine what we're doing string next = tz.peekToken(); // Check for type+name pair string type = ""; if (next != "=" && next != "{" && next != ";" && next != ":") { type = name; name = tz.getToken(); next = tz.peekToken(); } // Assignment if (S_CMP(next, "=")) { // Skip = tz.skipToken(); // Create item node ParseTreeNode* child = (ParseTreeNode*)addChild(name); child->type = type; // Check type of assignment list token = tz.getToken(); string list_end = ";"; if (token == "{") { list_end = "}"; token = tz.getToken(); } // Parse until ; or } while (1) { // Check for list end if (S_CMP(token, list_end) && !tz.quotedString()) break; // Setup value Property value; // Detect value type if (tz.quotedString()) // Quoted string value = token; else if (S_CMPNOCASE(token, "true")) // Boolean (true) value = true; else if (S_CMPNOCASE(token, "false")) // Boolean (false) value = false; else if (re_int1.Matches(token) || // Integer re_int2.Matches(token)) { long val; token.ToLong(&val); value = (int)val; } else if (re_int3.Matches(token)) // Hex (0xXXXXXX) { long val; token.ToLong(&val, 0); value = (int)val; //wxLogMessage("%s: %s is hex %d", CHR(name), CHR(token), value.getIntValue()); } else if (re_float.Matches(token)) // Floating point { double val; token.ToDouble(&val); value = (double)val; //LOG_MESSAGE(3, S_FMT("%s: %s is float %1.3f", CHR(name), CHR(token), val)); } else // Unknown, just treat as string value = token; // Add value child->values.push_back(value); // Check for , if (S_CMP(tz.peekToken(), ",")) tz.skipToken(); // Skip it else if (!(S_CMP(tz.peekToken(), list_end))) { string t = tz.getToken(); string n = tz.getName(); wxLogMessage("Parsing error: Expected \",\" or \"%s\", got \"%s\" in %s (line %d)", CHR(list_end), CHR(t), CHR(n), tz.lineNo()); return false; } token = tz.getToken(); } } // Child node else if (S_CMP(next, "{")) { // Add child node ParseTreeNode* child = (ParseTreeNode*)addChild(name); child->type = type; // Skip { tz.skipToken(); // Parse child node if (!child->parse(tz)) return false; } // Child node (with no values/children) else if (S_CMP(next, ";")) { // Add child node ParseTreeNode* child = (ParseTreeNode*)addChild(name); child->type = type; // Skip ; tz.skipToken(); } // Child node + inheritance else if (S_CMP(next, ":")) { // Skip : tz.skipToken(); // Read inherited name string inherit = tz.getToken(); // Check for opening brace if (tz.checkToken("{")) { // Add child node ParseTreeNode* child = (ParseTreeNode*)addChild(name); child->type = type; // Set its inheritance child->inherit = inherit; // Parse child node if (!child->parse(tz)) return false; } } // Unexpected token else { wxLogMessage("Parsing error: \"%s\" unexpected in %s (line %d)", CHR(next), CHR(tz.getName()), tz.lineNo()); return false; } // Continue parsing token = tz.getToken(); } return true; }
// ----------------------------------------------------------------------------- // Reads grp format data from a MemChunk // Returns true if successful, false otherwise // ----------------------------------------------------------------------------- bool GrpArchive::open(MemChunk& mc) { // Check data was given if (!mc.hasData()) return false; // Read grp header uint32_t num_lumps = 0; char ken_magic[13] = ""; mc.seek(0, SEEK_SET); mc.read(ken_magic, 12); // "KenSilverman" mc.read(&num_lumps, 4); // No. of lumps in grp // Byteswap values for big endian if needed num_lumps = wxINT32_SWAP_ON_BE(num_lumps); // Null-terminate the magic header ken_magic[12] = 0; // Check the header if (!(S_CMP(wxString::FromAscii(ken_magic), "KenSilverman"))) { Log::error(S_FMT("GrpArchive::openFile: File %s has invalid header", filename_)); Global::error = "Invalid grp header"; return false; } // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); // The header takes as much space as a directory entry uint32_t entryoffset = 16 * (1 + num_lumps); // Read the directory UI::setSplashProgressMessage("Reading grp archive data"); for (uint32_t d = 0; d < num_lumps; d++) { // Update splash window progress UI::setSplashProgress(((float)d / (float)num_lumps)); // Read lump info char name[13] = ""; uint32_t offset = entryoffset; uint32_t size = 0; mc.read(name, 12); // Name mc.read(&size, 4); // Size name[12] = '\0'; // Byteswap values for big endian if needed size = wxINT32_SWAP_ON_BE(size); // Increase offset of next entry by this entry's size entryoffset += size; // If the lump data goes past the end of the file, // the grpfile is invalid if (offset + size > mc.size()) { Log::error("GrpArchive::open: grp archive is invalid or corrupt"); Global::error = "Archive is invalid and/or corrupt"; setMuted(false); return false; } // Create & setup lump auto nlump = std::make_shared<ArchiveEntry>(wxString::FromAscii(name), size); nlump->setLoaded(false); nlump->exProp("Offset") = (int)offset; nlump->setState(ArchiveEntry::State::Unmodified); // Add to entry list rootDir()->addEntry(nlump); } // Detect all entry types MemChunk edata; UI::setSplashProgressMessage("Detecting entry types"); for (size_t a = 0; a < numEntries(); a++) { // Update splash window progress UI::setSplashProgress((((float)a / (float)num_lumps))); // Get entry auto entry = entryAt(a); // Read entry data if it isn't zero-sized if (entry->size() > 0) { // Read the entry data mc.exportMemChunk(edata, getEntryOffset(entry), entry->size()); entry->importMemChunk(edata); } // Detect entry type EntryType::detectEntryType(entry); // Unload entry data if needed if (!archive_load_data) entry->unloadData(); // Set entry to unchanged entry->setState(ArchiveEntry::State::Unmodified); } // Setup variables setMuted(false); setModified(false); announce("opened"); UI::setSplashProgressMessage(""); return true; }