//************************************************************************** //** //** w_wad.c : Heretic 2 : Raven Software, Corp. //** //** $RCSfile: w_wad.c,v $ //** $Revision: 1.6 $ //** $Date: 95/10/06 20:56:47 $ //** $Author: cjr $ //** //************************************************************************** // HEADER FILES ------------------------------------------------------------ #include #include #include #include #include #include "m_alloc.h" #include "doomtype.h" #include "m_argv.h" #include "i_system.h" #include "cmdlib.h" #include "w_wad.h" #include "m_crc32.h" #include "v_text.h" #include "templates.h" #include "gi.h" // MACROS ------------------------------------------------------------------ #define NULL_INDEX (0xffff) // TYPES ------------------------------------------------------------------- struct rffinfo_t { // Should be "RFF\x18" DWORD Magic; DWORD Version; DWORD DirOfs; DWORD NumLumps; }; union MergedHeader { DWORD magic; wadinfo_t wad; rffinfo_t rff; }; struct rfflump_t { BYTE IDontKnow[16]; DWORD FilePos; DWORD Size; BYTE IStillDontKnow[8]; BYTE Flags; char Extension[3]; char Name[8]; BYTE WhatIsThis[4]; }; // // WADFILE I/O related stuff. // struct FWadCollection::LumpRecord { char name[8]; int position; int size; int namespc; short wadnum; WORD flags; }; enum { LUMPF_BLOODCRYPT = 1 // Lump uses Blood-style encryption }; class FWadCollection::WadFileRecord : public FileReader { public: WadFileRecord (FILE *file); ~WadFileRecord (); char *Name; DWORD FirstLump; DWORD LastLump; }; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- void W_SysWadInit (); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static void BloodCrypt (void *data, int key, int len); static void PrintLastError (); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- // PUBLIC DATA DEFINITIONS ------------------------------------------------- FWadCollection Wads; // PRIVATE DATA DEFINITIONS ------------------------------------------------ // CODE -------------------------------------------------------------------- //========================================================================== // // strupr // // Suprisingly, glibc lacks this //========================================================================== #ifndef HAVE_STRUPR void strupr (char *s) { while (*s) *s++ = toupper (*s); } #endif //========================================================================== // // filelength // // Suprisingly, glibc lacks this //========================================================================== #ifndef HAVE_FILELENGTH int filelength (int handle) { struct stat fileinfo; if (fstat (handle, &fileinfo) == -1) { close (handle); I_Error ("Error fstating"); } return fileinfo.st_size; } #endif //========================================================================== // // uppercoppy // // [RH] Copy up to 8 chars, upper-casing them in the process //========================================================================== void uppercopy (char *to, const char *from) { int i; for (i = 0; i < 8 && from[i]; i++) to[i] = toupper (from[i]); for (; i < 8; i++) to[i] = 0; } FWadCollection::FWadCollection () : FirstLumpIndex(NULL), NextLumpIndex(NULL), LumpInfo(NULL), Wads(NULL), NumLumps(0), NumWads(0) { } //========================================================================== // // W_InitMultipleFiles // // Pass a null terminated list of files to use. All files are optional, // but at least one file must be found. Lump names can appear multiple // times. The name searcher looks backwards, so a later file can // override an earlier one. // //========================================================================== void FWadCollection::InitMultipleFiles (wadlist_t **filenames) { int numfiles; // open all the files, load headers, and count lumps numfiles = 0; NumWads = 0; NumLumps = 0; LumpInfo = NULL; // will be realloced as lumps are added while (*filenames) { wadlist_t *next = (*filenames)->next; int baselump = NumLumps; char name[PATH_MAX]; // [RH] Automatically append .wad extension if none is specified. strcpy (name, (*filenames)->name); FixPathSeperator (name); DefaultExtension (name, ".wad"); AddFile (name); free (*filenames); *filenames = next; // The first two files are always zdoom.wad and the IWAD, which // do not contain skins. if (++numfiles > 2) SkinHack (baselump); } if (NumLumps == 0) { I_FatalError ("W_InitMultipleFiles: no files found"); } // [RH] Merge sprite and flat groups. // (We don't need to bother with patches, since // Doom doesn't use markers to identify them.) RenameSprites (MergeLumps ("S_START", "S_END", ns_sprites)); MergeLumps ("F_START", "F_END", ns_flats); MergeLumps ("C_START", "C_END", ns_colormaps); MergeLumps ("A_START", "A_END", ns_acslibrary); MergeLumps ("TX_START", "TX_END", ns_newtextures); MergeLumps ("V_START", "V_END", ns_strifevoices); // [RH] Set up hash table FirstLumpIndex = new WORD[NumLumps]; NextLumpIndex = new WORD[NumLumps]; InitHashChains (); } //========================================================================== // // W_AddFile // // Files with a .wad extension are wadlink files with multiple lumps, // other files are single lumps with the base filename for the lump name. // // [RH] Removed reload hack //========================================================================== void FWadCollection::AddFile (const char *filename) { WadFileRecord *wadinfo; MergedHeader header; LumpRecord* lump_p; unsigned i; FILE* handle; int startlump; wadlump_t* fileinfo = NULL, *fileinfo2free = NULL; wadlump_t singleinfo; // open the file and add to directory handle = fopen (filename, "rb"); if (handle == NULL) { // Didn't find file Printf (TEXTCOLOR_RED " couldn't open %s\n", filename); PrintLastError (); return; } Printf (" adding %s", filename); startlump = NumLumps; wadinfo = new WadFileRecord (handle); // [RH] Determine if file is a WAD based on its signature, not its name. if (wadinfo->Read (&header, sizeof(header)) == 0) { Printf (TEXTCOLOR_RED " couldn't read %s\n", filename); PrintLastError (); delete wadinfo; return; } wadinfo->Name = copystring (filename); if (header.magic == IWAD_ID || header.magic == PWAD_ID) { // This is a WAD file header.wad.NumLumps = LittleLong(header.wad.NumLumps); header.wad.InfoTableOfs = LittleLong(header.wad.InfoTableOfs); fileinfo = fileinfo2free = new wadlump_t[header.wad.NumLumps]; wadinfo->Seek (header.wad.InfoTableOfs, SEEK_SET); wadinfo->Read (fileinfo, header.wad.NumLumps * sizeof(wadlump_t)); NumLumps += header.wad.NumLumps; Printf (" (%ld lumps)", header.wad.NumLumps); } else if (header.magic == RFF_ID) { // This is a Blood RFF file rfflump_t *lumps, *rff_p; int skipped = 0; header.rff.NumLumps = LittleLong(header.rff.NumLumps); header.rff.DirOfs = LittleLong(header.rff.DirOfs); lumps = new rfflump_t[header.rff.NumLumps]; wadinfo->Seek (header.rff.DirOfs, SEEK_SET); wadinfo->Read (lumps, header.rff.NumLumps * sizeof(rfflump_t)); BloodCrypt (lumps, header.rff.DirOfs, header.rff.NumLumps * sizeof(rfflump_t)); NumLumps += header.rff.NumLumps; LumpInfo = (LumpRecord *)Realloc (LumpInfo, NumLumps*sizeof(LumpRecord)); lump_p = &LumpInfo[startlump]; for (i = 0, rff_p = lumps; i < header.rff.NumLumps; ++i, ++rff_p) { if (rff_p->Extension[0] == 'S' && rff_p->Extension[1] == 'F' && rff_p->Extension[2] == 'X') { lump_p->namespc = ns_bloodsfx; } else if (rff_p->Extension[0] == 'R' && rff_p->Extension[1] == 'A' && rff_p->Extension[2] == 'W') { lump_p->namespc = ns_bloodraw; } else { //lump_p->namespc = ns_bloodmisc; --NumLumps; ++skipped; continue; } uppercopy (lump_p->name, rff_p->Name); lump_p->wadnum = (WORD)NumWads; lump_p->position = LittleLong(rff_p->FilePos); lump_p->size = LittleLong(rff_p->Size); lump_p->flags = (rff_p->Flags & 0x10) >> 4; lump_p++; } delete[] lumps; if (skipped != 0) { LumpInfo = (LumpRecord *)Realloc (LumpInfo, NumLumps*sizeof(LumpRecord)); } } else { // This is just a single lump file char name[PATH_MAX]; fileinfo2free = NULL; fileinfo = &singleinfo; singleinfo.FilePos = 0; singleinfo.Size = LittleLong(wadinfo->GetLength()); ExtractFileBase (filename, name); strupr (name); strncpy (singleinfo.Name, name, 8); NumLumps++; } Printf ("\n"); // Fill in lumpinfo if (header.magic != RFF_ID) { LumpInfo = (LumpRecord *)Realloc (LumpInfo, NumLumps*sizeof(LumpRecord)); lump_p = &LumpInfo[startlump]; for (i = startlump; i < (unsigned)NumLumps; i++, lump_p++, fileinfo++) { // [RH] Convert name to uppercase during copy uppercopy (lump_p->name, fileinfo->Name); lump_p->wadnum = (WORD)NumWads; lump_p->position = LittleLong(fileinfo->FilePos); lump_p->size = LittleLong(fileinfo->Size); lump_p->namespc = ns_global; lump_p->flags = 0; } if (fileinfo2free) { delete[] fileinfo2free; } ScanForFlatHack (startlump); } wadinfo->FirstLump = startlump; wadinfo->LastLump = NumLumps - 1; Wads = (WadFileRecord **)Realloc (Wads, (++NumWads)*sizeof(WadFileRecord*)); Wads[NumWads-1] = wadinfo; // [RH] Put the Strife Teaser voices into the voices namespace if (NumWads == IWAD_FILENUM+1 && gameinfo.gametype == GAME_Strife && gameinfo.flags & GI_SHAREWARE) { FindStrifeTeaserVoices (); } } //========================================================================== // // W_CheckIfWadLoaded // // Returns true if the specified wad is loaded, false otherwise. // If a fully-qualified path is specified, then the wad must match exactly. // Otherwise, any wad with that name will work, whatever its path. // //========================================================================== bool FWadCollection::CheckIfWadLoaded (const char *name) { unsigned int i; if (strrchr (name, '/') != NULL) { for (i = 0; i < NumWads; ++i) { if (stricmp (GetWadFullName (i), name) == 0) { return true; } } } else { for (i = 0; i < NumWads; ++i) { if (stricmp (GetWadName (i), name) == 0) { return true; } } } return false; } //========================================================================== // // W_NumLumps // //========================================================================== int FWadCollection::GetNumLumps () const { return NumLumps; } //========================================================================== // // W_CheckNumForName // // Returns -1 if name not found. The version with a third parameter will // look exclusively in the specified wad for the lump. // // [RH] Changed to use hash lookup ala BOOM instead of a linear search // and namespace parameter //========================================================================== int FWadCollection::CheckNumForName (const char *name, int space) { char uname[8]; WORD i; if (name == NULL) { return -1; } uppercopy (uname, name); i = FirstLumpIndex[LumpNameHash (uname) % NumLumps]; while (i != NULL_INDEX && (*(__int64 *)&LumpInfo[i].name != *(__int64 *)&uname || LumpInfo[i].namespc != space)) { i = NextLumpIndex[i]; } return i != NULL_INDEX ? i : -1; } int FWadCollection::CheckNumForName (const char *name, int space, int wadnum) { char uname[8]; WORD i; if (wadnum < 0) { return CheckNumForName (name, space); } uppercopy (uname, name); i = FirstLumpIndex[LumpNameHash (uname) % NumLumps]; while (i != NULL_INDEX && (*(__int64 *)&LumpInfo[i].name != *(__int64 *)&uname || LumpInfo[i].namespc != space || LumpInfo[i].wadnum != wadnum)) { i = NextLumpIndex[i]; } return i != NULL_INDEX ? i : -1; } //========================================================================== // // W_GetNumForName // // Calls W_CheckNumForName, but bombs out if not found. // //========================================================================== int FWadCollection::GetNumForName (const char *name) { int i; i = CheckNumForName (name); if (i == -1) I_Error ("W_GetNumForName: %s not found!", name); return i; } //========================================================================== // // W_LumpLength // // Returns the buffer size needed to load the given lump. // //========================================================================== int FWadCollection::LumpLength (int lump) const { if ((size_t)lump >= NumLumps) { I_Error ("W_LumpLength: %i >= NumLumps",lump); } return LumpInfo[lump].size; } //========================================================================== // // GetLumpOffset // // Returns the offset from the beginning of the file to the lump. // //========================================================================== int FWadCollection::GetLumpOffset (int lump) const { if ((size_t)lump >= NumLumps) { I_Error ("W_LumpLength: %i >= NumLumps",lump); } return LumpInfo[lump].position; } //========================================================================== // // W_LumpNameHash // // NOTE: s should already be uppercase, in contrast to the BOOM version. // // Hash function used for lump names. // Must be mod'ed with table size. // Can be used for any 8-character names. // //========================================================================== DWORD FWadCollection::LumpNameHash (const char *s) { const DWORD *table = GetCRCTable ();; DWORD hash = 0xffffffff; int i; for (i = 8; i > 0 && *s; --i, ++s) { hash = CRC1 (hash, *s, table); } return hash ^ 0xffffffff; } //========================================================================== // // W_InitHashChains // // Prepares the lumpinfos for hashing. // (Hey! This looks suspiciously like something from Boom! :-) // //========================================================================== void FWadCollection::InitHashChains (void) { char name[8]; unsigned int i, j; // Mark all buckets as empty memset (FirstLumpIndex, 255, NumLumps*sizeof(FirstLumpIndex[0])); memset (NextLumpIndex, 255, NumLumps*sizeof(FirstLumpIndex[0])); // Now set up the chains for (i = 0; i < (unsigned)NumLumps; i++) { uppercopy (name, LumpInfo[i].name); j = LumpNameHash (name) % NumLumps; NextLumpIndex[i] = FirstLumpIndex[j]; FirstLumpIndex[j] = i; } } //========================================================================== // // IsMarker // // (from BOOM) // //========================================================================== bool FWadCollection::IsMarker (const FWadCollection::LumpRecord *lump, const char *marker) const { if (lump->namespc != ns_global) { return false; } if (strncmp (lump->name, marker, 8) == 0) { // If the previous lump was of the form FF_END and this one is // of the form F_END, ignore this as a marker if (marker[2] == 'E' && lump > LumpInfo) { if ((lump - 1)->name[0] == *marker && strncmp ((lump - 1)->name + 1, marker, 7) == 0) { return false; } } return true; } // Treat double-character markers the same as single-character markers. // (So if FF_START appears in the wad, it will be treated as if it is F_START. // However, TTX_STAR will not be treated the same as TX_START because it // is not a single-character marker.) if (marker[1] == '_' && lump->name[0] == *marker && strncmp (lump->name + 1, marker, 7) == 0) { return true; } return false; } //========================================================================== // // ScanForFlatHack // // Try to detect wads that add extra flats by sticking an extra F_END // at the end of the flat list without any corresponding FF_START. // In other words, fix gothic2.wad. // //========================================================================== void FWadCollection::ScanForFlatHack (int startlump) { if (Args.CheckParm ("-noflathack")) { return; } for (int i = startlump; (DWORD)i < NumLumps; ++i) { if (LumpInfo[i].name[0] == 'F') { int j; if (strcmp (LumpInfo[i].name + 1, "_START") == 0 || strncmp (LumpInfo[i].name + 1, "F_START", 7) == 0) { // If the wad has a F_START/FF_START marker, check for // a FF_START-flats-FF_END-flats-F_END pattern as seen // in darkhour.wad. At what point do I stop making hacks // for wads that are incorrect? for (i = i + 1; (DWORD)i < NumLumps; ++i) { if (LumpInfo[i].name[0] == 'F' && strcmp (LumpInfo[i].name + 1, "F_END") == 0) { // Found FF_END break; } if (LumpInfo[i].size != 4096) { return; } } if (i < (int)NumLumps) { // Look for flats-F_END for (j = ++i; (DWORD)j < NumLumps; ++j) { if (LumpInfo[j].name[0] == 'F' && strcmp (LumpInfo[j].name + 1, "_END") == 0) { // Found F_END, so bump all the flats between FF_END/F_END up and move the // FF_END so it immediately precedes F_END. if (i != j - 1) { for (; i < j; ++i) { LumpInfo[i - 1] = LumpInfo[i]; } --i; strcpy (LumpInfo[i].name, "FF_END"); LumpInfo[i].size = 0; LumpInfo[i].namespc = ns_global; LumpInfo[i].flags = 0; } return; } if (LumpInfo[j].size != 4096) { return; } } } return; } // No need to look for FF_END, because Doom doesn't. One minor // nitpick: Doom will look for the last F_END; this finds the first // one if there is more than one in the file. Too bad. If there's // more than one F_END, this algorithm won't be able to properly // determine where to put the F_START anyway. if (strcmp (LumpInfo[i].name + 1, "_END") == 0) { // When F_END is found, back up past any lumps of length // 4096, then insert an F_START marker. for (j = i - 1; j >= startlump && LumpInfo[j].size == 4096; --j) { } if (j == i - 1) { // Oh no! There are no flats immediately before F_END. Maybe they are // at the beginning of the wad (e.g. slipgate.wad). for (j = startlump; LumpInfo[j].size == 4096; ++j) { } if (j > startlump) { // Okay, there are probably flats at the beginning of the wad. // Move the F_END marker so it immediately follows them, and // then add an F_START marker at the start of the wad. for (; i > j; --i) { LumpInfo[i] = LumpInfo[i-1]; } strcpy (LumpInfo[j].name, "F_END"); LumpInfo[j].size = 0; LumpInfo[j].namespc = ns_global; LumpInfo[j].flags = 0; j = startlump - 1; } else { // Oh well. There won't be any flats loaded from this wad, I guess. j = i - 1; } } ++NumLumps; LumpInfo = (LumpRecord *)Realloc (LumpInfo, NumLumps*sizeof(LumpRecord)); for (; i > j; --i) { LumpInfo[i+1] = LumpInfo[i]; } ++i; strcpy (LumpInfo[i].name, "F_START"); LumpInfo[i].size = 0; LumpInfo[i].namespc = ns_global; LumpInfo[i].flags = 0; return; } } } } //========================================================================== // // RenameSprites // // Renames sprites in IWADs so that unique actors can have unique sprites, // making it possible to import any actor from any game into any other // game without jumping through hoops to resolve duplicate sprite names. // You just need to know what the sprite's new name is. // //========================================================================== void FWadCollection::RenameSprites (int startlump) { bool renameAll; static const DWORD HereticRenames[] = { MAKE_ID('H','E','A','D'), MAKE_ID('L','I','C','H'), // Ironlich }; static const DWORD HexenRenames[] = { MAKE_ID('B','A','R','L'), MAKE_ID('Z','B','A','R'), // ZBarrel MAKE_ID('A','R','M','1'), MAKE_ID('A','R','_','1'), // MeshArmor MAKE_ID('A','R','M','2'), MAKE_ID('A','R','_','2'), // FalconShield MAKE_ID('A','R','M','3'), MAKE_ID('A','R','_','3'), // PlatinumHelm MAKE_ID('A','R','M','4'), MAKE_ID('A','R','_','4'), // AmuletOfWarding MAKE_ID('S','U','I','T'), MAKE_ID('Z','S','U','I'), // ZSuitOfArmor and ZArmorChunk MAKE_ID('T','R','E','1'), MAKE_ID('Z','T','R','E'), // ZTree and ZTreeDead MAKE_ID('T','R','E','2'), MAKE_ID('T','R','E','S'), // ZTreeSwamp150 MAKE_ID('C','A','N','D'), MAKE_ID('B','C','A','N'), // ZBlueCandle MAKE_ID('R','O','C','K'), MAKE_ID('R','O','K','K'), // rocks and dirt in a_debris.cpp MAKE_ID('W','A','T','R'), MAKE_ID('H','W','A','T'), // Strife also has WATR MAKE_ID('G','I','B','S'), MAKE_ID('P','O','L','5'), // RealGibs MAKE_ID('E','G','G','M'), MAKE_ID('P','R','K','M'), // PorkFX }; static const DWORD StrifeRenames[] = { MAKE_ID('M','I','S','L'), MAKE_ID('S','M','I','S'), // lots of places MAKE_ID('A','R','M','1'), MAKE_ID('A','R','M','3'), // MetalArmor MAKE_ID('A','R','M','2'), MAKE_ID('A','R','M','4'), // LeatherArmor MAKE_ID('P','M','A','P'), MAKE_ID('S','M','A','P'), // StrifeMap MAKE_ID('T','L','M','P'), MAKE_ID('T','E','C','H'), // TechLampSilver and TechLampBrass MAKE_ID('T','R','E','1'), MAKE_ID('T','R','E','T'), // TreeStub MAKE_ID('B','A','R','1'), MAKE_ID('B','A','R','C'), // BarricadeColumn MAKE_ID('S','H','T','2'), MAKE_ID('M','P','U','F'), // MaulerPuff MAKE_ID('B','A','R','L'), MAKE_ID('B','B','A','R'), // StrifeBurningBarrel MAKE_ID('T','R','C','H'), MAKE_ID('T','R','H','L'), // SmallTorchLit MAKE_ID('S','H','R','D'), MAKE_ID('S','H','A','R'), // glass shards MAKE_ID('B','L','S','T'), MAKE_ID('M','A','U','L'), // Mauler MAKE_ID('L','O','G','G'), MAKE_ID('L','O','G','W'), // StickInWater MAKE_ID('V','A','S','E'), MAKE_ID('V','A','Z','E'), // Pot and Pitcher MAKE_ID('C','N','D','L'), MAKE_ID('K','N','D','L'), // Candle MAKE_ID('P','O','T','1'), MAKE_ID('M','P','O','T'), // MetalPot MAKE_ID('S','P','I','D'), MAKE_ID('S','T','L','K'), // Stalker }; const DWORD *renames; int numrenames; switch (gameinfo.gametype) { case GAME_Doom: default: // Doom's sprites don't get renamed. return; case GAME_Heretic: renames = HereticRenames; numrenames = sizeof(HereticRenames)/8; break; case GAME_Hexen: renames = HexenRenames; numrenames = sizeof(HexenRenames)/8; break; case GAME_Strife: renames = StrifeRenames; numrenames = sizeof(StrifeRenames)/8; break; } renameAll = !!Args.CheckParm ("-oldsprites"); for (DWORD i = startlump + 1; i < NumLumps && *(DWORD *)LumpInfo[i].name != MAKE_ID('S','_','E','N') && *(((DWORD *)LumpInfo[i].name) + 1) != MAKE_ID('D',0,0,0); ++i) { // Only sprites in the IWAD normally get renamed if (renameAll || LumpInfo[i].wadnum == IWAD_FILENUM) { for (int j = 0; j < numrenames; ++j) { if (*(DWORD *)LumpInfo[i].name == renames[j*2]) { *(DWORD *)LumpInfo[i].name = renames[j*2+1]; } } } } } //========================================================================== // // MergeLumps // // Merge multiple tagged groups into one // Basically from BOOM, too, although I tried to write it independently. // //========================================================================== int FWadCollection::MergeLumps (const char *start, const char *end, int space) { char ustart[8], uend[8]; LumpRecord *newlumpinfos; int newlumps, oldlumps; int markerpos = -1; unsigned int i; BOOL insideBlock; uppercopy (ustart, start); uppercopy (uend, end); newlumpinfos = new LumpRecord[NumLumps]; newlumps = 0; oldlumps = 0; insideBlock = false; for (i = 0; i < NumLumps; i++) { if (!insideBlock) { // Check if this is the start of a block if (IsMarker (LumpInfo + i, ustart)) { insideBlock = true; markerpos = i; // Create start marker if we haven't already if (!newlumps) { newlumps++; strncpy (newlumpinfos[0].name, ustart, 8); newlumpinfos[0].wadnum = -1; newlumpinfos[0].position = newlumpinfos[0].size = 0; newlumpinfos[0].namespc = ns_global; } } else { // Copy lumpinfo down this list LumpInfo[oldlumps++] = LumpInfo[i]; } } else { if (i && LumpInfo[i].wadnum != LumpInfo[i-1].wadnum) { // Blocks cannot span multiple files insideBlock = false; LumpInfo[oldlumps++] = LumpInfo[i]; } else if (IsMarker (LumpInfo + i, uend)) { // It is the end of a block. We'll add the end marker once // we've processed everything. insideBlock = false; } else { newlumpinfos[newlumps] = LumpInfo[i]; newlumpinfos[newlumps++].namespc = space; } } } // Now copy the merged lumps to the end of the old list // and create the end marker entry. if (newlumps) { if (size_t(oldlumps + newlumps) > NumLumps) LumpInfo = (LumpRecord *)Realloc (LumpInfo, oldlumps + newlumps); memcpy (LumpInfo + oldlumps, newlumpinfos, sizeof(LumpRecord) * newlumps); markerpos = oldlumps; NumLumps = oldlumps + newlumps; strncpy (LumpInfo[NumLumps].name, uend, 8); LumpInfo[NumLumps].wadnum = -1; LumpInfo[NumLumps].position = LumpInfo[NumLumps].size = 0; LumpInfo[NumLumps].namespc = ns_global; NumLumps++; } delete[] newlumpinfos; return markerpos; } //========================================================================== // // FindStrifeTeaserVoices // // Strife0.wad does not have the voices between V_START/V_END markers, so // figure out which lumps are voices based on their names. // //========================================================================== void FWadCollection::FindStrifeTeaserVoices () { for (DWORD i = Wads[IWAD_FILENUM]->FirstLump; i <= Wads[IWAD_FILENUM]->LastLump; ++i) { if (LumpInfo[i].name[0] == 'V' && LumpInfo[i].name[1] == 'O' && LumpInfo[i].name[2] == 'C') { int j; for (j = 3; j < 8; ++j) { if (LumpInfo[i].name[j] != 0 && !isdigit(LumpInfo[i].name[j])) break; } if (j == 8) { LumpInfo[i].namespc = ns_strifevoices; } } } } //========================================================================== // // W_FindLump // // Find a named lump. Specifically allows duplicates for merging of e.g. // SNDINFO lumps. // //========================================================================== int FWadCollection::FindLump (const char *name, int *lastlump, bool anyns) { char name8[8]; LumpRecord *lump_p; uppercopy (name8, name); lump_p = LumpInfo + *lastlump; while (lump_p < LumpInfo + NumLumps) { if ((anyns || lump_p->namespc == ns_global) && *(__int64 *)&lump_p->name == *(__int64 *)&name8) { int lump = lump_p - LumpInfo; *lastlump = lump + 1; return lump; } lump_p++; } *lastlump = NumLumps; return -1; } //========================================================================== // // W_CheckLumpName // //========================================================================== bool FWadCollection::CheckLumpName (int lump, const char *name) { if ((size_t)lump >= NumLumps) return false; return !strnicmp (LumpInfo[lump].name, name, 8); } //========================================================================== // // W_GetLumpName // //========================================================================== void FWadCollection::GetLumpName (char *to, int lump) const { if ((size_t)lump >= NumLumps) *to = 0; else uppercopy (to, LumpInfo[lump].name); } //========================================================================== // // GetLumpNamespace // //========================================================================== int FWadCollection::GetLumpNamespace (int lump) const { if ((size_t)lump >= NumLumps) return ns_global; else return LumpInfo[lump].namespc; } //========================================================================== // // W_GetLumpFile // //========================================================================== int FWadCollection::GetLumpFile (int lump) const { if ((size_t)lump >= NumLumps) return -1; return LumpInfo[lump].wadnum; } //========================================================================== // // W_ReadLump // // Loads the lump into the given buffer, which must be >= W_LumpLength(). // //========================================================================== void FWadCollection::ReadLump (int lump, void *dest) { FWadLump lumpr = OpenLumpNum (lump); long size = lumpr.GetLength (); long numread = lumpr.Read (dest, size); if (numread != size) { I_Error ("W_ReadLump: only read %ld of %ld on lump %i\n", numread, size, lump); } if (LumpInfo[lump].flags & LUMPF_BLOODCRYPT) { BloodCrypt (dest, 0, MIN (size, 256)); } } //========================================================================== // // ReadLump - variant 2 // // Loads the lump into a newly created buffer and returns it. // //========================================================================== FMemLump FWadCollection::ReadLump (int lump) { FWadLump lumpr = OpenLumpNum (lump); long size = lumpr.GetLength (); BYTE *dest = new BYTE[size]; long numread = lumpr.Read (dest, size); if (numread != size) { I_Error ("W_ReadLump: only read %ld of %ld on lump %i\n", numread, size, lump); } if (LumpInfo[lump].flags & LUMPF_BLOODCRYPT) { BloodCrypt (dest, 0, MIN (size, 256)); } return FMemLump (dest); } //========================================================================== // // OpenLumpNum // // Returns a copy of the file object for a lump's wad and positions its // file pointer at the beginning of the lump. // //========================================================================== FWadLump FWadCollection::OpenLumpNum (int lump) { LumpRecord *l; WadFileRecord *wad; if ((unsigned)lump >= (unsigned)NumLumps) { I_Error ("W_MapLumpNum: %u >= NumLumps",lump); } l = &LumpInfo[lump]; wad = Wads[l->wadnum]; #if 0 // Blood encryption? if (writeable || l->flags & LUMPF_BLOODCRYPT) { ptr = Malloc (l->size); NonMappedPointers.insert (ptr); W_ReadLump (lump, ptr); return ptr; } #endif wad->Seek (l->position, SEEK_SET); return FWadLump (*wad, l->size); } //========================================================================== // // ReopenLumpNum // // Similar to OpenLumpNum, except a new, independant file object is created // for the lump's wad. Use this when you won't read the lump's data all at // once (e.g. for streaming an Ogg Vorbis stream from a wad as music). // //========================================================================== FWadLump *FWadCollection::ReopenLumpNum (int lump) { LumpRecord *l; WadFileRecord *wad; FILE *f; if ((unsigned)lump >= (unsigned)NumLumps) { I_Error ("W_MapLumpNum: %u >= NumLumps",lump); } l = &LumpInfo[lump]; wad = Wads[l->wadnum]; f = fopen (wad->Name, "rb"); if (f == NULL) { I_Error ("Could not reopen %s\n", wad->Name); } fseek (f, l->position, SEEK_SET); return new FWadLump (f, l->size); } //========================================================================== // // W_GetWadName // // Returns the name of the given wad. // //========================================================================== const char *FWadCollection::GetWadName (int wadnum) const { const char *name, *slash; if ((DWORD)wadnum >= NumWads) { return NULL; } name = Wads[wadnum]->Name; slash = strrchr (name, '/'); return slash != NULL ? slash+1 : name; } //========================================================================== // // W_GetWadFullName // // Returns the name of the given wad, including any path // //========================================================================== const char *FWadCollection::GetWadFullName (int wadnum) const { if ((unsigned int)wadnum >= NumWads) { return NULL; } return Wads[wadnum]->Name; } //========================================================================== // // W_SkinHack // // Tests a wad file to see if it contains an S_SKIN marker. If it does, // every lump in the wad is moved into a new namespace. Because skins are // only supposed to replace player sprites, sounds, or faces, this should // not be a problem. Yes, there are skins that replace more than that, but // they are such a pain, and breaking them like this was done on purpose. // This also renames any S_SKINxx lumps to just S_SKIN. // //========================================================================== void FWadCollection::SkinHack (int baselump) { bool skinned = false; bool hasmap = false; size_t i; for (i = baselump; i < NumLumps; i++) { if (LumpInfo[i].name[0] == 'S' && LumpInfo[i].name[1] == '_' && LumpInfo[i].name[2] == 'S' && LumpInfo[i].name[3] == 'K' && LumpInfo[i].name[4] == 'I' && LumpInfo[i].name[5] == 'N') { // Wad has at least one skin. LumpInfo[i].name[6] = LumpInfo[i].name[7] = 0; if (!skinned) { skinned = true; size_t j; for (j = baselump; j < NumLumps; j++) { // Using the baselump as the namespace is safe, because // zdoom.wad guarantees the first possible baselump // passed to this function is a largish number. LumpInfo[j].namespc = baselump; } } } if (LumpInfo[i].name[0] == 'M' && LumpInfo[i].name[1] == 'A' && LumpInfo[i].name[2] == 'P') { hasmap = true; } } if (skinned && hasmap) { Printf (TEXTCOLOR_BLUE "The maps in %s will not be loaded because it has a skin.\n" TEXTCOLOR_BLUE "You should remove the skin from the wad to play these maps.\n", Wads[LumpInfo[baselump].wadnum]->Name); } } //========================================================================== // // BloodCrypt // //========================================================================== static void BloodCrypt (void *data, int key, int len) { int p = (BYTE)key, i; for (i = 0; i < len; ++i) { ((BYTE *)data)[i] ^= (unsigned char)(p+(i>>1)); } } //========================================================================== // // PrintLastError // //========================================================================== #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include static void PrintLastError () { char *lpMsgBuf; FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR)&lpMsgBuf, 0, NULL ); Printf (TEXTCOLOR_RED " %s\n", lpMsgBuf); // Free the buffer. LocalFree( lpMsgBuf ); } #else static void PrintLastError () { Printf (TEXTCOLOR_RED " %s\n", strerror(errno)); } #endif // WadFileRecord ------------------------------------------------------------ FWadCollection::WadFileRecord::WadFileRecord (FILE *file) : FileReader(file), Name(NULL), FirstLump(0), LastLump(0) { } FWadCollection::WadFileRecord::~WadFileRecord () { if (Name != NULL) { delete[] Name; } } // FWadLump ----------------------------------------------------------------- FWadLump::FWadLump () : FileReader () { } FWadLump::FWadLump (const FWadLump ©) { File = copy.File; } FWadLump::FWadLump (const FileReader &other, long length) : FileReader (other, length) { } FWadLump::FWadLump (FILE *file, long length) : FileReader (file, length) { } // FMemLump ----------------------------------------------------------------- FMemLump::FMemLump () : Block (NULL) { } #ifdef __GNUC__ FMemLump::FMemLump (const FMemLump ©) #else FMemLump::FMemLump (FMemLump ©) #endif { Block = copy.Block; const_cast(©)->Block = NULL; } #ifdef __GNUC__ FMemLump &FMemLump::operator = (const FMemLump ©) #else FMemLump &FMemLump::operator = (FMemLump ©) #endif { if (Block != NULL) { delete[] Block; } Block = copy.Block; const_cast(©)->Block = NULL; return *this; } FMemLump::FMemLump (BYTE *data) : Block (data) { } FMemLump::~FMemLump () { if (Block != NULL) { delete[] Block; } }