/* 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::findLast * Returns the last entry matching the search criteria in [options], * or NULL if no matching entry was found *******************************************************************/ ArchiveEntry* WadArchive::findLast(search_options_t& options) { // Init search variables ArchiveEntry* start = getEntry(numEntries()-1); ArchiveEntry* end = NULL; options.match_name = options.match_name.Lower(); // "graphics" namespace is the global namespace in a wad if (options.match_namespace == "graphics") options.match_namespace = ""; // "global" namespace has no name, by the way if (options.match_namespace == "global") options.match_namespace = ""; // Check for namespace to search if (!options.match_namespace.IsEmpty()) { // Find matching namespace bool ns_found = false; for (unsigned a = 0; a < namespaces.size(); a++) { if (namespaces[a].name == options.match_namespace) { start = namespaces[a].end->prevEntry(); end = namespaces[a].start; ns_found = true; break; } } // Return none if namespace not found if (!ns_found) return NULL; } // Begin search ArchiveEntry* entry = start; while (entry != end) { // Check type if (options.match_type) { if (entry->getType() == EntryType::unknownType()) { if (!options.match_type->isThisType(entry)) { entry = entry->prevEntry(); continue; } } else if (options.match_type != entry->getType()) { entry = entry->prevEntry(); continue; } } // Check name if (!options.match_name.IsEmpty()) { if (!options.match_name.Matches(entry->getName().Lower())) { entry = entry->prevEntry(); continue; } } // Entry passed all checks so far, so we found a match return entry; } // No match found return NULL; }
/* WadArchive::getMapInfo * Returns the mapdesc_t information about the map beginning at * [maphead]. If [maphead] is not really a map header entry, an * invalid mapdesc_t will be returned (mapdesc_t::head == NULL) *******************************************************************/ Archive::mapdesc_t WadArchive::getMapInfo(ArchiveEntry* maphead) { mapdesc_t map; if (!maphead) return map; // Check for embedded wads (e.g., Doom 64 maps) if (maphead->getType()->getFormat() == "archive_wad") { map.archive = true; map.head = maphead; map.end = maphead; map.name = maphead->getName(); return map; } // Check for UDMF format map if (S_CMPNOCASE(maphead->nextEntry()->getName(), "TEXTMAP")) { // Get map info map.head = maphead; map.name = maphead->getName(); map.format = MAP_UDMF; // All entries until we find ENDMAP ArchiveEntry* entry = maphead->nextEntry(); while (true) { if (!entry || S_CMPNOCASE(entry->getName(), "ENDMAP")) break; // Check for unknown map lumps bool known = false; for (unsigned a = 0; a < NUMMAPLUMPS; a++) { if (S_CMPNOCASE(entry->getName(), map_lumps[a])) { known = true; a = NUMMAPLUMPS; } } if (!known) map.unk.push_back(entry); // Next entry entry = entry->nextEntry(); } // If we got to the end before we found ENDMAP, something is wrong if (!entry) return mapdesc_t(); // Set end entry map.end = entry; return map; } // Check for doom/hexen format map uint8_t existing_map_lumps[NUMMAPLUMPS]; memset(existing_map_lumps, 0, NUMMAPLUMPS); ArchiveEntry* entry = maphead->nextEntry(); while (entry) { // Check that the entry is a valid map-related entry bool mapentry = false; for (unsigned a = 0; a < NUMMAPLUMPS; a++) { if (S_CMPNOCASE(entry->getName(), map_lumps[a])) { mapentry = true; existing_map_lumps[a] = 1; break; } else if (a == LUMP_GL_HEADER) { string name = maphead->getName(true); name.Prepend("GL_"); if (S_CMPNOCASE(entry->getName(), name)) { mapentry = true; existing_map_lumps[a] = 1; break; } } } // If it wasn't a map entry, exit this loop if (!mapentry) { entry = entry->prevEntry(); break; } // If we've reached the end of the archive, exit this loop if (!entry->nextEntry()) break; // Go to next entry entry = entry->nextEntry(); } // Check for the required map entries for (unsigned a = 0; a < 5; a++) { if (existing_map_lumps[a] == 0) return mapdesc_t(); } // Setup map info map.head = maphead; map.end = entry; map.name = maphead->getName(); // If BEHAVIOR lump exists, it's a hexen format map if (existing_map_lumps[LUMP_BEHAVIOR]) map.format = MAP_HEXEN; // If LEAFS, LIGHTS and MACROS exist, it's a doom 64 format map else if (existing_map_lumps[LUMP_LEAFS] && existing_map_lumps[LUMP_LIGHTS] && existing_map_lumps[LUMP_MACROS]) map.format = MAP_DOOM64; // Otherwise it's doom format else map.format = MAP_DOOM; return map; }
/* MapEditorWindow::saveMap * Saves the current map to its archive, or opens the 'save as' * dialog if it doesn't currently belong to one *******************************************************************/ bool MapEditorWindow::saveMap() { // Check for newly created map if (!mdesc_current.head) return saveMapAs(); // Write map to temp wad WadArchive* wad = writeMap(); if (!wad) return false; // Check for map archive Archive* tempwad = NULL; Archive::mapdesc_t map = mdesc_current; if (mdesc_current.archive && mdesc_current.head) { tempwad = new WadArchive(); tempwad->open(mdesc_current.head); vector<Archive::mapdesc_t> amaps = tempwad->detectMaps(); if (amaps.size() > 0) map = amaps[0]; else return false; } // Unlock current map entries lockMapEntries(false); // Delete current map entries ArchiveEntry* entry = map.end; Archive* archive = map.head->getParent(); while (entry && entry != map.head) { ArchiveEntry* prev = entry->prevEntry(); archive->removeEntry(entry); entry = prev; } // Create backup if (!backup_manager->writeBackup(map_data, map.head->getTopParent()->getFilename(false), map.head->getName(true))) LOG_MESSAGE(1, "Warning: Failed to backup map data"); // Add new map entries for (unsigned a = 1; a < wad->numEntries(); a++) entry = archive->addEntry(wad->getEntry(a), archive->entryIndex(map.head) + a, NULL, true); // Clean up delete wad; if (tempwad) { tempwad->save(); delete tempwad; } else { // Update map description mdesc_current.end = entry; } // Finish lockMapEntries(); editor.getMap().setOpenedTime(); return true; }