-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #33 from Deweh/feat-serialization
add serialization entry points
- Loading branch information
Showing
7 changed files
with
322 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
#include "Hooks_Serialization.h" | ||
|
||
#include "sfse_common/BranchTrampoline.h" | ||
#include "sfse_common/Relocation.h" | ||
#include "sfse_common/SafeWrite.h" | ||
|
||
#include "sfse/PluginManager.h" | ||
#include "sfse/Serialization.h" | ||
|
||
#include "xbyak/xbyak.h" | ||
|
||
class BGSSaveLoadGame; | ||
class BGSSaveLoadManager; | ||
|
||
typedef void (*_SaveGame)(BGSSaveLoadGame* a_this, void* a_unk1, void* a_unk2, const char* a_name); | ||
RelocAddr <_SaveGame> SaveGame_Call(0x024ACCB0 + 0x12B); | ||
RelocAddr <_SaveGame> SaveGame_Original(0x024AFCC0); | ||
|
||
typedef bool (*_LoadGame)(BGSSaveLoadGame* a_this, const char* a_name, void* a_unk1, void* a_unk2); | ||
RelocAddr <_LoadGame> LoadGame_Call(0x024DFF80 + 0x572); | ||
RelocAddr <_LoadGame> LoadGame_Original(0x024B55B0); | ||
|
||
typedef bool (*_DeleteSaveFile)(const char* a_filePath); | ||
RelocAddr <_DeleteSaveFile> DeleteSaveFile_Call(0x024DF75C + 0x65); | ||
RelocAddr <_DeleteSaveFile> DeleteSaveFile_Original(0x024DE118); | ||
|
||
typedef bool (*_VM_SaveGame)(void* a_this, void* a_storage, void* a_handleReaderWriter, bool a_flag); | ||
typedef bool (*_VM_LoadGame)(void* a_this, void* a_storage, void* a_handleReaderWriter, bool* a_flag, bool* b_flag); | ||
typedef void* (*_VM_DropAllRunningData)(void* a_this); | ||
_VM_SaveGame VM_SaveGame_Original = nullptr; | ||
_VM_LoadGame VM_LoadGame_Original = nullptr; | ||
_VM_DropAllRunningData VM_DropAllRunningData_Original = nullptr; | ||
RelocAddr <uintptr_t> VirtualMachine_IVMSaveLoadInterface_VTable(0x0545A240); | ||
|
||
void SaveGame_Hook(BGSSaveLoadGame* a_this, void* a_unk1, void* a_unk2, const char* a_name) | ||
{ | ||
Serialization::SetSaveName(a_name, true); | ||
PluginManager::dispatchMessage(0, SFSEMessagingInterface::kMessage_PreSaveGame, (void*)a_name, (u32)strlen(a_name), NULL); | ||
SaveGame_Original(a_this, a_unk1, a_unk2, a_name); | ||
PluginManager::dispatchMessage(0, SFSEMessagingInterface::kMessage_PostSaveGame, (void*)a_name, (u32)strlen(a_name), NULL); | ||
Serialization::SetSaveName(NULL); | ||
} | ||
|
||
bool LoadGame_Hook(BGSSaveLoadGame* a_this, const char* a_name, void* a_unk1, void* a_unk2) | ||
{ | ||
Serialization::SetSaveName(a_name, false); | ||
Serialization::HandleBeginLoad(); | ||
PluginManager::dispatchMessage(0, SFSEMessagingInterface::kMessage_PreLoadGame, (void*)a_name, (u32)strlen(a_name), NULL); | ||
bool result = LoadGame_Original(a_this, a_name, a_unk1, a_unk2); | ||
PluginManager::dispatchMessage(0, SFSEMessagingInterface::kMessage_PostLoadGame, (void*)a_name, (u32)strlen(a_name), NULL); | ||
Serialization::HandleEndLoad(); | ||
Serialization::SetSaveName(NULL); | ||
return result; | ||
} | ||
|
||
bool DeleteSaveFile_Hook(const char* a_filePath) | ||
{ | ||
bool result = DeleteSaveFile_Original(a_filePath); | ||
Serialization::HandleDeleteSave(a_filePath); | ||
return result; | ||
} | ||
|
||
void* VM_DropAllRunningData_Hook(void* a_this) | ||
{ | ||
void* result = VM_DropAllRunningData_Original(a_this); | ||
Serialization::HandleRevertGlobalData(); | ||
return result; | ||
} | ||
|
||
bool VM_SaveGame_Hook(void* a_this, void* a_storage, void* a_handleReaderWriter, bool a_flag) | ||
{ | ||
bool result = VM_SaveGame_Original(a_this, a_storage, a_handleReaderWriter, a_flag); | ||
Serialization::HandleSaveGlobalData(); | ||
return result; | ||
} | ||
|
||
bool VM_LoadGame_Hook(void* a_this, void* a_storage, void* a_handleReaderWriter, bool* a_flag, bool* b_flag) | ||
{ | ||
bool result = VM_LoadGame_Original(a_this, a_storage, a_handleReaderWriter, a_flag, b_flag); | ||
Serialization::HandleLoadGlobalData(); | ||
return result; | ||
} | ||
|
||
void Hooks_Serialization_Apply() | ||
{ | ||
//write call hooks for SaveGame, LoadGame & DeleteSaveFile | ||
g_branchTrampoline.write5Call(SaveGame_Call.getUIntPtr(), (uintptr_t)SaveGame_Hook); | ||
g_branchTrampoline.write5Call(LoadGame_Call.getUIntPtr(), (uintptr_t)LoadGame_Hook); | ||
g_branchTrampoline.write5Call(DeleteSaveFile_Call.getUIntPtr(), (uintptr_t)DeleteSaveFile_Hook); | ||
|
||
//get pointers to IVMSaveLoadInterface vfunc entries | ||
uintptr_t VM_SaveGame_VFunc = VirtualMachine_IVMSaveLoadInterface_VTable.getUIntPtr() + (0x1 * 0x8); | ||
uintptr_t VM_LoadGame_VFunc = VirtualMachine_IVMSaveLoadInterface_VTable.getUIntPtr() + (0x2 * 0x8); | ||
uintptr_t VM_DropAllRunningData_VFunc = VirtualMachine_IVMSaveLoadInterface_VTable.getUIntPtr() + (0x7 * 0x8); | ||
|
||
//save original vfuncs | ||
VM_SaveGame_Original = *reinterpret_cast<_VM_SaveGame*>(VM_SaveGame_VFunc); | ||
VM_LoadGame_Original = *reinterpret_cast<_VM_LoadGame*>(VM_LoadGame_VFunc); | ||
VM_DropAllRunningData_Original = *reinterpret_cast<_VM_DropAllRunningData*>(VM_DropAllRunningData_VFunc); | ||
|
||
//overwrite vfuncs | ||
safeWrite64(VM_SaveGame_VFunc, (uintptr_t)VM_SaveGame_Hook); | ||
safeWrite64(VM_LoadGame_VFunc, (uintptr_t)VM_LoadGame_Hook); | ||
safeWrite64(VM_DropAllRunningData_VFunc, (uintptr_t)VM_DropAllRunningData_Hook); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
#pragma once | ||
|
||
void Hooks_Serialization_Apply(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
#include "Serialization.h" | ||
#include "GameEvents.h" | ||
|
||
#include "sfse_common/Log.h" | ||
#include "sfse_common/Errors.h" | ||
#include "sfse_common/sfse_version.h" | ||
#include "sfse_common/FileStream.h" | ||
#include "sfse/GameSettings.h" | ||
|
||
#include <ShlObj.h> | ||
#include <unordered_map> | ||
#include <unordered_set> | ||
#include <io.h> | ||
|
||
namespace Serialization | ||
{ | ||
const char* kSavegamePath = "\\My Games\\" SAVE_FOLDER_NAME "\\"; | ||
|
||
std::unordered_map<u32, u32> changedIDs; | ||
std::unordered_set<u32> deletedIDs; | ||
std::string s_savePath; | ||
|
||
struct IDRemapDeleteListener : | ||
public BSTEventSink<TESFormIDRemapEvent>, | ||
public BSTEventSink<TESFormDeleteEvent> | ||
{ | ||
IDRemapDeleteListener() | ||
{ | ||
GetEventSource<TESFormIDRemapEvent>()->RegisterSink(static_cast<BSTEventSink<TESFormIDRemapEvent>*>(this)); | ||
GetEventSource<TESFormDeleteEvent>()->RegisterSink(static_cast<BSTEventSink<TESFormDeleteEvent>*>(this)); | ||
} | ||
|
||
virtual EventResult ProcessEvent(const TESFormIDRemapEvent& arEvent, BSTEventSource<TESFormIDRemapEvent>* eventSource) | ||
{ | ||
changedIDs[arEvent.oldID] = arEvent.newID; | ||
return EventResult::kContinue; | ||
}; | ||
|
||
virtual EventResult ProcessEvent(const TESFormDeleteEvent& arEvent, BSTEventSource<TESFormDeleteEvent>* eventSource) | ||
{ | ||
deletedIDs.insert(arEvent.formId); | ||
return EventResult::kContinue; | ||
}; | ||
}; | ||
|
||
void RemoveFileExtension(std::string& path) | ||
{ | ||
size_t lastDot = path.find_last_of('.'); | ||
if (lastDot != std::string::npos) { | ||
path.erase(lastDot); | ||
} | ||
} | ||
|
||
std::string MakeSavePath(std::string name, const char* extension, bool hasExtension) | ||
{ | ||
if (hasExtension) | ||
{ | ||
RemoveFileExtension(name); | ||
} | ||
|
||
char path[MAX_PATH]; | ||
ASSERT(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_MYDOCUMENTS, NULL, SHGFP_TYPE_CURRENT, path))); | ||
|
||
std::string result = path; | ||
result += kSavegamePath; | ||
Setting* localSavePath = (*SettingT<INISettingCollection>::pCollection)->GetSetting("sLocalSavePath:General"); | ||
if (localSavePath && (localSavePath->GetType() == Setting::kType_String)) | ||
result += localSavePath->data.s; | ||
else | ||
result += "Saves\\"; | ||
|
||
result += "\\"; | ||
result += name; | ||
if (extension) | ||
result += extension; | ||
return result; | ||
} | ||
|
||
void SetSaveName(const char* name, bool hasExtension) | ||
{ | ||
if (name) | ||
{ | ||
_MESSAGE("save name is %s", name); | ||
s_savePath = MakeSavePath(name, ".sfse", hasExtension); | ||
_MESSAGE("full save path: %s", s_savePath.c_str()); | ||
} | ||
else | ||
{ | ||
_MESSAGE("cleared save path"); | ||
s_savePath.clear(); | ||
} | ||
} | ||
|
||
void HandleBeginLoad() | ||
{ | ||
//if the remap listener isn't already registered, register it now. | ||
static IDRemapDeleteListener listener{}; | ||
changedIDs.clear(); | ||
deletedIDs.clear(); | ||
} | ||
|
||
void HandleEndLoad() | ||
{ | ||
changedIDs.clear(); | ||
deletedIDs.clear(); | ||
} | ||
|
||
bool ResolveFormId(u32 formId, u32* formIdOut) | ||
{ | ||
if (auto iter = changedIDs.find(formId); iter != changedIDs.end()) { | ||
(*formIdOut) = iter->second; | ||
return true; | ||
} | ||
|
||
if (deletedIDs.find(formId) == deletedIDs.end()) | ||
{ | ||
(*formIdOut) = formId; | ||
return true; | ||
} | ||
else | ||
{ | ||
return false; | ||
} | ||
} | ||
|
||
bool ResolveHandle(u64 handle, u64* handleOut) | ||
{ | ||
u32 formId = static_cast<u32>(handle & 0x00000000FFFFFFFF); | ||
if (auto iter = changedIDs.find(formId); iter != changedIDs.end()) { | ||
(*handleOut) = (handle & 0xFFFFFFFF00000000) | static_cast<u64>(iter->second); | ||
return true; | ||
} | ||
|
||
if (deletedIDs.find(formId) == deletedIDs.end()) | ||
{ | ||
(*handleOut) = handle; | ||
return true; | ||
} | ||
else | ||
{ | ||
return false; | ||
} | ||
} | ||
|
||
void HandleRevertGlobalData() | ||
{ | ||
_MESSAGE("RevertGlobalData"); | ||
|
||
//TODO: add implementation for revert callbacks. | ||
} | ||
|
||
void HandleSaveGlobalData() | ||
{ | ||
_MESSAGE("SaveGlobalData"); | ||
|
||
//TODO: add implementation for serialization & save callbacks. | ||
} | ||
|
||
void HandleLoadGlobalData() | ||
{ | ||
_MESSAGE("LoadGlobalData"); | ||
|
||
//TODO: add implementation for deserialization & load callbacks. | ||
} | ||
|
||
void HandleDeleteSave(std::string filePath) | ||
{ | ||
//check if old file is gone | ||
FileStream saveFile; | ||
if (!saveFile.open(filePath.c_str())) | ||
{ | ||
RemoveFileExtension(filePath); | ||
filePath += ".sfse"; | ||
_MESSAGE("deleting co-save %s", filePath.c_str()); | ||
DeleteFile(filePath.c_str()); | ||
} | ||
else | ||
{ | ||
_MESSAGE("skipped delete of co-save for file %s", filePath.c_str()); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
#pragma once | ||
#include <string> | ||
#include "sfse_common/Types.h" | ||
|
||
namespace Serialization | ||
{ | ||
void SetSaveName(const char* name, bool hasExtension = false); | ||
void HandleBeginLoad(); | ||
void HandleEndLoad(); | ||
bool ResolveFormId(u32 formId, u32* formIdOut); | ||
bool ResolveHandle(u64 handle, u64* handleOut); | ||
void HandleRevertGlobalData(); | ||
void HandleSaveGlobalData(); | ||
void HandleLoadGlobalData(); | ||
|
||
void HandleDeleteSave(std::string filePath); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters