Skip to content

Commit

Permalink
feat: save (#294)
Browse files Browse the repository at this point in the history
- I renamed `TESForm::LookupByX` to `TESForm::GetFormByX` to match
CommonlibF4 and what Bethesda internally calls them
  • Loading branch information
qudix authored Nov 3, 2024
1 parent 351d976 commit 05ead67
Show file tree
Hide file tree
Showing 7 changed files with 286 additions and 29 deletions.
225 changes: 225 additions & 0 deletions include/RE/B/BGSSaveLoad.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
#pragma once

#include "RE/B/BSPauseRequester.h"
#include "RE/B/BSFixedString.h"
#include "RE/B/BSTArray.h"
#include "RE/B/BSTEvent.h"
#include "RE/B/BSTScatterTable.h"

namespace RE
{
namespace PlayerNameEvent
{
struct NameChangedEvent;
}

class BGSSaveLoadFile
{
public:

};

class BGSSaveLoadFileEntry
{
public:
[[nodiscard]] char* GetFileName() const
{
return fileName;
}

[[nodiscard]] char* GetPlayerName()
{
if (playerName)
return playerName;

LoadData();
return playerName;
}

[[nodiscard]] bool IsAutoSave() const
{
return fileName && !_strnicmp(fileName, "AutoSave", 8);
}

[[nodiscard]] bool IsExitSave() const
{
return fileName && !_strnicmp(fileName, "ExitSave", 8);
}

[[nodiscard]] bool IsGenerated() const
{
return fileName && !_strnicmp(fileName, "Save", 4);
}

[[nodiscard]] bool IsQuickSave() const
{
return fileName && !_strnicmp(fileName, "QuickSave", 9);
}

void LoadData()
{
using func_t = decltype(&BGSSaveLoadFileEntry::LoadData);
static REL::Relocation<func_t> func{ ID::BGSSaveLoadFileEntry::LoadData };
func(this);
}

// members
char* fileName; // 00
char* playerName; // 08
};

class BGSSaveLoadThread
{
public:
SF_RTTI_VTABLE(BGSSaveLoadThread);

struct AsyncRequest
{
using TaskFinishedCallback_t = std::add_pointer_t<void(bool a_result)>;
};
};

class BGSSaveLoadManager :
public BSTEventSink<PlayerNameEvent::NameChangedEvent>, // 000
public BSPauseRequester // 008
{
public:
SF_RTTI_VTABLE(BGSSaveLoadManager);

enum class SaveFileCategory : std::uint32_t
{
kUser = 0,
kAuto = 1,
kQuick = 2,
kExit = 3
};

enum class QueuedTask : std::uint32_t
{
kAutoSave = 0x1,
kForceSave = 0x2,
kQuickSave = 0x8,
kQuickLoad = 0x10,
kLoadGame = 0x40,
kAutoSaveCommit = 0x200,
kQuickSaveCommit = 0x400,
kBuildSaveGameList = 0x1000,
kSaveAndQuit = 0x4000,
};

virtual ~BGSSaveLoadManager(); // 00

// override (BSTEventSink)
BSEventNotifyControl ProcessEvent(const PlayerNameEvent::NameChangedEvent& a_event, BSTEventSource<PlayerNameEvent::NameChangedEvent>* a_source) override; // 01

[[nodiscard]] static auto GetSingleton()
{
static REL::Relocation<BGSSaveLoadManager**> singleton{ ID::BGSSaveLoadManager::Singleton };
return *singleton;
}

[[nodiscard]] static bool IsSaveFileNameAutoSave(const char* a_name)
{
return a_name && !_strnicmp(a_name, "AutoSave", 8);
}

[[nodiscard]] static bool IsSaveFileNameExitSave(const char* a_name)
{
return a_name && !_strnicmp(a_name, "ExitSave", 8);
}

[[nodiscard]] static bool IsSaveFileNameGenerated(const char* a_name)
{
return a_name && !_strnicmp(a_name, "Save", 4);
}

[[nodiscard]] static bool IsSaveFileNameQuickSave(const char* a_name)
{
return a_name && !_strnicmp(a_name, "QuickSave", 9);
}

bool DeleteSaveFile(const char* a_filename, void* a_unk1, bool a_skipRemainingSavesCheck)
{
using func_t = decltype(&BGSSaveLoadManager::DeleteSaveFile);
static REL::Relocation<func_t> func{ ID::BGSSaveLoadManager::DeleteSaveFile };
return func(this, a_filename, a_unk1, a_skipRemainingSavesCheck);
}

void QueueBuildSaveGameList(BGSSaveLoadThread::AsyncRequest::TaskFinishedCallback_t a_taskFinished = nullptr)
{
using func_t = decltype(&BGSSaveLoadManager::QueueBuildSaveGameList);
static REL::Relocation<func_t> func{ ID::BGSSaveLoadManager::QueueBuildSaveGameList };
func(this, a_taskFinished);
}

void QueueLoadGame(BGSSaveLoadFileEntry* a_entry)
{
queuedEntryToLoad = a_entry;
queuedTasks.set(QueuedTask::kLoadGame);
}

void QueueLoadMostRecentSaveGame()
{
if (!saveGameListBuilt) {
return QueueBuildSaveGameList([](bool a_result) {
if (a_result)
GetSingleton()->QueueLoadMostRecentSaveGame();
});
}

if (saveGameCount > 0)
QueueLoadGame(saveGameList.back());
}

void QueueSaveLoadTask(QueuedTask a_task)
{
using func_t = decltype(&BGSSaveLoadManager::QueueSaveLoadTask);
static REL::Relocation<func_t> func{ ID::BGSSaveLoadManager::QueueSaveLoadTask };
func(this, a_task);
}

[[nodiscard]] bool IsSaveGameListBuilt() const
{
return saveGameListBuilt;
}

// members
std::byte pad010[0x008]; // 010
BSTArray<BGSSaveLoadFileEntry*> saveGameList; // 018
std::byte pad028[0x008]; // 028
bool saveGameListBuilt; // 030
std::uint32_t saveGameCount; // 034
std::uint32_t currentSaveGameNumber; // 038
std::byte pad03C[0x004]; // 03C
std::uint64_t saveGameListBuildID; // 040
std::uint32_t currentAutoSaveNumber; // 048
std::byte pad04C[0x004]; // 04C
REX::EnumSet<QueuedTask> queuedTasks; // 050
std::byte pad054[0x004]; // 054
BGSSaveLoadFileEntry* queuedEntryToLoad; // 058
std::byte pad060[0x010]; // 060
char* mostRecentSaveGame; // 070
std::int32_t mostRecentSaveGameDeviceID; // 078
std::byte pad07C[0x08C]; // 07C
std::uint64_t currentPlayerID; // 108
std::uint64_t displayPlayerID; // 110
BSFixedString saveFileNameToDelete; // 118
BSTHashMap<std::uint32_t, BSFixedString> autoSaveFileNames; // 120
BSFixedString quickSaveFileName; // 158
BSFixedString exitSaveFileName; // 160
std::byte pad168[0x010]; // 168
BGSSaveLoadFile* saveLoadFile; // 178
bool returnedFromSysUtil; // 180
bool sysUtilLoadDataComplete; // 181
};
static_assert(offsetof(BGSSaveLoadManager, saveGameList) == 0x018);
static_assert(offsetof(BGSSaveLoadManager, saveGameListBuilt) == 0x030);
static_assert(offsetof(BGSSaveLoadManager, saveGameCount) == 0x034);
static_assert(offsetof(BGSSaveLoadManager, saveGameListBuildID) == 0x040);
static_assert(offsetof(BGSSaveLoadManager, queuedTasks) == 0x050);
static_assert(offsetof(BGSSaveLoadManager, mostRecentSaveGame) == 0x070);
static_assert(offsetof(BGSSaveLoadManager, currentPlayerID) == 0x108);
static_assert(offsetof(BGSSaveLoadManager, quickSaveFileName) == 0x158);
static_assert(offsetof(BGSSaveLoadManager, saveLoadFile) == 0x178);
static_assert(offsetof(BGSSaveLoadManager, sysUtilLoadDataComplete) == 0x181);
}
26 changes: 0 additions & 26 deletions include/RE/B/BGSSaveLoadManager.h

This file was deleted.

12 changes: 12 additions & 0 deletions include/RE/B/BSPauseRequester.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

namespace RE
{
class BSPauseRequester
{
public:
SF_RTTI_VTABLE(BSPauseRequester);

virtual ~BSPauseRequester();
};
}
32 changes: 32 additions & 0 deletions include/RE/E/Events.h
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,20 @@ namespace RE
}
};

struct BGSAppPausedEvent
{
[[nodiscard]] static BSTEventSource<BGSAppPausedEvent>* GetEventSource()
{
using func_t = decltype(&BGSAppPausedEvent::GetEventSource);
static REL::Relocation<func_t> func{ REL::ID(167011) };
return func();
}

// members
bool paused; // 00
};
static_assert(sizeof(BGSAppPausedEvent) == 0x1);

struct BGSCellGridLoadEvent
{
[[nodiscard]] static BSTEventSource<BGSCellGridLoadEvent>* GetEventSource()
Expand Down Expand Up @@ -2290,6 +2304,24 @@ namespace RE
}
};

struct PlayerDifficultySettingChanged
{
struct Event
{
[[nodiscard]] static BSTEventSource<PlayerDifficultySettingChanged::Event>* GetEventSource()
{
using func_t = decltype(&PlayerDifficultySettingChanged::Event::GetEventSource);
static REL::Relocation<func_t> func{ REL::ID(153667) };
return func();
}

// members
std::uint32_t oldDifficulty; // 00
std::uint32_t newDifficulty; // 04
};
static_assert(sizeof(PlayerDifficultySettingChanged::Event) == 0x8);
};

struct PlayerFastTravel
{
struct Event
Expand Down
9 changes: 8 additions & 1 deletion include/RE/IDs.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ namespace RE::ID
inline constexpr REL::ID ctor{ 101725 };
}

namespace BGSSaveLoadFileEntry
{
inline constexpr REL::ID LoadData{ 147331 };
}

namespace BGSSaveLoadGame
{
inline constexpr REL::ID SaveGame{ 147515 };
Expand All @@ -73,8 +78,10 @@ namespace RE::ID

namespace BGSSaveLoadManager
{
inline constexpr REL::ID Singleton{ 880997 };
inline constexpr REL::ID DeleteSaveFile{ 147844 };
inline constexpr REL::ID QueueBuildSaveGameList{ 147900 };
inline constexpr REL::ID QueueSaveLoadTask{ 1536882 };
inline constexpr REL::ID Singleton{ 880997 };
}

namespace BGSStoryTeller
Expand Down
8 changes: 7 additions & 1 deletion include/RE/M/Main.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,14 @@ namespace RE
}

// members
std::byte pad008[0x440]; // 008
std::byte pad008[0x020]; // 008
bool quitGame; // 028
std::byte pad029[0x005]; // 029
bool resetGame; // 02E
std::byte pad02F[0x419]; // 02F
bool isGameMenuPaused; // 448
};
static_assert(offsetof(Main, quitGame) == 0x028);
static_assert(offsetof(Main, resetGame) == 0x02E);
static_assert(offsetof(Main, isGameMenuPaused) == 0x448);
}
3 changes: 2 additions & 1 deletion include/RE/Starfield.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@
#include "RE/B/BGSResourceGenerationData.h"
#include "RE/B/BGSReverbParameters.h"
#include "RE/B/BGSSaveLoadGame.h"
#include "RE/B/BGSSaveLoadManager.h"
#include "RE/B/BGSSaveLoad.h"
#include "RE/B/BGSScene.h"
#include "RE/B/BGSSecondaryDamageList.h"
#include "RE/B/BGSShaderParticleGeometryData.h"
Expand Down Expand Up @@ -197,6 +197,7 @@
#include "RE/B/BSIntrusiveRefCounted.h"
#include "RE/B/BSLock.h"
#include "RE/B/BSLog.h"
#include "RE/B/BSPauseRequester.h"
#include "RE/B/BSPointerHandle.h"
#include "RE/B/BSReflection.h"
#include "RE/B/BSResourceEnums.h"
Expand Down

0 comments on commit 05ead67

Please sign in to comment.