-
Notifications
You must be signed in to change notification settings - Fork 82
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added ext data support for KKS game save files
- Loading branch information
1 parent
3a4d03a
commit 8fb3533
Showing
3 changed files
with
206 additions
and
0 deletions.
There are no files selected for viewing
102 changes: 102 additions & 0 deletions
102
src/KKS_ExtensibleSaveFormat/KKS.ExtendedSave.SaveData.Hooks.cs
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,102 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Reflection.Emit; | ||
using BepisPlugins; | ||
using HarmonyLib; | ||
using MessagePack; | ||
using SaveData; | ||
using UnityEngine; | ||
|
||
namespace ExtensibleSaveFormat | ||
{ | ||
public partial class ExtendedSave | ||
{ | ||
internal static partial class Hooks | ||
{ | ||
#region Loading | ||
|
||
[HarmonyPostfix, HarmonyPatch(typeof(WorldData), nameof(WorldData.SetBytes))] | ||
private static void SaveDataLoadHook(BinaryReader br, ref WorldData saveData) | ||
{ | ||
try | ||
{ | ||
string marker = br.ReadString(); | ||
int version = br.ReadInt32(); | ||
|
||
int length = br.ReadInt32(); | ||
|
||
if (marker == Marker && version == DataVersion && length > 0) | ||
{ | ||
byte[] bytes = br.ReadBytes(length); | ||
var dictionary = MessagePackSerializer.Deserialize<Dictionary<string, PluginData>>(bytes); | ||
|
||
internalSaveDataDictionary.Set(saveData, dictionary); | ||
} | ||
else | ||
internalSaveDataDictionary.Set(saveData, new Dictionary<string, PluginData>()); //Overriding with empty data just in case there is some remnant from former loads. | ||
|
||
} | ||
catch (EndOfStreamException) | ||
{ | ||
// Incomplete/non-existant data | ||
internalSaveDataDictionary.Set(saveData, new Dictionary<string, PluginData>()); | ||
} | ||
catch (InvalidOperationException) | ||
{ | ||
// Invalid/unexpected deserialized data | ||
internalSaveDataDictionary.Set(saveData, new Dictionary<string, PluginData>()); | ||
} | ||
|
||
//Firing the event in any case | ||
SaveDataReadEvent(saveData); | ||
} | ||
|
||
#endregion | ||
|
||
#region Saving | ||
|
||
// Nope, not patching the save lambda, not doing it, noooope | ||
[HarmonyPostfix, HarmonyPatch(typeof(WorldData), nameof(WorldData.GetBytes), typeof(WorldData))] | ||
private static void SaveDataSaveHook(WorldData saveData, ref byte[] __result) | ||
{ | ||
try | ||
{ | ||
SaveDataWriteEvent(saveData); | ||
|
||
Logger.Log(BepInEx.Logging.LogLevel.Debug, "SaveData hook!"); | ||
|
||
Dictionary<string, PluginData> extendedData = GetAllExtendedData(saveData); | ||
if (extendedData == null) | ||
return; | ||
|
||
// Append the ext data | ||
using (var ms = new MemoryStream()) | ||
{ | ||
using (BinaryWriter bw = new BinaryWriter(ms)) | ||
{ | ||
// Write the original data first | ||
bw.Write(__result); | ||
|
||
// Then write our data | ||
bw.Write(Marker); | ||
bw.Write(DataVersion); | ||
byte[] data = MessagePackSerializer.Serialize(extendedData); | ||
bw.Write(data.Length); | ||
bw.Write(data); | ||
} | ||
|
||
// Replace the result, not the most efficient way but save files shouldn't be big enough to matter since it's all done in memory | ||
__result = ms.ToArray(); | ||
} | ||
} | ||
catch (Exception ex) | ||
{ | ||
UnityEngine.Debug.LogException(ex); | ||
} | ||
} | ||
|
||
#endregion | ||
} | ||
} | ||
} |
102 changes: 102 additions & 0 deletions
102
src/KKS_ExtensibleSaveFormat/KKS.ExtendedSave.SaveData.cs
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,102 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using BepInEx; | ||
using BepisPlugins; | ||
using SaveData; | ||
|
||
namespace ExtensibleSaveFormat | ||
{ | ||
public partial class ExtendedSave : BaseUnityPlugin | ||
{ | ||
internal static WeakKeyDictionary<WorldData, Dictionary<string, PluginData>> internalSaveDataDictionary = new WeakKeyDictionary<WorldData, Dictionary<string, PluginData>>(); | ||
|
||
/// <summary> | ||
/// Get a dictionary of ID, PluginData containing all extended data for a SaveData | ||
/// </summary> | ||
/// <param name="saveData">SaveData for which to get extended data</param> | ||
/// <returns>Dictionary of ID, PluginData</returns> | ||
public static Dictionary<string, PluginData> GetAllExtendedData(SaveData.WorldData saveData) => internalSaveDataDictionary.Get(saveData); | ||
|
||
/// <summary> | ||
/// Get PluginData for a SaveData for the specified extended save data ID | ||
/// </summary> | ||
/// <param name="saveData">SaveData for which to get extended save file</param> | ||
/// <param name="id">ID of the data saved to the save file</param> | ||
/// <returns>PluginData</returns> | ||
public static PluginData GetExtendedDataById(WorldData saveData, string id) | ||
{ | ||
if (saveData == null || id == null) | ||
return null; | ||
|
||
var dict = internalSaveDataDictionary.Get(saveData); | ||
|
||
return dict != null && dict.TryGetValue(id, out var extendedSection) ? extendedSection : null; | ||
} | ||
|
||
/// <summary> | ||
/// Set PluginData for a SaveData for the specified extended save data ID | ||
/// </summary> | ||
/// <param name="saveData">SaveData for which to set extended data</param> | ||
/// <param name="id">ID of the data to be saved to the save file</param> | ||
/// <param name="extendedFormatData">PluginData to save to the save file</param> | ||
public static void SetExtendedDataById(WorldData saveData, string id, PluginData extendedFormatData) | ||
{ | ||
Dictionary<string, PluginData> chaDictionary = internalSaveDataDictionary.Get(saveData); | ||
|
||
if (chaDictionary == null) | ||
{ | ||
chaDictionary = new Dictionary<string, PluginData>(); | ||
internalSaveDataDictionary.Set(saveData, chaDictionary); | ||
} | ||
|
||
chaDictionary[id] = extendedFormatData; | ||
} | ||
|
||
|
||
/// <summary> SaveData event handler </summary> | ||
public delegate void SaveDataEventHandler(WorldData saveData); | ||
/// <summary> Register methods to trigger on save file being saved </summary> | ||
public static event SaveDataEventHandler SaveDataBeingSaved; | ||
/// <summary> Register methods to trigger on save file being loaded </summary> | ||
public static event SaveDataEventHandler SaveDataBeingLoaded; | ||
|
||
|
||
internal static void SaveDataWriteEvent(WorldData saveData) | ||
{ | ||
if (SaveDataBeingSaved == null) | ||
return; | ||
|
||
foreach (var entry in SaveDataBeingSaved.GetInvocationList()) | ||
{ | ||
var handler = (SaveDataEventHandler)entry; | ||
try | ||
{ | ||
handler.Invoke(saveData); | ||
} | ||
catch (Exception ex) | ||
{ | ||
Logger.LogError($"Subscriber crash in {nameof(ExtendedSave)}.{nameof(SaveDataBeingSaved)} - {ex}"); | ||
} | ||
} | ||
} | ||
|
||
internal static void SaveDataReadEvent(WorldData saveData) | ||
{ | ||
if (SaveDataBeingLoaded == null) | ||
return; | ||
|
||
foreach (var entry in SaveDataBeingLoaded.GetInvocationList()) | ||
{ | ||
var handler = (SaveDataEventHandler)entry; | ||
try | ||
{ | ||
handler.Invoke(saveData); | ||
} | ||
catch (Exception ex) | ||
{ | ||
Logger.LogError($"Subscriber crash in {nameof(ExtendedSave)}.{nameof(SaveDataBeingLoaded)} - {ex}"); | ||
} | ||
} | ||
} | ||
} | ||
} |
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