358 lines
8.4 KiB
C++
358 lines
8.4 KiB
C++
|
//fileio.h
|
||
|
//Utilities for reading and writing to files in the game's subdirectories
|
||
|
|
||
|
|
||
|
//fileio works by creating a table of all files within the applications directory
|
||
|
//at startup. this way all files we need can be addressed simply using reference numbers.
|
||
|
//sometimes also refered to as "ID numbers".
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <ctype.h>
|
||
|
#include "fileio.h"
|
||
|
#include "platform.h"
|
||
|
#include "gamemem.h"
|
||
|
#include "error.h"
|
||
|
#include "parser.h"
|
||
|
|
||
|
int gFileTableSize; //size of the file reference table
|
||
|
int gFileTableExtendedSize;
|
||
|
int gFileTampered=false;
|
||
|
tFileTableEntry *gFileTable; //the file reference table
|
||
|
char gNoName[]="";
|
||
|
|
||
|
void FileInitFileTable(tFileTableEntry *fileTable,int maxSize,int *fileTableSize,int reInit);
|
||
|
|
||
|
#define kNumValidateFiles 42
|
||
|
|
||
|
char gValidateNames[][256]={
|
||
|
"500gt.car",
|
||
|
"500gthotrod.car",
|
||
|
"959.car",
|
||
|
"maserati.car",
|
||
|
"bmw2002.car",
|
||
|
"charger.car",
|
||
|
"chargerdragdrag.car",
|
||
|
"corvette.car",
|
||
|
"diablo.car",
|
||
|
"dmc12.car",
|
||
|
"dmc12flying.car",
|
||
|
"gt40.car",
|
||
|
"gti.car",
|
||
|
"mini.car",
|
||
|
"55ford.car",
|
||
|
"tt.car",
|
||
|
"ttpimp.car",
|
||
|
"viper.car",
|
||
|
"viperracer.car",
|
||
|
"accelbrake.mapinfo",
|
||
|
"accelbrake2.mapinfo",
|
||
|
"canyon.mapinfo",
|
||
|
"canyoncorner.mapinfo",
|
||
|
"city2.mapinfo",
|
||
|
"citycorner.mapinfo",
|
||
|
"downhill.mapinfo",
|
||
|
"highspeed.mapinfo",
|
||
|
"highspeedtrial.mapinfo",
|
||
|
"mountainside.mapinfo",
|
||
|
"offroad.mapinfo",
|
||
|
"quarter.mapinfo",
|
||
|
"ralley2.mapinfo",
|
||
|
"scorner.mapinfo",
|
||
|
"slalom.mapinfo",
|
||
|
"slalom2.mapinfo",
|
||
|
"snow.mapinfo",
|
||
|
"snowtrial.mapinfo",
|
||
|
"tight.mapinfo",
|
||
|
"canyon.road",
|
||
|
"city2.road",
|
||
|
"mountainside.road",
|
||
|
"ralley2.road",
|
||
|
"snow.road",
|
||
|
};
|
||
|
|
||
|
int gValidateChecksums[]={
|
||
|
0xc98d7f36,
|
||
|
0x833e4a7d,
|
||
|
0xdcd7e89,
|
||
|
0x3e6a08b7,
|
||
|
0x251efab,
|
||
|
0xed2a6577,
|
||
|
0x6d654b5,
|
||
|
0x1c468570,
|
||
|
0x1380a984,
|
||
|
0x7c69956,
|
||
|
0xcc2746e4,
|
||
|
0x22349629,
|
||
|
0x8d0d50b4,
|
||
|
0x51994161,
|
||
|
0x90a4cc85,
|
||
|
0x931cbd6c,
|
||
|
0xe42301b9,
|
||
|
0xcc9bd572,
|
||
|
0x1b7951e7,
|
||
|
0x72d87975,
|
||
|
0x1eb05d11,
|
||
|
0xa2182ab9,
|
||
|
0xde1122e6,
|
||
|
0xc44b1aab,
|
||
|
0x815cb099,
|
||
|
0x8eb6b530,
|
||
|
0x6c93ce2,
|
||
|
0x77263bb7,
|
||
|
0x18ebbbad,
|
||
|
0xc57f8785,
|
||
|
0x117ba80c,
|
||
|
0xb513b806,
|
||
|
0x75938236,
|
||
|
0x2437d140,
|
||
|
0xb249c233,
|
||
|
0x6e92dc33,
|
||
|
0x9e5c31de,
|
||
|
0x8766b155,
|
||
|
0x1bbdc55d,
|
||
|
0x28f6a9b8,
|
||
|
0x66e5492e,
|
||
|
0x8b5a55d8,
|
||
|
};
|
||
|
//Initialize file reference table
|
||
|
void FileInitIO()
|
||
|
{
|
||
|
gFileTableExtendedSize=0;
|
||
|
gFileTableSize=0;
|
||
|
gFileTable=(tFileTableEntry*)calloc(kMaxFiles,sizeof(tFileTableEntry));
|
||
|
FileInitFileTable(gFileTable,kMaxFiles,&gFileTableSize,false);
|
||
|
|
||
|
for(int i=0;i<kNumValidateFiles;i++)
|
||
|
{
|
||
|
if(FileGetChecksum(FileGetReference(gValidateNames[i]))!=gValidateChecksums[i])
|
||
|
{
|
||
|
gFileTampered=true;
|
||
|
printf("Bad File! %s. Found Checksum %x. Need Checksum %x.\n",FileGetName(FileGetReference(gValidateNames[i])),
|
||
|
FileGetChecksum(FileGetReference(gValidateNames[i])),gValidateChecksums[i]);
|
||
|
}
|
||
|
//printf("0x%x,\n",FileGetChecksum(FileGetReference(gValidateNames[i])));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FileRescanDirectory()
|
||
|
{
|
||
|
int totalSize=gFileTableSize+gFileTableExtendedSize;
|
||
|
FileInitFileTable(gFileTable,kMaxFiles,&totalSize,true);
|
||
|
gFileTableExtendedSize=totalSize-gFileTableSize;
|
||
|
printf("ex:%d\n",gFileTableExtendedSize);
|
||
|
}
|
||
|
|
||
|
void FileReleaseData(tFileRef reference)
|
||
|
{
|
||
|
if(gFileTable[reference].loaded)
|
||
|
{
|
||
|
MemoryFreeBlock(gFileTable[reference].data);
|
||
|
gFileTable[reference].loaded=false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Compare two File Table entries for sorting and searching
|
||
|
int FileTableCompare(const void *a,const void *b)
|
||
|
{
|
||
|
return _stricmp(((tFileTableEntry*)a)->name,((tFileTableEntry*)b)->name);
|
||
|
}
|
||
|
|
||
|
int gFileErrorReporting=true;
|
||
|
|
||
|
//Search file reference table for a file matching a name and return its reference number
|
||
|
tFileRef FileGetReference(char *name)
|
||
|
{
|
||
|
if(*name=='\0')
|
||
|
return -1;
|
||
|
//the key to search for
|
||
|
tFileTableEntry key;
|
||
|
#ifndef __TARGET_TEXTURECOMPRESSOR
|
||
|
#ifndef __TARGET_TOOLAPP
|
||
|
snprintf(key.name,32,"%s.txr",name);
|
||
|
|
||
|
//search for an entry matching our search key
|
||
|
tFileTableEntry *result=(tFileTableEntry*)bsearch(&key,gFileTable,gFileTableSize,sizeof(tFileTableEntry),&FileTableCompare);
|
||
|
|
||
|
//return results
|
||
|
if(result)
|
||
|
return result-gFileTable;
|
||
|
else
|
||
|
{
|
||
|
snprintf(key.name,32,"%s.ima",name);
|
||
|
|
||
|
//search for an entry matching our search key
|
||
|
tFileTableEntry *result=(tFileTableEntry*)bsearch(&key,gFileTable,gFileTableSize,sizeof(tFileTableEntry),&FileTableCompare);
|
||
|
|
||
|
//return results
|
||
|
if(result)
|
||
|
return result-gFileTable;
|
||
|
else
|
||
|
|
||
|
{
|
||
|
#endif
|
||
|
#endif
|
||
|
strcpy(key.name,name);
|
||
|
tFileTableEntry *result=(tFileTableEntry*)bsearch(&key,gFileTable,gFileTableSize,sizeof(tFileTableEntry),&FileTableCompare);
|
||
|
if(result)
|
||
|
return result-gFileTable;
|
||
|
else
|
||
|
{
|
||
|
for(int i=gFileTableSize;i<gFileTableSize+gFileTableExtendedSize;i++)
|
||
|
if(_stricmp(gFileTable[i].name,name)==0)
|
||
|
return i;
|
||
|
if(gFileErrorReporting)
|
||
|
{
|
||
|
//or give an error message
|
||
|
PrintConsoleString("File not found!: %s\n",name);
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
#ifndef __TARGET_TEXTURECOMPRESSOR
|
||
|
#ifndef __TARGET_TOOLAPP
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
tFileRef FileGetIndexedReference(tFileRef base,int i)
|
||
|
{
|
||
|
if(i>0)
|
||
|
{
|
||
|
char nameCopy[kMaxFileNameLength];
|
||
|
strcpy(nameCopy,FileGetName(base));
|
||
|
char *found=strchr(nameCopy,'.');
|
||
|
|
||
|
char indexedName[kMaxFileNameLength];
|
||
|
if(found)
|
||
|
{
|
||
|
*found='\0';
|
||
|
sprintf(indexedName,"%s#%x.%s",nameCopy,i,found+1);
|
||
|
}
|
||
|
else
|
||
|
sprintf(indexedName,"%s#%x",nameCopy,i);
|
||
|
|
||
|
int seek=base-1;
|
||
|
while(_stricmp(gFileTable[seek].name,indexedName)>0&&seek>0)
|
||
|
seek--;
|
||
|
|
||
|
if(_stricmp(gFileTable[seek].name,indexedName))
|
||
|
return base;
|
||
|
else
|
||
|
return seek;
|
||
|
}
|
||
|
else
|
||
|
return base;
|
||
|
}
|
||
|
|
||
|
tFileRef FileGetBaseReference(tFileRef ref)
|
||
|
{
|
||
|
if(ref<0||ref>=gFileTableExtendedSize+gFileTableSize)
|
||
|
return kFileErr;
|
||
|
char *found=strchr(FileGetName(ref),'.');
|
||
|
if(!found)
|
||
|
return ref;
|
||
|
|
||
|
char *cross=found;
|
||
|
while(*cross!='#'&&cross>FileGetName(ref))
|
||
|
cross--;
|
||
|
if(*cross!='#')
|
||
|
return ref;
|
||
|
|
||
|
int l=cross-FileGetName(ref);
|
||
|
char nameCopy[kMaxFileNameLength];
|
||
|
memcpy(nameCopy,FileGetName(ref),l);
|
||
|
strcpy(nameCopy+l,found);
|
||
|
tFileRef base=FileGetReference(nameCopy);
|
||
|
return base!=kFileErr?base:ref;
|
||
|
}
|
||
|
|
||
|
//platform specific, can be found in macfileio.cpp
|
||
|
int FileLoadData(tFileRef fileRef);
|
||
|
void FileStoreData(tFileRef fileRef);
|
||
|
|
||
|
//return the extension of a file given it's reference number
|
||
|
char *FileGetExtension(tFileRef reference)
|
||
|
{
|
||
|
if(reference<0||reference>=gFileTableExtendedSize+gFileTableSize)
|
||
|
return NULL;
|
||
|
char *found=NULL;
|
||
|
char *dot=gFileTable[reference].name;
|
||
|
do{
|
||
|
dot=strchr(dot,'.');
|
||
|
if(dot){
|
||
|
found=dot;
|
||
|
dot++;
|
||
|
}
|
||
|
}while(dot);
|
||
|
if(found)
|
||
|
return found;
|
||
|
else
|
||
|
return gFileTable[reference].name+strlen(gFileTable[reference].name);//'\0';
|
||
|
}
|
||
|
|
||
|
//return the name of a file given it's reference number
|
||
|
char *FileGetName(tFileRef reference)
|
||
|
{
|
||
|
if(reference<0||reference>=gFileTableExtendedSize+gFileTableSize)
|
||
|
return gNoName;
|
||
|
return gFileTable[reference].name;
|
||
|
}
|
||
|
|
||
|
//return the size of a file given it's reference number
|
||
|
int FileGetSize(tFileRef reference)
|
||
|
{
|
||
|
if(reference<0||reference>=gFileTableExtendedSize+gFileTableSize)
|
||
|
return 0;
|
||
|
if(!gFileTable[reference].loaded)
|
||
|
gFileTable[reference].loaded=FileLoadData(reference);
|
||
|
return gFileTable[reference].size;
|
||
|
}
|
||
|
|
||
|
//return a pointer to the file's data (and load the file if necessary)
|
||
|
void *FileGetDataPtr(tFileRef reference)
|
||
|
{
|
||
|
if(reference<0||reference>=gFileTableExtendedSize+gFileTableSize)
|
||
|
return NULL;
|
||
|
|
||
|
if(!gFileTable[reference].loaded)
|
||
|
gFileTable[reference].loaded=FileLoadData(reference);
|
||
|
return gFileTable[reference].loaded?gFileTable[reference].data:(void*)0;
|
||
|
}
|
||
|
|
||
|
unsigned int FileGetChecksum(tFileRef reference)
|
||
|
{
|
||
|
if(reference<0||reference>=gFileTableExtendedSize+gFileTableSize)
|
||
|
return 0;
|
||
|
return MemoryChecksum(FileGetDataPtr(reference),FileGetSize(reference));
|
||
|
}
|
||
|
|
||
|
//Changes a file's contents to data, and writes the file to disk.
|
||
|
void FileSetData(tFileRef reference,void *data)
|
||
|
{
|
||
|
if(reference<0||reference>=gFileTableExtendedSize+gFileTableSize)
|
||
|
return;
|
||
|
if(gFileTable[reference].loaded&&data!=gFileTable[reference].data)
|
||
|
MemoryFreeBlock(gFileTable[reference].data);
|
||
|
gFileTable[reference].data=data;
|
||
|
gFileTable[reference].loaded=true;
|
||
|
gFileTable[reference].size=MemoryBlockSize(data);
|
||
|
FileStoreData(reference);
|
||
|
}
|
||
|
|
||
|
//parses a file's data using a file format parser
|
||
|
//and return a pointer to parsed data structure
|
||
|
void* FileGetParsedDataPtr(tFileRef reference, int parserType, int dataSize)
|
||
|
{
|
||
|
if(reference<0||reference>=gFileTableExtendedSize+gFileTableSize)
|
||
|
return NULL;
|
||
|
if(!gFileTable[reference].parsed)
|
||
|
{
|
||
|
gFileTable[reference].parsedData=MemoryAllocateZeroedBlock(dataSize);
|
||
|
ParseFile(reference,gFileTable[reference].parsedData,parserType);
|
||
|
gFileTable[reference].parsed=true;
|
||
|
}
|
||
|
return gFileTable[reference].parsedData;
|
||
|
}
|