Files
commandergenius/src/fileio.cpp
2009-08-01 12:20:22 +00:00

935 lines
27 KiB
C++

/* FILEIO.C
Functions responsible for loading data from files, such as the one that
decodes the level map files (loadmap()) and the one that loads in the
tile attribute data contained in ep?attr.dat (loadtileattributes()).
The functions for loading the graphics (EGALATCH&EGASPRIT) are in latch.c.
*/
#include "keen.h"
#include "sdl/CVideoDriver.h"
#include "sdl/sound/CSound.h"
#include "hqp/CMusic.h"
#include "include/fileio.h"
#include "include/fileio/rle.h"
#include "vorticon/CPlayer.h"
#include "CLogFile.h"
#include "CGraphics.h"
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include "StringUtils.h"
#include "Debug.h"
#include "FindFile.h"
extern CPlayer *Player;
unsigned int curmapx, curmapy;
unsigned char mapdone;
void addmaptile(unsigned int t)
{
map.mapdata[curmapx][curmapy] = t;
curmapx++;
if (curmapx >= map.xsize)
{
curmapx = 0;
curmapy++;
if (curmapy >= map.ysize) mapdone = 1;
}
}
short checkConsistencyofGameData(stGameData *p_GameData)
{
short ok = 0;
FILE *fp;
// Let's define which files need to be read!
if(p_GameData->Episode >= 1 && p_GameData->Episode <= 3)
{
p_GameData->FileList[1] = "keen" + itoa(p_GameData->Episode) + ".exe";
p_GameData->FileList[2] = "egahead.ck" + itoa(p_GameData->Episode);
p_GameData->FileList[3] = "egasprit.ck" + itoa(p_GameData->Episode);
p_GameData->FileList[4] = "egalatch.ck" + itoa(p_GameData->Episode);
p_GameData->FileList[5] = "finale.ck" + itoa(p_GameData->Episode);
// Levels!
p_GameData->FileList[6] = "level01.ck" + itoa(p_GameData->Episode);
p_GameData->FileList[7] = "level02.ck" + itoa(p_GameData->Episode);
p_GameData->FileList[8] = "level03.ck" + itoa(p_GameData->Episode);
p_GameData->FileList[9] = "level04.ck" + itoa(p_GameData->Episode);
p_GameData->FileList[10] = "level05.ck" + itoa(p_GameData->Episode);
p_GameData->FileList[11] = "level06.ck" + itoa(p_GameData->Episode);
p_GameData->FileList[12] = "level07.ck" + itoa(p_GameData->Episode);
p_GameData->FileList[13] = "level08.ck" + itoa(p_GameData->Episode);
p_GameData->FileList[14] = "level09.ck" + itoa(p_GameData->Episode);
p_GameData->FileList[15] = "level10.ck" + itoa(p_GameData->Episode);
p_GameData->FileList[16] = "level11.ck" + itoa(p_GameData->Episode);
p_GameData->FileList[17] = "level12.ck" + itoa(p_GameData->Episode);
p_GameData->FileList[18] = "level13.ck" + itoa(p_GameData->Episode);
p_GameData->FileList[19] = "level14.ck" + itoa(p_GameData->Episode);
p_GameData->FileList[20] = "level15.ck" + itoa(p_GameData->Episode);
p_GameData->FileList[21] = "level16.ck" + itoa(p_GameData->Episode);
p_GameData->FileList[22] = "level80.ck" + itoa(p_GameData->Episode);
p_GameData->FileList[23] = "level81.ck" + itoa(p_GameData->Episode);
p_GameData->FileList[24] = "level90.ck" + itoa(p_GameData->Episode);
// Other
if(p_GameData->Episode == 1)
p_GameData->FileList[24] = "sounds.ck" + itoa(p_GameData->Episode);
}
// Finally check if they really exist!
int c=0;
for(c = 0 ; c < MAX_NUMBER_OF_FILES ; c++)
{
if(p_GameData->FileList[c][0] == 0) // If there are no more files!
break;
std::string buf = "data/" + p_GameData->DataDirectory;
if(p_GameData->DataDirectory != "")
buf += "/";
buf += p_GameData->FileList[c];
if((fp = OpenGameFile(buf.c_str(),"r")) != NULL)
{
fclose(fp);
}
else
{
g_pLogFile->ftextOut("Error! File \"%s\" was not found! Please add it to the configured directory.<br>", p_GameData->FileList[c].c_str());
p_GameData->Episode = -1;
ok++;
}
}
if(ok==0)
g_pLogFile->ftextOut("Game data of Episode %d is complete.<br>", p_GameData->Episode);
return ok;
}
char NessieAlreadySpawned;
void addobjectlayertile(unsigned int t, stCloneKeenPlus *pCKP)
{
int o;
switch(t)
{
case 0: break; // blank
case 255: // player start
//Player[0].setCoord(curmapx << 4 << CSF, curmapy << 4 << CSF);
player[0].x = curmapx << 4 << CSF;
player[0].y = curmapy << 4 << CSF;
map.objectlayer[curmapx][curmapy] = 0;
break;
case NESSIE_PATH: // spawn nessie at first occurance of her path
if (pCKP->Control.levelcontrol.episode==3)
{
if (!NessieAlreadySpawned)
{
o = spawn_object(curmapx<<4<<CSF, curmapy<<4<<CSF, OBJ_NESSIE);
objects[o].hasbeenonscreen = 1;
NessieAlreadySpawned = 1;
NessieObjectHandle = o;
}
}
goto levelmarker;
break;
default: // level marker
levelmarker: ;
if ((t&0x7fff) < 256 && pCKP->Control.levelcontrol.levels_completed[t&0x00ff])
{
if(!options[OPT_LVLREPLAYABILITY].value)
map.objectlayer[curmapx][curmapy] = 0;
else
map.objectlayer[curmapx][curmapy] = t;
int newtile = tiles[map.mapdata[curmapx][curmapy]].chgtile;
// Consistency check! Some Mods have issues with that.
if(pCKP->Control.levelcontrol.episode == 1 || pCKP->Control.levelcontrol.episode == 2)
{
if(newtile<77 || newtile>81)
// something went wrong. Use default tile
newtile = 77;
// try to guess, if it is a 32x32 (4 16x16) Tile
if(map.mapdata[curmapx-1][curmapy-1] == (unsigned int) newtile &&
map.mapdata[curmapx][curmapy-1] == (unsigned int) newtile &&
map.mapdata[curmapx-1][curmapy] == (unsigned int) newtile)
{
map.mapdata[curmapx-1][curmapy-1] = 78; //ul
map.mapdata[curmapx][curmapy-1] = 79; //ur
map.mapdata[curmapx-1][curmapy] = 80; //bl
newtile = 81; // br. this one
}
}
else if(pCKP->Control.levelcontrol.episode == 3)
{
if(newtile<52 || newtile>56)
// something went wrong. Use default tile
newtile = 56;
// try to guess, if it is a 32x32 (4 16x16) Tile
if(map.mapdata[curmapx-1][curmapy-1] == (unsigned int) newtile &&
map.mapdata[curmapx][curmapy-1] == (unsigned int) newtile &&
map.mapdata[curmapx-1][curmapy] == (unsigned int) newtile)
{
map.mapdata[curmapx-1][curmapy-1] = 52; //bl
map.mapdata[curmapx][curmapy-1] = 53; //ur
map.mapdata[curmapx-1][curmapy] = 54; //ul
newtile = 55; // br. this one
}
}
map.mapdata[curmapx][curmapy] = newtile;
}
else
{
map.objectlayer[curmapx][curmapy] = t;
}
break;
}
curmapx++;
if (curmapx >= map.xsize)
{
curmapx = 0;
curmapy++;
if (curmapy >= map.ysize) mapdone = 1;
}
}
void addenemytile(unsigned int t, stCloneKeenPlus *pCKP)
{
int o,x;
map.objectlayer[curmapx][curmapy] = t;
if (t)
{
if (t==255)
{
if(curmapx >= map.xsize-2) // Edge bug. Keen would fall in some levels without this.
curmapx = 4;
if(curmapy >= map.ysize-2) // Edge bug. Keen would fall in some levels without this.
curmapx = 4;
//Player[0].setCoord(curmapx << 4 << CSF, ((curmapy << 4) + 8) << CSF);
player[0].x = curmapx << 4 << CSF;
player[0].y = ((curmapy << 4) + 8) << CSF;
}
else
{
switch(t)
{
case 0: break;
case -1: break;
case 1: // yorp (ep1) vort (ep2&3)
if (pCKP->Control.levelcontrol.episode==1)
{
x = curmapx;
if (TileProperty[map.mapdata[x][curmapy+1]][BLEFT]) x--;
spawn_object(x<<4<<CSF, ((curmapy<<4)+8)<<CSF, OBJ_YORP);
}
else
{
// in ep2 level 16 there a vorticon embedded in the floor for
// some reason! that's what the if() is for--to fix it.
if (TileProperty[map.mapdata[curmapx][curmapy+1]][BLEFT])
{
spawn_object(curmapx<<4<<CSF, ((curmapy<<4)-16)<<CSF, OBJ_VORT);
}
else
{
spawn_object(curmapx<<4<<CSF, curmapy<<4<<CSF, OBJ_VORT);
}
}
break;
case 2: // garg (ep1) baby vorticon (ep2&3)
if (pCKP->Control.levelcontrol.episode==1)
{
// those bastards. sometimes embedding garg's in the floor in
// the original maps.
if(TileProperty[map.mapdata[curmapx+1][curmapy+1]][BLEFT])
{
if (pCKP->Control.levelcontrol.chglevelto==7)
{
spawn_object(curmapx<<4<<CSF, (curmapy-1)<<4<<CSF, OBJ_GARG);
}
else
{
spawn_object((curmapx-1)<<4<<CSF, (curmapy)<<4<<CSF, OBJ_GARG);
}
}
else
{
spawn_object(curmapx<<4<<CSF, curmapy<<4<<CSF, OBJ_GARG);
}
}
else
{
spawn_object(curmapx<<4<<CSF, curmapy<<4<<CSF, OBJ_BABY);
}
break;
case 3: // vorticon (ep1) bear (ep2)
if (pCKP->Control.levelcontrol.episode==1)
{
spawn_object(curmapx<<4<<CSF, curmapy<<4<<CSF, OBJ_VORT);
}
else if (pCKP->Control.levelcontrol.episode==2)
{
spawn_object(curmapx<<4<<CSF, curmapy<<4<<CSF, OBJ_VORTELITE);
}
else if (pCKP->Control.levelcontrol.episode==3)
{
spawn_object(curmapx<<4<<CSF, curmapy<<4<<CSF, OBJ_MOTHER);
}
break;
case 4: // butler (ep1) OR walker (ep2) OR meep (ep3)
if (pCKP->Control.levelcontrol.episode==1)
spawn_object(curmapx<<4<<CSF, curmapy<<4<<CSF, OBJ_BUTLER);
else if (pCKP->Control.levelcontrol.episode==2)
spawn_object(curmapx<<4<<CSF, curmapy<<4<<CSF, OBJ_WALKER);
else if (pCKP->Control.levelcontrol.episode==3)
spawn_object(curmapx<<4<<CSF, ((curmapy<<4)+8)<<CSF, OBJ_MEEP);
break;
case 5: // tank robot (ep1&2) karate bear (ep3)
if (pCKP->Control.levelcontrol.episode==1)
{
o = spawn_object(curmapx<<4<<CSF, ((curmapy<<4)+8)<<CSF, OBJ_TANK);
// set tank robot guarding bonus level to be active at startup
if (pCKP->Control.levelcontrol.chglevelto==13)
{
objects[o].hasbeenonscreen = 1;
}
}
else if (pCKP->Control.levelcontrol.episode==2)
spawn_object(curmapx<<4<<CSF, ((curmapy<<4)+0)<<CSF, OBJ_TANKEP2);
else if (pCKP->Control.levelcontrol.episode==3)
{
if(TileProperty[map.mapdata[curmapx][curmapy+1]][BLEFT])
{
spawn_object(curmapx<<4<<CSF, (curmapy-1)<<4<<CSF, OBJ_NINJA);
}
else
{
spawn_object(curmapx<<4<<CSF, curmapy<<4<<CSF, OBJ_NINJA);
}
}
break;
case 6: // up-right-flying ice chunk (ep1) horiz platform (ep2)
// foob (ep3)
if (pCKP->Control.levelcontrol.episode==1)
{
o = spawn_object((((curmapx+1)<<4)+4)<<CSF, ((curmapy<<4)-4)<<CSF, OBJ_SECTOREFFECTOR);
objects[o].ai.se.type = SE_ICECANNON;
objects[o].ai.se.dir = DUPRIGHT;
objects[o].inhibitfall = 1;
objects[o].hasbeenonscreen = 1;
}
else if (pCKP->Control.levelcontrol.episode==2)
{
o = spawn_object(curmapx<<4<<CSF, ((curmapy<<4)-3)<<CSF, OBJ_PLATFORM);
}
else if (pCKP->Control.levelcontrol.episode==3)
{
o = spawn_object(curmapx<<4<<CSF, curmapy<<4<<CSF, OBJ_FOOB);
}
break;
case 7: // spark (ep2) ball (ep3)
if (pCKP->Control.levelcontrol.episode==2)
{
o = spawn_object(curmapx<<4<<CSF,curmapy<<4<<CSF,OBJ_SECTOREFFECTOR);
objects[o].ai.se.type = SE_SPARK;
pCKP->Control.levelcontrol.canexit = 0; // can't exit till spark is shot
}
else
{
o = spawn_object(curmapx<<4<<CSF,curmapy<<4<<CSF,OBJ_BALL);
objects[o].hasbeenonscreen = 1;
}
break;
case 8: // jack (ep3)
if (pCKP->Control.levelcontrol.episode==3)
{
o = spawn_object(curmapx<<4<<CSF,curmapy<<4<<CSF,OBJ_JACK);
objects[o].hasbeenonscreen = 1;
}
break;
case 9: // up-left-flying ice chunk (ep1) horiz platform (ep3)
if (pCKP->Control.levelcontrol.episode==1)
{
o = spawn_object(((curmapx<<4)-4)<<CSF, ((curmapy<<4)-4)<<CSF, OBJ_SECTOREFFECTOR);
// objects[o].ai.icechunk.movedir = DUPLEFT;
objects[o].ai.se.type = SE_ICECANNON;
objects[o].ai.se.dir = DUPLEFT;
objects[o].inhibitfall = 1;
objects[o].hasbeenonscreen = 1;
}
else if (pCKP->Control.levelcontrol.episode==3)
{
o = spawn_object(curmapx<<4<<CSF, (((curmapy)<<4)-4)<<CSF, OBJ_PLATFORM);
}
break;
case 10: // rope holding the stone above the final vorticon (ep1)
// vert platform (ep3)
if (pCKP->Control.levelcontrol.episode==1)
{
spawn_object(curmapx<<4<<CSF, curmapy<<4<<CSF, OBJ_ROPE);
}
else if (pCKP->Control.levelcontrol.episode==3)
{
spawn_object(curmapx<<4<<CSF, curmapy<<4<<CSF, OBJ_PLATVERT);
}
break;
case 11: // jumping vorticon (ep3)
if (pCKP->Control.levelcontrol.episode==3)
{
spawn_object(curmapx<<4<<CSF, ((curmapy<<4)-8)<<CSF, OBJ_VORT);
}
break;
case 12: // sparks in mortimer's machine
o = spawn_object(curmapx<<4<<CSF, curmapy<<4<<CSF, OBJ_SECTOREFFECTOR);
objects[o].ai.se.type = SE_MORTIMER_SPARK;
objects[o].hasbeenonscreen = 1;
break;
case 13: // mortimer's heart
o = spawn_object(curmapx<<4<<CSF, curmapy<<4<<CSF, OBJ_SECTOREFFECTOR);
objects[o].ai.se.type = SE_MORTIMER_HEART;
objects[o].hasbeenonscreen = 1;
break;
case 14: // right-pointing raygun (ep3)
if (pCKP->Control.levelcontrol.episode==3)
{
o = spawn_object(curmapx<<4<<CSF, curmapy<<4<<CSF, OBJ_SECTOREFFECTOR);
objects[o].ai.se.type = SE_GUN_RIGHT;
objects[o].hasbeenonscreen = 1;
}
break;
case 15: // vertical raygun (ep3)
if (pCKP->Control.levelcontrol.episode==3)
{
o = spawn_object(curmapx<<4<<CSF, curmapy<<4<<CSF, OBJ_SECTOREFFECTOR);
objects[o].ai.se.type = SE_GUN_VERT;
objects[o].hasbeenonscreen = 1;
}
break;
case 16: // mortimer's arms
o = spawn_object(curmapx<<4<<CSF, curmapy<<4<<CSF, OBJ_SECTOREFFECTOR);
objects[o].ai.se.type = SE_MORTIMER_ARM;
objects[o].hasbeenonscreen = 1;
break;
case 17: // mortimer's left leg
o = spawn_object(curmapx<<4<<CSF, curmapy<<4<<CSF, OBJ_SECTOREFFECTOR);
objects[o].ai.se.type = SE_MORTIMER_LEG_LEFT;
objects[o].hasbeenonscreen = 1;
break;
case 18: // mortimer's right leg
o = spawn_object(curmapx<<4<<CSF, curmapy<<4<<CSF, OBJ_SECTOREFFECTOR);
objects[o].ai.se.type = SE_MORTIMER_LEG_RIGHT;
objects[o].hasbeenonscreen = 1;
break;
default:
g_pGraphics->fadePalette(20);
g_pLogFile->ftextOut(PURPLE,"unknown enemy type %d at (%d,%d)<br>", t, curmapx, curmapy);
//while(g_pInput->getPressedCommand(KENTER));
break;
}
}
}
curmapx++;
if (curmapx >= map.xsize)
{
curmapx = 0;
curmapy++;
if (curmapy >= map.ysize) mapdone = 1;
}
}
unsigned int fgeti(FILE *fp) {
unsigned int temp1, temp2;
temp1 = fgetc(fp);
temp2 = fgetc(fp);
return (temp2<<8) | temp1;
}
unsigned long fgetl(FILE *fp) {
unsigned int temp1, temp2, temp3, temp4;
temp1 = fgetc(fp);
temp2 = fgetc(fp);
temp3 = fgetc(fp);
temp4 = fgetc(fp);
return (temp4<<24) | (temp3<<16) | (temp2<<8) | temp1;
}
unsigned int loadmap(const std::string& filename, const std::string& path, int lvlnum, int isworldmap, stCloneKeenPlus *pCKP)
{
// TODO: Tie that one up in convert stuff in C++
FILE *fp;
int t;
unsigned int c;
int numruns = 0;
int resetcnt, resetpt;
unsigned int planesize = 0;
NessieAlreadySpawned = 0;
map.isworldmap = isworldmap;
std::string buffer = formatPathString(path);
std::string fname = buffer + filename;
fp = OpenGameFile(fname.c_str(), "rb");
if (!fp)
{
// only record this error message on build platforms that log errors
// to a file and not to the screen.
g_pLogFile->ftextOut("loadmap(): unable to open file %s<br>", fname.c_str());
return 1;
}
g_pLogFile->ftextOut("loadmap(): file %s opened. Loading...<br>", fname.c_str());
// decompress map RLEW data
curmapx = curmapy = mapdone = 0;
unsigned int *filebuf; // big File Buffer for the uncompression
filebuf = (unsigned int*) malloc(1000000*sizeof(int));
if(filebuf == NULL)
{
g_pLogFile->ftextOut("loadmap(): unable to allocate memory for unpacking the level file<br>", fname.c_str());
return 1;
}
int finsize; // Final size
finsize = unRLEW(fp, filebuf);
c=0;
if(finsize == -1)
{
rewind(fp);
while(!feof(fp))
{
filebuf[c] = fgeti(fp);
c++;
}
}
map.xsize = filebuf[2];
map.ysize = filebuf[3];
if (map.xsize > 260 || map.ysize > 260)
{
g_pLogFile->textOut(RED,"loadmap(): level is too big<br>");
return 1;
}
planesize = filebuf[9];
planesize /= 2; // Size of two planes, but we only need one
for( c=18 ; c<planesize+18 ; c++ ) // Check against Tilesize
{
t = filebuf[c];
if(!mapdone)
addmaptile(t);
if(t > 255)
t=0; // If there are some invalid values in the file
}
// now do the sprites
// get sprite data
curmapx = curmapy = mapdone = numruns = 0;
resetcnt = resetpt = 0;
while(!mapdone)
{
t = filebuf[c];
if (map.isworldmap) addobjectlayertile(t, pCKP); else addenemytile(t, pCKP);
if (++resetcnt==resetpt) curmapx=curmapy=0;
c++;
}
free(filebuf);
fclose(fp);
// HQ Sounds. Load Music for a level if you have HQP
g_pMusicPlayer->stop();
if((fp=OpenGameFile("data/hqp/music/table.cfg","rt")) != NULL)
{
static const int MAX_STRING_LENGTH = 256;
char buf1[MAX_STRING_LENGTH];
char buf2[MAX_STRING_LENGTH];
memset(buf1,0,sizeof(char)*MAX_STRING_LENGTH);
memset(buf2,0,sizeof(char)*MAX_STRING_LENGTH);
while(!feof(fp))
{
fscanf(fp,"%s",buf1);
if(strcmp(buf1,filename.c_str()) == 0)
{
fscanf(fp,"%s",buf2);
break;
}
else
fgets(buf1,MAX_STRING_LENGTH,fp);
}
fclose(fp);
if(*buf2 != 0)
{
strcpy(buf1,"data/hqp/music/");
strcat(buf1,buf2);
g_pMusicPlayer->load(g_pSound->getAudioSpec(),buf1);
g_pMusicPlayer->play();
}
}
// Didn't it work? Don't matter. HQP is optional, so continue
// install enemy stoppoints as needed
if (pCKP->Control.levelcontrol.episode==1 && lvlnum==13)
{
map.objectlayer[94][13] = GARG_STOPPOINT;
map.objectlayer[113][13] = GARG_STOPPOINT;
map.objectlayer[48][6] = GARG_STOPPOINT;
map.objectlayer[80][5] = GARG_STOPPOINT;
map.objectlayer[87][5] = GARG_STOPPOINT;
map.objectlayer[39][18] = GARG_STOPPOINT;
}
else if (pCKP->Control.levelcontrol.episode==3 && lvlnum==6)
{
map.objectlayer[40][7] = BALL_NOPASSPOINT;
map.objectlayer[50][7] = BALL_NOPASSPOINT;
}
else if (pCKP->Control.levelcontrol.episode==3 && lvlnum==9)
{
map.objectlayer[45][106] = BALL_NOPASSPOINT;
}
else if (pCKP->Control.levelcontrol.episode==3 && lvlnum==4)
{
map.objectlayer[94][17] = BALL_NOPASSPOINT;
}
return 0;
}
char loadstrings_AddAttr(char *attr, int stringIndex)
{
char stAttrName[80];
char stAttrValue[80];
int attrvalue;
int RAMAllocSize;
char *copyPtr;
unsigned int i;
// if the attribute does not have an equals sign bail
if (!strstr(attr, "="))
{
g_pLogFile->ftextOut("loadstrings_AddAttr(): '%s' is not a valid attribute definition.<br>", attr);
return 1;
}
// split the attribute up into it's name and it's value
copyPtr = stAttrName;
for(i=0;i<strlen(attr);i++)
{
if (attr[i] != ' ' && attr[i]!=9) // strip out spaces and tabs
{
if (attr[i] != '=')
{
*copyPtr = attr[i];
copyPtr++;
}
else
{ // hit the equals sign
*copyPtr = 0;
copyPtr = stAttrValue;
}
}
}
*copyPtr = 0;
attrvalue = atoi(stAttrValue);
// malloc space for the attribute name
RAMAllocSize = strlen(stAttrName) + 1;
strings[stringIndex].attrnames[strings[stringIndex].numAttributes] = (unsigned char*) malloc(RAMAllocSize+1);
if (!strings[stringIndex].attrnames[strings[stringIndex].numAttributes])
{
g_pLogFile->textOut(RED,"loadstrings_AddAttr(): Unable to allocate space for attribute name ('%s').<br>", stAttrName);
return 1;
}
// copy the data into the strings structure
memcpy(strings[stringIndex].attrnames[strings[stringIndex].numAttributes], stAttrName, RAMAllocSize);
strings[stringIndex].attrvalues[strings[stringIndex].numAttributes] = attrvalue;
strings[stringIndex].numAttributes++;
return 0;
}
// load strings from file *fname ("strings.dat")
char loadstrings(const std::string& fname)
{
FILE *fp;
char state;
std::string stName;
std::string stString;
char stAttr[80];
int i,c;
int attrIndex=0;
int waitChar, gotoState;
char highlight;
#define STSTATE_WAITCHAR 0
#define STSTATE_READNAME 1
#define STSTATE_READSTRING 2
#define STSTATE_READATTR 3
g_pLogFile->ftextOut("loadstrings(): Opening string file '%s'.<br>", fname.c_str());
fp = OpenGameFile(fname.c_str(), "rb");
if (!fp)
{
g_pLogFile->ftextOut("loadstrings(): String file unable to open.<br>");
return 1;
}
// go through all the strings and NULL out the entries...this will
// let us know which ones are in use (and need to be free()d at shutdown)
for(i=0;i<MAX_STRINGS;i++)
{
strings[i].name = "";
strings[i].stringptr = "";
strings[i].numAttributes = 0;
}
numStrings = 0;
highlight = 0;
// read until we get to the first string name
state = STSTATE_WAITCHAR;
waitChar = '[';
gotoState = STSTATE_READNAME;
do
{
c = fgetc(fp); // read byte from file
if (c<0)
{ // EOF
break;
}
// ignore LF's
if (c==10) continue;
switch(state)
{
case STSTATE_WAITCHAR:
// ignore chars until we read a waitChar, then go to state gotoState
if (c==waitChar)
{
state = gotoState;
}
break;
case STSTATE_READATTR:
if (c==13)
{ // reached CR, start reading string
if (attrIndex)
{
stAttr[attrIndex] = 0;
if (loadstrings_AddAttr( (char*) stAttr, numStrings)) return 1;
}
state = STSTATE_READSTRING;
}
else if (c==' ')
{ // end of an attribute definition
if (attrIndex)
{
stAttr[attrIndex] = 0; // null-terminate
if (loadstrings_AddAttr( (char*) stAttr, numStrings)) return 1;
}
attrIndex = 0;
}
else
{ // save char to attribute buffer
stAttr[attrIndex] = c;
attrIndex++;
}
break;
case STSTATE_READNAME:
// read in the string name until we get to ']'
if (c != ']')
{
stName += (char)c;
}
else
{
highlight = 0;
// read any attributes until the CR
state = STSTATE_READATTR;
attrIndex = 0;
}
break;
case STSTATE_READSTRING:
// read in string data until we see another '['
if (c != '[')
{
// allow delimiters:
// you can put [ and ] in the string by using \( and \).
// set a highlight (change font color to the +128 font) with \H
// stop highlighting with \h
if (stString.size()>0 && stString[stString.size()-1]==char('\\'+(highlight*128)))
{ // delimiter detected
if (c=='(')
{
stString[stString.size()-1] = '[' + (highlight*128);
}
else if (c==')')
{
stString[stString.size()-1] = ']' + (highlight*128);
}
else if (c=='H')
{
highlight = 1;
stString.erase(stString.size()-1);
}
else if (c=='h')
{
highlight = 0;
stString.erase(stString.size()-1);
}
else if (c=='\\')
{
stString[stString.size() - 1] = '\\' + (highlight*128);
}
}
else
{ // normal non-delimited char
stString += (char)c + ((highlight && c!=0 && c!=13) ? 128 : 0);
}
}
else
{
// HexDump(GetConstIterator(stName), printOnLogger<notes>, 0);
// HexDump(GetConstIterator(stString), printOnLogger<notes>, 0);
// notes.flush();
/* save the string to the strings[] structure */
// copy the string info to the newly malloc()'d memory area
strings[numStrings].name = stName;
strings[numStrings].stringptr = stString;
stName = "";
stString = "";
numStrings++;
// read the name of the next string
state = STSTATE_READNAME;
}
break;
}
} while(1);
g_pLogFile->ftextOut("loadstrings(): loaded %d strings from '%s'.<br>", numStrings, fname.c_str());
fclose(fp);
return 0;
}
int freestrings(void)
{
int i,j;
int NumStringsFreed;
NumStringsFreed = 0;
for(i=0;i<MAX_STRINGS;i++)
{
if (strings[i].name != "")
{
// free all attribute names
for(j=0;j<strings[i].numAttributes;j++)
{
free(strings[i].attrnames[j]);
}
strings[i].numAttributes = 0;
NumStringsFreed++;
}
}
return NumStringsFreed;
}
/*static void dumpstrings() {
notes << "Available strings: ";
for(int i=0;i<numStrings;i++)
notes << strings[i].name << ", ";
notes << endl;
}*/
// returns a pointer to the string with name 'name'
std::string getstring(const std::string& name)
{
for(int i=0;i<numStrings;i++)
{
if (name == strings[i].name)
{
return strings[i].stringptr;
}
}
//dumpstrings();
return "UNKNOWN '" + name + "' STRING";
}
// because windows and linux read files differently, these is to function to get them correctly
std::string formatPathString(const std::string& path)
{
size_t p = path.find('\r');
return "data/" + path.substr(0,p) + "/";
}
// returns attribute attrname of string stringname, or -1 if it doesn't exist.
int GetStringAttribute(const std::string& stringName, const char *attrName)
{
int i,j;
for(i=0;i<numStrings;i++)
{
if (stringName == strings[i].name)
{
// we found the string, now find the requested attribute
for(j=0;j<strings[i].numAttributes;j++)
{
if (!strcmp((char*) attrName, (char*) strings[i].attrnames[j]))
{
return strings[i].attrvalues[j];
}
}
// failed to find attribute
return -1;
}
}
// failed to find string
return -1;
}