mirror of
https://github.com/DrBeef/Raze.git
synced 2025-01-27 03:01:05 +00:00
1149b4f4aa
* saving of demos and savegames no longer mindlessly writes to the mod directory. All such access is now being rerouted through the special paths interface so that the game data can reside in write protected locations. * refactored all occurences of klistpath except fnlist_getnames. * do not allow CON scripts to write to arbitrary files. This is a massive exploit and can be used to cause real damage if someone knows how to play this thing - it's far easier than people may think! It will now write any such data to a special section in the main config which is safe and cannot be manipulated to write to random locations on the hard drive.
504 lines
16 KiB
C++
504 lines
16 KiB
C++
//-------------------------------------------------------------------------
|
|
/*
|
|
Copyright (C) 2010-2019 EDuke32 developers and contributors
|
|
Copyright (C) 2019 Nuke.YKT
|
|
|
|
This file is part of NBlood.
|
|
|
|
NBlood is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License version 2
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
//-------------------------------------------------------------------------
|
|
#include "ns.h" // Must come before everything else!
|
|
|
|
#include <stdio.h>
|
|
#include "build.h"
|
|
#include "compat.h"
|
|
#include "mmulti.h"
|
|
#include "common_game.h"
|
|
#include "config.h"
|
|
#include "ai.h"
|
|
#include "asound.h"
|
|
#include "blood.h"
|
|
#include "demo.h"
|
|
#include "globals.h"
|
|
#include "db.h"
|
|
#include "messages.h"
|
|
#include "menu.h"
|
|
#include "network.h"
|
|
#include "loadsave.h"
|
|
#include "resource.h"
|
|
#include "screen.h"
|
|
#include "sectorfx.h"
|
|
#include "seq.h"
|
|
#include "sfx.h"
|
|
#include "sound.h"
|
|
#include "i_specialpaths.h"
|
|
#include "view.h"
|
|
|
|
BEGIN_BLD_NS
|
|
|
|
GAMEOPTIONS gSaveGameOptions[10];
|
|
char *gSaveGamePic[10];
|
|
unsigned int gSavedOffset = 0;
|
|
|
|
unsigned int dword_27AA38 = 0;
|
|
unsigned int dword_27AA3C = 0;
|
|
unsigned int dword_27AA40 = 0;
|
|
void *dword_27AA44 = NULL;
|
|
|
|
LoadSave LoadSave::head(123);
|
|
FILE *LoadSave::hSFile = NULL;
|
|
FileReader LoadSave::hLFile;
|
|
|
|
short word_27AA54 = 0;
|
|
|
|
void sub_76FD4(void)
|
|
{
|
|
#if 0
|
|
if (!dword_27AA44)
|
|
dword_27AA44 = Resource::Alloc(0x186a0);
|
|
#endif
|
|
}
|
|
|
|
void LoadSave::Save(void)
|
|
{
|
|
ThrowError("Pure virtual function called");
|
|
}
|
|
|
|
void LoadSave::Load(void)
|
|
{
|
|
ThrowError("Pure virtual function called");
|
|
}
|
|
|
|
void LoadSave::Read(void *pData, int nSize)
|
|
{
|
|
dword_27AA38 += nSize;
|
|
dassert(hLFile.isOpen());
|
|
if (hLFile.Read(pData, nSize) != nSize)
|
|
ThrowError("Error reading save file.");
|
|
}
|
|
|
|
void LoadSave::Write(void *pData, int nSize)
|
|
{
|
|
dword_27AA38 += nSize;
|
|
dword_27AA3C += nSize;
|
|
dassert(hSFile != NULL);
|
|
if (fwrite(pData, 1, nSize, hSFile) != (size_t)nSize)
|
|
ThrowError("File error #%d writing save file.", errno);
|
|
}
|
|
|
|
void LoadSave::LoadGame(char *pzFile)
|
|
{
|
|
bool demoWasPlayed = gDemo.at1;
|
|
if (gDemo.at1)
|
|
gDemo.Close();
|
|
|
|
sndKillAllSounds();
|
|
sfxKillAllSounds();
|
|
ambKillAll();
|
|
seqKillAll();
|
|
if (!gGameStarted)
|
|
{
|
|
memset(xsprite, 0, sizeof(xsprite));
|
|
memset(sprite, 0, sizeof(spritetype)*kMaxSprites);
|
|
automapping = 1;
|
|
}
|
|
hLFile = fopenFileReader(pzFile, 0);
|
|
if (!hLFile.isOpen())
|
|
ThrowError("Error loading save file.");
|
|
LoadSave *rover = head.next;
|
|
while (rover != &head)
|
|
{
|
|
rover->Load();
|
|
rover = rover->next;
|
|
}
|
|
hLFile.Close();
|
|
if (!gGameStarted)
|
|
scrLoadPLUs();
|
|
InitSectorFX();
|
|
viewInitializePrediction();
|
|
PreloadCache();
|
|
if (!bVanilla && !gMe->packSlots[1].isActive) // if diving suit is not active, turn off reverb sound effect
|
|
sfxSetReverb(0);
|
|
ambInit();
|
|
#ifdef YAX_ENABLE
|
|
yax_update(numyaxbunches > 0 ? 2 : 1);
|
|
#endif
|
|
memset(myMinLag, 0, sizeof(myMinLag));
|
|
otherMinLag = 0;
|
|
myMaxLag = 0;
|
|
gNetFifoClock = 0;
|
|
gNetFifoTail = 0;
|
|
memset(gNetFifoHead, 0, sizeof(gNetFifoHead));
|
|
gPredictTail = 0;
|
|
gNetFifoMasterTail = 0;
|
|
memset(gFifoInput, 0, sizeof(gFifoInput));
|
|
memset(gChecksum, 0, sizeof(gChecksum));
|
|
memset(gCheckFifo, 0, sizeof(gCheckFifo));
|
|
memset(gCheckHead, 0, sizeof(gCheckHead));
|
|
gSendCheckTail = 0;
|
|
gCheckTail = 0;
|
|
gBufferJitter = 0;
|
|
bOutOfSync = 0;
|
|
for (int i = 0; i < gNetPlayers; i++)
|
|
playerSetRace(&gPlayer[i], gPlayer[i].lifeMode);
|
|
if (VanillaMode())
|
|
viewSetMessage("");
|
|
else
|
|
gGameMessageMgr.Clear();
|
|
viewSetErrorMessage("");
|
|
if (!gGameStarted)
|
|
{
|
|
netWaitForEveryone(0);
|
|
memset(gPlayerReady, 0, sizeof(gPlayerReady));
|
|
}
|
|
gFrameTicks = 0;
|
|
gFrame = 0;
|
|
gCacheMiss = 0;
|
|
gFrameRate = 0;
|
|
totalclock = 0;
|
|
gPaused = 0;
|
|
gGameStarted = 1;
|
|
bVanilla = false;
|
|
|
|
if (mus_restartonload
|
|
|| demoWasPlayed
|
|
|| (gMusicPrevLoadedEpisode != gGameOptions.nEpisode || gMusicPrevLoadedLevel != gGameOptions.nLevel))
|
|
{
|
|
levelTryPlayMusic(gGameOptions.nEpisode, gGameOptions.nLevel);
|
|
}
|
|
gMusicPrevLoadedEpisode = gGameOptions.nEpisode;
|
|
gMusicPrevLoadedLevel = gGameOptions.nLevel;
|
|
|
|
netBroadcastPlayerInfo(myconnectindex);
|
|
//sndPlaySong(gGameOptions.zLevelSong, 1);
|
|
}
|
|
|
|
void LoadSave::SaveGame(char *pzFile)
|
|
{
|
|
hSFile = fopen(pzFile, "wb");
|
|
if (hSFile == NULL)
|
|
ThrowError("File error #%d creating save file.", errno);
|
|
dword_27AA38 = 0;
|
|
dword_27AA40 = 0;
|
|
LoadSave *rover = head.next;
|
|
while (rover != &head)
|
|
{
|
|
rover->Save();
|
|
if (dword_27AA38 > dword_27AA40)
|
|
dword_27AA40 = dword_27AA38;
|
|
dword_27AA38 = 0;
|
|
rover = rover->next;
|
|
}
|
|
fclose(hSFile);
|
|
hSFile = NULL;
|
|
}
|
|
|
|
class MyLoadSave : public LoadSave
|
|
{
|
|
public:
|
|
virtual void Load(void);
|
|
virtual void Save(void);
|
|
};
|
|
|
|
void MyLoadSave::Load(void)
|
|
{
|
|
psky_t *pSky = tileSetupSky(0);
|
|
int id;
|
|
Read(&id, sizeof(id));
|
|
if (id != 0x5653424e/*'VSBN'*/)
|
|
ThrowError("Old saved game found");
|
|
short version;
|
|
Read(&version, sizeof(version));
|
|
if (version != BYTEVERSION)
|
|
ThrowError("Incompatible version of saved game found!");
|
|
Read(&gGameOptions, sizeof(gGameOptions));
|
|
Read(&numsectors, sizeof(numsectors));
|
|
Read(&numwalls, sizeof(numwalls));
|
|
Read(&numsectors, sizeof(numsectors));
|
|
int nNumSprites;
|
|
Read(&nNumSprites, sizeof(nNumSprites));
|
|
memset(sector, 0, sizeof(sector[0])*kMaxSectors);
|
|
memset(wall, 0, sizeof(wall[0])*kMaxWalls);
|
|
memset(sprite, 0, sizeof(sprite[0])*kMaxSprites);
|
|
Read(sector, sizeof(sector[0])*numsectors);
|
|
Read(wall, sizeof(wall[0])*numwalls);
|
|
Read(sprite, sizeof(sprite[0])*kMaxSprites);
|
|
Read(qsector_filler, sizeof(qsector_filler[0])*numsectors);
|
|
Read(qsprite_filler, sizeof(qsprite_filler[0])*kMaxSprites);
|
|
Read(&randomseed, sizeof(randomseed));
|
|
Read(¶llaxtype, sizeof(parallaxtype));
|
|
Read(&showinvisibility, sizeof(showinvisibility));
|
|
Read(&pSky->horizfrac, sizeof(pSky->horizfrac));
|
|
Read(&pSky->yoffs, sizeof(pSky->yoffs));
|
|
Read(&pSky->yscale, sizeof(pSky->yscale));
|
|
Read(&gVisibility, sizeof(gVisibility));
|
|
Read(&g_visibility, sizeof(g_visibility));
|
|
Read(¶llaxvisibility, sizeof(parallaxvisibility));
|
|
Read(pSky->tileofs, sizeof(pSky->tileofs));
|
|
Read(&pSky->lognumtiles, sizeof(pSky->lognumtiles));
|
|
Read(headspritesect, sizeof(headspritesect));
|
|
Read(headspritestat, sizeof(headspritestat));
|
|
Read(prevspritesect, sizeof(prevspritesect));
|
|
Read(prevspritestat, sizeof(prevspritestat));
|
|
Read(nextspritesect, sizeof(nextspritesect));
|
|
Read(nextspritestat, sizeof(nextspritestat));
|
|
Read(show2dsector, sizeof(show2dsector));
|
|
Read(show2dwall, sizeof(show2dwall));
|
|
Read(show2dsprite, sizeof(show2dsprite));
|
|
Read(&automapping, sizeof(automapping));
|
|
Read(gotpic, sizeof(gotpic));
|
|
Read(gotsector, sizeof(gotsector));
|
|
Read(&gFrameClock, sizeof(gFrameClock));
|
|
Read(&gFrameTicks, sizeof(gFrameTicks));
|
|
Read(&gFrame, sizeof(gFrame));
|
|
ClockTicks nGameClock;
|
|
Read(&totalclock, sizeof(totalclock));
|
|
totalclock = nGameClock;
|
|
Read(&gLevelTime, sizeof(gLevelTime));
|
|
Read(&gPaused, sizeof(gPaused));
|
|
Read(&gbAdultContent, sizeof(gbAdultContent));
|
|
Read(baseWall, sizeof(baseWall[0])*numwalls);
|
|
Read(baseSprite, sizeof(baseSprite[0])*nNumSprites);
|
|
Read(baseFloor, sizeof(baseFloor[0])*numsectors);
|
|
Read(baseCeil, sizeof(baseCeil[0])*numsectors);
|
|
Read(velFloor, sizeof(velFloor[0])*numsectors);
|
|
Read(velCeil, sizeof(velCeil[0])*numsectors);
|
|
Read(&gHitInfo, sizeof(gHitInfo));
|
|
Read(&byte_1A76C6, sizeof(byte_1A76C6));
|
|
Read(&byte_1A76C8, sizeof(byte_1A76C8));
|
|
Read(&byte_1A76C7, sizeof(byte_1A76C7));
|
|
Read(&byte_19AE44, sizeof(byte_19AE44));
|
|
Read(gStatCount, sizeof(gStatCount));
|
|
Read(nextXSprite, sizeof(nextXSprite));
|
|
Read(nextXWall, sizeof(nextXWall));
|
|
Read(nextXSector, sizeof(nextXSector));
|
|
memset(xsprite, 0, sizeof(xsprite));
|
|
for (int nSprite = 0; nSprite < kMaxSprites; nSprite++)
|
|
{
|
|
if (sprite[nSprite].statnum < kMaxStatus)
|
|
{
|
|
int nXSprite = sprite[nSprite].extra;
|
|
if (nXSprite > 0)
|
|
Read(&xsprite[nXSprite], sizeof(XSPRITE));
|
|
}
|
|
}
|
|
memset(xwall, 0, sizeof(xwall));
|
|
for (int nWall = 0; nWall < numwalls; nWall++)
|
|
{
|
|
int nXWall = wall[nWall].extra;
|
|
if (nXWall > 0)
|
|
Read(&xwall[nXWall], sizeof(XWALL));
|
|
}
|
|
memset(xsector, 0, sizeof(xsector));
|
|
for (int nSector = 0; nSector < numsectors; nSector++)
|
|
{
|
|
int nXSector = sector[nSector].extra;
|
|
if (nXSector > 0)
|
|
Read(&xsector[nXSector], sizeof(XSECTOR));
|
|
}
|
|
Read(xvel, nNumSprites*sizeof(xvel[0]));
|
|
Read(yvel, nNumSprites*sizeof(yvel[0]));
|
|
Read(zvel, nNumSprites*sizeof(zvel[0]));
|
|
Read(&gMapRev, sizeof(gMapRev));
|
|
Read(&gSongId, sizeof(gSkyCount));
|
|
Read(&gFogMode, sizeof(gFogMode));
|
|
#ifdef YAX_ENABLE
|
|
Read(&numyaxbunches, sizeof(numyaxbunches));
|
|
#endif
|
|
gCheatMgr.sub_5BCF4();
|
|
|
|
}
|
|
|
|
void MyLoadSave::Save(void)
|
|
{
|
|
psky_t *pSky = tileSetupSky(0);
|
|
int nNumSprites = 0;
|
|
int id = 0x5653424e/*'VSBN'*/;
|
|
Write(&id, sizeof(id));
|
|
short version = BYTEVERSION;
|
|
Write(&version, sizeof(version));
|
|
for (int nSprite = 0; nSprite < kMaxSprites; nSprite++)
|
|
{
|
|
if (sprite[nSprite].statnum < kMaxStatus && nSprite > nNumSprites)
|
|
nNumSprites = nSprite;
|
|
}
|
|
//nNumSprites += 2;
|
|
nNumSprites++;
|
|
Write(&gGameOptions, sizeof(gGameOptions));
|
|
Write(&numsectors, sizeof(numsectors));
|
|
Write(&numwalls, sizeof(numwalls));
|
|
Write(&numsectors, sizeof(numsectors));
|
|
Write(&nNumSprites, sizeof(nNumSprites));
|
|
Write(sector, sizeof(sector[0])*numsectors);
|
|
Write(wall, sizeof(wall[0])*numwalls);
|
|
Write(sprite, sizeof(sprite[0])*kMaxSprites);
|
|
Write(qsector_filler, sizeof(qsector_filler[0])*numsectors);
|
|
Write(qsprite_filler, sizeof(qsprite_filler[0])*kMaxSprites);
|
|
Write(&randomseed, sizeof(randomseed));
|
|
Write(¶llaxtype, sizeof(parallaxtype));
|
|
Write(&showinvisibility, sizeof(showinvisibility));
|
|
Write(&pSky->horizfrac, sizeof(pSky->horizfrac));
|
|
Write(&pSky->yoffs, sizeof(pSky->yoffs));
|
|
Write(&pSky->yscale, sizeof(pSky->yscale));
|
|
Write(&gVisibility, sizeof(gVisibility));
|
|
Write(&g_visibility, sizeof(g_visibility));
|
|
Write(¶llaxvisibility, sizeof(parallaxvisibility));
|
|
Write(pSky->tileofs, sizeof(pSky->tileofs));
|
|
Write(&pSky->lognumtiles, sizeof(pSky->lognumtiles));
|
|
Write(headspritesect, sizeof(headspritesect));
|
|
Write(headspritestat, sizeof(headspritestat));
|
|
Write(prevspritesect, sizeof(prevspritesect));
|
|
Write(prevspritestat, sizeof(prevspritestat));
|
|
Write(nextspritesect, sizeof(nextspritesect));
|
|
Write(nextspritestat, sizeof(nextspritestat));
|
|
Write(show2dsector, sizeof(show2dsector));
|
|
Write(show2dwall, sizeof(show2dwall));
|
|
Write(show2dsprite, sizeof(show2dsprite));
|
|
Write(&automapping, sizeof(automapping));
|
|
Write(gotpic, sizeof(gotpic));
|
|
Write(gotsector, sizeof(gotsector));
|
|
Write(&gFrameClock, sizeof(gFrameClock));
|
|
Write(&gFrameTicks, sizeof(gFrameTicks));
|
|
Write(&gFrame, sizeof(gFrame));
|
|
ClockTicks nGameClock = totalclock;
|
|
Write(&nGameClock, sizeof(nGameClock));
|
|
Write(&gLevelTime, sizeof(gLevelTime));
|
|
Write(&gPaused, sizeof(gPaused));
|
|
Write(&gbAdultContent, sizeof(gbAdultContent));
|
|
Write(baseWall, sizeof(baseWall[0])*numwalls);
|
|
Write(baseSprite, sizeof(baseSprite[0])*nNumSprites);
|
|
Write(baseFloor, sizeof(baseFloor[0])*numsectors);
|
|
Write(baseCeil, sizeof(baseCeil[0])*numsectors);
|
|
Write(velFloor, sizeof(velFloor[0])*numsectors);
|
|
Write(velCeil, sizeof(velCeil[0])*numsectors);
|
|
Write(&gHitInfo, sizeof(gHitInfo));
|
|
Write(&byte_1A76C6, sizeof(byte_1A76C6));
|
|
Write(&byte_1A76C8, sizeof(byte_1A76C8));
|
|
Write(&byte_1A76C7, sizeof(byte_1A76C7));
|
|
Write(&byte_19AE44, sizeof(byte_19AE44));
|
|
Write(gStatCount, sizeof(gStatCount));
|
|
Write(nextXSprite, sizeof(nextXSprite));
|
|
Write(nextXWall, sizeof(nextXWall));
|
|
Write(nextXSector, sizeof(nextXSector));
|
|
for (int nSprite = 0; nSprite < kMaxSprites; nSprite++)
|
|
{
|
|
if (sprite[nSprite].statnum < kMaxStatus)
|
|
{
|
|
int nXSprite = sprite[nSprite].extra;
|
|
if (nXSprite > 0)
|
|
Write(&xsprite[nXSprite], sizeof(XSPRITE));
|
|
}
|
|
}
|
|
for (int nWall = 0; nWall < numwalls; nWall++)
|
|
{
|
|
int nXWall = wall[nWall].extra;
|
|
if (nXWall > 0)
|
|
Write(&xwall[nXWall], sizeof(XWALL));
|
|
}
|
|
for (int nSector = 0; nSector < numsectors; nSector++)
|
|
{
|
|
int nXSector = sector[nSector].extra;
|
|
if (nXSector > 0)
|
|
Write(&xsector[nXSector], sizeof(XSECTOR));
|
|
}
|
|
Write(xvel, nNumSprites*sizeof(xvel[0]));
|
|
Write(yvel, nNumSprites*sizeof(yvel[0]));
|
|
Write(zvel, nNumSprites*sizeof(zvel[0]));
|
|
Write(&gMapRev, sizeof(gMapRev));
|
|
Write(&gSongId, sizeof(gSkyCount));
|
|
Write(&gFogMode, sizeof(gFogMode));
|
|
#ifdef YAX_ENABLE
|
|
Write(&numyaxbunches, sizeof(numyaxbunches));
|
|
#endif
|
|
}
|
|
|
|
void LoadSavedInfo(void)
|
|
{
|
|
FString path = M_GetSavegamesPath() + "%sgame*.sav";
|
|
TArray<FString> saves;
|
|
D_AddWildFile(saves, path);
|
|
int nCount = 0;
|
|
for (auto & savename : saves)
|
|
{
|
|
auto hFile = fopenFileReader(savename, 0);
|
|
if (!hFile.isOpen())
|
|
ThrowError("Error loading save file header.");
|
|
int vc;
|
|
short v4;
|
|
vc = 0;
|
|
v4 = word_27AA54;
|
|
if ((uint32_t)hFile.Read(&vc, sizeof(vc)) != sizeof(vc))
|
|
{
|
|
continue;
|
|
}
|
|
if (vc != 0x5653424e/*'VSBN'*/)
|
|
{
|
|
continue;
|
|
}
|
|
hFile.Read(&v4, sizeof(v4));
|
|
if (v4 != BYTEVERSION)
|
|
{
|
|
continue;
|
|
}
|
|
if ((uint32_t)hFile.Read(&gSaveGameOptions[nCount], sizeof(gSaveGameOptions[0])) != sizeof(gSaveGameOptions[0]))
|
|
ThrowError("Error reading save file.");
|
|
strcpy(strRestoreGameStrings[gSaveGameOptions[nCount].nSaveGameSlot], gSaveGameOptions[nCount].szUserGameName);
|
|
nCount++;
|
|
}
|
|
}
|
|
|
|
void UpdateSavedInfo(int nSlot)
|
|
{
|
|
strcpy(strRestoreGameStrings[gSaveGameOptions[nSlot].nSaveGameSlot], gSaveGameOptions[nSlot].szUserGameName);
|
|
}
|
|
|
|
static MyLoadSave *myLoadSave;
|
|
|
|
|
|
void ActorLoadSaveConstruct(void);
|
|
void AILoadSaveConstruct(void);
|
|
void EndGameLoadSaveConstruct(void);
|
|
void EventQLoadSaveConstruct(void);
|
|
void LevelsLoadSaveConstruct(void);
|
|
void MessagesLoadSaveConstruct(void);
|
|
void MirrorLoadSaveConstruct(void);
|
|
void PlayerLoadSaveConstruct(void);
|
|
void SeqLoadSaveConstruct(void);
|
|
void TriggersLoadSaveConstruct(void);
|
|
void ViewLoadSaveConstruct(void);
|
|
void WarpLoadSaveConstruct(void);
|
|
void WeaponLoadSaveConstruct(void);
|
|
|
|
void LoadSaveSetup(void)
|
|
{
|
|
myLoadSave = new MyLoadSave();
|
|
|
|
ActorLoadSaveConstruct();
|
|
AILoadSaveConstruct();
|
|
EndGameLoadSaveConstruct();
|
|
EventQLoadSaveConstruct();
|
|
LevelsLoadSaveConstruct();
|
|
MessagesLoadSaveConstruct();
|
|
MirrorLoadSaveConstruct();
|
|
PlayerLoadSaveConstruct();
|
|
SeqLoadSaveConstruct();
|
|
TriggersLoadSaveConstruct();
|
|
ViewLoadSaveConstruct();
|
|
WarpLoadSaveConstruct();
|
|
WeaponLoadSaveConstruct();
|
|
}
|
|
|
|
END_BLD_NS
|