00001
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "../client.h"
00027 #include "../cl_game.h"
00028 #include "../ui/ui_main.h"
00029 #include "../ui/ui_popup.h"
00030 #include "cp_campaign.h"
00031 #include "cp_save.h"
00032 #include "cp_time.h"
00033 #include "save/save.h"
00034
00035 #define SAVEGAME_EXTENSION "savx"
00036
00037 typedef struct saveFileHeader_s {
00038 uint32_t version;
00039 uint32_t compressed;
00040 uint32_t dummy[14];
00041 char gameVersion[16];
00042 char name[32];
00043 char gameDate[32];
00044 char realDate[32];
00045 uint32_t xmlSize;
00046 } saveFileHeader_t;
00047
00048 typedef struct saveSubsystems_s {
00049 const char *name;
00050 qboolean (*save) (mxml_node_t *parent);
00051 qboolean (*load) (mxml_node_t *parent);
00052 } saveSubsystems_t;
00053
00054 static saveSubsystems_t saveSubsystems[MAX_SAVESUBSYSTEMS];
00055 static int saveSubsystemsAmount;
00056 static cvar_t* save_compressed;
00057
00064 static qboolean SAV_GameActionsAfterLoad (void)
00065 {
00066 B_PostLoadInit();
00067 AIR_PostLoadInit();
00068
00069
00070 CL_UpdateTime();
00071
00072
00073 RADAR_SetRadarAfterLoading();
00074
00075 return qtrue;
00076 }
00077
00082 static qboolean SAV_VerifyHeader (saveFileHeader_t const * const header)
00083 {
00084 int len;
00085
00086 len = strlen(header->name);
00087 if (len < 0 || len > sizeof(header->name)) {
00088 Com_DPrintf(DEBUG_CLIENT, "Name is %d Bytes long, max is "UFO_SIZE_T"\n", len, sizeof(header->name));
00089 return qfalse;
00090 }
00091 len = strlen(header->gameVersion);
00092 if (len < 0 || len > sizeof(header->gameVersion)) {
00093 Com_DPrintf(DEBUG_CLIENT, "gameVersion is %d Bytes long, max is "UFO_SIZE_T"\n", len, sizeof(header->gameVersion));
00094 return qfalse;
00095 }
00096 len = strlen(header->gameDate);
00097 if (len < 0 || len > sizeof(header->gameDate)) {
00098 Com_DPrintf(DEBUG_CLIENT, "gameDate is %d Bytes long, max is "UFO_SIZE_T"\n", len, sizeof(header->gameDate));
00099 return qfalse;
00100 }
00101 len = strlen(header->realDate);
00102 if (len < 0 || len > sizeof(header->realDate)) {
00103 Com_DPrintf(DEBUG_CLIENT, "realDate is %d Bytes long, max is "UFO_SIZE_T"\n", len, sizeof(header->realDate));
00104 return qfalse;
00105 }
00106
00107
00108 if (header->xmlSize < 0 || header->xmlSize > 15 * 1024 * 1024) {
00109 Com_DPrintf(DEBUG_CLIENT, "Save size seems to be to large (over 15 MB) %i.\n", header->xmlSize);
00110 return qfalse;
00111 }
00112 if (header->version == 0) {
00113 Com_DPrintf(DEBUG_CLIENT, "Version is invalid - must be greater than zero\n");
00114 return qfalse;
00115 }
00116 if (header->version > SAVE_FILE_VERSION) {
00117 Com_Printf("Savefile is newer than the game!\n");
00118 }
00119 return qtrue;
00120 }
00121
00128 static qboolean SAV_GameLoad (const char *file, char **error)
00129 {
00130 uLongf len;
00131 char filename[MAX_OSPATH];
00132 qFILE f;
00133 byte *cbuf, *buf;
00134 int i, clen;
00135 mxml_node_t *topNode, *node;
00136 saveFileHeader_t header;
00137
00138 Q_strncpyz(filename, file, sizeof(filename));
00139
00140
00141 FS_OpenFile(va("save/%s.%s", filename, SAVEGAME_EXTENSION), &f, FILE_READ);
00142 if (!f.f) {
00143 Com_Printf("Couldn't open file '%s'\n", filename);
00144 return qfalse;
00145 }
00146
00147 clen = FS_FileLength(&f);
00148 cbuf = (byte *) Mem_PoolAlloc(sizeof(byte) * clen, cl_genericPool, 0);
00149 if (FS_Read(cbuf, clen, &f) != clen)
00150 Com_Printf("Warning: Could not read %i bytes from savefile\n", clen);
00151 FS_CloseFile(&f);
00152 Com_Printf("Loading savegame xml (size %d)\n", clen);
00153
00154 memcpy(&header, cbuf, sizeof(header));
00155
00156 header.compressed = LittleLong(header.compressed);
00157 header.version = LittleLong(header.version);
00158 header.xmlSize = LittleLong(header.xmlSize);
00159
00160 if (!SAV_VerifyHeader(&header)) {
00161
00162 Com_Printf("The Header of the savegame '%s.%s' is corrupted. Loading aborted\n", filename, SAVEGAME_EXTENSION);
00163 Mem_Free(cbuf);
00164 return qfalse;
00165 }
00166
00167 Com_Printf("Loading savegame\n"
00168 "...version: %i\n"
00169 "...game version: %s\n"
00170 "...xml Size: %i, compressed? %c\n",
00171 header.version, header.gameVersion, header.xmlSize, header.compressed ? 'y' : 'n');
00172 len = header.xmlSize + 50;
00173 buf = (byte *) Mem_PoolAlloc(sizeof(byte)*len, cl_genericPool, 0);
00174
00175 if (header.compressed) {
00176
00177 const int res = uncompress(buf, &len, cbuf + sizeof(header), clen - sizeof(header));
00178 Mem_Free(cbuf);
00179
00180 if (res != Z_OK) {
00181 Mem_Free(buf);
00182 *error = _("Error decompressing data");
00183 Com_Printf("Error decompressing data in '%s'.\n", filename);
00184 return qfalse;
00185 }
00186 topNode = mxmlLoadString(NULL, (char*)buf , mxml_ufo_type_cb);
00187 if (!topNode) {
00188 Mem_Free(buf);
00189 Com_Printf("Error: Failure in loading the xml data!\n");
00190 return qfalse;
00191 }
00192 } else {
00193
00194 topNode = mxmlLoadString(NULL, (char*)(cbuf + sizeof(header)) , mxml_ufo_type_cb);
00195 Mem_Free(cbuf);
00196 if (!topNode) {
00197 Com_Printf("Error: Failure in loading the xml data!\n");
00198 return qfalse;
00199 }
00200 }
00201
00202
00203 GAME_ReloadMode();
00204 node = mxml_GetNode(topNode, SAVE_ROOTNODE);
00205 if (!node) {
00206 Com_Printf("Error: Failure in loading the xml data! (savegame node not found)\n");
00207 Mem_Free(buf);
00208 mxmlDelete(topNode);
00209 return qfalse;
00210 }
00211
00212 Com_Printf("Load '%s' %d subsystems\n", filename, saveSubsystemsAmount);
00213 for (i = 0; i < saveSubsystemsAmount; i++) {
00214 Com_Printf("...Running subsystem '%s'\n", saveSubsystems[i].name);
00215 if (!saveSubsystems[i].load(node)) {
00216 Com_Printf("...subsystem '%s' returned false - savegame could not be loaded\n",
00217 saveSubsystems[i].name);
00218 return qfalse;
00219 } else
00220 Com_Printf("...subsystem '%s' - loaded.\n", saveSubsystems[i].name);
00221 }
00222 mxmlDelete(node);
00223
00224 SAV_GameActionsAfterLoad();
00225
00226 Com_Printf("File '%s' successfully loaded from %s xml savegame.\n",
00227 filename, header.compressed ? "compressed" : "");
00228 Mem_Free(buf);
00229
00230 mxmlDelete(topNode);
00231
00232 UI_InitStack("geoscape", NULL, qtrue, qtrue);
00233 return qtrue;
00234 }
00235
00242 static qboolean SAV_GameSave (const char *filename, const char *comment, char **error)
00243 {
00244 mxml_node_t *topNode, *node;
00245 char savegame[MAX_OSPATH];
00246 int res;
00247 int requiredBufferLength;
00248 byte *buf, *fbuf;
00249 uLongf bufLen;
00250 saveFileHeader_t header;
00251 char dummy[2];
00252 int i;
00253 dateLong_t date;
00254 char message[30];
00255 char timeStampBuffer[32];
00256
00257 if (!CP_IsRunning()) {
00258 *error = _("No campaign active.");
00259 Com_Printf("Error: No campaign active.\n");
00260 return qfalse;
00261 }
00262
00263 if (!ccs.numBases) {
00264 *error = _("Nothing to save yet.");
00265 Com_Printf("Error: Nothing to save yet.\n");
00266 return qfalse;
00267 }
00268
00269 Com_MakeTimestamp(timeStampBuffer, sizeof(timeStampBuffer));
00270 Com_sprintf(savegame, sizeof(savegame), "save/%s.%s", filename, SAVEGAME_EXTENSION);
00271 topNode = mxmlNewXML("1.0");
00272 node = mxml_AddNode(topNode, SAVE_ROOTNODE);
00273
00274 mxml_AddInt(node, SAVE_SAVEVERSION, SAVE_FILE_VERSION);
00275 mxml_AddString(node, SAVE_COMMENT, comment);
00276 mxml_AddString(node, SAVE_UFOVERSION, UFO_VERSION);
00277 mxml_AddString(node, SAVE_REALDATE, timeStampBuffer);
00278 CL_DateConvertLong(&ccs.date, &date);
00279 Com_sprintf(message, sizeof(message), _("%i %s %02i"),
00280 date.year, Date_GetMonthName(date.month - 1), date.day);
00281 mxml_AddString(node, SAVE_GAMEDATE, message);
00282
00283 Com_Printf("Calling subsystems\n");
00284 for (i = 0; i < saveSubsystemsAmount; i++) {
00285 if (!saveSubsystems[i].save(node))
00286 Com_Printf("...subsystem '%s' failed to save the data\n", saveSubsystems[i].name);
00287 else
00288 Com_Printf("...subsystem '%s' - saved\n", saveSubsystems[i].name);
00289 }
00290
00291
00292 memset(&header, 0, sizeof(header));
00293 header.compressed = LittleLong(save_compressed->integer);
00294 header.version = LittleLong(SAVE_FILE_VERSION);
00295 Q_strncpyz(header.name, comment, sizeof(header.name));
00296 Q_strncpyz(header.gameVersion, UFO_VERSION, sizeof(header.gameVersion));
00297 CL_DateConvertLong(&ccs.date, &date);
00298 Com_sprintf(header.gameDate, sizeof(header.gameDate), _("%i %s %02i"),
00299 date.year, Date_GetMonthName(date.month - 1), date.day);
00300 Q_strncpyz(header.realDate, timeStampBuffer, sizeof(header.realDate));
00301
00302 requiredBufferLength = mxmlSaveString(topNode, dummy, 2, MXML_NO_CALLBACK);
00303
00304 header.xmlSize = LittleLong(requiredBufferLength);
00305 buf = (byte *) Mem_PoolAlloc(sizeof(byte) * requiredBufferLength + 1, cl_genericPool, 0);
00306 if (!buf) {
00307 mxmlDelete(topNode);
00308 *error = _("Could not allocate enough memory to save this game");
00309 Com_Printf("Error: Could not allocate enough memory to save this game\n");
00310 return qfalse;
00311 }
00312 res = mxmlSaveString(topNode, (char*)buf, requiredBufferLength + 1, MXML_NO_CALLBACK);
00313 mxmlDelete(topNode);
00314 Com_Printf("XML Written to buffer (%d Bytes)\n", res);
00315
00316 if (header.compressed)
00317 bufLen = (uLongf) (24 + 1.02 * requiredBufferLength);
00318 else
00319 bufLen = requiredBufferLength;
00320
00321 fbuf = (byte *) Mem_PoolAlloc(sizeof(byte) * bufLen + sizeof(header), cl_genericPool, 0);
00322 memcpy(fbuf, &header, sizeof(header));
00323
00324 if (header.compressed) {
00325 res = compress(fbuf + sizeof(header), &bufLen, buf, requiredBufferLength + 1);
00326 Mem_Free(buf);
00327
00328 if (res != Z_OK) {
00329 Mem_Free(fbuf);
00330 *error = _("Memory error compressing save-game data - set save_compressed cvar to 0");
00331 Com_Printf("Memory error compressing save-game data (%s) (Error: %i)!\n", comment, res);
00332 return qfalse;
00333 }
00334 } else {
00335 memcpy(fbuf + sizeof(header), buf, requiredBufferLength + 1);
00336 Mem_Free(buf);
00337 }
00338
00339
00340 res = FS_WriteFile(fbuf, bufLen + sizeof(header), savegame);
00341 Mem_Free(fbuf);
00342
00343 return qtrue;
00344 }
00345
00351 static void SAV_GameSave_f (void)
00352 {
00353 char comment[MAX_VAR] = "";
00354 char *error = NULL;
00355 qboolean result;
00356
00357
00358 if (Cmd_Argc() < 2) {
00359 Com_Printf("Usage: %s <filename> <comment|*cvar>\n", Cmd_Argv(0));
00360 return;
00361 }
00362
00363 if (!CP_IsRunning()) {
00364 Com_Printf("No running game - no saving...\n");
00365 return;
00366 }
00367
00368
00369 if (Cmd_Argc() > 2) {
00370 const char *arg = Cmd_Argv(2);
00371 Q_strncpyz(comment, arg, sizeof(comment));
00372 }
00373
00374 result = SAV_GameSave(Cmd_Argv(1), comment, &error);
00375 if (!result) {
00376 if (error)
00377 Com_sprintf(popupText, sizeof(popupText), "%s\n%s", _("Error saving game."), error);
00378 else
00379 Com_sprintf(popupText, sizeof(popupText), "%s\n", _("Error saving game."));
00380 UI_Popup(_("Note"), popupText);
00381 }
00382 }
00383
00389 static void SAV_GameReadGameComment (const int idx)
00390 {
00391 saveFileHeader_t header;
00392 qFILE f;
00393
00394 FS_OpenFile(va("save/slot%i.%s", idx, SAVEGAME_EXTENSION), &f, FILE_READ);
00395 if (f.f || f.z) {
00396 if (FS_Read(&header, sizeof(header), &f) != sizeof(header))
00397 Com_Printf("Warning: Savefile header may be corrupted\n");
00398
00399 if (!SAV_VerifyHeader(&header))
00400 Com_Printf("Savegame header for slot%d is corrupted!\n", idx);
00401 else
00402 UI_ExecuteConfunc("update_save_game_info %i \"%s\" \"%s\" \"%s\"", idx, header.name, header.gameDate, header.realDate);
00403
00404 FS_CloseFile(&f);
00405 }
00406 }
00407
00417 static void SAV_GameReadGameComments_f (void)
00418 {
00419 if (Cmd_Argc() == 2) {
00420
00421 if (!CP_IsRunning() && !strncmp(Cmd_Argv(1), "save", 4)) {
00422 UI_PopWindow(qfalse);
00423 return;
00424 }
00425 }
00426
00427 if (Cmd_Argc() == 2) {
00428 int idx = atoi(Cmd_Argv(1));
00429 SAV_GameReadGameComment(idx);
00430 } else {
00431 int i;
00432
00433 for (i = 0; i < 8; i++) {
00434 SAV_GameReadGameComment(i);
00435 }
00436 }
00437 }
00438
00443 static void SAV_GameLoad_f (void)
00444 {
00445 char *error = NULL;
00446 const cvar_t *gamedesc;
00447
00448
00449 if (Cmd_Argc() < 2) {
00450 Com_Printf("Usage: %s <filename>\n", Cmd_Argv(0));
00451 return;
00452 }
00453
00454
00455 gamedesc = Cvar_FindVar(va("mn_%s", Cmd_Argv(1)));
00456 if (!gamedesc || gamedesc->string[0] == '\0') {
00457 Com_DPrintf(DEBUG_CLIENT, "don't load file '%s', there is no description for it\n", Cmd_Argv(1));
00458 return;
00459 }
00460
00461 Com_DPrintf(DEBUG_CLIENT, "load file '%s'\n", Cmd_Argv(1));
00462
00463
00464 if (!SAV_GameLoad(Cmd_Argv(1), &error)) {
00465 Cbuf_Execute();
00466 Com_sprintf(popupText, sizeof(popupText), "%s\n%s", _("Error loading game."), error ? error : "");
00467 UI_Popup(_("Error"), popupText);
00468 Cmd_ExecuteString("game_exit");
00469 }
00470 }
00471
00477 static void SAV_GameContinue_f (void)
00478 {
00479 char *error = NULL;
00480
00481 if (CL_OnBattlescape()) {
00482 UI_PopWindow(qfalse);
00483 return;
00484 }
00485
00486 if (!CP_IsRunning()) {
00487
00488 if (!SAV_GameLoad(cl_lastsave->string, &error)) {
00489 Cbuf_Execute();
00490 Com_sprintf(popupText, sizeof(popupText), "%s\n%s", _("Error loading game."), error ? error : "");
00491 UI_Popup(_("Error"), popupText);
00492 Cmd_ExecuteString("game_exit");
00493 }
00494 } else {
00495
00496 UI_PopWindow(qfalse);
00497 }
00498 }
00499
00505 static qboolean SAV_AddSubsystem (saveSubsystems_t *subsystem)
00506 {
00507 if (saveSubsystemsAmount >= MAX_SAVESUBSYSTEMS)
00508 return qfalse;
00509
00510 saveSubsystems[saveSubsystemsAmount].name = subsystem->name;
00511 saveSubsystems[saveSubsystemsAmount].load = subsystem->load;
00512 saveSubsystems[saveSubsystemsAmount].save = subsystem->save;
00513 saveSubsystemsAmount++;
00514
00515 Com_Printf("added %s subsystem\n", subsystem->name);
00516 return qtrue;
00517 }
00518
00524 static void SAV_GameSaveNameCleanup_f (void)
00525 {
00526 int slotID;
00527 char cvar[16];
00528 qFILE f;
00529 saveFileHeader_t header;
00530
00531
00532 if (Cmd_Argc() < 2) {
00533 Com_Printf("Usage: %s <[0-7]>\n", Cmd_Argv(0));
00534 return;
00535 }
00536
00537 slotID = atoi(Cmd_Argv(1));
00538 if (slotID < 0 || slotID > 7)
00539 return;
00540
00541 FS_OpenFile(va("save/slot%i.%s", slotID, SAVEGAME_EXTENSION), &f, FILE_READ);
00542 if (!f.f && !f.z)
00543 return;
00544
00545
00546 if (FS_Read(&header, sizeof(header), &f) != sizeof(header))
00547 Com_Printf("Warning: Savefile header may be corrupted\n");
00548
00549 Com_sprintf(cvar, sizeof(cvar), "mn_slot%i", slotID);
00550 Cvar_Set(cvar, header.name);
00551 FS_CloseFile(&f);
00552 }
00553
00558 qboolean SAV_QuickSave (void)
00559 {
00560 char *error = NULL;
00561 qboolean result;
00562
00563 if (CL_OnBattlescape())
00564 return qfalse;
00565
00566 result = SAV_GameSave("slotquick", _("QuickSave"), &error);
00567 if (!result)
00568 Com_Printf("Error saving the xml game: %s\n", error ? error : "");
00569
00570 return qtrue;
00571 }
00572
00577 static void SAV_GameQuickLoadInit_f (void)
00578 {
00579 qFILE f;
00580
00581 FS_OpenFile(va("save/slotquick.%s", SAVEGAME_EXTENSION), &f, FILE_READ);
00582 if (f.f || f.z) {
00583 UI_PushWindow("quickload", NULL);
00584 FS_CloseFile(&f);
00585 }
00586 }
00587
00592 static void SAV_GameQuickSave_f (void)
00593 {
00594 if (!CP_IsRunning())
00595 return;
00596
00597 if (!SAV_QuickSave())
00598 Com_Printf("Could not save the campaign\n");
00599 else
00600 MS_AddNewMessage(_("Quicksave"), _("Campaign was successfully saved."), qfalse, MSG_INFO, NULL);
00601 }
00602
00607 static void SAV_GameQuickLoad_f (void)
00608 {
00609 char *error = NULL;
00610
00611 if (CL_OnBattlescape()) {
00612 Com_Printf("Could not load the campaign while you are on the battlefield\n");
00613 return;
00614 }
00615
00616 if (!SAV_GameLoad("slotquick", &error)) {
00617 Cbuf_Execute();
00618 Com_sprintf(popupText, sizeof(popupText), "%s\n%s", _("Error loading game."), error ? error : "");
00619 UI_Popup(_("Error"), popupText);
00620 } else {
00621 MS_AddNewMessage(_("Campaign loaded"), _("Quicksave campaign was successfully loaded."), qfalse, MSG_INFO, NULL);
00622 Cmd_ExecuteString("check_baseattacks");
00623 }
00624 }
00625
00631 void SAV_Init (void)
00632 {
00633 static saveSubsystems_t b_subsystemXML = {"base", B_SaveXML, B_LoadXML};
00634 static saveSubsystems_t rs_subsystemXML = {"research", RS_SaveXML, RS_LoadXML};
00635 static saveSubsystems_t cp_subsystemXML = {"campaign", CP_SaveXML, CP_LoadXML};
00636 static saveSubsystems_t hos_subsystemXML = {"hospital", HOS_SaveXML, HOS_LoadXML};
00637 static saveSubsystems_t bs_subsystemXML = {"market", BS_SaveXML, BS_LoadXML};
00638 static saveSubsystems_t e_subsystemXML = {"employee", E_SaveXML, E_LoadXML};
00639 static saveSubsystems_t ac_subsystemXML = {"aliencont", AC_SaveXML, AC_LoadXML};
00640 static saveSubsystems_t pr_subsystemXML = {"production", PR_SaveXML, PR_LoadXML};
00641 static saveSubsystems_t air_subsystemXML = {"aircraft", AIR_SaveXML, AIR_LoadXML};
00642 static saveSubsystems_t ab_subsystemXML = {"alien base", AB_SaveXML, AB_LoadXML};
00643 static saveSubsystems_t int_subsystemXML = {"interest", CP_SaveInterestsXML, CP_LoadInterestsXML};
00644 static saveSubsystems_t mis_subsystemXML = {"mission", CP_SaveMissionsXML, CP_LoadMissionsXML};
00645 static saveSubsystems_t ms_subsystemXML = {"messagesystem", MS_SaveXML, MS_LoadXML};
00646 static saveSubsystems_t stats_subsystemXML = {"stats", STATS_SaveXML, STATS_LoadXML};
00647 static saveSubsystems_t na_subsystemXML = {"nations", NAT_SaveXML, NAT_LoadXML};
00648 static saveSubsystems_t trans_subsystemXML = {"transfer", TR_SaveXML, TR_LoadXML};
00649 static saveSubsystems_t xvi_subsystemXML = {"xvirate", XVI_SaveXML, XVI_LoadXML};
00650 static saveSubsystems_t ins_subsystemXML = {"installation", INS_SaveXML, INS_LoadXML};
00651 static saveSubsystems_t mso_subsystemXML = {"messageoptions", MSO_SaveXML, MSO_LoadXML};
00652 static saveSubsystems_t us_subsystemXML = {"ufostores", US_SaveXML, US_LoadXML};
00653
00654 saveSubsystemsAmount = 0;
00655 memset(&saveSubsystems, 0, sizeof(saveSubsystems));
00656
00657 Com_Printf("\n--- save subsystem initialization --\n");
00658
00659
00660 SAV_AddSubsystem(&b_subsystemXML);
00661 SAV_AddSubsystem(&rs_subsystemXML);
00662 SAV_AddSubsystem(&cp_subsystemXML);
00663 SAV_AddSubsystem(&hos_subsystemXML);
00664 SAV_AddSubsystem(&bs_subsystemXML);
00665 SAV_AddSubsystem(&e_subsystemXML);
00666 SAV_AddSubsystem(&ac_subsystemXML);
00667 SAV_AddSubsystem(&air_subsystemXML);
00668 SAV_AddSubsystem(&ab_subsystemXML);
00669 SAV_AddSubsystem(&int_subsystemXML);
00670 SAV_AddSubsystem(&mis_subsystemXML);
00671 SAV_AddSubsystem(&ins_subsystemXML);
00672 SAV_AddSubsystem(&us_subsystemXML);
00673 SAV_AddSubsystem(&pr_subsystemXML);
00674 SAV_AddSubsystem(&ms_subsystemXML);
00675 SAV_AddSubsystem(&stats_subsystemXML);
00676 SAV_AddSubsystem(&na_subsystemXML);
00677 SAV_AddSubsystem(&trans_subsystemXML);
00678 SAV_AddSubsystem(&xvi_subsystemXML);
00679 SAV_AddSubsystem(&mso_subsystemXML);
00680
00681 Cmd_AddCommand("game_quickloadinit", SAV_GameQuickLoadInit_f, "Check whether there is a quicksave at all");
00682 Cmd_AddCommand("game_quicksave", SAV_GameQuickSave_f, _("Saves to the quick save slot"));
00683 Cmd_AddCommand("game_quickload", SAV_GameQuickLoad_f, "Loads the quick save slot");
00684 Cmd_AddCommand("game_save", SAV_GameSave_f, "Saves to a given filename");
00685 Cmd_AddCommand("game_load", SAV_GameLoad_f, "Loads a given filename");
00686 Cmd_AddCommand("game_comments", SAV_GameReadGameComments_f, "Loads the savegame names");
00687 Cmd_AddCommand("game_continue", SAV_GameContinue_f, "Continue with the last saved game");
00688 Cmd_AddCommand("game_savenamecleanup", SAV_GameSaveNameCleanup_f, "Remove the date string from mn_slotX cvars");
00689 save_compressed = Cvar_Get("save_compressed", "1", CVAR_ARCHIVE, "Save the savefiles compressed if set to 1");
00690 }
00691