/* ** p_openmap.cpp ** ** creates the data structures needed to load a map from the resource files. ** **--------------------------------------------------------------------------- ** Copyright 2005-2018 Christoph Oelckers ** Copyright 2005-2016 Randy Heit ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **--------------------------------------------------------------------------- ** */ #include "p_setup.h" #include "cmdlib.h" #include "filesystem.h" #include "md5.h" #include "g_levellocals.h" #include "cmdlib.h" #define IWAD_ID MAKE_ID('I','W','A','D') #define PWAD_ID MAKE_ID('P','W','A','D') inline bool P_IsBuildMap(MapData *map) { return false; } //=========================================================================== // // GetMapIndex // // Gets the type of map lump or -1 if invalid or -2 if required and not found. // //=========================================================================== struct checkstruct { const char lumpname[9]; bool required; }; static int GetMapIndex(const char *mapname, int lastindex, const char *lumpname, bool needrequired) { static const checkstruct check[] = { {"", true}, {"THINGS", true}, {"LINEDEFS", true}, {"SIDEDEFS", true}, {"VERTEXES", true}, {"SEGS", false}, {"SSECTORS", false}, {"NODES", false}, {"SECTORS", true}, {"REJECT", false}, {"BLOCKMAP", false}, {"BEHAVIOR", false}, //{"SCRIPTS", false}, }; if (lumpname==NULL) lumpname=""; for(size_t i=lastindex+1;iresource = FResourceFile::OpenResourceFile(mapname, true); wadReader = map->resource->GetReader(); } else { FString fmt; int lump_wad; int lump_map; int lump_name = -1; // Check for both *.wad and *.map in order to load Build maps // as well. The higher one will take precedence. // Names with more than 8 characters will only be checked as .wad and .map. if (strlen(mapname) <= 8) lump_name = fileSystem.CheckNumForName(mapname); fmt.Format("maps/%s.wad", mapname); lump_wad = fileSystem.CheckNumForFullName(fmt); fmt.Format("maps/%s.map", mapname); lump_map = fileSystem.CheckNumForFullName(fmt); if (lump_name > lump_wad && lump_name > lump_map && lump_name != -1) { int lumpfile = fileSystem.GetFileContainer(lump_name); int nextfile = fileSystem.GetFileContainer(lump_name+1); map->lumpnum = lump_name; if (lumpfile != nextfile) { // The following lump is from a different file so whatever this is, // it is not a multi-lump Doom level so let's assume it is a Build map. map->MapLumps[0].Reader = fileSystem.ReopenFileReader(lump_name); if (!P_IsBuildMap(map)) { delete map; return NULL; } return map; } // This case can only happen if the lump is inside a real WAD file. // As such any special handling for other types of lumps is skipped. map->MapLumps[0].Reader = fileSystem.ReopenFileReader(lump_name); strncpy(map->MapLumps[0].Name, fileSystem.GetFileFullName(lump_name), 8); map->InWad = true; int index = 0; if (stricmp(fileSystem.GetFileFullName(lump_name + 1), "TEXTMAP") != 0) { for(int i = 1;; i++) { // Since levels must be stored in WADs they can't really have full // names and for any valid level lump this always returns the short name. const char * lumpname = fileSystem.GetFileFullName(lump_name + i); try { index = GetMapIndex(mapname, index, lumpname, !justcheck); } catch(...) { delete map; throw; } if (index == -2) { delete map; return NULL; } if (index == ML_BEHAVIOR) map->HasBehavior = true; // The next lump is not part of this map anymore if (index < 0) break; map->MapLumps[index].Reader = fileSystem.ReopenFileReader(lump_name + i); strncpy(map->MapLumps[index].Name, lumpname, 8); } } else { map->isText = true; map->MapLumps[1].Reader = fileSystem.ReopenFileReader(lump_name + 1); for(int i = 2;; i++) { const char * lumpname = fileSystem.GetFileFullName(lump_name + i); if (lumpname == NULL) { I_Error("Invalid map definition for %s", mapname); } else if (!stricmp(lumpname, "ZNODES")) { index = ML_GLZNODES; } else if (!stricmp(lumpname, "BLOCKMAP")) { // there is no real point in creating a blockmap but let's use it anyway index = ML_BLOCKMAP; } else if (!stricmp(lumpname, "REJECT")) { index = ML_REJECT; } else if (!stricmp(lumpname, "DIALOGUE")) { index = ML_CONVERSATION; } else if (!stricmp(lumpname, "BEHAVIOR")) { index = ML_BEHAVIOR; map->HasBehavior = true; } else if (!stricmp(lumpname, "ENDMAP")) { break; } else continue; map->MapLumps[index].Reader = fileSystem.ReopenFileReader(lump_name + i); strncpy(map->MapLumps[index].Name, lumpname, 8); } } return map; } else { if (lump_map > lump_wad) { lump_wad = lump_map; } if (lump_wad == -1) { delete map; return NULL; } map->lumpnum = lump_wad; auto reader = fileSystem.ReopenFileReader(lump_wad); map->resource = FResourceFile::OpenResourceFile(fileSystem.GetFileFullName(lump_wad), reader, true); wadReader = map->resource->GetReader(); } } uint32_t id; // Although we're using the resource system, we still want to be sure we're // reading from a wad file. wadReader->Seek(0, FileReader::SeekSet); wadReader->Read(&id, sizeof(id)); if (id == IWAD_ID || id == PWAD_ID) { char maplabel[9]=""; int index=0; map->MapLumps[0].Reader = map->resource->GetLump(0)->NewReader(); uppercopy(map->MapLumps[0].Name, map->resource->GetLump(0)->getName()); for(uint32_t i = 1; i < map->resource->LumpCount(); i++) { const char* lumpname = map->resource->GetLump(i)->getName(); if (i == 1 && !strnicmp(lumpname, "TEXTMAP", 8)) { map->isText = true; map->MapLumps[ML_TEXTMAP].Reader = map->resource->GetLump(i)->NewReader(); strncpy(map->MapLumps[ML_TEXTMAP].Name, lumpname, 8); for(int i = 2;; i++) { lumpname = map->resource->GetLump(i)->getName(); if (!strnicmp(lumpname, "ZNODES",8)) { index = ML_GLZNODES; } else if (!strnicmp(lumpname, "BLOCKMAP",8)) { // there is no real point in creating a blockmap but let's use it anyway index = ML_BLOCKMAP; } else if (!strnicmp(lumpname, "REJECT",8)) { index = ML_REJECT; } else if (!strnicmp(lumpname, "DIALOGUE",8)) { index = ML_CONVERSATION; } else if (!strnicmp(lumpname, "BEHAVIOR",8)) { index = ML_BEHAVIOR; map->HasBehavior = true; } else if (!strnicmp(lumpname, "ENDMAP",8)) { return map; } else continue; map->MapLumps[index].Reader = map->resource->GetLump(i)->NewReader(); strncpy(map->MapLumps[index].Name, lumpname, 8); } } if (i>0) { try { index = GetMapIndex(maplabel, index, lumpname, !justcheck); } catch(...) { delete map; throw; } if (index == -2) { delete map; return NULL; } if (index == ML_BEHAVIOR) map->HasBehavior = true; // The next lump is not part of this map anymore if (index < 0) break; } else { strncpy(maplabel, lumpname, 8); maplabel[8]=0; } map->MapLumps[index].Reader = map->resource->GetLump(i)->NewReader(); strncpy(map->MapLumps[index].Name, lumpname, 8); } } else { // This is a Build map and not subject to WAD consistency checks. //map->MapLumps[0].Size = wadReader->GetLength(); if (!P_IsBuildMap(map)) { delete map; return NULL; } } return map; } bool P_CheckMapData(const char *mapname) { MapData *mapd = P_OpenMapData(mapname, true); if (mapd == NULL) return false; delete mapd; return true; } //=========================================================================== // // MapData :: GetChecksum // // Hashes a map based on its header, THINGS, LINEDEFS, SIDEDEFS, SECTORS, // and BEHAVIOR lumps. Node-builder generated lumps are not included. // //=========================================================================== void MapData::GetChecksum(uint8_t cksum[16]) { MD5Context md5; if (isText) { md5Update(Reader(ML_TEXTMAP), md5, Size(ML_TEXTMAP)); } else { md5Update(Reader(ML_LABEL), md5, Size(ML_LABEL)); md5Update(Reader(ML_THINGS), md5, Size(ML_THINGS)); md5Update(Reader(ML_LINEDEFS), md5, Size(ML_LINEDEFS)); md5Update(Reader(ML_SIDEDEFS), md5, Size(ML_SIDEDEFS)); md5Update(Reader(ML_SECTORS), md5, Size(ML_SECTORS)); } if (HasBehavior) { md5Update(Reader(ML_BEHAVIOR), md5, Size(ML_BEHAVIOR)); } md5.Final(cksum); }