/* 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 #include #include #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.
", p_GameData->FileList[c].c_str()); p_GameData->Episode = -1; ok++; } } if(ok==0) g_pLogFile->ftextOut("Game data of Episode %d is complete.
", 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<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<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<Control.levelcontrol.episode==1) { spawn_object(curmapx<<4<Control.levelcontrol.episode==2) { spawn_object(curmapx<<4<Control.levelcontrol.episode==3) { spawn_object(curmapx<<4<Control.levelcontrol.episode==1) spawn_object(curmapx<<4<Control.levelcontrol.episode==2) spawn_object(curmapx<<4<Control.levelcontrol.episode==3) spawn_object(curmapx<<4<Control.levelcontrol.episode==1) { o = spawn_object(curmapx<<4<Control.levelcontrol.chglevelto==13) { objects[o].hasbeenonscreen = 1; } } else if (pCKP->Control.levelcontrol.episode==2) spawn_object(curmapx<<4<Control.levelcontrol.episode==3) { if(TileProperty[map.mapdata[curmapx][curmapy+1]][BLEFT]) { spawn_object(curmapx<<4<Control.levelcontrol.episode==1) { o = spawn_object((((curmapx+1)<<4)+4)<Control.levelcontrol.episode==2) { o = spawn_object(curmapx<<4<Control.levelcontrol.episode==3) { o = spawn_object(curmapx<<4<Control.levelcontrol.episode==2) { o = spawn_object(curmapx<<4<Control.levelcontrol.canexit = 0; // can't exit till spark is shot } else { o = spawn_object(curmapx<<4<Control.levelcontrol.episode==3) { o = spawn_object(curmapx<<4<Control.levelcontrol.episode==1) { o = spawn_object(((curmapx<<4)-4)<Control.levelcontrol.episode==3) { o = spawn_object(curmapx<<4<Control.levelcontrol.episode==1) { spawn_object(curmapx<<4<Control.levelcontrol.episode==3) { spawn_object(curmapx<<4<Control.levelcontrol.episode==3) { spawn_object(curmapx<<4<Control.levelcontrol.episode==3) { o = spawn_object(curmapx<<4<Control.levelcontrol.episode==3) { o = spawn_object(curmapx<<4<fadePalette(20); g_pLogFile->ftextOut(PURPLE,"unknown enemy type %d at (%d,%d)
", 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
", fname.c_str()); return 1; } g_pLogFile->ftextOut("loadmap(): file %s opened. Loading...
", 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
", 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
"); return 1; } planesize = filebuf[9]; planesize /= 2; // Size of two planes, but we only need one for( c=18 ; c 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.
", attr); return 1; } // split the attribute up into it's name and it's value copyPtr = stAttrName; for(i=0;itextOut(RED,"loadstrings_AddAttr(): Unable to allocate space for attribute name ('%s').
", 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'.
", fname.c_str()); fp = OpenGameFile(fname.c_str(), "rb"); if (!fp) { g_pLogFile->ftextOut("loadstrings(): String file unable to open.
"); 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;i0 && 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, 0); // HexDump(GetConstIterator(stString), printOnLogger, 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'.
", numStrings, fname.c_str()); fclose(fp); return 0; } int freestrings(void) { int i,j; int NumStringsFreed; NumStringsFreed = 0; for(i=0;i