Skip to content

Commit

Permalink
Do not freeze with bugged cosaves from 6.1.8
Browse files Browse the repository at this point in the history
  • Loading branch information
korri123 committed Aug 1, 2021
1 parent 64073b0 commit 6b2cb79
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 12 deletions.
13 changes: 12 additions & 1 deletion nvse/nvse/ArrayVar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
#include "GameForms.h"
#include <algorithm>
#include <intrin.h>
#include <set>

#include "Core_Serialization.h"

#if RUNTIME
#include "GameAPI.h"
Expand Down Expand Up @@ -1604,7 +1607,9 @@ void ArrayVarMap::Save(NVSESerializationInterface* intfc)

Serialization::OpenRecord('ARVE', kVersion);
}

#if _DEBUG
std::set<std::string> g_modsWithCosaveVars;
#endif
void ArrayVarMap::Load(NVSESerializationInterface* intfc)
{
_MESSAGE("Loading array variables");
Expand Down Expand Up @@ -1634,6 +1639,9 @@ void ArrayVarMap::Load(NVSESerializationInterface* intfc)
case 'ARVR':
{
modIndex = Serialization::ReadRecord8();
#if _DEBUG
g_modsWithCosaveVars.insert(g_modsLoaded.at(modIndex));
#endif
if (!Serialization::ResolveRefID(modIndex << 24, &tempRefID))
{
// owning mod was removed, but there may be references to it from other mods
Expand Down Expand Up @@ -1666,6 +1674,9 @@ void ArrayVarMap::Load(NVSESerializationInterface* intfc)
for (UInt32 i = 0; i < numRefs; i++)
{
curModIndex = Serialization::ReadRecord8();
#if _DEBUG
g_modsWithCosaveVars.insert(g_modsLoaded.at(curModIndex));
#endif
if (!modIndex)
{
if (Serialization::ResolveRefID(curModIndex << 24, &tempRefID))
Expand Down
45 changes: 35 additions & 10 deletions nvse/nvse/Serialization.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#include "Serialization.h"

#include <stdexcept>

#include "Core_Serialization.h"
#include "common/IFileStream.h"
#include "PluginManager.h"
Expand All @@ -24,7 +27,7 @@ static UInt32 g_lastLoadSize = 0x40000;
// PluginHeader plugin[header.numPlugins]
// ChunkHeader chunk[plugin.numChunks]
// UInt8 data[chunk.length]

struct Header
{
enum
Expand Down Expand Up @@ -250,9 +253,12 @@ void SerializationTask::SetOffset(UInt32 offset)
bufferPtr = bufferStart.get() + offset;
}

void SerializationTask::Skip(UInt32 size)
void SerializationTask::Skip(UInt32 size, bool read)
{
CheckResize(size);
if (read)
ValidateOffset(size);
else
CheckResize(size);
bufferPtr += size;
}

Expand Down Expand Up @@ -336,33 +342,38 @@ void SerializationTask::CheckResize(UInt32 size)

UInt8 SerializationTask::Read8()
{
ValidateOffset(1);
UInt8 result = *bufferPtr;
bufferPtr++;
return result;
}

UInt16 SerializationTask::Read16()
{
ValidateOffset(2);
UInt16 result = *(UInt16*)bufferPtr;
bufferPtr += 2;
return result;
}

UInt32 SerializationTask::Read32()
{
ValidateOffset(4);
UInt32 result = *(UInt32*)bufferPtr;
bufferPtr += 4;
return result;
}

void SerializationTask::Read64(void *outData)
{
ValidateOffset(8);
*(double*)outData = *(double*)bufferPtr;
bufferPtr += 8;
}

void SerializationTask::ReadBuf(void *outData, UInt32 size)
{
ValidateOffset(size);
switch (size)
{
case 0:
Expand All @@ -389,6 +400,7 @@ void SerializationTask::ReadBuf(void *outData, UInt32 size)

void SerializationTask::PeekBuf(void *outData, UInt32 size)
{
ValidateOffset(size);
switch (size)
{
case 0:
Expand All @@ -409,6 +421,14 @@ void SerializationTask::PeekBuf(void *outData, UInt32 size)
}
}

void SerializationTask::ValidateOffset(UInt32 size) const
{
if (GetOffset() + size > this->bufferSize)
{
throw std::out_of_range("");
}
}

//==========================================================================

bool WriteRecord(UInt32 type, UInt32 version, const void * buf, UInt32 length)
Expand Down Expand Up @@ -448,13 +468,13 @@ bool OpenRecord(UInt32 type, UInt32 version)
ASSERT(!s_chunkOpen);

s_pluginHeaderOffset = s_serializationTask.GetOffset();
s_serializationTask.Skip(sizeof(s_pluginHeader));
s_serializationTask.Skip(sizeof(s_pluginHeader), false);
}

FlushWriteChunk();

s_chunkHeaderOffset = s_serializationTask.GetOffset();
s_serializationTask.Skip(sizeof(s_chunkHeader));
s_serializationTask.Skip(sizeof(s_chunkHeader), false);

s_pluginHeader.numChunks++;

Expand Down Expand Up @@ -502,7 +522,7 @@ static void FlushReadRecord(void)
{
if (!ignoreNextChunk)
_WARNING("plugin didn't finish reading chunk");
s_serializationTask.Skip(s_chunkHeader.length);
s_serializationTask.Skip(s_chunkHeader.length, true);
}

s_chunkOpen = false;
Expand Down Expand Up @@ -600,7 +620,7 @@ void SkipNBytes(UInt32 byteNum)
if (byteNum > s_chunkHeader.length)
byteNum = s_chunkHeader.length;

s_serializationTask.Skip(byteNum);
s_serializationTask.Skip(byteNum, false);

s_chunkHeader.length -= byteNum;
}
Expand Down Expand Up @@ -662,7 +682,7 @@ void HandleSaveGame(const char * path)
s_fileHeader.falloutVersion = RUNTIME_VERSION;
s_fileHeader.numPlugins = 0;

s_serializationTask.Skip(sizeof(s_fileHeader));
s_serializationTask.Skip(sizeof(s_fileHeader), false);

// iterate through plugins
_MESSAGE("saving %d plugins to %s", s_pluginCallbacks.size(), g_savePath.c_str());
Expand Down Expand Up @@ -812,6 +832,11 @@ void HandleLoadGame(const char * path, NVSESerializationInterface::EventCallback
while (s_serializationTask.GetRemain() >= sizeof(PluginHeader))
{
s_serializationTask.ReadBuf(&s_pluginHeader, sizeof(s_pluginHeader));
if (!s_pluginHeader.length)
{
_WARNING("cosave header has size 0");
break;
}

UInt32 pluginChunkStart = s_serializationTask.GetOffset();

Expand All @@ -832,15 +857,15 @@ void HandleLoadGame(const char * path, NVSESerializationInterface::EventCallback
// ### wtf?
_WARNING("plugin has data in save file but no handler");

s_serializationTask.Skip(s_pluginHeader.length);
s_serializationTask.Skip(s_pluginHeader.length, true);
}
}
else
{
// ### TODO: save the data temporarily?
_WARNING("data in save file for plugin, but plugin isn't loaded");

s_serializationTask.Skip(s_pluginHeader.length);
s_serializationTask.Skip(s_pluginHeader.length, true);
}

UInt32 expectedOffset = pluginChunkStart + s_pluginHeader.length;
Expand Down
3 changes: 2 additions & 1 deletion nvse/nvse/Serialization.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ struct SerializationTask
UInt32 GetOffset() const;
void SetOffset(UInt32 offset);

void Skip(UInt32 size);
void Skip(UInt32 size, bool read);

void Write8(UInt8 inData);
void Write16(UInt16 inData);
Expand All @@ -55,6 +55,7 @@ struct SerializationTask
void PeekBuf(void *outData, UInt32 size);

UInt32 GetRemain() const {return length - GetOffset();}
void ValidateOffset(UInt32 size) const;
};

struct PluginCallbacks
Expand Down
7 changes: 7 additions & 0 deletions nvse/nvse/StringVar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include "GameApi.h"
#include <set>

#include "Core_Serialization.h"

StringVar::StringVar(const char* in_data, UInt8 modIndex)
{
data = in_data;
Expand Down Expand Up @@ -268,6 +270,10 @@ void StringVarMap::Save(NVSESerializationInterface* intfc)
Serialization::OpenRecord('STVE', 0);
}

#if _DEBUG
extern std::set<std::string> g_modsWithCosaveVars;
#endif

void StringVarMap::Load(NVSESerializationInterface* intfc)
{
_MESSAGE("Loading strings");
Expand Down Expand Up @@ -305,6 +311,7 @@ void StringVarMap::Load(NVSESerializationInterface* intfc)
case 'STVR':
modIndex = Serialization::ReadRecord8();
#if _DEBUG
g_modsWithCosaveVars.insert(g_modsLoaded.at(modIndex));
modVarCounts[modIndex] += 1;
if (modVarCounts[modIndex] == varCountThreshold) {
exceededMods.Insert(modIndex);
Expand Down

0 comments on commit 6b2cb79

Please sign in to comment.