loadsave.cpp
1 //------------------------------------------------------------------------- 2 /* 3 Copyright (C) 2010-2019 EDuke32 developers and contributors 4 Copyright (C) 2019 Nuke.YKT 5 6 This file is part of NBlood. 7 8 NBlood is free software; you can redistribute it and/or 9 modify it under the terms of the GNU General Public License version 2 10 as published by the Free Software Foundation. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 15 16 See the GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 */ 22 //------------------------------------------------------------------------- 23 #include <stdio.h> 24 #include "build.h" 25 #include "compat.h" 26 #include "lz4.h" 27 #include "mmulti.h" 28 #include "common_game.h" 29 #include "config.h" 30 #include "ai.h" 31 #include "asound.h" 32 #include "blood.h" 33 #include "demo.h" 34 #include "globals.h" 35 #include "db.h" 36 #include "messages.h" 37 #include "menu.h" 38 #include "network.h" 39 #include "loadsave.h" 40 #include "resource.h" 41 #include "screen.h" 42 #include "sectorfx.h" 43 #include "seq.h" 44 #include "sfx.h" 45 #include "sound.h" 46 #include "view.h" 47 #ifdef NOONE_EXTENSIONS 48 #include "nnexts.h" 49 #endif 50 51 #define LZ4_THRESHOLD_SIZE (0x8000) 52 53 GAMEOPTIONS gSaveGameOptions[kMaxLoadSaveSlot]; 54 int gSaveGameProfileSkill[kMaxLoadSaveSlot]; 55 char *gSaveGamePic[kMaxLoadSaveSlot]; 56 unsigned int gSavedOffset = 0; 57 58 LoadSave LoadSave::head(123); 59 FILE *LoadSave::hSFile = NULL; 60 int LoadSave::hLFile = -1; 61 62 void LoadSave::Save(void) 63 { 64 ThrowError("Pure virtual function called"); 65 } 66 67 void LoadSave::Load(void) 68 { 69 ThrowError("Pure virtual function called"); 70 } 71 72 void LoadSave::Read(void *pData, int nSize) 73 { 74 dassert(hLFile != -1); 75 while (nSize >= LZ4_THRESHOLD_SIZE) 76 { 77 int nCompSize; 78 if (kread(hLFile, (void *)&nCompSize, sizeof(nCompSize)) != sizeof(nCompSize)) 79 ThrowError("Error reading save file."); 80 nCompSize = B_LITTLE32(nCompSize); 81 if (nCompSize >= nSize) 82 ThrowError("Error reading size file compress offset."); 83 if (nCompSize == 0) // uncompressed data 84 break; 85 86 void *pIn = Xaligned_alloc(16, nCompSize); 87 if (!pIn) 88 { 89 ThrowError("File error #%d could not allocate %d bytes.", errno, nCompSize); 90 return; 91 } 92 if (kread(hLFile, pIn, nCompSize) != nCompSize) 93 ThrowError("Error reading save file."); 94 LZ4_decompress_safe((const char *)pIn, (char *)pData, nCompSize, nSize); 95 Xaligned_free(pIn); 96 return; 97 } 98 if (kread(hLFile, pData, nSize) != nSize) 99 ThrowError("Error reading save file."); 100 } 101 102 void LoadSave::Write(void const *pData, int nSize) 103 { 104 dassert(hSFile != NULL); 105 while (nSize >= LZ4_THRESHOLD_SIZE) 106 { 107 int nCompSize = LZ4_compressBound(nSize); 108 void *pOut = Xaligned_alloc(16, nCompSize); 109 if (!pOut) 110 { 111 nCompSize = 0; 112 if (fwrite((void *)&nCompSize, 1, sizeof(nCompSize), hSFile) != sizeof(nCompSize)) 113 ThrowError("File error #%d writing save file.", errno); 114 break; 115 } 116 nCompSize = LZ4_compress_fast((const char *)pData, (char *)pOut, nSize, nCompSize, lz4CompressionLevel); 117 const char bBadRatio = nCompSize >= nSize; // compressed size is bigger than uncompressed, store as uncompressed data 118 if (bBadRatio) 119 { 120 nCompSize = 0; 121 if (fwrite((void *)&nCompSize, 1, sizeof(nCompSize), hSFile) != sizeof(nCompSize)) 122 ThrowError("File error #%d writing save file.", errno); 123 Xaligned_free(pOut); 124 break; 125 } 126 127 int nCompSizeBSwap = B_LITTLE32(nCompSize); 128 if (fwrite((void *)&nCompSizeBSwap, 1, sizeof(nCompSizeBSwap), hSFile) != sizeof(nCompSizeBSwap)) 129 ThrowError("File error #%d writing save file.", errno); 130 if (fwrite(pOut, 1, nCompSize, hSFile) != (size_t)nCompSize) 131 ThrowError("File error #%d writing save file.", errno); 132 Xaligned_free(pOut); 133 return; 134 } 135 if (fwrite(pData, 1, nSize, hSFile) != (size_t)nSize) 136 ThrowError("File error #%d writing save file.", errno); 137 } 138 139 void LoadSave::LoadGame(char *pzFile) 140 { 141 const char bDemoWasPlayed = gDemo.bPlaying; 142 const char bGameWasStarted = gGameStarted; 143 if (gDemo.bPlaying || gDemo.bRecording) 144 gDemo.Close(); 145 146 gViewPos = VIEWPOS_0; 147 gViewIndex = myconnectindex; 148 sndKillAllSounds(); 149 sfxKillAllSounds(); 150 ambKillAll(); 151 seqKillAll(); 152 if (!gGameStarted) 153 { 154 memset(xvel,0,sizeof(xvel[0])*kMaxSprites); 155 memset(yvel,0,sizeof(yvel[0])*kMaxSprites); 156 memset(zvel,0,sizeof(zvel[0])*kMaxSprites); 157 memset(xsprite, 0, sizeof(XSPRITE)*kMaxXSprites); 158 memset(sprite, 0, sizeof(spritetype)*kMaxSprites); 159 memset(qsprite_filler,0,sizeof(qsprite_filler[0])*kMaxSprites); 160 memset(qsector_filler,0,sizeof(qsector_filler[0])*kMaxSectors); 161 automapping = 1; 162 } 163 hLFile = kopen4load(pzFile, 0); 164 if (hLFile == -1) 165 ThrowError("Error loading save file."); 166 LoadSave *rover = head.next; 167 while (rover != &head) 168 { 169 rover->Load(); 170 rover = rover->next; 171 } 172 kclose(hLFile); 173 hLFile = -1; 174 if (!gGameStarted) 175 scrLoadPLUs(); 176 InitSectorFX(); 177 viewInitializePrediction(); 178 PreloadCache(); 179 if (!VanillaMode()) // set reverb sound effect state 180 sfxSetReverb(packItemActive(gMe, kPackDivingSuit) || powerupCheck(gMe, kPwUpReflectShots)); 181 ambInit(); 182 #ifdef YAX_ENABLE 183 yax_update(numyaxbunches > 0 ? 2 : 1); 184 #endif 185 calc_sector_reachability(); 186 memset(myMinLag, 0, sizeof(myMinLag)); 187 otherMinLag = 0; 188 myMaxLag = 0; 189 gNetFifoClock = 0; 190 gNetFifoTail = 0; 191 memset(gNetFifoHead, 0, sizeof(gNetFifoHead)); 192 gPredictTail = 0; 193 gNetFifoMasterTail = 0; 194 memset(gFifoInput, 0, sizeof(gFifoInput)); 195 memset(gChecksum, 0, sizeof(gChecksum)); 196 memset(gCheckFifo, 0, sizeof(gCheckFifo)); 197 memset(gCheckHead, 0, sizeof(gCheckHead)); 198 gSendCheckTail = 0; 199 gCheckTail = 0; 200 gBufferJitter = 0; 201 bOutOfSync = 0; 202 for (int i = 0; i < gNetPlayers; i++) 203 playerSetRace(&gPlayer[i], gPlayer[i].lifeMode); 204 if (VanillaMode()) 205 viewSetMessage(""); 206 else 207 gGameMessageMgr.Clear(); 208 viewSetErrorMessage(""); 209 viewResizeView(gViewSize); 210 if (!gGameStarted) 211 { 212 netWaitForEveryone(0); 213 memset(gPlayerReady, 0, sizeof(gPlayerReady)); 214 } 215 gFrameTicks = 0; 216 gFrame = 0; 217 gCacheMiss = 0; 218 gFrameRate = 0; 219 totalclock = 0; 220 gPaused = 0; 221 gGameStarted = 1; 222 223 #ifdef USE_STRUCT_TRACKERS 224 Bmemset(sectorchanged, 0, sizeof(sectorchanged)); 225 Bmemset(spritechanged, 0, sizeof(spritechanged)); 226 Bmemset(wallchanged, 0, sizeof(wallchanged)); 227 #endif 228 229 #ifdef USE_OPENGL 230 Polymost_prepare_loadboard(); 231 #endif 232 233 #ifdef POLYMER 234 if (videoGetRenderMode() == REND_POLYMER) 235 polymer_loadboard(); 236 237 // this light pointer nulling needs to be outside the videoGetRenderMode check 238 // because we might be loading the savegame using another renderer but 239 // change to Polymer later 240 for (int i=0; i<kMaxSprites; i++) 241 { 242 gPolymerLight[i].lightptr = NULL; 243 gPolymerLight[i].lightId = -1; 244 } 245 #endif 246 247 if (gGameOptions.nEpisode >= gEpisodeCount || gGameOptions.nLevel >= gEpisodeInfo[gGameOptions.nEpisode].nLevels 248 || Bstrcasecmp(gEpisodeInfo[gGameOptions.nEpisode].levelsInfo[gGameOptions.nLevel].Filename, gGameOptions.zLevelName) != 0) 249 { 250 if (!gSysRes.Lookup(gGameOptions.zLevelName, "MAP")) 251 { 252 gGameOptions.nEpisode = 0; 253 gGameOptions.nLevel = 0; 254 } 255 else 256 { 257 levelAddUserMap(gGameOptions.zLevelName); 258 } 259 } 260 261 if (MusicRestartsOnLoadToggle 262 || bDemoWasPlayed 263 || !bGameWasStarted 264 || (gMusicPrevLoadedEpisode != gGameOptions.nEpisode || gMusicPrevLoadedLevel != gGameOptions.nLevel)) 265 { 266 levelTryPlayMusicOrNothing(gGameOptions.nEpisode, gGameOptions.nLevel); 267 } 268 gMusicPrevLoadedEpisode = gGameOptions.nEpisode; 269 gMusicPrevLoadedLevel = gGameOptions.nLevel; 270 271 netBroadcastPlayerInfo(myconnectindex); 272 //sndPlaySong(gGameOptions.zLevelSong, 1); 273 274 if ((gGameOptions.nGameType == kGameTypeSinglePlayer) && (numplayers == 1)) // if single-player, update the game options/player profile by loading the current set settings 275 { 276 gGameOptions.bQuadDamagePowerup = gQuadDamagePowerup; 277 gGameOptions.nDamageInvul = gDamageInvul; 278 gGameOptions.nProjectileBehavior = gProjectileBehavior; 279 gGameOptions.bNapalmFalloff = gNapalmFalloff; 280 gGameOptions.nEnemyBehavior = gEnemyBehavior; 281 gGameOptions.bEnemyRandomTNT = gEnemyRandomTNT; 282 gGameOptions.nWeaponsVer = gWeaponsVer; 283 gGameOptions.nAmmoScale = gAmmoScale; 284 gGameOptions.bSectorBehavior = gSectorBehavior; 285 gGameOptions.nHitscanProjectiles = gHitscanProjectiles; 286 gGameOptions.nGoreBehavior = gGoreBehavior; 287 gGameOptions.nPlayerSpeed = gPlayerModSpeed; 288 gGameOptions.nRandomizerMode = gRandomizerMode; 289 Bmemcpy(gGameOptions.szRandomizerSeed, gzRandomizerSeed, sizeof(gGameOptions.szRandomizerSeed)); 290 } 291 } 292 293 // TODO: when starting to use buildvfs in SaveGame(), remove this function and use buildvfs_exists() instead 294 static int file_exists(char const* path) 295 { 296 #ifdef _WIN32 297 return GetFileAttributes(path) != INVALID_FILE_ATTRIBUTES; 298 #else 299 struct Bstat st; 300 return !Bstat(path, &st); 301 #endif 302 } 303 304 void LoadSave::SaveGame(char *pzFile) 305 { 306 // TODO: use buildvfs_open_write() etc (probably in the whole file?) 307 char fileNameTmp[BMAX_PATH+4]; 308 const char* saveFileName = pzFile; 309 bool saveFileExists = file_exists(saveFileName); 310 if (saveFileExists) 311 { 312 // write to a different file first, so in case the game crashes while saving 313 // (which would result in a corrupted savegame) at least the old savegame is preserved 314 strcpy(fileNameTmp, pzFile); 315 strcat(fileNameTmp, "_tmp"); 316 saveFileName = fileNameTmp; 317 } 318 319 hSFile = fopen(saveFileName, "wb"); 320 if (hSFile == NULL) 321 ThrowError("File error #%d creating save file.", errno); 322 LoadSave *rover = head.next; 323 while (rover != &head) 324 { 325 rover->Save(); 326 rover = rover->next; 327 } 328 fclose(hSFile); 329 hSFile = NULL; 330 if (saveFileExists) 331 { 332 // the savegame was written successfully, so we can rename the saved file 333 // to the requested name (from gameXXX.sav_tmp to gameXXX.sav) 334 #ifdef _WIN32 335 _unlink(pzFile); 336 #endif 337 rename(saveFileName, pzFile); 338 } 339 } 340 341 class MyLoadSave : public LoadSave 342 { 343 public: 344 virtual void Load(void); 345 virtual void Save(void); 346 }; 347 348 void MyLoadSave::Load(void) 349 { 350 psky_t *pSky = tileSetupSky(0); 351 int id; 352 Read(&id, sizeof(id)); 353 if (id != 0x5653424e/*'VSBN'*/) 354 ThrowError("Old saved game found"); 355 short version; 356 Read(&version, sizeof(version)); 357 if (version != BYTEVERSION) 358 ThrowError("Incompatible version of saved game found!"); 359 short nSkill; 360 Read(&nSkill, sizeof(nSkill)); 361 Read(&gGameOptions, sizeof(gGameOptions)); 362 Read(&numsectors, sizeof(numsectors)); 363 Read(&numwalls, sizeof(numwalls)); 364 Read(&numsectors, sizeof(numsectors)); 365 int nNumSprites; 366 Read(&nNumSprites, sizeof(nNumSprites)); 367 memset(sector, 0, sizeof(sector[0])*kMaxSectors); 368 memset(wall, 0, sizeof(wall[0])*kMaxWalls); 369 memset(sprite, 0, sizeof(sprite[0])*kMaxSprites); 370 memset(spriteext, 0, sizeof(spriteext[0])*kMaxSprites); 371 memset(qsprite_filler,0,sizeof(qsprite_filler[0])*kMaxSprites); 372 memset(qsector_filler,0,sizeof(qsector_filler[0])*kMaxSectors); 373 Read(sector, sizeof(sector[0])*numsectors); 374 Read(wall, sizeof(wall[0])*numwalls); 375 Read(sprite, sizeof(sprite[0])*kMaxSprites); 376 Read(spriteext, sizeof(spriteext[0])*kMaxSprites); 377 Read(qsector_filler, sizeof(qsector_filler[0])*numsectors); 378 Read(qsprite_filler, sizeof(qsprite_filler[0])*kMaxSprites); 379 Read(&randomseed, sizeof(randomseed)); 380 Read(¶llaxtype, sizeof(parallaxtype)); 381 char showinvisibilitytemp; // don't set to current showinvisibility var 382 Read(&showinvisibilitytemp, sizeof(showinvisibilitytemp)); 383 Read(&pSky->horizfrac, sizeof(pSky->horizfrac)); 384 Read(&pSky->yoffs, sizeof(pSky->yoffs)); 385 Read(&pSky->yscale, sizeof(pSky->yscale)); 386 Read(&gVisibility, sizeof(gVisibility)); 387 Read(&g_visibility, sizeof(g_visibility)); 388 Read(¶llaxvisibility, sizeof(parallaxvisibility)); 389 Read(pSky->tileofs, sizeof(pSky->tileofs)); 390 Read(&pSky->lognumtiles, sizeof(pSky->lognumtiles)); 391 Read(headspritesect, sizeof(headspritesect)); 392 Read(headspritestat, sizeof(headspritestat)); 393 Read(prevspritesect, sizeof(prevspritesect)); 394 Read(prevspritestat, sizeof(prevspritestat)); 395 Read(nextspritesect, sizeof(nextspritesect)); 396 Read(nextspritestat, sizeof(nextspritestat)); 397 Read(show2dsector, sizeof(show2dsector)); 398 Read(show2dwall, sizeof(show2dwall)); 399 Read(show2dsprite, sizeof(show2dsprite)); 400 Read(&automapping, sizeof(automapping)); 401 Read(gotpic, sizeof(gotpic)); 402 Read(gotsector, sizeof(gotsector)); 403 Read(&gFrameClock, sizeof(gFrameClock)); 404 Read(&gFrameTicks, sizeof(gFrameTicks)); 405 Read(&gFrame, sizeof(gFrame)); 406 ClockTicks nGameClock; 407 Read(&totalclock, sizeof(totalclock)); 408 totalclock = nGameClock; 409 Read(&gLevelTime, sizeof(gLevelTime)); 410 Read(&gPaused, sizeof(gPaused)); 411 Read(baseWall, sizeof(baseWall[0])*numwalls); 412 Read(baseSprite, sizeof(baseSprite[0])*nNumSprites); 413 Read(baseFloor, sizeof(baseFloor[0])*numsectors); 414 Read(baseCeil, sizeof(baseCeil[0])*numsectors); 415 Read(velFloor, sizeof(velFloor[0])*numsectors); 416 Read(velCeil, sizeof(velCeil[0])*numsectors); 417 Read(&gHitInfo, sizeof(gHitInfo)); 418 Read(&byte_1A76C6, sizeof(byte_1A76C6)); 419 Read(&byte_1A76C8, sizeof(byte_1A76C8)); 420 Read(&byte_1A76C7, sizeof(byte_1A76C7)); 421 Read(&byte_19AE44, sizeof(byte_19AE44)); 422 Read(gStatCount, sizeof(gStatCount)); 423 Read(nextXSprite, sizeof(nextXSprite)); 424 Read(nextXWall, sizeof(nextXWall)); 425 Read(nextXSector, sizeof(nextXSector)); 426 memset(xsprite, 0, sizeof(xsprite[0])*kMaxXSprites); 427 for (int nSprite = 0; nSprite < kMaxSprites; nSprite++) 428 { 429 int nXSprite = sprite[nSprite].extra; 430 if (nXSprite > 0) 431 Read(&xsprite[nXSprite], sizeof(XSPRITE)); 432 } 433 memset(xwall, 0, sizeof(xwall)); 434 for (int nWall = 0; nWall < numwalls; nWall++) 435 { 436 int nXWall = wall[nWall].extra; 437 if (nXWall > 0) 438 Read(&xwall[nXWall], sizeof(XWALL)); 439 } 440 memset(xsector, 0, sizeof(xsector)); 441 for (int nSector = 0; nSector < numsectors; nSector++) 442 { 443 int nXSector = sector[nSector].extra; 444 if (nXSector > 0) 445 Read(&xsector[nXSector], sizeof(XSECTOR)); 446 } 447 Read(xvel, nNumSprites*sizeof(xvel[0])); 448 Read(yvel, nNumSprites*sizeof(yvel[0])); 449 Read(zvel, nNumSprites*sizeof(zvel[0])); 450 Read(&gMapRev, sizeof(gMapRev)); 451 Read(&gSongId, sizeof(gSongId)); 452 Read(&gSkyCount, sizeof(gSkyCount)); 453 Read(&gFogMode, sizeof(gFogMode)); 454 #ifdef NOONE_EXTENSIONS 455 Read(&gModernMap, sizeof(gModernMap)); 456 #endif 457 #ifdef YAX_ENABLE 458 Read(&numyaxbunches, sizeof(numyaxbunches)); 459 #endif 460 psky_t skyInfo; 461 Read(&skyInfo, sizeof(skyInfo)); 462 463 *tileSetupSky(0) = skyInfo; 464 gCheatMgr.ResetCheats(); 465 466 } 467 468 void MyLoadSave::Save(void) 469 { 470 psky_t *pSky = tileSetupSky(0); 471 int nNumSprites = 0; 472 int id = 0x5653424e/*'VSBN'*/; 473 Write(&id, sizeof(id)); 474 short version = BYTEVERSION; 475 Write(&version, sizeof(version)); 476 short nSkill = (short)gProfile[myconnectindex].skill; 477 Write(&nSkill, sizeof(nSkill)); 478 for (int nSprite = 0; nSprite < kMaxSprites; nSprite++) 479 { 480 if (sprite[nSprite].statnum < kMaxStatus && nSprite > nNumSprites) 481 nNumSprites = nSprite; 482 } 483 nNumSprites++; 484 Write(&gGameOptions, sizeof(gGameOptions)); 485 Write(&numsectors, sizeof(numsectors)); 486 Write(&numwalls, sizeof(numwalls)); 487 Write(&numsectors, sizeof(numsectors)); 488 Write(&nNumSprites, sizeof(nNumSprites)); 489 Write(sector, sizeof(sector[0])*numsectors); 490 Write(wall, sizeof(wall[0])*numwalls); 491 Write(sprite, sizeof(sprite[0])*kMaxSprites); 492 Write(spriteext, sizeof(spriteext[0])*kMaxSprites); 493 Write(qsector_filler, sizeof(qsector_filler[0])*numsectors); 494 Write(qsprite_filler, sizeof(qsprite_filler[0])*kMaxSprites); 495 Write(&randomseed, sizeof(randomseed)); 496 Write(¶llaxtype, sizeof(parallaxtype)); 497 char showinvisibilitytemp; // don't set to current showinvisibility var 498 Write(&showinvisibilitytemp, sizeof(showinvisibilitytemp)); 499 Write(&pSky->horizfrac, sizeof(pSky->horizfrac)); 500 Write(&pSky->yoffs, sizeof(pSky->yoffs)); 501 Write(&pSky->yscale, sizeof(pSky->yscale)); 502 Write(&gVisibility, sizeof(gVisibility)); 503 Write(&g_visibility, sizeof(g_visibility)); 504 Write(¶llaxvisibility, sizeof(parallaxvisibility)); 505 Write(pSky->tileofs, sizeof(pSky->tileofs)); 506 Write(&pSky->lognumtiles, sizeof(pSky->lognumtiles)); 507 Write(headspritesect, sizeof(headspritesect)); 508 Write(headspritestat, sizeof(headspritestat)); 509 Write(prevspritesect, sizeof(prevspritesect)); 510 Write(prevspritestat, sizeof(prevspritestat)); 511 Write(nextspritesect, sizeof(nextspritesect)); 512 Write(nextspritestat, sizeof(nextspritestat)); 513 Write(show2dsector, sizeof(show2dsector)); 514 Write(show2dwall, sizeof(show2dwall)); 515 Write(show2dsprite, sizeof(show2dsprite)); 516 Write(&automapping, sizeof(automapping)); 517 Write(gotpic, sizeof(gotpic)); 518 Write(gotsector, sizeof(gotsector)); 519 Write(&gFrameClock, sizeof(gFrameClock)); 520 Write(&gFrameTicks, sizeof(gFrameTicks)); 521 Write(&gFrame, sizeof(gFrame)); 522 ClockTicks nGameClock = totalclock; 523 Write(&nGameClock, sizeof(nGameClock)); 524 Write(&gLevelTime, sizeof(gLevelTime)); 525 Write(&gPaused, sizeof(gPaused)); 526 Write(baseWall, sizeof(baseWall[0])*numwalls); 527 Write(baseSprite, sizeof(baseSprite[0])*nNumSprites); 528 Write(baseFloor, sizeof(baseFloor[0])*numsectors); 529 Write(baseCeil, sizeof(baseCeil[0])*numsectors); 530 Write(velFloor, sizeof(velFloor[0])*numsectors); 531 Write(velCeil, sizeof(velCeil[0])*numsectors); 532 Write(&gHitInfo, sizeof(gHitInfo)); 533 Write(&byte_1A76C6, sizeof(byte_1A76C6)); 534 Write(&byte_1A76C8, sizeof(byte_1A76C8)); 535 Write(&byte_1A76C7, sizeof(byte_1A76C7)); 536 Write(&byte_19AE44, sizeof(byte_19AE44)); 537 Write(gStatCount, sizeof(gStatCount)); 538 Write(nextXSprite, sizeof(nextXSprite)); 539 Write(nextXWall, sizeof(nextXWall)); 540 Write(nextXSector, sizeof(nextXSector)); 541 for (int nSprite = 0; nSprite < kMaxSprites; nSprite++) 542 { 543 int nXSprite = sprite[nSprite].extra; 544 if (nXSprite > 0) 545 Write(&xsprite[nXSprite], sizeof(XSPRITE)); 546 } 547 for (int nWall = 0; nWall < numwalls; nWall++) 548 { 549 int nXWall = wall[nWall].extra; 550 if (nXWall > 0) 551 Write(&xwall[nXWall], sizeof(XWALL)); 552 } 553 for (int nSector = 0; nSector < numsectors; nSector++) 554 { 555 int nXSector = sector[nSector].extra; 556 if (nXSector > 0) 557 Write(&xsector[nXSector], sizeof(XSECTOR)); 558 } 559 Write(xvel, nNumSprites*sizeof(xvel[0])); 560 Write(yvel, nNumSprites*sizeof(yvel[0])); 561 Write(zvel, nNumSprites*sizeof(zvel[0])); 562 Write(&gMapRev, sizeof(gMapRev)); 563 Write(&gSongId, sizeof(gSongId)); 564 Write(&gSkyCount, sizeof(gSkyCount)); 565 Write(&gFogMode, sizeof(gFogMode)); 566 #ifdef NOONE_EXTENSIONS 567 Write(&gModernMap, sizeof(gModernMap)); 568 #endif 569 #ifdef YAX_ENABLE 570 Write(&numyaxbunches, sizeof(numyaxbunches)); 571 #endif 572 psky_t skyInfo = *tileSetupSky(0); 573 Write(&skyInfo, sizeof(skyInfo)); 574 } 575 576 void LoadSavedInfo(void) 577 { 578 int const bakpathsearchmode = pathsearchmode; 579 pathsearchmode = 1; 580 const int nNameMin = strlen("##.sav"); // length offset for string numbers 581 auto pList = klistpath((g_modDir[0] != '/') ? g_modDir : "./", "game00*.sav", BUILDVFS_FIND_FILE); 582 for (auto pIterator = pList; pIterator != NULL; pIterator = pIterator->next) 583 { 584 int hFile = kopen4loadfrommod(pIterator->name, 0); 585 if (hFile == -1) 586 ThrowError("Error loading save file header."); 587 int id = 0; 588 short version = 0; 589 short nSkill = 0, nSlot = 0; 590 if ((uint32_t)kread(hFile, &id, sizeof(id)) != sizeof(id)) 591 { 592 kclose(hFile); 593 continue; 594 } 595 if (id != 0x5653424e/*'VSBN'*/) 596 { 597 kclose(hFile); 598 continue; 599 } 600 kread(hFile, &version, sizeof(version)); 601 if (version != BYTEVERSION) 602 { 603 kclose(hFile); 604 continue; 605 } 606 nSlot = strlen(pIterator->name); 607 if (nSlot < nNameMin) // unexpected size, abort 608 { 609 kclose(hFile); 610 continue; 611 } 612 nSlot = Batoi(&pIterator->name[nSlot-nNameMin]); 613 if (nSlot > kLoadSaveSlot10) // slot id too big, skip 614 { 615 kclose(hFile); 616 continue; 617 } 618 kread(hFile, &nSkill, sizeof(nSkill)); 619 if ((uint32_t)kread(hFile, &gSaveGameOptions[nSlot], sizeof(gSaveGameOptions[0])) != sizeof(gSaveGameOptions[0])) 620 ThrowError("Error reading save file."); 621 LoadUpdateSaveGame(nSlot, nSkill); 622 kclose(hFile); 623 } 624 klistfree(pList); 625 pathsearchmode = bakpathsearchmode; 626 } 627 628 void LoadAutosavedInfo(void) 629 { 630 int const bakpathsearchmode = pathsearchmode; 631 pathsearchmode = 1; 632 const int nNameMin = strlen("#.sav"); // length offset for string numbers 633 auto pList = klistpath((g_modDir[0] != '/') ? g_modDir : "./", "gameautosave*.sav", BUILDVFS_FIND_FILE); 634 for (auto pIterator = pList; pIterator != NULL; pIterator = pIterator->next) 635 { 636 int hFile = kopen4loadfrommod(pIterator->name, 0); 637 if (hFile == -1) 638 ThrowError("Error loading save file header."); 639 int id = 0; 640 short version = 0; 641 short nSkill = 0, nSlot = 0; 642 if ((uint32_t)kread(hFile, &id, sizeof(id)) != sizeof(id)) 643 { 644 kclose(hFile); 645 continue; 646 } 647 if (id != 0x5653424e/*'VSBN'*/) 648 { 649 kclose(hFile); 650 continue; 651 } 652 kread(hFile, &version, sizeof(version)); 653 if (version != BYTEVERSION) 654 { 655 kclose(hFile); 656 continue; 657 } 658 nSlot = strlen(pIterator->name); 659 if (nSlot < nNameMin) // unexpected size, abort 660 { 661 kclose(hFile); 662 continue; 663 } 664 nSlot = kLoadSaveSlotAutosave + Batoi(&pIterator->name[nSlot-nNameMin]); 665 if (nSlot < kLoadSaveSlotAutosave || nSlot > kLoadSaveSlotKey) // slot id too small/big, skip 666 { 667 kclose(hFile); 668 continue; 669 } 670 kread(hFile, &nSkill, sizeof(nSkill)); 671 if ((uint32_t)kread(hFile, &gSaveGameOptions[nSlot], sizeof(gSaveGameOptions[0])) != sizeof(gSaveGameOptions[0])) 672 ThrowError("Error reading save file."); 673 LoadUpdateSaveGame(nSlot, nSkill); 674 kclose(hFile); 675 } 676 klistfree(pList); 677 pathsearchmode = bakpathsearchmode; 678 } 679 680 bool LoadSavedInCurrentSession(int nSlot) 681 { 682 if ((nSlot < kLoadSaveSlot0) || (nSlot >= kMaxLoadSaveSlot)) 683 return false; 684 if (gSaveGameOptions[nSlot].nEpisode != gGameOptions.nEpisode) 685 return false; 686 if (gSaveGameOptions[nSlot].nLevel != gGameOptions.nLevel) 687 return false; 688 if (gSaveGameOptions[nSlot].nDifficulty != gGameOptions.nDifficulty) 689 return false; 690 if (gSaveGameOptions[nSlot].nEnemyHealth != gGameOptions.nEnemyHealth) 691 return false; 692 if (gSaveGameOptions[nSlot].nEnemyQuantity != gGameOptions.nEnemyQuantity) 693 return false; 694 if (gSaveGameProfileSkill[nSlot] != gProfile[myconnectindex].skill) 695 return false; 696 if (gSaveGameOptions[nSlot].nEnemySpeed != gGameOptions.nEnemySpeed) 697 return false; 698 if (gSaveGameOptions[nSlot].bEnemyShuffle != gGameOptions.bEnemyShuffle) 699 return false; 700 if (gSaveGameOptions[nSlot].bPitchforkOnly != gGameOptions.bPitchforkOnly) 701 return false; 702 if (gSaveGameOptions[nSlot].bPermaDeath != gGameOptions.bPermaDeath) 703 return false; 704 if (gSaveGameOptions[nSlot].uSpriteBannedFlags != gGameOptions.uSpriteBannedFlags) 705 return false; 706 if (gSaveGameOptions[nSlot].nRandomizerCheat != gGameOptions.nRandomizerCheat) 707 return false; 708 if (Bstrncmp(gSaveGameOptions[nSlot].zLevelName, gGameOptions.zLevelName, sizeof(gSaveGameOptions[nSlot].zLevelName))) 709 return false; 710 return true; 711 } 712 713 void LoadUpdateSaveGame(int nSlot, int nSkill) 714 { 715 gSaveGameProfileSkill[nSlot] = nSkill; 716 Bstrncpyz(strRestoreGameStrings[gSaveGameOptions[nSlot].nSaveGameSlot], gSaveGameOptions[nSlot].szUserGameName, sizeof(strRestoreGameStrings[gSaveGameOptions[nSlot].nSaveGameSlot])); 717 718 char nDifficulty = gSaveGameOptions[nSlot].nDifficulty; 719 if (gSaveGameOptions[nSlot].nDifficulty != gSaveGameOptions[nSlot].nEnemyHealth) 720 nDifficulty = 5; 721 else if (gSaveGameOptions[nSlot].nDifficulty != gSaveGameOptions[nSlot].nEnemyQuantity) 722 nDifficulty = 5; 723 else if (gSaveGameOptions[nSlot].nDifficulty != (char)gSaveGameProfileSkill[nSlot]) 724 nDifficulty = 5; 725 else if (gSaveGameOptions[nSlot].nEnemySpeed) 726 nDifficulty = 5; 727 else if (gSaveGameOptions[nSlot].bEnemyShuffle) 728 nDifficulty = 5; 729 else if (gSaveGameOptions[nSlot].bPitchforkOnly) 730 nDifficulty = 5; 731 else if (gSaveGameOptions[nSlot].bPermaDeath) 732 nDifficulty = 5; 733 else if (gSaveGameOptions[nSlot].uSpriteBannedFlags) 734 nDifficulty = 5; 735 restoreGameDifficulty[gSaveGameOptions[nSlot].nSaveGameSlot] = nDifficulty; 736 } 737 738 static MyLoadSave *myLoadSave; 739 740 741 void LoadSaveSetup(void) 742 { 743 void ActorLoadSaveConstruct(void); 744 void AILoadSaveConstruct(void); 745 void EndGameLoadSaveConstruct(void); 746 void EventQLoadSaveConstruct(void); 747 void LevelsLoadSaveConstruct(void); 748 void MessagesLoadSaveConstruct(void); 749 void MirrorLoadSaveConstruct(void); 750 void PlayerLoadSaveConstruct(void); 751 void SeqLoadSaveConstruct(void); 752 void TriggersLoadSaveConstruct(void); 753 void ViewLoadSaveConstruct(void); 754 void WarpLoadSaveConstruct(void); 755 void WeaponLoadSaveConstruct(void); 756 #ifdef NOONE_EXTENSIONS 757 void nnExtLoadSaveConstruct(void); 758 #endif 759 myLoadSave = new MyLoadSave(); 760 761 // NoOne: Seq must be in top of AI because of AISTATE callbacks (and seq callbacks in general). 762 // Ex: cultist throwing TNT - if you saved the game while it plays animation 763 // before fire trigger seq flag, you will get assertion fail on load since 764 // target == -1 after aiInitSprite() call. 765 766 // Another reason is that it just spawns the wrong animation. 767 // Ex: save the game when some dude moves, load it and quckly 768 // use ONERING cheat. You will see that dude plays moving 769 // animation, but have idle AISTATE after aiInitSprite() 770 // call. In vanilla they just stand still. 771 772 SeqLoadSaveConstruct(); 773 ActorLoadSaveConstruct(); 774 AILoadSaveConstruct(); 775 EndGameLoadSaveConstruct(); 776 EventQLoadSaveConstruct(); 777 LevelsLoadSaveConstruct(); 778 MessagesLoadSaveConstruct(); 779 MirrorLoadSaveConstruct(); 780 PlayerLoadSaveConstruct(); 781 TriggersLoadSaveConstruct(); 782 ViewLoadSaveConstruct(); 783 WarpLoadSaveConstruct(); 784 WeaponLoadSaveConstruct(); 785 #ifdef NOONE_EXTENSIONS 786 nnExtLoadSaveConstruct(); 787 #endif 788 }