Main Page | Modules | Alphabetical List | Data Structures | File List | Data Fields | Globals | Related Pages

src/files.c

Go to the documentation of this file.
00001 /* {{{
00002  * CalcRogue, a roguelike game for PCs, calculators and PDAs
00003  * Copyright (C) 2003 Jim Babcock
00004  * 
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  * 
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  * 
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00018  * }}} */
00019 // file.c
00022 
00023 #include "crogue.h"
00024 
00025 void compress_tiles(void);
00026 
00027 static const uchar save_file_extension[6] = { 0,'S','A','V',0,0xf8 };
00028 
00029 #ifdef IS_CALCULATOR
00030 //{{{
00031 static void archive_file(const char *filename, int filesize)
00032 {
00033     ulong space_free, block_free;
00034     
00035     filesize += 16; // Margin of error for various overheads.
00036     
00037     EM_survey(NULL, &space_free, &block_free, NULL, NULL, NULL);
00038     space_free += block_free;
00039     
00040     if(space_free < filesize) {
00041         message(gettext("WARNING: Not enough archive free. Using main memory."));
00042         read_char();
00043         return;
00044     } else if(block_free < filesize) {
00045         if(prompt(gettext("Garbage collect archive memory?")))
00046         {
00047             message(gettext("Garbage collecting archive memory."));
00048             EM_GC(FALSE);
00049             EM_survey(NULL, &space_free, &block_free, NULL, NULL, NULL);
00050             if(space_free < filesize)
00051             {
00052                 message(gettext("Still not enough archive free. Using main memory."));
00053                 return;
00054             }
00055         } else {
00056             message(gettext("Not enough archive free. Using main memory."));
00057             read_char();
00058             return;
00059         }
00060     }
00061     EM_moveSymToExtMem(SYMSTR(filename), HS_NULL);
00062 }
00063 //}}}
00064 #endif
00065 
00066 //{{{
00067 void link_data_file(void)
00068 {
00069     w->tiledescs     = (struct tiledesc* )deref_file_ptr(w->desc.tileoffset);
00070     w->itemdescs     = (struct filelink* )deref_file_ptr(w->desc.itemoffset);
00071     w->spelldescs    = (struct spelldesc*)deref_file_ptr(w->desc.spelllist);
00072     w->shopdescs     = (struct shopdesc* )deref_file_ptr(w->desc.shopdescs);
00073     w->playerclasses = (struct classdesc*)deref_file_ptr(w->desc.playerclasses);
00074     w->shuffledata   = (struct filelink* )deref_file_ptr(w->desc.stdata);
00075 #ifdef IS_CALCULATOR
00076     w->dll_functions = _DLL_call(const void**,(const import_functions *f),0)(&body_functions);
00077     w->dll_interface = (const export_functions*)w->dll_functions;
00078 #else
00079     w->dll_functions = dll_load(&body_functions);
00080     w->dll_interface = (const export_functions*)w->dll_functions;
00081 #endif
00082 }
00083 //}}}
00084 
00085 //{{{
00086 //
00087 // Save the game state
00088 // Presently, just byte-dumps the main data structure
00089 // This needs to be changed to include compression for the
00090 // bigger parts.
00091 //
00092 void save_game(void)
00093 {
00094     FILE *sg;
00095     const char *filename;
00096 #   ifdef EXTERNAL_COMPRESSOR
00097     char cmd[512];
00098 #   endif
00099     
00100 #   ifdef IS_CALCULATOR
00101         int filesize;
00102 #   endif
00103     
00104     // If a previous version of this game exists, delete it before writing new one
00105 #   ifdef IS_CALCULATOR
00106         EM_moveSymFromExtMem(SYMSTR(SAVEFILENAME), HS_NULL);
00107         SymDel(SYMSTR(SAVEFILENAME));
00108 #   endif
00109     
00110 #   ifdef SHARED_SCORES
00111     filename = retprintf("%s%s_%s", file_prefix, getlogin(), SAVEFILENAME);
00112 #   else
00113     filename = SAVEFILENAME;
00114 #   endif
00115     
00116     sg = fopen(filename, "wb");
00117     
00118     if(sg)
00119     {
00120         frleinit_write(sg, &fhuffmanputc);
00121         fhuffmaninit_write();
00122         frlewrite(w, sizeof(world), sizeof(monster), sg);
00123         frlewrite(&w->t[0][0], sizeof(tile)*w->mapsize_x*w->mapsize_y, sizeof(tile), sg);
00124         frlewrite(w->shuffletranslation, sizeof(uint)*w->desc.shuffletabsize, sizeof(uint), sg);
00125         frlewrite(w->itemids, (w->desc.itementries/32 + 1) * 4, 1, sg);
00126         frlewrite(w->plr.spellknowledge, sizeof(slong)*w->desc.numspells, sizeof(slong), sg);
00127         frlewrite(w->items.items, sizeof(item)*w->items.alloced, sizeof(item), sg);
00128         if(w->wandering_monsters_num)
00129             frlewrite(w->wandering_monsters, sizeof(monster)*w->wandering_monsters_num, sizeof(monster), sg);
00130         frleflush(sg);
00131         fhuffmanflush(sg);
00132 #       ifdef IS_CALCULATOR
00133             fwrite(save_file_extension, 6, 1, sg);
00134             filesize = peek_w(sg->base);
00135 #       endif
00136         frlefinalize(sg);
00137         fclose(sg);
00138 #       ifdef IS_CALCULATOR
00139             if(w->options[OPTION_AUTOARCHIVE] == OPTION_ARCHIVE_YES)
00140                 archive_file(SAVEFILENAME, filesize);
00141 #       endif
00142 #       ifdef EXTERNAL_COMPRESSOR
00143         sprintf(cmd, EXTERNAL_COMPRESSOR " -f -q %s", filename);
00144         system(cmd);
00145 #       endif
00146     }
00147 }
00148 //}}}
00149 //{{{
00150 //
00151 // Attempts to load a saved game from the filesystem.
00152 // Returns 1 if successful or 0 if no saved game exists.
00153 //
00154 int restore_game(void)
00155 {
00156     FILE *fin;
00157     
00158 #ifdef SHARED_SCORES
00159     char filename[512];
00160     sprintf(filename, "%s%s_%s", file_prefix, getlogin(), SAVEFILENAME);
00161 #else
00162     const char *filename = SAVEFILENAME;
00163 #endif
00164     
00165 #ifdef EXTERNAL_COMPRESSOR
00166     system( retprintf(EXTERNAL_COMPRESSOR " -f -q -d %s.gz", filename) );
00167 #endif
00168     
00169     fin = fopen( filename, "rb");
00170     if( fin )
00171     {
00172         if(!frleverify(fin))
00173         {
00174             message(gettext("There's a corrupted save file here..."));
00175             fclose(fin);
00176             read_char();
00177             return 0;
00178         }
00179         frleinit_read(fin);
00180         fhuffmaninit_read();
00181         
00182         frleread(w, sizeof(world), sizeof(monster), fin);
00183         w->t = NULL;
00184         resize_map_store(w->mapsize_x, w->mapsize_y);
00185         frleread(&w->t[0][0], sizeof(tile)*w->mapsize_x*w->mapsize_y, sizeof(tile), fin);
00186         sys_init_data_file();
00187         
00188         w->shuffletranslation = (uint*)debug_malloc( sizeof(uint) * w->desc.shuffletabsize );
00189         init_discoveries();
00190         w->items.items = (item*)debug_malloc(w->items.alloced*sizeof(item));
00191         init_spells();
00192         
00193         frleread(w->shuffletranslation, sizeof(uint)*w->desc.shuffletabsize, sizeof(uint), fin);
00194         frleread(w->itemids, (w->desc.itementries/32 + 1) * 4, 1, fin);
00195         frleread(w->plr.spellknowledge, sizeof(slong)*w->desc.numspells, sizeof(slong), fin);
00196         frleread(w->items.items, sizeof(item)*w->items.alloced, sizeof(item), fin);
00197         if(w->wandering_monsters_num)
00198         {
00199             w->wandering_monsters = debug_malloc(sizeof(monster)*w->wandering_monsters_num);
00200             frleread(w->wandering_monsters, sizeof(monster)*w->wandering_monsters_num, sizeof(monster), fin);
00201         }
00202         
00203         w->current_map = deref_file_ptr(w->current_map_link);
00204         
00205         fclose(fin);
00206 #ifdef EXTERNAL_COMPRESSOR
00207         // Recompress save
00208         system( retprintf(EXTERNAL_COMPRESSOR " -f -q %s", filename) );
00209 #endif
00210         return 1;
00211     } else {
00212         return 0;
00213     }
00214 }
00215 //}}}
00216 
00217 const uchar level_file_extension[6] = { 0,'L','E','V',0,0xf8 };
00218 
00219 //{{{
00220 const char *levelfile_name(int levelnum)
00221 {
00222 #ifdef IS_CALCULATOR
00223     return retprintf("ROGLVL%i", levelnum);
00224 #endif
00225 #ifdef REALCOMPUTER
00226     #ifdef SHARED_SCORES
00227     return retprintf("%s%s_roglvl%i.sav", file_prefix, getlogin(), levelnum);
00228     #else
00229     return retprintf("roglvl%i.sav", levelnum);
00230     #endif
00231 #endif  
00232 #ifdef PALMOS
00233     return retprintf("roglvl%i", levelnum);
00234 #endif
00235 }
00236 //}}}
00237 //{{{
00238 //
00239 // Save the current level (the parts of the game state which should be restored
00240 // when the player comes back: items, monsters, walls/floor, etc). The two large
00241 // tables (monsters and tiles) are written using a special form of run-length
00242 // encoding. The first byts of each struct are ordered sequentially, followed
00243 // by the second byte of each struct, etc. This avoids the complexity of having
00244 // a single run length handle more than one character, while keeping similar
00245 // bytes together.
00246 //
00247 // File naming convention depends on platform.
00248 //
00249 void save_level(void)
00250 {
00251     FILE *save;
00252     const char *name;
00253     ushort map_size[2];
00254 #   ifdef EXTERNAL_COMPRESSOR
00255     char cmd[512];
00256 #   endif
00257 #   ifdef IS_CALCULATOR
00258     int filesize;
00259 #   endif
00260     
00261     name = levelfile_name(w->level);
00262     
00263 #ifdef IS_CALCULATOR
00264     // Unlink pre-existing level of the same name
00265     EM_moveSymFromExtMem(SYMSTR(name), HS_NULL);
00266     SymDel(SYMSTR(name));
00267 #endif
00268     
00269     // Prep for efficient writing
00270     compress_items();
00271     compress_monsters();
00272     
00273     save = fopen(name, "wb");
00274     
00275     frleinit_write(save, &fhuffmanputc);
00276     fhuffmaninit_write();
00277     
00278     frlewrite( (char*)(&w->m[0]), sizeof(monster)*MONSTERS_MAX, sizeof(monster), save );
00279     map_size[0] = w->mapsize_x; map_size[1] = w->mapsize_y;
00280     frlewrite( (char*)(map_size), sizeof(ushort)*2, sizeof(ushort), save );
00281     frlewrite( (char*)(&w->t[0][0]), sizeof(tile)*MAPSIZE_X*MAPSIZE_Y, sizeof(tile), save );
00282     
00283     // Write items
00284     frlewrite(&w->items, sizeof(itemsinfo), 1, save);
00285     frlewrite(w->items.items, sizeof(item)*w->items.alloced, sizeof(item), save);
00286     
00287     frleflush(save);
00288     fhuffmanflush(save);
00289     
00290 #   ifdef IS_CALCULATOR
00291         fwrite(level_file_extension, 6, 1, save);
00292         filesize = peek_w(save->base);
00293 #   endif
00294     frlefinalize(save);
00295     fclose(save);
00296 
00297 #   ifdef EXTERNAL_COMPRESSOR
00298         sprintf(cmd, EXTERNAL_COMPRESSOR " -f -q %s", name);
00299         system(cmd);
00300 #   endif
00301 #   ifdef IS_CALCULATOR 
00302         if(w->options[OPTION_AUTOARCHIVE] == OPTION_ARCHIVE_YES)
00303             archive_file(name, filesize);
00304 #   endif
00305 }
00306 //}}}
00307 //{{{
00308 //
00309 // Load the level number given in w->level from a run-length encoded file.
00310 // Returns 1 if successful, 0 if the file does not exist.
00311 //
00312 int load_level(int source, coord pos)
00313 {
00314     FILE *save;
00315     const char *name;
00316     ushort map_size[2];
00317 #ifdef EXTERNAL_COMPRESSOR
00318     char cmd[512];
00319 #endif
00320     
00321     name = levelfile_name(w->level);
00322     
00323 #ifdef EXTERNAL_COMPRESSOR
00324     // Check for file existance and accessibility
00325     sprintf(cmd, "%s.gz", name);
00326     if(access(cmd, R_OK) != 0)
00327         return 0;
00328     
00329     // Open file
00330     sprintf(cmd, EXTERNAL_COMPRESSOR " -f -q -d %s.gz", name);
00331     system(cmd);
00332 #endif
00333     
00334     save = fopen(name, "rb");
00335     
00336     if( !save )
00337         return 0;
00338     
00339     if(!frleverify(save))
00340     {
00341         message(gettext("This level is corrupt... I'll build a new one."));
00342         fclose(save);
00343         return 0;
00344     }
00345     frleinit_read(save);
00346     fhuffmaninit_read();
00347     
00348     frleread( (char*)(&w->m[0]), sizeof(monster)*MONSTERS_MAX, sizeof(monster), save );
00349     frleread( (char*)(map_size), sizeof(ushort)*2, sizeof(ushort), save );
00350     resize_map_store(map_size[0], map_size[1]);
00351     frleread( (char*)(&w->t[0][0]), sizeof(tile)*MAPSIZE_X*MAPSIZE_Y, sizeof(tile), save );
00352     
00353     place_player_on_map(source, pos);
00354     
00355     // Read items
00356     cleanup_items();
00357     frleread(&w->items, sizeof(itemsinfo), 1, save);
00358     w->items.items = (item*)debug_malloc(w->items.alloced*sizeof(item));
00359     frleread(w->items.items, sizeof(item)*w->items.alloced, sizeof(item), save);
00360     
00361     fclose(save);
00362     
00363 #ifdef EXTERNAL_COMPRESSOR
00364     // Recompress file
00365     sprintf(cmd, EXTERNAL_COMPRESSOR " -f -q %s", name);
00366     system(cmd);
00367 #endif
00368 
00369     full_redraw();
00370     
00371     return 1;
00372 }
00373 //}}}
00374 //{{{
00375 //
00376 // Delete any remnants of saved games (the saved game and saved levels). This
00377 // is called when the player dies to prevent him from using the files to cheat
00378 // and bring his character back.
00379 //
00380 void cleanup_save(void)
00381 {
00382 #ifdef IS_CALCULATOR
00383     int i;
00384     const char *name;
00385     
00386     EM_moveSymFromExtMem(SYMSTR(SAVEFILENAME), HS_NULL);
00387     SymDel(SYMSTR(SAVEFILENAME));
00388     
00389     for(i=1; i<=w->maxlevel; i++)
00390     {
00391         name = levelfile_name(i);
00392         EM_moveSymFromExtMem(SYMSTR(name), HS_NULL);
00393         SymDel(SYMSTR(name));
00394     }
00395 #else
00396     int i;
00397     const char *name;
00398     #ifdef SHARED_SCORES
00399         #ifdef EXTERNAL_COMPRESSOR
00400         name = retprintf("%s%s_%s.gz", file_prefix, getlogin(), SAVEFILENAME);
00401         #else
00402         name = retprintf("%s%s_%s", file_prefix, getlogin(), SAVEFILENAME);
00403         #endif
00404     #else
00405     name = SAVEFILENAME;
00406     #endif
00407     
00408     remove(name);
00409     
00410     for(i=1; i<=w->maxlevel+1; i++)
00411     {
00412         name = levelfile_name(i);
00413         remove(name);
00414     }
00415 #endif
00416 }
00417 //}}}
00418 
00419 //{{{
00420 void compress_tiles(void)
00421 {
00422     int i, j;
00423     
00424     for(i=0; i<MAPSIZE_Y; i++)
00425     for(j=0; j<MAPSIZE_X; j++)
00426     {
00427         w->t[i][j].flags &= ~TFLAG_LIT;
00428     }
00429 }
00430 //}}}
00431 

Generated on Thu May 20 13:12:09 2004 for CalcRogue by doxygen 1.3.6