gamedat.cc

Go to the documentation of this file.
00001 /*
00002  *  gamedat.cc - Create gamedat files from a savegame.
00003  *
00004  *  Copyright (C) 2000-2001  The Exult Team
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00019  */
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #  include <config.h>
00023 #endif
00024 
00025 #ifdef WIN32
00026 #ifndef UNDER_CE
00027 #include <io.h>
00028 #endif
00029 #endif
00030 
00031 #ifndef ALPHA_LINUX_CXX
00032 #  include <fstream>
00033 #  include <cstdio>
00034 #  include <cstdlib>
00035 #  include <cstring>
00036 #  include <ctime>
00037 #endif
00038 
00039 #if (defined(XWIN) || defined(BEOS))
00040 #include <sys/stat.h>
00041 #elif defined(MACOS)
00042 #include <stat.h>
00043 #endif
00044 
00045 #include "exceptions.h"
00046 #include "fnames.h"
00047 #include "gamewin.h"
00048 #include "gameclk.h"
00049 #include "gamemap.h"
00050 #include "utils.h"
00051 #include "gump_utils.h"
00052 #include "game.h"
00053 #include "Flex.h"
00054 #include "databuf.h"
00055 #include "Newfile_gump.h"
00056 #include "Yesno_gump.h"
00057 #include "actors.h"
00058 #include "party.h"
00059 #include "version.h"
00060 
00061 #ifndef UNDER_CE
00062 using std::cerr;
00063 using std::cout;
00064 using std::endl;
00065 using std::ifstream;
00066 using std::ios;
00067 using std::localtime;
00068 using std::memset;
00069 using std::ofstream;
00070 using std::ostream;
00071 using std::size_t;
00072 using std::strchr;
00073 using std::strcmp;
00074 using std::strcpy;
00075 using std::strlen;
00076 using std::strncpy;
00077 using std::time_t;
00078 using std::tm;
00079 using std::time_t;
00080 #endif
00081 
00082 // Save game compression level
00083 extern int save_compression;
00084 
00085 /*
00086  *  Write out the gamedat directory from a saved game.
00087  *
00088  *  Output: Aborts if error.
00089  */
00090 
00091 void Game_window::restore_gamedat
00092   (
00093   const char *fname   // Name of savegame file.
00094   )
00095   {
00096           // Check IDENTITY.
00097   char *id = get_game_identity(fname);
00098   char *static_identity = get_game_identity(INITGAME);
00099           // Note: "*" means an old game.
00100   if(!id || (*id != '*' && strcmp(static_identity, id) != 0))
00101     {
00102     std::string msg("Wrong identity '");
00103     msg += id; msg += "'.  Open anyway?";
00104     int ok = Yesno_gump::ask(msg.c_str());
00105     if (!ok)
00106       return;
00107     }
00108   // Check for a ZIP file first
00109 #ifdef HAVE_ZIP_SUPPORT
00110   if (restore_gamedat_zip(fname) != false)
00111     return;
00112 #endif
00113 
00114   ifstream in_stream;
00115 
00116 #ifdef RED_PLASMA
00117   // Display red plasma during load...
00118   setup_load_palette();
00119 #endif
00120                   
00121   U7mkdir("<GAMEDAT>", 0755);   // Create dir. if not already there. Don't
00122                   // use GAMEDAT define cause that's got a
00123                   // trailing slash
00124   try
00125     {
00126     U7open(in_stream, fname); // Open file; throws an exception 
00127     }
00128   catch(const file_exception & f)
00129     {
00130     if (!Game::is_editing())  // Ok if map-editing.
00131       throw f;
00132     std::cerr << "Warning (map-editing): Couldn't open '" << 
00133               fname << "'" << endl;
00134     return;
00135     }
00136 
00137   StreamDataSource in(&in_stream);
00138 
00139   U7remove (USEDAT);
00140   U7remove (USEVARS);
00141   U7remove (U7NBUF_DAT);
00142   U7remove (NPC_DAT);
00143   U7remove (MONSNPCS);
00144   U7remove (FLAGINIT);
00145   U7remove (GWINDAT);
00146   U7remove (IDENTITY);
00147   U7remove (GSCHEDULE);
00148   U7remove ("<STATIC>/flags.flg");
00149   U7remove (GSCRNSHOT);
00150   U7remove (GSAVEINFO);
00151   U7remove (GNEWGAMEVER);
00152   U7remove (GEXULTVER);
00153   U7remove (KEYRINGDAT);
00154 
00155   cout.flush();
00156 
00157   in.seek(0x54);      // Get to where file count sits.
00158   int numfiles = in.read4();
00159   in.seek(0x80);      // Get to file info.
00160           // Read pos., length of each file.
00161   long *finfo = new long[2*numfiles];
00162   int i;
00163   for (i = 0; i < numfiles; i++)
00164     {
00165     finfo[2*i] = in.read4();  // The position, then the length.
00166     finfo[2*i + 1] = in.read4();
00167     }
00168   for (i = 0; i < numfiles; i++)  // Now read each file.
00169     {
00170           // Get file length.
00171     int len = finfo[2*i + 1] - 13;
00172     if (len <= 0)
00173       continue;
00174     in.seek(finfo[2*i]);  // Get to it.
00175     char fname[50];   // Set up name.
00176     strcpy(fname, GAMEDAT);
00177     in.read(&fname[sizeof(GAMEDAT) - 1], 13);
00178     int namelen = strlen(fname);
00179           // Watch for names ending in '.'.
00180     if (fname[namelen - 1] == '.')
00181       fname[namelen - 1] = 0;
00182     ofstream out;
00183     U7open(out, fname);
00184           // Now read the file.
00185     char *buf = new char[len];
00186     in.read(buf, len);
00187     out.write(buf, len);  // Then write it out.
00188     delete [] buf;
00189     if (!out.good())
00190       abort("Error writing '%s'.", fname);
00191     out.close();
00192     CYCLE_RED_PLASMA();
00193     }
00194   delete [] finfo;
00195   cout.flush();
00196 
00197 #ifdef RED_PLASMA
00198   load_palette_timer = 0;
00199 #endif
00200   }
00201 
00202 /*
00203  *  Write out the gamedat directory from a saved game.
00204  *
00205  *  Output: Aborts if error.
00206  */
00207 
00208 void Game_window::restore_gamedat
00209   (
00210   int num       // 0-9, currently.
00211   )
00212   {
00213   char fname[50];     // Set up name.
00214   snprintf(fname, 50, SAVENAME, num, 
00215     Game::get_game_type() == BLACK_GATE ? "bg" : "si");
00216   restore_gamedat(fname);
00217   }
00218 
00219 /*
00220  *  List of 'gamedat' files to save (in addition to 'iregxx'):
00221  */
00222 static const char *bgsavefiles[] = {
00223   GSCRNSHOT,  GSAVEINFO,  // MUST BE FIRST!!
00224   IDENTITY,     // MUST BE #2
00225   GEXULTVER,  GNEWGAMEVER,
00226   NPC_DAT,  MONSNPCS,
00227   USEVARS,  USEDAT,
00228   FLAGINIT, GWINDAT,
00229   GSCHEDULE
00230   };
00231 static const int bgnumsavefiles = sizeof(bgsavefiles)/sizeof(bgsavefiles[0]);
00232 
00233 static const char *sisavefiles[] = {
00234   GSCRNSHOT,  GSAVEINFO,  // MUST BE FIRST!!
00235   IDENTITY,     // MUST BE #2
00236   GEXULTVER,  GNEWGAMEVER,
00237   NPC_DAT,  MONSNPCS,
00238   USEVARS,  USEDAT,
00239   FLAGINIT, GWINDAT,
00240   GSCHEDULE,  KEYRINGDAT
00241   };
00242 static const int sinumsavefiles = sizeof(sisavefiles)/sizeof(sisavefiles[0]);
00243 
00244 /*
00245  *  Save a single file into an IFF repository.
00246  *
00247  *  Output: Length of data saved.
00248  *    Errors reported.
00249  */
00250 
00251 static long Savefile
00252   (
00253   ostream& out,     // Write here.
00254   const char *fname     // Name of file to save.
00255   )
00256   {
00257   ifstream in_stream;
00258   try {
00259     U7open(in_stream, fname);
00260   } catch (exult_exception& e) {
00261     if (Game::is_editing())
00262       return 0; // Newly developed game.
00263     throw e;
00264   }
00265   StreamDataSource in(&in_stream);
00266   long len = in.getSize();
00267   in.seek(0);
00268   char namebuf[13];   // First write 13-byte name.
00269   memset(namebuf, 0, sizeof(namebuf));
00270   const char *base = strchr(fname, '/');// Want the base name.
00271   if (!base)
00272     base = strchr(fname, '\\');
00273   if (base)
00274     base++;
00275   else
00276     base = fname;
00277   strncpy(namebuf, base, sizeof(namebuf));
00278   out.write(namebuf, sizeof(namebuf));
00279   char *buf = new char[len];  // Get it all at once.
00280   in.read(buf, len);
00281   out.write(buf, len);
00282   delete [] buf;
00283   if (!in_stream.good())
00284     throw file_read_exception(fname);
00285   return len + 13;    // Include filename.
00286   }
00287 
00288 static long SavefileFromDataSource(
00289   ostream& out,   // write here
00290   DataSource& source, // read from here
00291   const char *fname // store data using this filename
00292 )
00293 {
00294   long len = source.getSize();
00295   char namebuf[13];
00296   memset(namebuf, 0, sizeof(namebuf));
00297   strncpy(namebuf, fname, sizeof(namebuf));
00298   out.write(namebuf, sizeof(namebuf));
00299   char *buf = new char[len];
00300   source.read(buf, len);
00301   out.write(buf, len);
00302   delete [] buf;
00303   return len + 13;
00304 }
00305 
00306 /*
00307  *  Save 'gamedat' into a given file.
00308  *
00309  *  Output: 0 if error (reported).
00310  */
00311 
00312 void Game_window::save_gamedat
00313   (
00314   const char *fname,      // File to create.
00315   const char *savename      // User's savegame name.
00316   )
00317   {
00318 
00319     // First check for compressed save game
00320 #ifdef HAVE_ZIP_SUPPORT
00321   if (save_compression > 0 && save_gamedat_zip(fname, savename) != false)
00322     return;
00323 #endif
00324 
00325   // setup correct file list 
00326   int numsavefiles = (Game::get_game_type() == BLACK_GATE) ?
00327       bgnumsavefiles : sinumsavefiles;
00328   const char **savefiles = (Game::get_game_type() == BLACK_GATE) ?
00329       bgsavefiles : sisavefiles;  
00330 
00331   ofstream out;
00332   U7open(out, fname);
00333           // Doing all IREG's + what's listed.
00334   int count = 12*12 + numsavefiles;
00335           // Use samename for title.
00336   Flex_writer flex(out, savename, count);
00337   int i;        // Start with listed files.
00338   for (i = 0; i < numsavefiles; i++)
00339     {
00340     Savefile(out, savefiles[i]);
00341     flex.mark_section_done();
00342     }
00343           // Now the Ireg's.
00344   for (int schunk = 0; schunk < 12*12; schunk++, i++)
00345     {
00346     char iname[80];
00347     Savefile(out, map->get_schunk_file_name(U7IREG,
00348               schunk, iname));
00349     flex.mark_section_done();
00350     }
00351   bool result = flex.close(); // Write it all out.
00352   if (!result)      // ++++Better error system needed??
00353     throw file_write_exception(fname);
00354   return;
00355   }
00356 
00357 /*
00358  *  Save to one of the numbered savegame files (and update save_names).
00359  *
00360  *  Output: false if error (reported).
00361  */
00362 
00363 void Game_window::save_gamedat
00364   (
00365   int num,      // 0-9, currently.
00366   const char *savename      // User's savegame name.
00367   )
00368   {
00369   char fname[50];     // Set up name.
00370   snprintf(fname, 50, SAVENAME, num,
00371     Game::get_game_type() == BLACK_GATE ? "bg" : "si");
00372   save_gamedat(fname, savename);
00373   if (num >=0 && num < 10)
00374   {
00375     delete [] save_names[num];  // Update name
00376     save_names[num] = newstrdup(savename);
00377   }
00378   }
00379 
00380 /*
00381  *  Read in the saved game names.
00382  */
00383 void Game_window::read_save_names
00384   (
00385   )
00386 {
00387   for (size_t i = 0; i < sizeof(save_names)/sizeof(save_names[0]); i++)
00388   {
00389     char fname[50];   // Set up name.
00390     snprintf(fname, 50, SAVENAME, i,
00391       Game::get_game_type() == BLACK_GATE ? "bg" : "si");
00392     ifstream in;
00393     try
00394     {
00395       U7open(in, fname);
00396       char buf[0x50];   // It's at start of file.
00397       memset(buf, 0, sizeof(buf));
00398       in.read(buf, sizeof(buf) - 1);
00399       if (in.good())    // Okay if file not there.
00400         save_names[i] = newstrdup(buf);
00401       else
00402         save_names[i] = newstrdup("");
00403       in.close();
00404     }
00405     catch(const file_exception & f)
00406     {
00407       save_names[i] = newstrdup("");
00408     }
00409   }
00410 }
00411 
00412 
00413 void Game_window::write_saveinfo()
00414 {
00415   ofstream out_stream;
00416   int i, j;
00417 
00418   int save_count = 1;
00419 
00420   try
00421   {
00422     ifstream in;
00423     U7open(in, GSAVEINFO);    // Open file; throws an exception 
00424 
00425     StreamDataSource ds(&in);
00426     ds.skip(10);  // Skip 10 bytes.
00427     save_count += ds.read2();
00428 
00429     in.close();
00430   }
00431   catch(const file_exception & f)
00432   {
00433   }
00434 
00435   int party_size = party_man->get_count()+1;
00436 
00437   time_t t = std::time(0);
00438   struct tm *timeinfo = localtime (&t); 
00439 
00440   U7open(out_stream, GSAVEINFO);    // Open file; throws an exception - Don't care
00441   StreamDataSource out(&out_stream);
00442 
00443   // This order must match struct SaveGame_Details
00444 
00445   // Time that the game was saved
00446   out.write1(timeinfo->tm_min);
00447   out.write1(timeinfo->tm_hour);
00448   out.write1(timeinfo->tm_mday);
00449   out.write1(timeinfo->tm_mon+1);
00450   out.write2(timeinfo->tm_year + 1900);
00451 
00452   // The Game Time that the save was done at
00453   out.write1(clock->get_minute());
00454   out.write1(clock->get_hour());
00455   out.write2(clock->get_day());
00456 
00457   out.write2(save_count);
00458   out.write1(party_size);
00459 
00460   out.write1(0);      // Unused
00461 
00462   out.write1(timeinfo->tm_sec); // 15
00463 
00464   // Packing for the rest of the structure
00465   for (j = reinterpret_cast<long>(&(((SaveGame_Details *)0)->reserved0)); j < sizeof(SaveGame_Details); j++)
00466     out.write1(0);
00467 
00468   for (i=0; i<party_size ; i++)
00469   {
00470     Actor *npc;
00471     if (i == 0)
00472       npc = main_actor;
00473     else
00474       npc = (Npc_actor *)get_npc(party_man->get_member(i-1));
00475 
00476     char name[18];
00477     std::string namestr = npc->get_npc_name();
00478     strncpy (name, namestr.c_str(), 18);
00479     out.write(name, 18);
00480     out.write2(npc->get_shapenum());
00481 
00482     out.write4(npc->get_property(Actor::exp));
00483     out.write4(npc->get_flags());
00484     out.write4(npc->get_flags2());
00485 
00486     out.write1(npc->get_property(Actor::food_level));
00487     out.write1(npc->get_property(Actor::strength));
00488     out.write1(npc->get_property(Actor::combat));
00489     out.write1(npc->get_property(Actor::dexterity));
00490     out.write1(npc->get_property(Actor::intelligence));
00491     out.write1(npc->get_property(Actor::magic));
00492     out.write1(npc->get_property(Actor::mana));
00493     out.write1(npc->get_property(Actor::training));
00494 
00495     out.write2(npc->get_property(Actor::health));
00496     out.write2(npc->get_shapefile());
00497 
00498     // Packing for the rest of the structure
00499     for (j = reinterpret_cast<long>(&(((SaveGame_Party *)0)->reserved1)); j < sizeof(SaveGame_Party); j++)
00500       out.write1(0);
00501   }
00502 
00503   out_stream.close();
00504 
00505   // Save Shape
00506   Shape_file *map = create_mini_screenshot();
00507   U7open(out_stream, GSCRNSHOT);    // Open file; throws an exception - Don't care
00508   map->save(&out);
00509   out_stream.close();
00510   delete map;
00511 
00512   // Current Exult version
00513 
00514   U7open(out_stream, GEXULTVER);
00515   getVersionInfo(out_stream);
00516   out_stream.close();
00517 
00518   // Exult version that started this game
00519   if (!U7exists(GNEWGAMEVER)) {
00520     U7open(out_stream, GNEWGAMEVER);
00521     out_stream << "Unknown" << endl;
00522     out_stream.close();
00523   }
00524 }
00525 
00526 
00527 void Game_window::read_saveinfo(DataSource *in,
00528     SaveGame_Details *&details,
00529     SaveGame_Party *& party)
00530 {
00531   int i;
00532   details = new SaveGame_Details;
00533 
00534   // This order must match struct SaveGame_Details
00535   // Time that the game was saved
00536   details->real_minute = in->read1();
00537   details->real_hour = in->read1();
00538   details->real_day = in->read1();
00539   details->real_month = in->read1();
00540   details->real_year = in->read2();
00541   
00542 
00543   // The Game Time that the save was done at
00544   details->game_minute = in->read1();
00545   details->game_hour = in->read1();
00546   details->game_day = in->read2();
00547 
00548   details->save_count = in->read2();
00549   details->party_size = in->read1();
00550 
00551   details->unused = in->read1();  // Unused
00552 
00553   details->real_second = in->read1(); // 15
00554 
00555   // Packing for the rest of the structure
00556   in->skip(sizeof(SaveGame_Details) - reinterpret_cast<long>(&(((SaveGame_Details *)0)->reserved0)));
00557 
00558   party = new SaveGame_Party[details->party_size];
00559   for (i=0; i<8 && i<details->party_size ; i++)
00560   {
00561     in->read(party[i].name, 18);
00562     party[i].shape = in->read2();
00563 
00564     party[i].exp = in->read4();
00565     party[i].flags = in->read4();
00566     party[i].flags2 = in->read4();
00567 
00568     party[i].food = in->read1();
00569     party[i].str = in->read1();
00570     party[i].combat = in->read1();
00571     party[i].dext = in->read1();
00572     party[i].intel = in->read1();
00573     party[i].magic = in->read1();
00574     party[i].mana = in->read1();
00575     party[i].training = in->read1();
00576 
00577     party[i].health = in->read2();
00578     party[i].shape_file = in->read2();
00579 
00580     // Packing for the rest of the structure
00581     in->skip (sizeof(SaveGame_Party) -  reinterpret_cast<long>(&(((SaveGame_Party *)0)->reserved1)));
00582   }
00583 }
00584 
00585 bool Game_window::get_saveinfo(int num, char *&name, Shape_file *&map, SaveGame_Details *&details, SaveGame_Party *& party)
00586 {
00587   char fname[50];     // Set up name.
00588   snprintf(fname, 50, SAVENAME, num, 
00589     Game::get_game_type() == BLACK_GATE ? "bg" : "si");
00590 
00591   // First check for compressed save game
00592 #ifdef HAVE_ZIP_SUPPORT
00593   if (get_saveinfo_zip(fname, name, map, details, party) != false)
00594     return true;
00595 #endif
00596 
00597   ifstream in_stream;
00598   U7open(in_stream, fname);   // Open file; throws an exception 
00599   StreamDataSource in(&in_stream);
00600           // in case of an error.
00601   // Always try to Read Name
00602   char buf[0x50];
00603   memset(buf, 0, sizeof(buf));
00604   in.read(buf, sizeof(buf) - 1);
00605   name = new char [strlen (buf)+1];
00606   strcpy (name, buf);
00607 
00608   // Isn't a flex, can't actually read it
00609   if (!Flex::is_flex(&in)) return false;
00610 
00611   // Now get dir info
00612   in.seek(0x54);      // Get to where file count sits.
00613   int numfiles = in.read4();
00614   in.seek(0x80);      // Get to file info.
00615           // Read pos., length of each file.
00616   long *finfo = new long[2*numfiles];
00617   int i;
00618   for (i = 0; i < numfiles; i++)
00619   {
00620     finfo[2*i] = in.read4();  // The position, then the length.
00621     finfo[2*i + 1] = in.read4();
00622   }
00623 
00624   // Always first two entires
00625   for (i = 0; i < 2; i++) // Now read each file.
00626   {
00627           // Get file length.
00628     int len = finfo[2*i + 1] - 13;
00629     if (len <= 0)
00630       continue;
00631     in.seek(finfo[2*i]);  // Get to it.
00632     char fname[50];   // Set up name.
00633     strcpy(fname, GAMEDAT);
00634     in.read(&fname[sizeof(GAMEDAT) - 1], 13);
00635     int namelen = strlen(fname);
00636           // Watch for names ending in '.'.
00637     if (fname[namelen - 1] == '.')
00638       fname[namelen - 1] = 0;
00639 
00640     if (!strcmp (fname, GSCRNSHOT))
00641     {
00642       char *buf = new char[len];
00643       in.read(buf, len);
00644       BufferDataSource ds(buf, len);
00645       map = new Shape_file(&ds);
00646       delete [] buf;
00647     }
00648     else if (!strcmp (fname, GSAVEINFO))
00649     {
00650       read_saveinfo (&in, details, party);
00651     }
00652 
00653   }
00654   in_stream.close();
00655 
00656   delete [] finfo;
00657 
00658   return true;
00659 }
00660 
00661 void Game_window::get_saveinfo(Shape_file *&map, SaveGame_Details *&details, SaveGame_Party *& party)
00662 {
00663   try
00664   {
00665     ifstream in;
00666     U7open(in, GSAVEINFO);    // Open file; throws an exception 
00667     StreamDataSource ds(&in);
00668     read_saveinfo (&ds, details, party);
00669     in.close();
00670   }
00671   catch(const file_exception & f)
00672   {
00673     details = NULL;
00674     party = NULL;
00675   }
00676 
00677   try
00678   {
00679     ifstream in;
00680     U7open(in, GSCRNSHOT);    // Open file; throws an exception 
00681     StreamDataSource ds(&in);
00682     map = new Shape_file(&ds);
00683     in.close();
00684   }
00685   catch(const file_exception & f)
00686   {
00687     // yes, this is weird, but seems to work-around a compiler
00688     // problem... (gcc-2.95.2-12mdk)    -wjp
00689     map = 0; map = 0;
00690   }
00691 }
00692 
00693 /*
00694  *  Return string from IDENTITY in a savegame.
00695  *
00696  *  Output: ->identity if found.
00697  *    0 if error (or may throw exception).
00698  *    "*" if older savegame.
00699  */
00700 char *Game_window::get_game_identity(const char *savename)
00701 {
00702     char *game_identity = 0;
00703 #ifdef HAVE_ZIP_SUPPORT
00704     game_identity = get_game_identity_zip(savename);
00705     if (game_identity)
00706   return game_identity;
00707 #endif
00708     ifstream in_stream;
00709     try {
00710         U7open(in_stream, savename);    // Open file.
00711     } catch (const exult_exception &e) {
00712   if (Game::is_editing()) { // Okay if creating a new game.
00713     std::string titlestr = Game::get_gametitle();
00714     return newstrdup(titlestr.c_str());
00715   }
00716   throw e;
00717     }
00718   StreamDataSource in(&in_stream);
00719 
00720     in.seek(0x54);      // Get to where file count sits.
00721     int numfiles = in.read4();
00722     in.seek(0x80);      // Get to file info.
00723     // Read pos., length of each file.
00724     sint32 *finfo = new sint32[2*numfiles];
00725     int i;
00726     for (i = 0; i < numfiles; i++)
00727       {
00728   finfo[2*i] = in.read4();  // The position, then the length.
00729   finfo[2*i + 1] = in.read4();
00730       }
00731     for (i = 0; i < numfiles; i++)  // Now read each file.
00732       {
00733   // Get file length.
00734   int len = finfo[2*i + 1] - 13;
00735   if (len <= 0)
00736     continue;
00737   in.seek(finfo[2*i]);  // Get to it.
00738   char fname[50];   // Set up name.
00739   in.read(fname, 13);
00740   if (!strcmp("identity",fname))
00741       {
00742               game_identity = new char[len];
00743         in.read(game_identity, len);
00744         // Truncate identity
00745         char *ptr = game_identity;
00746         for(; (*ptr!=0x1a && *ptr!=0x0d); ptr++)
00747           ;
00748         *ptr = 0;
00749         break;
00750       }
00751       }
00752     delete [] finfo;
00753     return game_identity;
00754 }
00755 
00756 // Zip file support
00757 #ifdef HAVE_ZIP_SUPPORT
00758 
00759 #include "files/zip/unzip.h"
00760 #include "files/zip/zip.h"
00761 
00762 static const char *remove_dir(const char *fname)
00763 {
00764   const char *base = strchr(fname, '/');// Want the base name.
00765   if (!base)
00766     base = strchr(fname, '\\');
00767   if (base)
00768     return base+1;
00769 
00770   return fname;
00771 }
00772 
00773 
00774 bool Game_window::get_saveinfo_zip(const char *fname, char *&name, Shape_file *&map, SaveGame_Details *&details, SaveGame_Party *& party)
00775 {
00776   // If a flex, so can't read it
00777   if (Flex::is_flex(fname)) return false;
00778 
00779   std::string filestr = get_system_path(fname);
00780   unzFile unzipfile = unzOpen(filestr.c_str());
00781   if (!unzipfile) return false;
00782 
00783   // Name comes from comment
00784   char namebuf[0x50];
00785   if (unzGetGlobalComment(unzipfile, namebuf, 0x50) <= 0) strncpy (namebuf, "UNNAMED", 0x50);
00786   name = new char [strlen (namebuf)+1];
00787   strcpy (name, namebuf);
00788 
00789 
00790   // Things we need
00791   unz_file_info file_info;
00792   char *buf = 0;
00793 
00794   // Get the screenshot first
00795   if (unzLocateFile(unzipfile, remove_dir(GSCRNSHOT), 2) == UNZ_OK)
00796   {
00797     unzGetCurrentFileInfo(unzipfile, &file_info, NULL, 0, NULL, 0, NULL, 0);
00798     buf = new char[file_info.uncompressed_size];
00799 
00800     unzOpenCurrentFile(unzipfile);
00801     unzReadCurrentFile(unzipfile, buf, file_info.uncompressed_size);
00802     if (unzCloseCurrentFile(unzipfile) == UNZ_OK)
00803     {
00804       BufferDataSource ds(buf, file_info.uncompressed_size);
00805       map = new Shape_file(&ds);
00806     }
00807 
00808     delete [] buf;
00809   }
00810 
00811   // Now saveinfo
00812   if (unzLocateFile(unzipfile, remove_dir(GSAVEINFO), 2) == UNZ_OK)
00813   {
00814     unzGetCurrentFileInfo(unzipfile, &file_info, NULL, 0, NULL, 0, NULL, 0);
00815     buf = new char[file_info.uncompressed_size];
00816 
00817     unzOpenCurrentFile(unzipfile);
00818     unzReadCurrentFile(unzipfile, buf, file_info.uncompressed_size);
00819     if (unzCloseCurrentFile(unzipfile) == UNZ_OK)
00820     {
00821       BufferDataSource ds(buf, file_info.uncompressed_size);
00822       read_saveinfo (&ds, details, party);
00823     }
00824 
00825     delete [] buf;
00826   }
00827 
00828   unzClose (unzipfile);
00829 
00830   return true;
00831 }
00832 
00833 
00834 // Level 2 Compression
00835 bool Game_window::Restore_level2 (void *uzf)
00836 {
00837   unzFile unzipfile = static_cast<unzFile>(uzf);
00838 
00839   char oname[50];   // Set up name.
00840   char *oname2 = oname+sizeof(GAMEDAT) - 1;   // Set up name.
00841   char size_buffer[4];
00842   int size;
00843   strcpy(oname, GAMEDAT);
00844 
00845   if (unzOpenCurrentFile(unzipfile) != UNZ_OK)
00846   {
00847     std::cerr << "Couldn't open current file" << std::endl;
00848     return false;
00849   }
00850 
00851   while (!unzeof(unzipfile))
00852   {
00853     // Read Filename
00854     oname2[12] = 0;
00855     if (unzReadCurrentFile(unzipfile, oname2, 12) != 12)
00856     {
00857       std::cerr << "Couldn't read for filename" << std::endl;
00858       return false;
00859     }
00860 
00861     // Check to see if was are at the end of the list
00862     if (*oname2 == 0) break;
00863 
00864     // Get file length.
00865     if (unzReadCurrentFile(unzipfile, size_buffer, 4) != 4)
00866     {
00867       std::cerr << "Couldn't read for size" << std::endl;
00868       return false;
00869     }
00870     BufferDataSource ds(size_buffer, 4);
00871     size = ds.read4();
00872 
00873     if (size)
00874     {
00875       // Watch for names ending in '.'.
00876       int namelen = strlen(oname);
00877       if (oname[namelen - 1] == '.')
00878         oname[namelen - 1] = 0;
00879 
00880 
00881       // Now read the file.
00882       char *buf = new char[size];
00883       if (unzReadCurrentFile(unzipfile, buf, size) != size)
00884       {
00885         delete [] buf;
00886         std::cerr << "Couldn't read for buf" << std::endl;
00887         return false;
00888       }
00889 
00890       // Then write it out.
00891       ofstream out;
00892       U7open(out, oname);
00893       out.write(buf, size);
00894 
00895       delete [] buf;
00896       if (!out.good())
00897       {
00898         std::cerr << "out was bad" << std::endl;
00899         return false;
00900       }
00901       out.close();
00902       CYCLE_RED_PLASMA();
00903     }
00904   }
00905 
00906   return unzCloseCurrentFile(unzipfile) == UNZ_OK;
00907 }
00908 
00909 
00910 /*
00911  *  Write out the gamedat directory from a saved game.
00912  *
00913  *  Output: Aborts if error.
00914  */
00915 
00916 bool Game_window::restore_gamedat_zip
00917   (
00918   const char *fname   // Name of savegame file.
00919   )
00920   {
00921   // If a flex, so can't read it
00922   try
00923     {
00924     if (Flex::is_flex(fname)) return false;
00925     }
00926   catch(const file_exception & f)
00927     {
00928     return false;   // Ignore if not found.
00929     }
00930 #ifdef RED_PLASMA
00931   // Display red plasma during load...
00932   setup_load_palette();
00933 #endif
00934   std::string filestr = get_system_path(fname);
00935   unzFile unzipfile = unzOpen(filestr.c_str());
00936   if (!unzipfile) return false;
00937 
00938   U7mkdir("<GAMEDAT>", 0755);   // Create dir. if not already there. Don't
00939                   // use GAMEDAT define cause that's got a
00940                   // trailing slash
00941   U7remove (USEDAT);
00942   U7remove (USEVARS);
00943   U7remove (U7NBUF_DAT);
00944   U7remove (NPC_DAT);
00945   U7remove (MONSNPCS);
00946   U7remove (FLAGINIT);
00947   U7remove (GWINDAT);
00948   U7remove (IDENTITY);
00949   U7remove (GSCHEDULE);
00950   U7remove ("<STATIC>/flags.flg");
00951   U7remove (GSCRNSHOT);
00952   U7remove (GSAVEINFO);
00953   U7remove (GNEWGAMEVER);
00954   U7remove (GEXULTVER);
00955   U7remove (KEYRINGDAT);
00956 
00957   cout.flush();
00958 
00959   unz_global_info global;
00960   unzGetGlobalInfo(unzipfile, &global);
00961 
00962   // Now read each file.
00963   char oname[50];   // Set up name.
00964   char *oname2 = oname + sizeof(GAMEDAT) - 1;
00965   strcpy(oname, GAMEDAT);
00966 
00967   do
00968   {
00969     unz_file_info file_info;
00970   
00971     unzGetCurrentFileInfo(unzipfile, &file_info,
00972       oname2, 13,
00973       NULL, 0,
00974       NULL, 0);
00975 
00976     // Get file length.
00977     int len = file_info.uncompressed_size;
00978     if (len <= 0)
00979       continue;
00980 
00981     // Level 2 compression handling
00982     if (!std::strcmp("GAMEDAT", oname2))
00983     {
00984       if (Restore_level2(unzipfile) == false)
00985         abort("Error reading level2 from zip '%s'.", fname);
00986 
00987       continue;
00988     }
00989 
00990           // Watch for names ending in '.'.
00991     int namelen = strlen(oname);
00992     if (oname[namelen - 1] == '.')
00993       oname[namelen - 1] = 0;
00994 
00995 
00996           // Open the file in the zip
00997     if (unzOpenCurrentFile(unzipfile) != UNZ_OK)
00998       abort("Error opening current from zipfile '%s'.", fname);
00999 
01000           // Now read the file.
01001     char *buf = new char[len];
01002     if (unzReadCurrentFile(unzipfile, buf, len) != len)
01003       abort("Error reading current from zip '%s'.", fname);
01004 
01005           // now write it out.
01006     ofstream out;
01007     U7open(out, oname);
01008     out.write(buf, len);
01009     if (!out.good()) abort("Error writing to '%s'.", oname);
01010     out.close();
01011 
01012           // Close the file in the zip
01013     if (unzCloseCurrentFile(unzipfile) != UNZ_OK)
01014       abort("Error closing current in zip '%s'.", fname);
01015     delete [] buf;
01016 
01017     CYCLE_RED_PLASMA();
01018   }
01019   while (unzGoToNextFile(unzipfile) == UNZ_OK);
01020 
01021   unzClose(unzipfile);
01022 
01023   cout.flush();
01024 
01025 #ifdef RED_PLASMA
01026   load_palette_timer = 0;
01027 #endif
01028 
01029   return true;
01030   }
01031 
01032 
01033 // Level 1 Compression
01034 static bool Save_level1 (zipFile zipfile, const char *fname)
01035 {
01036   ifstream in;
01037   try {
01038     U7open (in, fname);
01039   } catch (exult_exception& e) {
01040     if (Game::is_editing())
01041       return false; // Newly developed game.
01042     throw e;
01043   }
01044 
01045 
01046   StreamDataSource ds(&in);
01047 
01048   unsigned int size = ds.getSize();
01049   char *buf = new char[size];
01050   ds.read(buf, size);
01051   
01052 
01053   zipOpenNewFileInZip (zipfile, remove_dir(fname), NULL, NULL, 0,
01054         NULL, 0, NULL, Z_DEFLATED, Z_BEST_COMPRESSION);
01055 
01056   zipWriteInFileInZip (zipfile, buf, size);
01057   delete [] buf;
01058 
01059   return zipCloseFileInZip (zipfile) == ZIP_OK;
01060 }
01061 
01062 // Level 2 Compression
01063 static bool Begin_level2 (zipFile zipfile)
01064 {
01065   return zipOpenNewFileInZip (zipfile, "GAMEDAT", NULL, NULL, 0,
01066         NULL, 0, NULL, Z_DEFLATED, Z_BEST_COMPRESSION) == ZIP_OK;
01067 }
01068 
01069 static bool Save_level2 (zipFile zipfile, const char *fname)
01070 {
01071   ifstream in;
01072   try {
01073     U7open (in, fname);
01074   } catch (exult_exception& e) {
01075     if (Game::is_editing())
01076       return false; // Newly developed game.
01077     throw e;
01078   }
01079 
01080   StreamDataSource ds(&in);
01081 
01082   uint32 size = ds.getSize();
01083   char *buf = new char[size<13?13:size]; // We want at least 13 bytes
01084   
01085   // Filename first
01086   memset (buf, 0, 13);
01087   strncpy (buf, remove_dir(fname), 13);
01088   int err = zipWriteInFileInZip (zipfile, buf, 12);
01089 
01090   // Size of the file
01091   if (err == ZIP_OK)
01092   {
01093     // Must be platform independant
01094     BufferDataSource bds(buf, 4);
01095     bds.write4(size);
01096     err = zipWriteInFileInZip (zipfile, buf, 4);
01097   }
01098 
01099   // Now the actual file
01100   if (err == ZIP_OK)
01101   {
01102     ds.read(buf, size);
01103     err = zipWriteInFileInZip (zipfile, buf, size);
01104   }
01105 
01106   delete [] buf;
01107 
01108   return err == ZIP_OK;
01109 }
01110 
01111 static bool End_level2 (zipFile zipfile)
01112 {
01113   uint32 zeros = 0;
01114 
01115   // Write a terminator (12 zeros)
01116   int err = zipWriteInFileInZip (zipfile, &zeros, 4);
01117   if (err == ZIP_OK) err = zipWriteInFileInZip (zipfile, &zeros, 4);
01118   if (err == ZIP_OK) err = zipWriteInFileInZip (zipfile, &zeros, 4);
01119 
01120   return zipCloseFileInZip (zipfile) == ZIP_OK;
01121 }
01122 
01123 
01124 bool Game_window::save_gamedat_zip
01125   (
01126   const char *fname,      // File to create.
01127   const char *savename      // User's savegame name.
01128   )
01129 {
01130   // If no compression return
01131   if (save_compression < 1) return false;
01132 
01133   // setup correct file list 
01134   int numsavefiles = (Game::get_game_type() == BLACK_GATE) ?
01135       bgnumsavefiles : sinumsavefiles;
01136   const char **savefiles = (Game::get_game_type() == BLACK_GATE) ?
01137       bgsavefiles : sisavefiles;  
01138 
01139   // Name
01140   {
01141     ofstream out;
01142     char title[0x50];
01143     memset (title, 0, 0x50);
01144     std::strncpy (title, savename, 0x50);
01145     U7open(out, fname);
01146     out.write(title, 0x50);
01147     out.close();
01148   }
01149   
01150   std::string filestr = get_system_path(fname);
01151   zipFile zipfile = zipOpen(filestr.c_str(), 1);
01152 
01153   // Level 1 Compression
01154   if (save_compression != 2)
01155   {
01156     for (int i = 0; i < numsavefiles; i++)
01157       Save_level1(zipfile, savefiles[i]);
01158 
01159     // Now the Ireg's.
01160     for (int schunk = 0; schunk < 12*12; schunk++)
01161     {
01162       char iname[80];
01163       Save_level1(zipfile, 
01164           map->get_schunk_file_name(U7IREG, schunk, iname));
01165     }
01166   }
01167   // Level 2 Compression
01168   else
01169   {
01170     // Keep saveinfo, screenshot, identity using normal compression
01171     // There are always files 0 - 2
01172     Save_level1(zipfile, GSCRNSHOT);
01173     Save_level1(zipfile, GSAVEINFO);
01174     Save_level1(zipfile, IDENTITY);
01175 
01176     Begin_level2(zipfile);
01177 
01178     for (int i = 3; i < numsavefiles; i++)
01179       Save_level2(zipfile, savefiles[i]);
01180 
01181     // Now the Ireg's.
01182     for (int schunk = 0; schunk < 12*12; schunk++)
01183     {
01184       char iname[80];
01185       Save_level2(zipfile, map->get_schunk_file_name(
01186             U7IREG, schunk, iname));
01187     }
01188 
01189     End_level2(zipfile);
01190   }
01191 
01192   // ++++Better error system needed??
01193   if (zipClose(zipfile, savename) != ZIP_OK)
01194     throw file_write_exception(fname);
01195 
01196   return true;
01197 }
01198 
01199 /*
01200  *  Return string from IDENTITY in a savegame.
01201  *
01202  *  Output: ->identity string.
01203  *    0 if error.
01204  *    "*" if not found.
01205  */
01206 char *Game_window::get_game_identity_zip
01207   (
01208   const char *savename
01209   )
01210   {
01211   // If a flex, so can't read it
01212   try
01213     {
01214     if (Flex::is_flex(savename)) 
01215       return 0;
01216     }
01217   catch(const file_exception & f)
01218     {
01219     return 0;   // Ignore if not found.
01220     }
01221   unzFile unzipfile = unzOpen(get_system_path(savename).c_str());
01222   if (!unzipfile) 
01223     return 0;
01224           // Find IDENTITY, ignoring case.
01225   if (unzLocateFile(unzipfile, "identity", 2) != UNZ_OK)
01226     {
01227     unzClose(unzipfile);
01228     return "*";   // Old game.  Return wildcard.
01229     }
01230           // Open the file in the zip
01231   if (unzOpenCurrentFile(unzipfile) != UNZ_OK)
01232     {
01233     unzClose(unzipfile);
01234     throw file_read_exception(savename);
01235     }
01236           // Now read the file.
01237   char buf[256];
01238   int cnt = unzReadCurrentFile(unzipfile, buf, sizeof(buf) - 1);
01239   if (cnt <= 0)
01240     {
01241     unzCloseCurrentFile(unzipfile);
01242     unzClose(unzipfile);
01243     throw file_read_exception(savename);
01244     }
01245   buf[cnt] = 0;     // 0-delimit.
01246   unzCloseCurrentFile(unzipfile);
01247   unzClose(unzipfile);
01248   char *ptr = buf;
01249   for(; (*ptr != 0 && *ptr!=0x1a && *ptr!=0x0d); ptr++)
01250           ;
01251   *ptr = 0;
01252   return newstrdup(buf);
01253   }
01254 
01255 #endif

Generated on Mon Jul 9 14:42:45 2007 for ExultEngine by  doxygen 1.5.1