#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;
}