1
0
mirror of https://github.com/soarqin/DSP_Mods.git synced 2025-12-09 10:13:31 +08:00

cleanup codes

This commit is contained in:
2023-09-26 16:08:52 +08:00
parent 18fca87813
commit 6311e3b9fb
4 changed files with 239 additions and 259 deletions

View File

@@ -19,31 +19,31 @@ public enum CompressionType
[BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)] [BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)]
public class CompressSave : BaseUnityPlugin public class CompressSave : BaseUnityPlugin
{ {
private Harmony patchSave, patchUISave, patchUILoad; private Harmony _patchSave, _patchUISave, _patchUILoad;
string StringFromCompresstionType(CompressionType type) private static string StringFromCompresstionType(CompressionType type)
{ {
switch (type) return type switch
{ {
case CompressionType.LZ4: return "lz4"; CompressionType.LZ4 => "lz4",
case CompressionType.Zstd: return "zstd"; CompressionType.Zstd => "zstd",
case CompressionType.None: return "none"; CompressionType.None => "none",
default: throw new ArgumentException("Unknown compression type."); _ => throw new ArgumentException("Unknown compression type.")
} };
} }
CompressionType CompressionTypeFromString(string str) private static CompressionType CompressionTypeFromString(string str)
{ {
switch (str) return str switch
{ {
case "lz4": return CompressionType.LZ4; "lz4" => CompressionType.LZ4,
case "zstd": return CompressionType.Zstd; "zstd" => CompressionType.Zstd,
default: return CompressionType.None; _ => CompressionType.None
} };
} }
public void Awake() public void Awake()
{ {
SaveUtil.logger = Logger; SaveUtil.Logger = Logger;
if (LZ4API.Avaliable && ZstdAPI.Avaliable) if (LZ4API.Avaliable && ZstdAPI.Avaliable)
{ {
PatchSave.CompressionTypeForSaves = CompressionTypeFromString( PatchSave.CompressionTypeForSaves = CompressionTypeFromString(
@@ -68,36 +68,36 @@ public class CompressSave : BaseUnityPlugin
PatchSave.CreateCompressBuffer(); PatchSave.CreateCompressBuffer();
if (GameConfig.gameVersion != SaveUtil.VerifiedVersion) if (GameConfig.gameVersion != SaveUtil.VerifiedVersion)
{ {
SaveUtil.logger.LogWarning( SaveUtil.Logger.LogWarning(
$"Save version mismatch. Expect:{SaveUtil.VerifiedVersion}, Current:{GameConfig.gameVersion}. MOD may not work as expected."); $"Save version mismatch. Expect:{SaveUtil.VerifiedVersion}, Current:{GameConfig.gameVersion}. MOD may not work as expected.");
} }
patchSave = Harmony.CreateAndPatchAll(typeof(PatchSave)); _patchSave = Harmony.CreateAndPatchAll(typeof(PatchSave));
if (PatchSave.EnableCompress) if (PatchSave.EnableCompress)
patchUISave = Harmony.CreateAndPatchAll(typeof(PatchUISaveGame)); _patchUISave = Harmony.CreateAndPatchAll(typeof(PatchUISaveGame));
patchUILoad = Harmony.CreateAndPatchAll(typeof(PatchUILoadGame)); _patchUILoad = Harmony.CreateAndPatchAll(typeof(PatchUILoadGame));
} }
else else
SaveUtil.logger.LogWarning("Either lz4warp.dll or zstdwrap.dll is not avaliable."); SaveUtil.Logger.LogWarning("Either lz4warp.dll or zstdwrap.dll is not avaliable.");
} }
public void OnDestroy() public void OnDestroy()
{ {
if (patchUISave != null) if (_patchUISave != null)
{ {
PatchUISaveGame.OnDestroy(); PatchUISaveGame.OnDestroy();
patchUISave.UnpatchSelf(); _patchUISave.UnpatchSelf();
} }
if (patchUILoad != null) if (_patchUILoad != null)
{ {
PatchUILoadGame.OnDestroy(); PatchUILoadGame.OnDestroy();
patchUILoad.UnpatchSelf(); _patchUILoad.UnpatchSelf();
} }
patchSave?.UnpatchSelf(); _patchSave?.UnpatchSelf();
} }
} }
class PatchSave public class PatchSave
{ {
public static readonly WrapperDefines LZ4Wrapper = new LZ4API(), ZstdWrapper = new ZstdAPI(); public static readonly WrapperDefines LZ4Wrapper = new LZ4API(), ZstdWrapper = new ZstdAPI();
private static readonly WrapperDefines NoneWrapper = new NoneAPI(); private static readonly WrapperDefines NoneWrapper = new NoneAPI();
@@ -105,7 +105,7 @@ class PatchSave
public static bool UseCompressSave; public static bool UseCompressSave;
private static CompressionType _compressionTypeForLoading = CompressionType.None; private static CompressionType _compressionTypeForLoading = CompressionType.None;
private static CompressionType _compressionTypeForSaving = CompressionType.Zstd; private static CompressionType _compressionTypeForSaving = CompressionType.Zstd;
private static int _compressionLevelForSaving = 0; private static int _compressionLevelForSaving;
public static CompressionType CompressionTypeForSaves = CompressionType.Zstd; public static CompressionType CompressionTypeForSaves = CompressionType.Zstd;
public static CompressionType CompressionTypeForAutoSaves = CompressionType.Zstd; public static CompressionType CompressionTypeForAutoSaves = CompressionType.Zstd;
public static int CompressionLevelForSaves; public static int CompressionLevelForSaves;
@@ -116,7 +116,7 @@ class PatchSave
public static void CreateCompressBuffer() public static void CreateCompressBuffer()
{ {
var bufSize = CompressionStream.MB; const int bufSize = CompressionStream.MB;
var outBufSize = LZ4Wrapper.CompressBufferBound(bufSize); var outBufSize = LZ4Wrapper.CompressBufferBound(bufSize);
outBufSize = Math.Max(outBufSize, ZstdWrapper.CompressBufferBound(bufSize)); outBufSize = Math.Max(outBufSize, ZstdWrapper.CompressBufferBound(bufSize));
outBufSize = Math.Max(outBufSize, NoneWrapper.CompressBufferBound(bufSize)); outBufSize = Math.Max(outBufSize, NoneWrapper.CompressBufferBound(bufSize));
@@ -127,6 +127,7 @@ class PatchSave
private static void WriteHeader(FileStream fileStream) private static void WriteHeader(FileStream fileStream)
{ {
if (fileStream == null) throw new ArgumentNullException(nameof(fileStream));
switch (_compressionTypeForSaving) switch (_compressionTypeForSaving)
{ {
case CompressionType.Zstd: case CompressionType.Zstd:
@@ -148,59 +149,57 @@ class PatchSave
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(typeof(GameSave), "AutoSave")] [HarmonyPatch(typeof(GameSave), "AutoSave")]
[HarmonyPatch(typeof(GameSave), "SaveAsLastExit")] [HarmonyPatch(typeof(GameSave), "SaveAsLastExit")]
static void BeforeAutoSave() private static void BeforeAutoSave()
{ {
UseCompressSave = EnableForAutoSaves && EnableCompress; UseCompressSave = EnableForAutoSaves && EnableCompress;
if (UseCompressSave) if (!UseCompressSave) return;
{
_compressionTypeForSaving = CompressionTypeForAutoSaves; _compressionTypeForSaving = CompressionTypeForAutoSaves;
_compressionLevelForSaving = CompressionLevelForAutoSaves; _compressionLevelForSaving = CompressionLevelForAutoSaves;
} }
}
[HarmonyTranspiler] [HarmonyTranspiler]
[HarmonyPatch(typeof(GameSave), "SaveCurrentGame")] [HarmonyPatch(typeof(GameSave), "SaveCurrentGame")]
static IEnumerable<CodeInstruction> SaveCurrentGame_Transpiler(IEnumerable<CodeInstruction> instructions, private static IEnumerable<CodeInstruction> SaveCurrentGame_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
ILGenerator generator)
{ {
/* BinaryWriter binaryWriter = new BinaryWriter(fileStream); => Create compressionStream and replace binaryWriter. /* BinaryWriter binaryWriter = new BinaryWriter(fileStream); => Create compressionStream and replace binaryWriter.
* set PerformanceMonitor.BeginStream to compressionStream. * set PerformanceMonitor.BeginStream to compressionStream.
* fileStream.Seek(6L, SeekOrigin.Begin); binaryWriter.Write(position); => Disable seek&write function. * fileStream.Seek(6L, SeekOrigin.Begin); binaryWriter.Write(position); => Disable seek&write function.
* binaryWriter.Dispose(); => Dispose compressionStream before fileStream close. * binaryWriter.Dispose(); => Dispose compressionStream before fileStream close.
*/ */
var matcher = new CodeMatcher(instructions, generator);
try try
{ {
var matcher = new CodeMatcher(instructions, generator) matcher.MatchForward(false,
.MatchForward(false, new CodeMatch(OpCodes.Newobj, AccessTools.Constructor(typeof(BinaryWriter), new [] { typeof(FileStream) }))
new CodeMatch(OpCodes.Newobj, ).Set(
AccessTools.Constructor(typeof(BinaryWriter), new Type[] { typeof(FileStream) }))) OpCodes.Call, AccessTools.Method(typeof(PatchSave), "CreateBinaryWriter")
.Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "CreateBinaryWriter")) ).MatchForward(false,
.MatchForward(false, new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(PerformanceMonitor), "BeginStream"))
new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(PerformanceMonitor), "BeginStream"))) ).Set(
.Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "MonitorStream")) OpCodes.Call, AccessTools.Method(typeof(PatchSave), "MonitorStream")
.MatchForward(false, ).MatchForward(false,
new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(System.IO.Stream), "Seek"))) new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(Stream), "Seek"))
.Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "FileLengthWrite0")) ).Set(
.MatchForward(false, OpCodes.Call, AccessTools.Method(typeof(PatchSave), "FileLengthWrite0")
new CodeMatch(OpCodes.Callvirt, ).MatchForward(false,
AccessTools.Method(typeof(BinaryWriter), "Write", new Type[] { typeof(long) }))) new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(BinaryWriter), "Write", new [] { typeof(long) }))
.Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "FileLengthWrite1")) ).Set(
.MatchForward(false, OpCodes.Call, AccessTools.Method(typeof(PatchSave), "FileLengthWrite1")
new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(System.IDisposable), "Dispose"))) ).MatchForward(false,
.Advance(1) new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(IDisposable), "Dispose"))
.Insert(new CodeInstruction(OpCodes.Call, ).Advance(1).Insert(
AccessTools.Method(typeof(PatchSave), "DisposeCompressionStream"))); new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "DisposeCompressionStream"))
);
EnableCompress = true; EnableCompress = true;
return matcher.InstructionEnumeration(); return matcher.InstructionEnumeration();
} }
catch (Exception ex) catch (Exception ex)
{ {
SaveUtil.logger.LogError( SaveUtil.Logger.LogError(
"SaveCurrentGame_Transpiler failed. Mod version not compatible with game version."); "SaveCurrentGame_Transpiler failed. Mod version not compatible with game version.");
SaveUtil.logger.LogError(ex); SaveUtil.Logger.LogError(ex);
} }
return matcher.InstructionEnumeration();
return instructions;
} }
public static void MonitorStream(Stream fileStream) public static void MonitorStream(Stream fileStream)
@@ -212,44 +211,33 @@ class PatchSave
{ {
if (UseCompressSave) if (UseCompressSave)
{ {
SaveUtil.logger.LogDebug("Begin compress save"); SaveUtil.Logger.LogDebug("Begin compress save");
WriteHeader(fileStream); WriteHeader(fileStream);
switch (_compressionTypeForSaving) _compressionStream = _compressionTypeForSaving switch
{ {
case CompressionType.LZ4: CompressionType.LZ4 => new CompressionStream(LZ4Wrapper, _compressionLevelForSaving, fileStream, _compressBuffer, true),
_compressionStream = new CompressionStream(LZ4Wrapper, _compressionLevelForSaving, fileStream, _compressBuffer, true); CompressionType.Zstd => new CompressionStream(ZstdWrapper, _compressionLevelForSaving, fileStream, _compressBuffer, true),
break; CompressionType.None => new CompressionStream(NoneWrapper, 0, fileStream, _compressBuffer, true),
case CompressionType.Zstd: _ => _compressionStream
_compressionStream = new CompressionStream(ZstdWrapper, _compressionLevelForSaving, fileStream, _compressBuffer, true); };
break;
case CompressionType.None:
_compressionStream = new CompressionStream(NoneWrapper, 0, fileStream, _compressBuffer, true);
break;
}
return ((CompressionStream)_compressionStream).BufferWriter; return ((CompressionStream)_compressionStream).BufferWriter;
} }
SaveUtil.logger.LogDebug("Begin normal save"); SaveUtil.Logger.LogDebug("Begin normal save");
return new BinaryWriter(fileStream); return new BinaryWriter(fileStream);
} }
public static long FileLengthWrite0(FileStream fileStream, long offset, SeekOrigin origin) public static long FileLengthWrite0(FileStream fileStream, long offset, SeekOrigin origin)
{ {
if (!UseCompressSave) return UseCompressSave ? 0L : fileStream.Seek(offset, origin);
{
return fileStream.Seek(offset, origin);
}
return 0L;
} }
public static void FileLengthWrite1(BinaryWriter binaryWriter, long value) public static void FileLengthWrite1(BinaryWriter binaryWriter, long value)
{ {
if (!UseCompressSave) if (UseCompressSave) return;
{
binaryWriter.Write(value); binaryWriter.Write(value);
} }
}
public static void DisposeCompressionStream() public static void DisposeCompressionStream()
{ {
@@ -265,10 +253,11 @@ class PatchSave
{ {
stream = ((CompressionStream)_compressionStream).outStream; stream = ((CompressionStream)_compressionStream).outStream;
} }
_compressionStream.Dispose(); //Dispose need to be done before fstream closed. // Dispose need to be done before fstream closed.
_compressionStream.Dispose();
_compressionStream = null; _compressionStream = null;
if (writeflag) //Reset UseCompressSave after writing to file if (!writeflag) return;
{ // Reset UseCompressSave after writing to file
if (stream != null) if (stream != null)
{ {
// Ugly implementation, but it works. May find a better solution someday. // Ugly implementation, but it works. May find a better solution someday.
@@ -282,7 +271,6 @@ class PatchSave
_compressionLevelForSaving = CompressionLevelForSaves; _compressionLevelForSaving = CompressionLevelForSaves;
UseCompressSave = false; UseCompressSave = false;
} }
}
[HarmonyTranspiler] [HarmonyTranspiler]
[HarmonyPatch(typeof(GameSave), "LoadCurrentGame")] [HarmonyPatch(typeof(GameSave), "LoadCurrentGame")]
@@ -290,8 +278,7 @@ class PatchSave
[HarmonyPatch(typeof(GameSave), "ReadHeader")] [HarmonyPatch(typeof(GameSave), "ReadHeader")]
[HarmonyPatch(typeof(GameSave), "ReadHeaderAndDescAndProperty")] [HarmonyPatch(typeof(GameSave), "ReadHeaderAndDescAndProperty")]
[HarmonyPatch(typeof(GameSave), "ReadModes")] [HarmonyPatch(typeof(GameSave), "ReadModes")]
static IEnumerable<CodeInstruction> LoadCurrentGame_Transpiler(IEnumerable<CodeInstruction> instructions, private static IEnumerable<CodeInstruction> LoadCurrentGame_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
ILGenerator iLGenerator)
{ {
/* using (BinaryReader binaryReader = new BinaryReader(fileStream)) => Create decompressionStream and replace binaryReader. /* using (BinaryReader binaryReader = new BinaryReader(fileStream)) => Create decompressionStream and replace binaryReader.
* set PerformanceMonitor.BeginStream to decompressionStream. * set PerformanceMonitor.BeginStream to decompressionStream.
@@ -299,29 +286,31 @@ class PatchSave
* fileStream.Seek((long)num2, SeekOrigin.Current); => Use decompressionStream.Read to seek forward * fileStream.Seek((long)num2, SeekOrigin.Current); => Use decompressionStream.Read to seek forward
* binaryReader.Dispose(); => Dispose decompressionStream before fileStream close. * binaryReader.Dispose(); => Dispose decompressionStream before fileStream close.
*/ */
var matcher = new CodeMatcher(instructions, generator);
try try
{ {
var matcher = new CodeMatcher(instructions, iLGenerator) matcher.MatchForward(false,
.MatchForward(false, new CodeMatch(OpCodes.Newobj, AccessTools.Constructor(typeof(BinaryReader), new [] { typeof(FileStream) }))
new CodeMatch(OpCodes.Newobj, ).Set(
AccessTools.Constructor(typeof(BinaryReader), new Type[] { typeof(FileStream) }))) OpCodes.Call, AccessTools.Method(typeof(PatchSave), "CreateBinaryReader")
.Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "CreateBinaryReader")) ).MatchForward(false,
.MatchForward(false, new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(PerformanceMonitor), "BeginStream"))
new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(PerformanceMonitor), "BeginStream"))); );
if (matcher.IsValid) if (matcher.IsValid)
matcher.Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "MonitorStream")); matcher.Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "MonitorStream"));
matcher.Start().MatchForward(false, matcher.Start().MatchForward(false,
new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(BinaryReader), "ReadInt64"))) new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(BinaryReader), "ReadInt64"))
.Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "FileLengthRead")) ).Set(
.MatchForward(false, OpCodes.Call, AccessTools.Method(typeof(PatchSave), "FileLengthRead")
new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(System.IDisposable), "Dispose"))) ).MatchForward(false,
.Advance(1) new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(IDisposable), "Dispose"))
.Insert(new CodeInstruction(OpCodes.Call, ).Advance(1).Insert(
AccessTools.Method(typeof(PatchSave), "DisposeCompressionStream"))) new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "DisposeCompressionStream"))
.MatchBack(false, ).MatchBack(false,
new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(System.IO.Stream), "Seek"))); new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(Stream), "Seek"))
);
if (matcher.IsValid) if (matcher.IsValid)
matcher.Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "ReadSeek")); matcher.Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "ReadSeek"));
@@ -329,12 +318,12 @@ class PatchSave
} }
catch (Exception ex) catch (Exception ex)
{ {
SaveUtil.logger.LogError( SaveUtil.Logger.LogError(
"LoadCurrentGame_Transpiler failed. Mod version not compatible with game version."); "LoadCurrentGame_Transpiler failed. Mod version not compatible with game version.");
SaveUtil.logger.LogError(ex); SaveUtil.Logger.LogError(ex);
} }
return instructions; return matcher.InstructionEnumeration();
} }
public static BinaryReader CreateBinaryReader(FileStream fileStream) public static BinaryReader CreateBinaryReader(FileStream fileStream)

View File

@@ -1,4 +1,3 @@
using System.Collections.Generic;
using System.Linq; using System.Linq;
using HarmonyLib; using HarmonyLib;
using UnityEngine; using UnityEngine;
@@ -8,44 +7,42 @@ namespace CompressSave;
class PatchUILoadGame class PatchUILoadGame
{ {
static UIButton decompressButton; static UIButton _decompressButton;
[HarmonyPatch(typeof(UILoadGameWindow), "OnSelectedChange"), HarmonyPostfix] [HarmonyPatch(typeof(UILoadGameWindow), "OnSelectedChange"), HarmonyPostfix]
static void OnSelectedChange(UILoadGameWindow __instance, Text ___prop3Text) private static void OnSelectedChange(UILoadGameWindow __instance)
{ {
var compressedType = SaveUtil.SaveGetCompressType(__instance.selected?.saveName); var selected = __instance.selected;
switch (compressedType) var compressedType = SaveUtil.SaveGetCompressType(selected == null ? null : selected.saveName);
var prop3Text = __instance.prop3Text;
prop3Text.text = compressedType switch
{ {
case CompressionType.LZ4: CompressionType.LZ4 => "(LZ4)" + prop3Text.text,
___prop3Text.text = "(LZ4)" + ___prop3Text.text; CompressionType.Zstd => "(ZSTD)" + prop3Text.text,
break; _ => "(N)" + prop3Text.text
case CompressionType.Zstd: };
___prop3Text.text = "(ZSTD)" + ___prop3Text.text; if (!_decompressButton) return;
break; _decompressButton.button.interactable = compressedType != CompressionType.None;
default: _decompressButton.gameObject.SetActive(compressedType != CompressionType.None);
___prop3Text.text = "(N)" + ___prop3Text.text;
break;
}
if (!decompressButton) return;
decompressButton.button.interactable = compressedType != CompressionType.None;
decompressButton.gameObject.SetActive(compressedType != CompressionType.None);
} }
[HarmonyPatch(typeof(UILoadGameWindow), "_OnOpen"), HarmonyPostfix] [HarmonyPatch(typeof(UILoadGameWindow), "_OnOpen"), HarmonyPostfix]
static void _OnOpen(UILoadGameWindow __instance, UIButton ___loadButton, GameObject ___loadSandboxGroup, List<UIGameSaveEntry> ___entries) static void _OnOpen(UILoadGameWindow __instance)
{ {
if (!decompressButton) if (_decompressButton) return;
{ var loadButton = __instance.loadButton;
decompressButton = ___loadButton;
decompressButton = (__instance.transform.Find("button-decompress")?.gameObject ?? GameObject.Instantiate(___loadButton.gameObject, ___loadButton.transform.parent)).GetComponent<UIButton>(); var gameObj = __instance.transform.Find("button-decompress")?.gameObject;
if (gameObj == null)
gameObj = Object.Instantiate(loadButton.gameObject, loadButton.transform.parent);
_decompressButton = gameObj.GetComponent<UIButton>();
___loadSandboxGroup.transform.Translate(new Vector3(-2.5f, 0, 0)); __instance.loadSandboxGroup.transform.Translate(new Vector3(-2.5f, 0, 0));
decompressButton.gameObject.name = "button-decompress"; _decompressButton.gameObject.name = "button-decompress";
decompressButton.transform.Translate(new Vector3(-2.0f, 0, 0)); _decompressButton.transform.Translate(new Vector3(-2.0f, 0, 0));
decompressButton.button.image.color = new Color32(0, 0xf4, 0x92, 0x77); _decompressButton.button.image.color = new Color32(0, 0xf4, 0x92, 0x77);
var localizer = decompressButton.transform.Find("button-text")?.GetComponent<Localizer>(); var localizer = _decompressButton.transform.Find("button-text")?.GetComponent<Localizer>();
var text = decompressButton.transform.Find("button-text")?.GetComponent<Text>(); var text = _decompressButton.transform.Find("button-text")?.GetComponent<Text>();
if (localizer) if (localizer)
{ {
@@ -55,22 +52,20 @@ class PatchUILoadGame
if (text) if (text)
text.text = "Decompress".Translate(); text.text = "Decompress".Translate();
decompressButton.onClick += _ =>{ _decompressButton.onClick += _ =>
if(SaveUtil.DecompressSave(__instance.selected.saveName, out var newfileName))
{ {
if (!SaveUtil.DecompressSave(__instance.selected.saveName, out var newfileName)) return;
__instance.RefreshList(); __instance.RefreshList();
__instance.selected = ___entries.First(e => e.saveName == newfileName); __instance.selected = __instance.entries.First(e => e.saveName == newfileName);
}
}; };
decompressButton.button.interactable = false; _decompressButton.button.interactable = false;
decompressButton.gameObject.SetActive(false); _decompressButton.gameObject.SetActive(false);
}
} }
public static void OnDestroy() public static void OnDestroy()
{ {
if (decompressButton) if (_decompressButton)
GameObject.Destroy(decompressButton.gameObject); Object.Destroy(_decompressButton.gameObject);
decompressButton = null; _decompressButton = null;
} }
} }

View File

@@ -6,104 +6,100 @@ namespace CompressSave;
static class PatchUISaveGame static class PatchUISaveGame
{ {
[HarmonyPatch(typeof(UISaveGameWindow), "OnSelectedChange"), HarmonyPostfix] public static void OnDestroy()
static void OnSelectedChange(UISaveGameWindow __instance, Text ___prop3Text)
{ {
var compressedType = SaveUtil.SaveGetCompressType(__instance.selected?.saveName); if (_context.ButtonCompress)
switch (compressedType) Object.Destroy(_context.ButtonCompress.gameObject);
if (_context.Window)
{ {
case CompressionType.LZ4: _context.SaveButton.onClick -= WrapClick;
___prop3Text.text = "(LZ4)" + ___prop3Text.text; _context.SaveButton.onClick += _context.Window.OnSaveClick;
break;
case CompressionType.Zstd:
___prop3Text.text = "(ZSTD)" + ___prop3Text.text;
break;
default:
___prop3Text.text = "(N)" + ___prop3Text.text;
break;
} }
_OnDestroy();
}
[HarmonyPatch(typeof(UISaveGameWindow), "OnSelectedChange"), HarmonyPostfix]
static void OnSelectedChange(UISaveGameWindow __instance)
{
var selected = __instance.selected;
var compressedType = SaveUtil.SaveGetCompressType(selected == null ? null : selected.saveName);
var prop3Text = __instance.prop3Text;
prop3Text.text = compressedType switch
{
CompressionType.LZ4 => "(LZ4)" + prop3Text.text,
CompressionType.Zstd => "(ZSTD)" + prop3Text.text,
_ => "(N)" + prop3Text.text
};
} }
[HarmonyPatch(typeof(UISaveGameWindow), "_OnDestroy"), HarmonyPostfix] [HarmonyPatch(typeof(UISaveGameWindow), "_OnDestroy"), HarmonyPostfix]
static void _OnDestroy() private static void _OnDestroy()
{ {
//Console.WriteLine("OnCreate"); //Console.WriteLine("OnCreate");
context = new UIContext(); _context = new UIContext();
} }
[HarmonyPatch(typeof(UISaveGameWindow), "OnSaveClick"), HarmonyReversePatch] [HarmonyPatch(typeof(UISaveGameWindow), "OnSaveClick"), HarmonyReversePatch]
static void OSaveGameAs(this UISaveGameWindow ui, int data) { } private static void OSaveGameAs(this UISaveGameWindow ui, int data) { }
[HarmonyPatch(typeof(UISaveGameWindow), "CheckAndSetSaveButtonEnable"), HarmonyPostfix] [HarmonyPatch(typeof(UISaveGameWindow), "CheckAndSetSaveButtonEnable"), HarmonyPostfix]
static void CheckAndSetSaveButtonEnable(UISaveGameWindow __instance, UIButton ___saveButton, Text ___saveButtonText) private static void CheckAndSetSaveButtonEnable(UISaveGameWindow __instance)
{ {
_OnOpen(__instance, ___saveButton, ___saveButtonText); _OnOpen(__instance);
if (context.saveButtonText && context.saveButton) if (_context.SaveButtonText && _context.SaveButton)
SetButtonState(context.saveButtonText.text, context.saveButton.button.interactable); SetButtonState(_context.SaveButtonText.text, _context.SaveButton.button.interactable);
} }
static void SetButtonState(string text, bool interactable) private static void SetButtonState(string text, bool interactable)
{ {
context.buttonCompress.button.interactable = interactable; _context.ButtonCompress.button.interactable = interactable;
context.buttonCompressText.text = text; _context.ButtonCompressText.text = text;
} }
class UIContext private class UIContext
{ {
public UIButton buttonCompress; public UIButton ButtonCompress;
public UIButton saveButton; public UIButton SaveButton;
public Text buttonCompressText; public Text ButtonCompressText;
public Text saveButtonText; public Text SaveButtonText;
public UISaveGameWindow ui; public UISaveGameWindow Window;
} }
[HarmonyPatch(typeof(UISaveGameWindow), "OnSaveClick"), HarmonyPrefix] [HarmonyPatch(typeof(UISaveGameWindow), "OnSaveClick"), HarmonyPrefix]
static void OnSaveClick() private static void OnSaveClick()
{ {
PatchSave.UseCompressSave = true; PatchSave.UseCompressSave = true;
} }
static UIContext context = new UIContext(); private static UIContext _context = new UIContext();
[HarmonyPatch(typeof(UISaveGameWindow), "_OnOpen"), HarmonyPostfix] [HarmonyPatch(typeof(UISaveGameWindow), "_OnOpen"), HarmonyPostfix]
static void _OnOpen(UISaveGameWindow __instance, UIButton ___saveButton, Text ___saveButtonText) private static void _OnOpen(UISaveGameWindow __instance)
{ {
if (context.buttonCompress) return; if (_context.ButtonCompress) return;
context.saveButton = ___saveButton; _context.SaveButton = __instance.saveButton;
context.saveButtonText = ___saveButtonText; _context.SaveButtonText = __instance.saveButtonText;
context.ui = __instance; _context.Window = __instance;
context.buttonCompress = var gameObj = __instance.transform.Find("button-compress")?.gameObject;
(__instance.transform.Find("button-compress")?.gameObject ?? if (gameObj == null)
GameObject.Instantiate(___saveButton.gameObject, ___saveButton.transform.parent)) gameObj = Object.Instantiate(__instance.saveButton.gameObject, __instance.saveButton.transform.parent);
.GetComponent<UIButton>(); _context.ButtonCompress = gameObj.GetComponent<UIButton>();
context.buttonCompress.gameObject.name = "button-compress"; _context.ButtonCompress.gameObject.name = "button-compress";
context.buttonCompress.transform.Translate(new Vector3(-2.0f, 0, 0)); _context.ButtonCompress.transform.Translate(new Vector3(-2.0f, 0, 0));
context.buttonCompress.button.image.color = new Color32(0xfc, 0x6f, 00, 0x77); _context.ButtonCompress.button.image.color = new Color32(0xfc, 0x6f, 00, 0x77);
context.buttonCompressText = context.buttonCompress.transform.Find("button-text")?.GetComponent<Text>(); _context.ButtonCompressText = _context.ButtonCompress.transform.Find("button-text")?.GetComponent<Text>();
context.buttonCompress.onClick += __instance.OnSaveClick; _context.ButtonCompress.onClick += __instance.OnSaveClick;
context.saveButton.onClick -= __instance.OnSaveClick; _context.SaveButton.onClick -= __instance.OnSaveClick;
context.saveButton.onClick += WrapClick; _context.SaveButton.onClick += WrapClick;
} }
static void WrapClick(int data) private static void WrapClick(int data)
{ {
PatchSave.UseCompressSave = false; PatchSave.UseCompressSave = false;
context.ui.OSaveGameAs(data); _context.Window.OSaveGameAs(data);
} }
public static void OnDestroy()
{
if (context.buttonCompress)
GameObject.Destroy(context.buttonCompress.gameObject);
if (context.ui)
{
context.saveButton.onClick -= WrapClick;
context.saveButton.onClick += context.ui.OnSaveClick;
}
_OnDestroy();
}
} }

View File

@@ -7,9 +7,9 @@ namespace CompressSave;
public static class SaveUtil public static class SaveUtil
{ {
public static ManualLogSource logger; public static ManualLogSource Logger;
public static readonly Version VerifiedVersion = new Version public static readonly Version VerifiedVersion = new()
{ {
Major = 0, Major = 0,
Minor = 9, Minor = 9,
@@ -19,19 +19,21 @@ public static class SaveUtil
private static string UnzipToFile(DecompressionStream lzStream, string fullPath) private static string UnzipToFile(DecompressionStream lzStream, string fullPath)
{ {
lzStream.ResetStream(); lzStream.ResetStream();
string dir = Path.GetDirectoryName(fullPath); var dir = Path.GetDirectoryName(fullPath);
string filename = "[Recovery]-" + Path.GetFileNameWithoutExtension(fullPath); var filename = "[Recovery]-" + Path.GetFileNameWithoutExtension(fullPath);
fullPath = Path.Combine(dir, filename + GameSave.saveExt); fullPath = filename + GameSave.saveExt;
int i = 0; if (dir != null) fullPath = Path.Combine(dir, fullPath);
var i = 0;
while(File.Exists(fullPath)) while(File.Exists(fullPath))
{ {
fullPath = Path.Combine(dir, $"{filename}[{i++}]{GameSave.saveExt}"); fullPath = $"{filename}[{i++}]{GameSave.saveExt}";
if (dir != null) fullPath = Path.Combine(dir, fullPath);
} }
var buffer = new byte[1024 * 1024]; var buffer = new byte[1024 * 1024];
using (var fs = new FileStream(fullPath, FileMode.Create)) using (var fs = new FileStream(fullPath, FileMode.Create))
using (var br = new BinaryWriter(fs)) using (var br = new BinaryWriter(fs))
{ {
for (int read = lzStream.Read(buffer, 0, buffer.Length); read > 0; read = lzStream.Read(buffer, 0, buffer.Length)) for (var read = lzStream.Read(buffer, 0, buffer.Length); read > 0; read = lzStream.Read(buffer, 0, buffer.Length))
{ {
fs.Write(buffer, 0, read); fs.Write(buffer, 0, read);
} }
@@ -45,11 +47,10 @@ public static class SaveUtil
public static bool DecompressSave(string saveName, out string newSaveName) public static bool DecompressSave(string saveName, out string newSaveName)
{ {
newSaveName = string.Empty; newSaveName = string.Empty;
string path = GameConfig.gameSaveFolder + saveName + GameSave.saveExt; var path = GameConfig.gameSaveFolder + saveName + GameSave.saveExt;
try try
{ {
using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read)) using var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
{
var compressType = SaveGetCompressType(fileStream); var compressType = SaveGetCompressType(fileStream);
switch (compressType) switch (compressType)
{ {
@@ -66,16 +67,15 @@ public static class SaveUtil
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }
} }
}
catch (Exception e) catch (Exception e)
{ {
logger.LogError(e); Logger.LogError(e);
return false; return false;
} }
} }
public static CompressionType SaveGetCompressType(FileStream fs) public static CompressionType SaveGetCompressType(FileStream fs)
{ {
for (int i = 0; i < 3; i++) for (var i = 0; i < 3; i++)
{ {
if (0xCC != fs.ReadByte()) if (0xCC != fs.ReadByte())
return CompressionType.None; return CompressionType.None;
@@ -94,12 +94,12 @@ public static class SaveUtil
if (string.IsNullOrEmpty(saveName)) return CompressionType.None; if (string.IsNullOrEmpty(saveName)) return CompressionType.None;
try try
{ {
using (FileStream fileStream = new FileStream(GetFullSavePath(saveName), FileMode.Open)) using var fileStream = new FileStream(GetFullSavePath(saveName), FileMode.Open);
return SaveGetCompressType(fileStream); return SaveGetCompressType(fileStream);
} }
catch (Exception e) catch (Exception e)
{ {
logger.LogWarning(e); Logger.LogWarning(e);
return CompressionType.None; return CompressionType.None;
} }
} }