#include <stdio.h> #include <string.h> #include "Wad.h" GameWad::GameWad( void ) { // Default constructor - Initializes data members. strcpy(WadFileName,""); WadFilePtr = NULL; WadFileInitialized = 0; DirectoryOffset = 0; NumberOfLumps = 0; NbrTextures = 0; Things = NULL; Vertices = NULL; Linedefs = NULL; Sectors = NULL; SSectors = NULL; Sidedefs = NULL; Nodes = NULL; Pnames = NULL; Textures = NULL; } GameWad::GameWad( char* Fname ) { strcpy(WadFileName, Fname); WadFilePtr = NULL; WadFileInitialized = 0; DirectoryOffset = 0; NumberOfLumps = 0; NbrTextures = 0; Things = NULL; Vertices = NULL; Linedefs = NULL; Sectors = NULL; SSectors = NULL; Sidedefs = NULL; Nodes = NULL; Pnames = NULL; Textures = NULL; InitWadFile( Fname ); } GameWad::~GameWad( void ) { clearMap(); if (NULL != Pnames ) { delete Pnames; Pnames = NULL; } if (NULL != Textures ) { delete[] Textures; Textures = NULL; } } int GameWad::clearMap(void) { if (NULL != Things ) { delete Things; Things = NULL; } if (NULL != Vertices ) { delete Vertices; Vertices = NULL; } if (NULL != Linedefs ) { delete Linedefs; Linedefs = NULL; } if (NULL != Sectors ) { delete Sectors; Sectors = NULL; } if (NULL != SSectors ) { delete SSectors; SSectors = NULL; } if (NULL != Sidedefs ) { delete Sidedefs; Sidedefs = NULL; } if (NULL != Nodes ) { delete Nodes; Nodes = NULL; } return 1; } int GameWad::InitWadFile( char* Fname ) { WadHdrLoad WadHeader; // Step 1. Open the file. strcpy(WadFileName, Fname); if( NULL == (WadFilePtr = fopen(Fname, "rb"))) { return -1; } // Step 2. Read in the header. Ensure we're reading a 'IWAD' file. if( 1 != fread((void *)&WadHeader, sizeof(WadHdrLoad), 1, WadFilePtr) ) { return -1; } if ( WadHeader.Type[0] != 'I' && WadHeader.Type[1] != 'W' && WadHeader.Type[2] != 'A' && WadHeader.Type[3] != 'D' ) { return -2; } NumberOfLumps = WadHeader.NbrLumps; DirectoryOffset = WadHeader.DirectoryOffset; if (findDirectoryEntry("PLAYPAL")) loadPalettes(); if (findDirectoryEntry("COLORMAP")) loadColorMaps(); if (findDirectoryEntry("PNAMES")) loadPNames(); if (findDirectoryEntry("TEXTURE1")) loadTextures(); // // This is so we can check that this step was done later. // WadFileInitialized = 1; // Step 5. Close the file. fclose(WadFilePtr); return 1; } int GameWad::LoadMap( int episode, int level ) { // // This function loads a level, i.e. E1M1 into the appropriate structures. // char MapName[5]; char EntryName[9]; int ReadLvl = 0; int StillReadingLumps = 1; WadHdrLoad WadHeader; DirectoryEntryLoad DirectoryEntry; if (0 == WadFileInitialized) return -1; // // If there is another map loaded, unload it. // clearMap(); // Step 1. Open the file. (It was closed after the call to ReadWadFile) if( NULL == (WadFilePtr = fopen(WadFileName, "rb"))) return -1; // Step 2. Read in the header to ensure we're reading the 'IWAD' file. if( 1 != fread((void *)&WadHeader, sizeof(WadHdrLoad), 1, WadFilePtr) ) return -1; if ( WadHeader.Type[0] != 'I' && WadHeader.Type[1] != 'W' && WadHeader.Type[2] != 'A' && WadHeader.Type[3] != 'D' ) return -2; // // Step 3. Generate the name of the tag we're going to search for. // sprintf(MapName, "E%1dM%1d", episode, level); if( 1 == findDirectoryEntry((const char*)MapName)) { // // The next 10 lumps define the level. They could be in any order. // for (int i=0; i < 10; ++i) { // Step 4a. Get the entry from the file. fread((void *)&DirectoryEntry, sizeof(DirectoryEntryLoad), 1, WadFilePtr); memset(EntryName, 0, 9); strncpy(EntryName, DirectoryEntry.LumpName, 8); if ( 0 == strcmp(EntryName,"THINGS")) { // Read the list of things. int NumThings = DirectoryEntry.LumpSize / sizeof(Thing); loadThings(DirectoryEntry.LumpStartOffset, NumThings); } if ( 0 == strcmp(EntryName,"LINEDEFS")) { // Read the linedefs. int NumLineDefs = DirectoryEntry.LumpSize / sizeof(LineDef); loadLineDefs(DirectoryEntry.LumpStartOffset, NumLineDefs); } if ( 0 == strcmp(EntryName,"SIDEDEFS")) { // Read the sidedefs. int NumSideDefs = DirectoryEntry.LumpSize / sizeof(SideDef); loadSideDefs(DirectoryEntry.LumpStartOffset, NumSideDefs); } if ( 0 == strcmp(EntryName,"VERTEXES")) { // Read the vertex list. int NumVertexes = DirectoryEntry.LumpSize / sizeof(Vertex); loadVertexes(DirectoryEntry.LumpStartOffset, NumVertexes); } if ( 0 == strcmp(EntryName,"SEGS")) { // Read the list of segments. int NumSegments = DirectoryEntry.LumpSize / sizeof(Seg); loadSegments(DirectoryEntry.LumpStartOffset, NumSegments); } if ( 0 == strcmp(EntryName,"SSECTORS")) { // Read the list of sub sectors. int NumSSectors = DirectoryEntry.LumpSize / sizeof(SSector); loadSSectors(DirectoryEntry.LumpStartOffset, NumSSectors); } if ( 0 == strcmp(EntryName,"NODES")) { // Read the list of nodes ( BSP Tree ). int NumNodes = DirectoryEntry.LumpSize / sizeof(Node); loadNodes(DirectoryEntry.LumpStartOffset, NumNodes); } if ( 0 == strcmp(EntryName,"SECTORS")) { // Read the list of sectors. int NumSectors = DirectoryEntry.LumpSize / sizeof(Sector); loadSectors(DirectoryEntry.LumpStartOffset, NumSectors); } } // Close the file. fclose(WadFilePtr); // Indicate a successful load. return 1; } // Close the file. fclose(WadFilePtr); // Indicate we couldn't find the level. return -1; } int GameWad::findDirectoryEntry(const char* SearchName, long* LumpSize) { // // Will search the WAD directory for a specific lump. If found, it will position the // file pointer to point to that lump, and return NON-ZERO (True). If the found entry is // a flag, i.e. E1M1, the pointer will point to the directory entry just after the one found. // If *LumpSize is specified, it will place the size of the lump there. // If the lump is not found, the file pointer will not be changed, and the function will // return zero (False). // char EntryName[9]; long startingOffset; DirectoryEntryLoad DirectoryEntry; startingOffset = ftell(WadFilePtr); // Move the file pointer to the beginning of the Directory. if( 0 != fseek(WadFilePtr, DirectoryOffset, SEEK_SET)) return 0; // For each item in the directory, for( int i=0; i < NumberOfLumps; ++i ) { // Get the entry from the file. fread((void *)&DirectoryEntry, sizeof(DirectoryEntryLoad), 1, WadFilePtr); memset(EntryName, 0, 9); strncpy(EntryName, DirectoryEntry.LumpName, 8); if (strcmp(EntryName, SearchName) == 0) { if (DirectoryEntry.LumpSize > 0) { fseek(WadFilePtr, DirectoryEntry.LumpStartOffset, SEEK_SET); } if (NULL != LumpSize) *LumpSize = DirectoryEntry.LumpSize; return 1; } } fseek(WadFilePtr, startingOffset, SEEK_SET); return 0; } int GameWad::loadThings(long StartOffset, long NumItems) { // // This will load the various "Thing" definitions. // Things = new Thing[NumItems]; long originalOffset = 0; originalOffset = ftell(WadFilePtr); fseek(WadFilePtr, StartOffset, SEEK_SET); fread((void *)&Things[0], sizeof(Thing), NumItems, WadFilePtr); fseek(WadFilePtr, originalOffset, SEEK_SET); for (int i=0; i< NumItems; ++i) if (Things[i].ThingType == 1) { PlayerX = Things[i].StartX; PlayerY = Things[i].StartY; PlayerAngle = Things[i].StartAngle; } return 1; } int GameWad::loadLineDefs(long StartOffset, long NumItems) { Linedefs = new LineDef[NumItems]; long originalOffset = 0; originalOffset = ftell(WadFilePtr); fseek(WadFilePtr, StartOffset, SEEK_SET); fread((void *)&Linedefs[0], sizeof(LineDef), NumItems, WadFilePtr); fseek(WadFilePtr, originalOffset, SEEK_SET); return 1; } int GameWad::loadSideDefs(long StartOffset, long NumItems) { Sidedefs = new SideDef[NumItems]; long originalOffset = 0; originalOffset = ftell(WadFilePtr); fseek(WadFilePtr, StartOffset, SEEK_SET); fread((void *)&Sidedefs[0], sizeof(SideDef), NumItems, WadFilePtr); fseek(WadFilePtr, originalOffset, SEEK_SET); return 1; } int GameWad::loadVertexes(long StartOffset, long NumItems) { long originalOffset = 0; Vertices = new Vertex[NumItems]; NbrVertices = NumItems; originalOffset = ftell(WadFilePtr); fseek(WadFilePtr, StartOffset, SEEK_SET); fread((void *)&Vertices[0], sizeof(Vertex), NumItems, WadFilePtr); fseek(WadFilePtr, originalOffset, SEEK_SET); return 1; } int GameWad::loadSegments(long StartOffset, long NumItems) { Segs = new Seg[NumItems]; long originalOffset = 0; originalOffset = ftell(WadFilePtr); fseek(WadFilePtr, StartOffset, SEEK_SET); fread((void *)&Segs[0], sizeof(Seg), NumItems, WadFilePtr); fseek(WadFilePtr, originalOffset, SEEK_SET); return 1; } int GameWad::loadSSectors(long StartOffset, long NumItems) { SSectors = new SSector[NumItems]; long originalOffset = 0; originalOffset = ftell(WadFilePtr); fseek(WadFilePtr, StartOffset, SEEK_SET); fread((void *)&SSectors[0], sizeof(SSector), NumItems, WadFilePtr); fseek(WadFilePtr, originalOffset, SEEK_SET); return 1; } int GameWad::loadNodes(long StartOffset, long NumItems) { long originalOffset = 0; Nodes = new Node[NumItems+1]; MaxNode = NumItems - 1; originalOffset = ftell(WadFilePtr); fseek(WadFilePtr, StartOffset, SEEK_SET); fread((void *)&Nodes[0], sizeof(Node), NumItems, WadFilePtr); fseek(WadFilePtr, originalOffset, SEEK_SET); return 1; } int GameWad::loadSectors(long StartOffset, long NumItems) { Sectors = new Sector[NumItems]; long originalOffset = 0; originalOffset = ftell(WadFilePtr); fseek(WadFilePtr, StartOffset, SEEK_SET); fread((void *)&Sectors[0], sizeof(Sector), NumItems, WadFilePtr); fseek(WadFilePtr, originalOffset, SEEK_SET); return 1; } int GameWad::loadPalettes( void ) { // // This will load the 14 palettes used by the system. // for (int i=0; i<14; ++i) fread((void*)&GamePalettes[i][0], 768, 1, WadFilePtr); return 1; } int GameWad::loadColorMaps( void ) { // // This will load the 34 palettes used by the system. // for (int i=0; i<34; ++i) fread((void*)&ColorMap[i][0], 256, 1, WadFilePtr); return 1; } int GameWad::loadPNames( void ) { long NumItems = 0; // Read the number of Pnames fread((void*)&NumItems, sizeof(long), 1, WadFilePtr); Pnames = new PName[NumItems]; // Read each PName and save it in the array. for (int i=0; i<NumItems; ++i) { Pnames[i].index = i; fread((void*)&Pnames[i].Name[0], 1, 8, WadFilePtr); Pnames[i].Name[8] = '\0'; } return 1; } int GameWad::loadTextures( void ) { // // This will load the various Texture definitions. // long StartOffset = 0; long nextoffset = 0; long NumItems = 0; long* TxtOffset; int i = 0, j=0; TextureLdrRec TextureRecord; PatchDscr* TxtPatchRec; StartOffset = ftell(WadFilePtr); // Read the number of Textures fread((void*)&NumItems, sizeof(long), 1, WadFilePtr); Textures = new Texture[NumItems]; NbrTextures = NumItems; // Read the texture offsets TxtOffset = new long[NumItems]; fread((void*)TxtOffset, sizeof(long), NumItems, WadFilePtr); // For each texture definition: for (i=0; i<NumItems; ++i) { // Get the texture header. fseek(WadFilePtr, TxtOffset[i] + StartOffset, SEEK_SET); fread((void*)&TextureRecord, sizeof(TextureLdrRec), 1, WadFilePtr); // Set up the texture item in the array. strncpy(Textures[i].TxtName, TextureRecord.TxtName, 8); Textures[i].TxtName[8] = '\0'; Textures[i].TxtWidth = TextureRecord.TxtWidth; Textures[i].TxtHeight = TextureRecord.TxtHeight; Textures[i].TxtPtr = new char[(unsigned long)TextureRecord.TxtWidth * (unsigned long)TextureRecord.TxtHeight]; TxtPatchRec = new PatchDscr[TextureRecord.NumPatches]; if (NULL != TxtPatchRec) fread((void*)&TxtPatchRec[0], sizeof(PatchDscr), TextureRecord.NumPatches, WadFilePtr); // Now, for each texture patch, for (j=0;((NULL != TxtPatchRec) && (j< TextureRecord.NumPatches)); ++j) { long PatchWidth = 0; long PatchHeight = 0; long TxtPatchStartX = TxtPatchRec[j].StartX; long TxtPatchStartY = TxtPatchRec[j].StartY; // Now load the patch data char* Patch = ParseTextureData(Pnames[TxtPatchRec[j].PnameNbr].Name, PatchWidth, PatchHeight); // and add it to the current texture // (Let's transpose the texture too. It'll make rendering MUCH faster!) if( NULL != Patch ) { for( long col = TxtPatchStartX; col < TxtPatchStartX + PatchWidth; ++col ) for( long row = TxtPatchStartY; row < TxtPatchStartY + PatchHeight; ++row ) if (((col >= 0) && (col < Textures[i].TxtWidth)) && ((row >= 0) && (row < Textures[i].TxtHeight))) *(Textures[i].TxtPtr + col * Textures[i].TxtHeight + row ) = *(Patch + col - TxtPatchStartX + (row - TxtPatchStartY) * PatchWidth ); delete Patch; } } if (NULL != TxtPatchRec) delete TxtPatchRec; } delete TxtOffset; return 1; } char* GameWad::ParseTextureData( const char* DirectoryName, long& Width, long& Height) { long OrigOffset = 0; long PatchLumpStart = 0; long row = 0; unsigned char firstRow; unsigned char numPels; long StartRow = 0; long* ColumnOffsets; char* PatchData = NULL; char bt; PatchLdrRec PatchHdr; Width = 0; Height = 0; // Find the Patch Lump in the WAD file. OrigOffset = ftell(WadFilePtr); if (findDirectoryEntry(DirectoryName)) { PatchLumpStart = ftell(WadFilePtr); // If found, load the header for the patch. fread((void*)&PatchHdr, sizeof(PatchLdrRec), 1, WadFilePtr); PatchData = new char[PatchHdr.Width * PatchHdr.Height]; Width = (long)PatchHdr.Width; Height = (long)PatchHdr.Height; // Now read the offsets to the columns. ColumnOffsets = new long[PatchHdr.Width]; fread((void*)&ColumnOffsets[0], sizeof(long), PatchHdr.Width, WadFilePtr); // Now, for each column, there will be an offset pointer. for( int i=0; i< PatchHdr.Width; ++i) { // Go to the column. fseek(WadFilePtr, ColumnOffsets[i] + PatchLumpStart, SEEK_SET); // Parse the posts. firstRow = fgetc(WadFilePtr); while(firstRow != 0xFF ) { numPels = fgetc(WadFilePtr); fgetc(WadFilePtr); for( row = firstRow; row < firstRow + numPels; ++row ) { bt = (unsigned char)getc(WadFilePtr); if( row >= 0 && row < PatchHdr.Height) *(PatchData + (long)row*(PatchHdr.Width) + i ) = bt; } getc(WadFilePtr); firstRow = fgetc(WadFilePtr); } } } fseek(WadFilePtr, OrigOffset, SEEK_SET); return PatchData; } void GameWad::getPlayerStart( short& X, short& Y, short& Ang) { X = PlayerX; Y = PlayerY; Ang = PlayerAngle; } int GameWad::Save_Pcx(char *filename, unsigned char *Buffer, unsigned long width, unsigned long height) { unsigned long count, // the count for the RLE run. bcount, // the count for the line. index, // Overall byte count. TotalSize; unsigned char data; // The data byte written. FILE *outfile; // The file pointer. PCX_HEADER header; // The PCX header info. outfile = fopen(filename, "wb"); if(NULL == outfile) { return -1; } // Build a PCX file header. Must be little endian, so beware header.manufacturer = 10; header.version = 5; header.encoding = 1; header.bits_per_pixel = 8; header.x = 0; header.y = 0; header.width = (short)width-1; header.height = (short)height-1; header.horiz_rez = (short)width; header.vert_rez = (short)height; header.Num_Planes = 1; header.bytes_per_line = (short)width; header.palette_type = 1; // Write the PCX file header fwrite((void *)&header,sizeof(PCX_HEADER),1,outfile); // Do run length encoding for pixel runs of 2 to 63. Add 192 to the // run count, and write it before the pixel to indicate how many are // present. Runs are not encoded across line boundaries, so // bcount keeps track of how many bytes have been written per line, // and when it hits "width", the run is written even if less than 63. bcount = 0; index = 0; TotalSize = width * height; while (index < TotalSize) { if((index < TotalSize-1) && (Buffer[index] == Buffer[index + 1]) && (bcount < width-1)) { // We have a run of two or more data = Buffer[index]; count = 2; while((index + count < TotalSize) && (data == Buffer[index + count]) && (count < 63) && (bcount + count < width) ) count++; index = index + count; bcount = bcount + count; putc((char)(count + 192), outfile); putc(data, outfile); } else { // We are writing one byte. If byte is 192-255, put a run count of 1 (193) in front of it if(Buffer[index] >= 192) putc((char)193, outfile); putc((char)Buffer[index], outfile); index++; bcount++; } if(bcount == width) bcount = 0; } // Write the palette // 256 color palette must be prefixed with this byte putc((char)12, outfile); for(index=0; index<768; index++) putc((char)GamePalettes[0][index], outfile); fclose(outfile); return 1; } /* char* GameWad::getBSPTree( void ) { // Return a pointer to the BSP tree. } */ RGBQUAD* GameWad::getPalette( int paletteNbr ) { RGBQUAD *palettePtr = new RGBQUAD[256]; for (int i= 0; i < 256; ++i) { palettePtr[i].rgbRed = GamePalettes[0][i*3]; palettePtr[i].rgbGreen = GamePalettes[0][i*3+1]; palettePtr[i].rgbBlue = GamePalettes[0][i*3+2]; } return palettePtr; } char* GameWad::getTexturePtr( const char* txtName, short& width, short& height ) { // // this method will search the array of loaded textures, and find the one with the // specified name. If found, it will place the width and height into the appropriate // reference parameters, and return the pointer to the texture data. If not found, it // will set the reference parameters to 0, and return NULL. // for (int i=0; i< NbrTextures; ++i) { if( 0 == strncmp(Textures[i].TxtName, txtName,8)) { width = Textures[i].TxtWidth; height = Textures[i].TxtHeight; return Textures[i].TxtPtr; } } width = 0; height = 0; return NULL; }