From 8077f7fa3034d27951f24f4315c987c2442c8b94 Mon Sep 17 00:00:00 2001 From: Soar Qin Date: Sun, 25 Jan 2026 15:49:39 +0800 Subject: [PATCH] CompressSave 1.3.8 --- CompressSave/CHANGELOG.md | 214 ++++++++++++----------------- CompressSave/CompressSave.cs | 60 ++++---- CompressSave/CompressSave.csproj | 2 +- CompressSave/PatchUILoadGame.cs | 4 +- CompressSave/PatchUISaveGame.cs | 204 +++++++++++++-------------- CompressSave/SaveUtil.cs | 2 +- CompressSave/UI/MyComboBox.cs | 156 +++++++++++++++++++++ CompressSave/package/manifest.json | 2 +- 8 files changed, 374 insertions(+), 270 deletions(-) create mode 100644 CompressSave/UI/MyComboBox.cs diff --git a/CompressSave/CHANGELOG.md b/CompressSave/CHANGELOG.md index 5a63540..9afec4d 100644 --- a/CompressSave/CHANGELOG.md +++ b/CompressSave/CHANGELOG.md @@ -1,177 +1,137 @@ +
+Read me in English + ## Changelog -### 1.3.7 -* Remove use of MonoMod.Util on loading dll import functions to make mod compatible with HarmonyX 2.13.0 (Although it is not merged yet. Check the PR [here](https://github.com/BepInEx/BepInEx/pull/902)). +* 1.3.8 + * Support for game version 0.10.34. -### 1.3.6 -* Support for using subfolder in save filepath (only MODs are doing this currently). +* 1.3.7 + * Remove use of MonoMod.Util on loading dll import functions to make mod compatible with HarmonyX 2.13.0 (Although it is not merged yet. Check the PR [here](https://github.com/BepInEx/BepInEx/pull/902)). -### 1.3.5 -* Fix a crash issue on choosing language other than English and Chinese. +* 1.3.6 + * Support for using subfolder in save filepath (only MODs are doing this currently). -### 1.3.4 -* Support for game version 0.10.28.20759. +* 1.3.5 + * Fix a crash issue on choosing language other than English and Chinese. -### 1.3.3 -* Fix a display issue on combobox of compression type. +* 1.3.4 + * Support for game version 0.10.28.20759. -### 1.3.2 -* Add config UI on Save Game dialog, to set compression types. -* Change button text to `Save (Compress)` for better understanding. +* 1.3.3 + * Fix a display issue on combobox of compression type. -### 1.3.1 -* Add config to disable feature for auto saves. -* Fix bug that first save after game start is always compressed in Zstd. +* 1.3.2 + * Add config UI on Save Game dialog, to set compression types. * Change button text to `Save (Compress)` for better understanding. -### 1.3.0 -* Separate config entries for manual save and auto save. -* Now you can still get speed benefit while setting compression type to `None` for auto saves, and for manual saves if using the new `Save` button. - + Adds a `nonewrap.dll` for this function. -* Update `LZ4` and `Zstd` library to latest version. -* `lz4wrap.dll` and `zstdwrap.dll` are compiled using `-O3` instead of `-Os`, expect to be slightly faster but larger. +* 1.3.1 + * Add config to disable feature for auto saves. * Fix bug that first save after game start is always compressed in Zstd. + +* 1.3.0 + * Separate config entries for manual save and auto save. * Now you can still get speed benefit while setting compression type to `None` for auto saves, and for manual saves if using the new `Save` button. * Adds a `nonewrap.dll` for this function. * Update `LZ4` and `Zstd` library to latest version. * `lz4wrap.dll` and `zstdwrap.dll` are compiled using `-O3` instead of `-Os`, expect to be slightly faster but larger.
Older versions -### 1.2.2 -* Fix #4, a bug caused by non-ASCII UTF-8 characters. -* Remove use of Harmony.UnpatchAll() to avoid warnings in BepInEx log. +* 1.2.2 + * Fix #4, a bug caused by non-ASCII UTF-8 characters. * Remove use of Harmony.UnpatchAll() to avoid warnings in BepInEx log. -### 1.2.1 -* Simplified codes to display compression type and `Decompress` button on save/load UI, making CompressSave compatible with other MODs(like GalacticScale) which override `UILoadGameWindow::OnSelectedChange()`. -* Add compression level -5 to -1 for zstd, which makes it working better than lz4(which is actually lz4hc used by lz4frame) now: - * -5 gets faster compression speed than lz4 with still a little better compression ratio. - * -1 has almost the same speed against lz4 with greater compression ratio. - * Due to bug of r2modman UI which does not support negative integer, the config value of compression level is not limited any more. -* move native wrapper DLLs into `x64` folder to avoid warning logs on loading BepInEx plugins. +* 1.2.1 + * Simplified codes to display compression type and `Decompress` button on save/load UI, making CompressSave compatible with other MODs(like GalacticScale) which override `UILoadGameWindow::OnSelectedChange()`. * Add compression level -5 to -1 for zstd, which makes it working better than lz4(which is actually lz4hc used by lz4frame) now: * -5 gets faster compression speed than lz4 with still a little better compression ratio. * -1 has almost the same speed against lz4 with greater compression ratio. * Due to bug of r2modman UI which does not support negative integer, the config value of compression level is not limited any more. * move native wrapper DLLs into `x64` folder to avoid warning logs on loading BepInEx plugins. -### 1.2.0 -* Match game version 0.9.27.15033. -* Add new compression type: zstd (a bit slower but get better compression ratio than lz4). -* Add config to set compression type and level(Don't use high compression levels for zstd as they are very slow). -* Hide decompress button for normal save files. -* Optimize native dlls for other compression library support: - * Unified naming rules for filenames and export functions. - * Add compression level support. +* 1.2.0 + * Match game version 0.9.27.15033. * Add new compression type: zstd (a bit slower but get better compression ratio than lz4). * Add config to set compression type and level(Don't use high compression levels for zstd as they are very slow). * Hide decompress button for normal save files. * Optimize native dlls for other compression library support: * Unified naming rules for filenames and export functions. * Add compression level support. -### 1.1.14 -* Fix Sandbox info on Save/Load Panel. -* Fix DLL version info. +* 1.1.14 + * Fix Sandbox info on Save/Load Panel. * Fix DLL version info. -### 1.1.13 +* 1.1.13 + * Match game version 0.9.26.13026. * Move "Sandbox Mode" checkbox on Save Panel to avoid overlap. * Avoid warning message on "Continue" button of main menu. -* Match game version 0.9.26.13026. -* Move "Sandbox Mode" checkbox on Save Panel to avoid overlap. -* Avoid warning message on "Continue" button of main menu. +* 1.1.12 + * Match game version 0.9.25.12007. -### 1.1.12 +* 1.1.11 + * Fix 1.1.10 package issue. -* Match game version 0.9.25.12007. +* 1.1.10 + * Fix 1.1.8 Archive corruption with DIY System, corrupted archives can be fixed by using \[Fix118\] mod -### 1.1.11 + Fix118: -* Fix 1.1.10 package issue. +* 1.1.9 + * CompressSave is temporarily disabled due to some error with the DIY system. -### 1.1.10 +* 1.1.8 + * Match game version 0.9.24.11029 -* Fix 1.1.8 Archive corruption with DIY System, corrupted archives can be fixed by using \[Fix118\] mod +* 1.1.7 + * Fix incorrect data on statistic panel. * Improve performance. - Fix118: https://github.com/bluedoom/DSP_Mod/blob/master/Fix118 +* 1.1.6 + * fix memory leak -### 1.1.9 +* 1.1.5 (Game Version 0.8.22) + * Match game version 0.8.22. * Thanks [@starfi5h] for * PatchSave now use transpiler for better robustness. * Change version check to soft warning. * Add PeekableReader so other mods can use BinaryReader.PeekChar(). * Change LZ4DecompressionStream.Position behavior. Position setter i - available now. -* CompressSave is temporarily disabled due to some error with the DIY system. +* 1.1.4 (Game Version 0.8.19) + * Match game version 0.8.19. -### 1.1.8 +* 1.1.3 (2021/05/29) (Game Version 0.7.18) + * Match game version 0.7.18. * Fix memory leak. -* Match game version 0.9.24.11029 +* 1.1.2 (2021/03/24) (Game Version 0.6.17) + * Handle lz4 library missing Error -### 1.1.7 +* 1.1.1 (2021/03/17) (Game Version 0.6.17) + * Fix Load Error -* Fix incorrect data on statistic panel. -* Improve performance. - -### 1.1.6 - -* fix memory leak - -### 1.1.5 (Game Version 0.8.22) - -* Match game version 0.8.22. -* Thanks [@starfi5h] for - - PatchSave now use transpiler for better robustness. - - Change version check to soft warning. - - Add PeekableReader so other mods can use BinaryReader.PeekChar(). - - Change LZ4DecompressionStream.Position behavior. Position setter i - available now. - -### 1.1.4 (Game Version 0.8.19) - -* Match game version 0.8.19. - -### 1.1.3 (2021/05/29) (Game Version 0.7.18) - -* Match game version 0.7.18. -* Fix memory leak. - -### 1.1.2 (2021/03/24) (Game Version 0.6.17) - -* Handle lz4 library missing Error - -### 1.1.1 (2021/03/17) (Game Version 0.6.17) - -* Fix Load Error - -### 1.1.0 (2021/03/17) (Game Version 0.6.17) - -* Add UI button +* 1.1.0 (2021/03/17) (Game Version 0.6.17) + * Add UI button
+
+ +
+中文读我 ## 更新日志 -### 1.3.7 -* 移除使用MonoMod.Util加载dll导入函数,以使MOD与HarmonyX 2.13.0兼容(尽管此改动尚未合并。[在此](https://github.com/BepInEx/BepInEx/pull/902)查看合并请求)。 +* 1.3.8 + * 支持游戏版本 0.10.34。 -### 1.3.6 -* 支持在存档路径中使用子文件夹(目前只有MOD会这么干)。 +* 1.3.7 + * 移除使用MonoMod.Util加载dll导入函数,以使MOD与HarmonyX 2.13.0兼容(尽管此改动尚未合并。[在此](https://github.com/BepInEx/BepInEx/pull/902)查看合并请求)。 -### 1.3.5 -* 修复了选择英文和中文以外的语言时的崩溃问题。 +* 1.3.6 + * 支持在存档路径中使用子文件夹(目前只有MOD会这么干)。 -### 1.3.4 -* 支持游戏版本 0.10.28.20759。 +* 1.3.5 + * 修复了选择英文和中文以外的语言时的崩溃问题。 -### 1.3.3 -* 修复压缩类型下拉框显示问题。 +* 1.3.4 + * 支持游戏版本 0.10.28.20759。 -### 1.3.2 -* 在保存面板上增加设置压缩方式的UI。 -* 将按钮文本改为`压缩保存`以区分功能。 +* 1.3.3 + * 修复压缩类型下拉框显示问题。 -### 1.3.1 -* 增加在自动存档中禁用压缩的设置项。 -* 修复一个导致游戏开始后第一次保存总是使用Zstd压缩的bug。 +* 1.3.2 + * 在保存面板上增加设置压缩方式的UI。 * 将按钮文本改为`压缩保存`以区分功能。 -### 1.3.0 -* 分离手动存档和自动存档的设置项。 -* 现在在自动存档设置压缩类型为`存储`也可以获得速度提升,手动存档也可以在使用新的`保存`按钮后获得速度提升。 - + 为此增加了`nonewrap.dll`。 -* 更新`LZ4`和`Zstd`库到最新版本。 -* `lz4wrap.dll`和`zstdwrap.dll`使用`-O3`编译而不是`-Os`,速度略有提升但体积变大。 +* 1.3.1 + * 增加在自动存档中禁用压缩的设置项。 * 修复一个导致游戏开始后第一次保存总是使用Zstd压缩的bug。 + +* 1.3.0 + * 分离手动存档和自动存档的设置项。 * 现在在自动存档设置压缩类型为`存储`也可以获得速度提升,手动存档也可以在使用新的`保存`按钮后获得速度提升。 * 为此增加了`nonewrap.dll`。 * 更新`LZ4`和`Zstd`库到最新版本。 * `lz4wrap.dll`和`zstdwrap.dll`使用`-O3`编译而不是`-Os`,速度略有提升但体积变大。
Older versions -### 1.2.2 -* 修复 #4,一个导致非ASCII UTF-8字符导致的bug。 -* 移除使用Harmony.UnpatchAll()以避免在BepInEx日志中出现警告。 +* 1.2.2 + * 修复 #4,一个导致非ASCII UTF-8字符导致的bug。 * 移除使用Harmony.UnpatchAll()以避免在BepInEx日志中出现警告。 -### 1.2.1 -* 简化代码以在存档读取面板上显示压缩类型和`解压`按钮,使得CompressSave与其他MOD(如GalacticScale)兼容,因为它们都覆盖了`UILoadGameWindow::OnSelectedChange()`。 -* 为zstd添加了压缩等级-5到-1,现在它比lz4(实际上是lz4frame)表现更好了: - * -5比lz4更快,但压缩比略有提升。 - * -1和lz4几乎一样快,但压缩比更高。 - * 由于r2modman UI的bug,压缩等级的设置项不再限制范围。 -* 将本地的wrapper DLL移动到`x64`目录。 +* 1.2.1 + * 简化代码以在存档读取面板上显示压缩类型和`解压`按钮,使得CompressSave与其他MOD(如GalacticScale)兼容,因为它们都覆盖了`UILoadGameWindow::OnSelectedChange()`。 * 为zstd添加了压缩等级-5到-1,现在它比lz4(实际上是lz4frame)表现更好了: * -5比lz4更快,但压缩比略有提升。 * -1和lz4几乎一样快,但压缩比更高。 * 由于r2modman UI的bug,压缩等级的设置项不再限制范围。 * 将本地的wrapper DLL移动到`x64`目录。
+
diff --git a/CompressSave/CompressSave.cs b/CompressSave/CompressSave.cs index 850e9bd..1475bd9 100644 --- a/CompressSave/CompressSave.cs +++ b/CompressSave/CompressSave.cs @@ -156,9 +156,16 @@ public class PatchSave } } + public static void UseCommonSaveCompressionType() + { + _compressionTypeForSaving = CompressionTypeForSaves; + _compressionLevelForSaving = CompressionLevelForSaves; + } + [HarmonyPrefix] - [HarmonyPatch(typeof(GameSave), "AutoSave")] - [HarmonyPatch(typeof(GameSave), "SaveAsLastExit")] + [HarmonyPatch(typeof(GameSave), nameof(GameSave.AutoSave))] + [HarmonyPatch(typeof(GameSave), nameof(GameSave.AutoSaveAfterErrored))] + [HarmonyPatch(typeof(GameSave), nameof(GameSave.SaveAsLastExit))] private static void BeforeAutoSave() { UseCompressSave = EnableForAutoSaves.Value && EnableCompress; @@ -168,7 +175,7 @@ public class PatchSave } [HarmonyTranspiler] - [HarmonyPatch(typeof(GameSave), "SaveCurrentGame")] + [HarmonyPatch(typeof(GameSave), nameof(GameSave.SaveCurrentGame))] private static IEnumerable SaveCurrentGame_Transpiler(IEnumerable instructions, ILGenerator generator) { /* BinaryWriter binaryWriter = new BinaryWriter(fileStream); => Create compressionStream and replace binaryWriter. @@ -182,23 +189,23 @@ public class PatchSave matcher.MatchForward(false, new CodeMatch(OpCodes.Newobj, AccessTools.Constructor(typeof(BinaryWriter), [typeof(FileStream)])) ).Set( - OpCodes.Call, AccessTools.Method(typeof(PatchSave), "CreateBinaryWriter") + OpCodes.Call, AccessTools.Method(typeof(PatchSave), nameof(CreateBinaryWriter)) ).MatchForward(false, - new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(PerformanceMonitor), "BeginStream")) + new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(PerformanceMonitor), nameof(PerformanceMonitor.BeginStream))) ).Set( - OpCodes.Call, AccessTools.Method(typeof(PatchSave), "MonitorStream") + OpCodes.Call, AccessTools.Method(typeof(PatchSave), nameof(MonitorStream)) ).MatchForward(false, - new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(Stream), "Seek")) + new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(Stream), nameof(Stream.Seek))) ).Set( - OpCodes.Call, AccessTools.Method(typeof(PatchSave), "FileLengthWrite0") + OpCodes.Call, AccessTools.Method(typeof(PatchSave), nameof(FileLengthWrite0)) ).MatchForward(false, - new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(BinaryWriter), "Write", [typeof(long)])) + new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(BinaryWriter), nameof(BinaryWriter.Write), [typeof(long)])) ).Set( - OpCodes.Call, AccessTools.Method(typeof(PatchSave), "FileLengthWrite1") + OpCodes.Call, AccessTools.Method(typeof(PatchSave), nameof(FileLengthWrite1)) ).MatchForward(false, - new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(IDisposable), "Dispose")) + new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(IDisposable), nameof(IDisposable.Dispose))) ).Advance(1).Insert( - new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "DisposeCompressionStream")) + new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(PatchSave), nameof(DisposeCompressionStream))) ); EnableCompress = true; return matcher.InstructionEnumeration(); @@ -221,7 +228,6 @@ public class PatchSave { if (UseCompressSave) { - SaveUtil.Logger.LogDebug("Begin compress save"); WriteHeader(fileStream); _compressionStream = _compressionTypeForSaving switch { @@ -283,11 +289,11 @@ public class PatchSave } [HarmonyTranspiler] - [HarmonyPatch(typeof(GameSave), "LoadCurrentGame")] - [HarmonyPatch(typeof(GameSave), "LoadGameDesc")] - [HarmonyPatch(typeof(GameSave), "ReadHeader")] - [HarmonyPatch(typeof(GameSave), "ReadHeaderAndDescAndProperty")] - [HarmonyPatch(typeof(GameSave), "ReadModes")] + [HarmonyPatch(typeof(GameSave), nameof(GameSave.LoadCurrentGame))] + [HarmonyPatch(typeof(GameSave), nameof(GameSave.LoadGameDesc))] + [HarmonyPatch(typeof(GameSave), nameof(GameSave.ReadHeader))] + [HarmonyPatch(typeof(GameSave), nameof(GameSave.ReadHeaderAndDescAndProperty))] + [HarmonyPatch(typeof(GameSave), nameof(GameSave.ReadModes))] private static IEnumerable LoadCurrentGame_Transpiler(IEnumerable instructions, ILGenerator generator) { /* using (BinaryReader binaryReader = new BinaryReader(fileStream)) => Create decompressionStream and replace binaryReader. @@ -302,27 +308,27 @@ public class PatchSave matcher.MatchForward(false, new CodeMatch(OpCodes.Newobj, AccessTools.Constructor(typeof(BinaryReader), [typeof(FileStream)])) ).Set( - OpCodes.Call, AccessTools.Method(typeof(PatchSave), "CreateBinaryReader") + OpCodes.Call, AccessTools.Method(typeof(PatchSave), nameof(CreateBinaryReader)) ).MatchForward(false, - new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(PerformanceMonitor), "BeginStream")) + new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(PerformanceMonitor), nameof(PerformanceMonitor.BeginStream))) ); if (matcher.IsValid) - matcher.Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "MonitorStream")); + matcher.Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), nameof(MonitorStream))); matcher.Start().MatchForward(false, - new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(BinaryReader), "ReadInt64")) + new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(BinaryReader), nameof(BinaryReader.ReadInt64))) ).Set( - OpCodes.Call, AccessTools.Method(typeof(PatchSave), "FileLengthRead") + OpCodes.Call, AccessTools.Method(typeof(PatchSave), nameof(FileLengthRead)) ).MatchForward(false, - new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(IDisposable), "Dispose")) + new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(IDisposable), nameof(IDisposable.Dispose))) ).Advance(1).Insert( - new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "DisposeCompressionStream")) + new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(PatchSave), nameof(DisposeCompressionStream))) ).MatchBack(false, - new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(Stream), "Seek")) + new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(Stream), nameof(Stream.Seek))) ); if (matcher.IsValid) - matcher.Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "ReadSeek")); + matcher.Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), nameof(ReadSeek))); return matcher.InstructionEnumeration(); } diff --git a/CompressSave/CompressSave.csproj b/CompressSave/CompressSave.csproj index c526c44..85a6efc 100644 --- a/CompressSave/CompressSave.csproj +++ b/CompressSave/CompressSave.csproj @@ -4,7 +4,7 @@ CompressSave org.soardev.compresssave DSP MOD - CompressSave - 1.3.7 + 1.3.8 true latest net472 diff --git a/CompressSave/PatchUILoadGame.cs b/CompressSave/PatchUILoadGame.cs index 3b86208..caf84b1 100644 --- a/CompressSave/PatchUILoadGame.cs +++ b/CompressSave/PatchUILoadGame.cs @@ -9,7 +9,7 @@ class PatchUILoadGame { static UIButton _decompressButton; - [HarmonyPatch(typeof(UILoadGameWindow), "OnSelectedChange"), HarmonyPostfix] + [HarmonyPatch(typeof(UILoadGameWindow), nameof(UILoadGameWindow.OnSelectedChange)), HarmonyPostfix] private static void OnSelectedChange(UILoadGameWindow __instance) { var selected = __instance.selected; @@ -26,7 +26,7 @@ class PatchUILoadGame _decompressButton.gameObject.SetActive(compressedType != CompressionType.None); } - [HarmonyPatch(typeof(UILoadGameWindow), "_OnOpen"), HarmonyPostfix] + [HarmonyPatch(typeof(UILoadGameWindow), nameof(UILoadGameWindow._OnOpen)), HarmonyPostfix] static void _OnOpen(UILoadGameWindow __instance) { if (_decompressButton) return; diff --git a/CompressSave/PatchUISaveGame.cs b/CompressSave/PatchUISaveGame.cs index 98b17ce..779a7d7 100644 --- a/CompressSave/PatchUISaveGame.cs +++ b/CompressSave/PatchUISaveGame.cs @@ -1,3 +1,4 @@ +using CompressSave.UI; using HarmonyLib; using UnityEngine; using UnityEngine.UI; @@ -18,7 +19,7 @@ static class PatchUISaveGame _OnDestroy(); } - [HarmonyPatch(typeof(UISaveGameWindow), "OnSelectedChange"), HarmonyPostfix] + [HarmonyPatch(typeof(UISaveGameWindow), nameof(UISaveGameWindow.OnSelectedChange)), HarmonyPostfix] private static void OnSelectedChange(UISaveGameWindow __instance) { var selected = __instance.selected; @@ -32,17 +33,17 @@ static class PatchUISaveGame }; } - [HarmonyPatch(typeof(UISaveGameWindow), "_OnDestroy"), HarmonyPostfix] + [HarmonyPatch(typeof(UISaveGameWindow), nameof(UISaveGameWindow._OnDestroy)), HarmonyPostfix] private static void _OnDestroy() { //Console.WriteLine("OnCreate"); _context = new UIContext(); } - [HarmonyPatch(typeof(UISaveGameWindow), "OnSaveClick"), HarmonyReversePatch] + [HarmonyPatch(typeof(UISaveGameWindow), nameof(UISaveGameWindow.OnSaveClick)), HarmonyReversePatch] private static void OSaveGameAs(this UISaveGameWindow ui, int data) { } - [HarmonyPatch(typeof(UISaveGameWindow), "CheckAndSetSaveButtonEnable"), HarmonyPostfix] + [HarmonyPatch(typeof(UISaveGameWindow), nameof(UISaveGameWindow.CheckAndSetSaveButtonEnable)), HarmonyPostfix] private static void CheckAndSetSaveButtonEnable(UISaveGameWindow __instance) { _OnOpen(__instance); @@ -61,37 +62,43 @@ static class PatchUISaveGame public UISaveGameWindow Window; } - [HarmonyPatch(typeof(UISaveGameWindow), "OnSaveClick"), HarmonyPrefix] + [HarmonyPatch(typeof(UISaveGameWindow), nameof(UISaveGameWindow.OnSaveClick)), HarmonyPrefix] private static void OnSaveClick() { PatchSave.UseCompressSave = true; + PatchSave.UseCommonSaveCompressionType(); } private static UIContext _context = new(); - [HarmonyPatch(typeof(UISaveGameWindow), "_OnOpen"), HarmonyPostfix] + [HarmonyPatch(typeof(UISaveGameWindow), nameof(UISaveGameWindow._OnOpen)), HarmonyPostfix] private static void _OnOpen(UISaveGameWindow __instance) { - if (_context.ButtonCompress) return; + if (_context.ButtonCompress) + { + var dist = __instance.cancelButton.transform.localPosition.x - __instance.saveButton.transform.localPosition.x - ((RectTransform)__instance.cancelButton.transform).sizeDelta.x; + var cRectTrans = (RectTransform)_context.ButtonCompress.transform; + var localPos = __instance.saveButton.transform.localPosition; + cRectTrans.localPosition = new Vector3(localPos.x - dist - ((RectTransform)__instance.saveButton.transform).sizeDelta.x, localPos.y, localPos.z); + return; + } RectTransform rtrans; Vector3 pos; _context.SaveButton = __instance.saveButton; _context.SaveButtonText = __instance.saveButtonText; _context.Window = __instance; var gameObj = __instance.transform.Find("button-compress")?.gameObject; - var created = false; + var isCreating = false; + var theParent = __instance.saveButton.transform.parent; if (gameObj == null) { - gameObj = Object.Instantiate(__instance.saveButton.gameObject, __instance.saveButton.transform.parent); - created = true; + gameObj = Object.Instantiate(__instance.saveButton.gameObject, theParent); + isCreating = true; } _context.ButtonCompress = gameObj.GetComponent(); - if (created) + if (isCreating) { _context.ButtonCompress.gameObject.name = "button-compress"; - rtrans = (RectTransform)_context.ButtonCompress.transform; - pos = rtrans.anchoredPosition3D; - rtrans.anchoredPosition3D = new Vector3(pos.x - 180, pos.y, pos.z); _context.ButtonCompress.button.image.color = new Color32(0xfc, 0x6f, 00, 0x77); var textTrans = _context.ButtonCompress.transform.Find("button-text"); _context.ButtonCompressText = textTrans.GetComponent(); @@ -106,126 +113,89 @@ static class PatchUISaveGame localizer.translation = "Save with Compression".Translate(); } } + var distance = __instance.cancelButton.transform.localPosition.x - __instance.saveButton.transform.localPosition.x - ((RectTransform)__instance.cancelButton.transform).sizeDelta.x; + rtrans = (RectTransform)_context.ButtonCompress.transform; + pos = __instance.saveButton.transform.localPosition; + rtrans.localPosition = new Vector3(pos.x - distance - ((RectTransform)__instance.saveButton.transform).sizeDelta.x, pos.y, pos.z); - created = false; + isCreating = false; gameObj = __instance.transform.Find("manual-save-type-combobox")?.gameObject; if (gameObj == null) { - gameObj = Object.Instantiate(UIRoot.instance.optionWindow.resolutionComp.transform.parent.gameObject, __instance.saveButton.transform.parent); - created = true; + gameObj = UI.MyComboBox.CreateComboBox("manual-save-type-combobox"); + isCreating = true; } _context.ManualSaveTypeComboBox = gameObj; - if (created) + + if (isCreating) { - gameObj.name = "manual-save-type-combobox"; + var btnCompressTrans = (RectTransform)_context.ButtonCompress.transform; + var text = AddText("Compression for manual saves", 14, "manual-save-type-combobox-text"); + rtrans = text.rectTransform; + rtrans.SetParent(theParent, false); + pos = btnCompressTrans.localPosition; + rtrans.anchorMin = btnCompressTrans.anchorMin; + rtrans.anchorMax = btnCompressTrans.anchorMax; + rtrans.pivot = btnCompressTrans.pivot; + rtrans.localPosition = new Vector3(pos.x - 250f, pos.y + 45f, pos.z); + rtrans = (RectTransform)gameObj.transform; - var rtrans2 = (RectTransform)_context.ButtonCompress.transform; - pos = rtrans2.anchoredPosition3D; - rtrans.anchorMin = rtrans2.anchorMin; - rtrans.anchorMax = rtrans2.anchorMax; - rtrans.pivot = rtrans2.pivot; - rtrans.anchoredPosition3D = new Vector3(pos.x + 100, pos.y + 45, pos.z); - var cbctrl = rtrans.transform.Find("ComboBox"); - var content = cbctrl.Find("Dropdown List ScrollBox")?.Find("Mask")?.Find("Content Panel"); - if (content != null) + rtrans.SetParent(theParent, false); + rtrans.anchorMin = btnCompressTrans.anchorMin; + rtrans.anchorMax = btnCompressTrans.anchorMax; + rtrans.pivot = btnCompressTrans.pivot; + text.UpdateGeometry(); + rtrans.localPosition = new Vector3(pos.x - 50f, pos.y + 45f, pos.z); + var cb = rtrans.GetComponent(); + cb.WithItems("Store".Translate(), "LZ4", "Zstd").WithIndex((int)PatchSave.CompressionTypeForSaves).WithOnSelChanged((idx) => { - for (var i = content.childCount - 1; i >= 0; i--) - { - var theTrans = content.GetChild(i); - if (theTrans.name == "Item Button(Clone)") - { - Object.Destroy(theTrans.gameObject); - } - } - } - var cb = cbctrl.GetComponent(); - cb.onSubmit.RemoveAllListeners(); - cb.onItemIndexChange.RemoveAllListeners(); - cb.Items = ["Store".Translate(), "LZ4", "Zstd"]; - cb.itemIndex = (int)PatchSave.CompressionTypeForSaves; - cb.onItemIndexChange.AddListener(()=> - { - PatchSave.CompressionTypeForSaves = (CompressionType)cb.itemIndex; + PatchSave.CompressionTypeForSaves = (CompressionType)idx; PatchSave.CompressionTypeForSavesConfig.Value = CompressSave.StringFromCompresstionType(PatchSave.CompressionTypeForSaves); }); - rtrans = (RectTransform)cb.transform; - pos = rtrans.anchoredPosition3D; - rtrans.anchoredPosition3D = new Vector3(pos.x - 50, pos.y, pos.z); - var size = rtrans.sizeDelta; - rtrans.sizeDelta = new Vector2(150f, size.y); - var txt = gameObj.GetComponent(); - txt.text = "Compression for manual saves".Translate(); - var localizer = gameObj.GetComponent(); - if (localizer != null) - { - localizer.stringKey = "Compression for manual saves"; - localizer.translation = "Compression for manual saves".Translate(); - } } - created = false; + isCreating = false; gameObj = __instance.transform.Find("auto-save-type-combobox")?.gameObject; if (gameObj == null) { - gameObj = Object.Instantiate(UIRoot.instance.optionWindow.resolutionComp.transform.parent.gameObject, __instance.saveButton.transform.parent); - created = true; + gameObj = UI.MyComboBox.CreateComboBox("auto-save-type-combobox"); + isCreating = true; } _context.AutoSaveTypeComboBox = gameObj; - if (created) + if (isCreating) { - gameObj.name = "auto-save-type-combobox"; + var btnCompressTrans = (RectTransform)_context.ButtonCompress.transform; + + var text = AddText("Compression for auto saves", 14, "auto-save-type-combobox-text"); + rtrans = text.rectTransform; + rtrans.SetParent(theParent, false); + pos = btnCompressTrans.localPosition; + rtrans.anchorMin = btnCompressTrans.anchorMin; + rtrans.anchorMax = btnCompressTrans.anchorMax; + rtrans.pivot = btnCompressTrans.pivot; + rtrans.localPosition = new Vector3(pos.x + 160f, pos.y + 45f, pos.z); + rtrans = (RectTransform)gameObj.transform; - var rtrans2 = (RectTransform)_context.ButtonCompress.transform; - pos = rtrans2.anchoredPosition3D; - rtrans.anchorMin = rtrans2.anchorMin; - rtrans.anchorMax = rtrans2.anchorMax; - rtrans.pivot = rtrans2.pivot; - rtrans.anchoredPosition3D = new Vector3(pos.x + 510, pos.y + 45, pos.z); - var cbctrl = rtrans.transform.Find("ComboBox"); - var content = cbctrl.Find("Dropdown List ScrollBox")?.Find("Mask")?.Find("Content Panel"); - if (content != null) - { - for (var i = content.childCount - 1; i >= 0; i--) + rtrans.SetParent(theParent, false); + rtrans.anchorMin = btnCompressTrans.anchorMin; + rtrans.anchorMax = btnCompressTrans.anchorMax; + rtrans.pivot = btnCompressTrans.pivot; + rtrans.localPosition = new Vector3(pos.x + 360f, pos.y + 45f, pos.z); + var cb = rtrans.GetComponent(); + cb.WithItems(["已停用".Translate(), "Store".Translate(), "LZ4", "Zstd"]).WithIndex(PatchSave.EnableForAutoSaves.Value ? (int)PatchSave.CompressionTypeForAutoSaves + 1 : 0) + .WithOnSelChanged((idx) => { - var theTrans = content.GetChild(i); - if (theTrans.name == "Item Button(Clone)") + if (idx == 0) { - Object.Destroy(theTrans.gameObject); + PatchSave.EnableForAutoSaves.Value = false; } - } - } - var cb = cbctrl.GetComponent(); - cb.onSubmit.RemoveAllListeners(); - cb.onItemIndexChange.RemoveAllListeners(); - cb.Items = ["已停用".Translate(), "Store".Translate(), "LZ4", "Zstd"]; - cb.itemIndex = PatchSave.EnableForAutoSaves.Value ? (int)PatchSave.CompressionTypeForAutoSaves + 1 : 0; - cb.onItemIndexChange.AddListener(() => - { - var idx = cb.itemIndex; - if (idx == 0) - { - PatchSave.EnableForAutoSaves.Value = false; - } - else - { - PatchSave.EnableForAutoSaves.Value = true; - PatchSave.CompressionTypeForAutoSaves = (CompressionType)idx - 1; - PatchSave.CompressionTypeForAutoSavesConfig.Value = CompressSave.StringFromCompresstionType(PatchSave.CompressionTypeForAutoSaves); - } - }); - rtrans = (RectTransform)cb.transform; - pos = rtrans.anchoredPosition3D; - rtrans.anchoredPosition3D = new Vector3(pos.x - 50, pos.y, pos.z); - var size = rtrans.sizeDelta; - rtrans.sizeDelta = new Vector2(150f, size.y); - var txt = gameObj.GetComponent(); - txt.text = "Compression for auto saves".Translate(); - var localizer = gameObj.GetComponent(); - if (localizer != null) - { - localizer.stringKey = "Compression for auto saves"; - localizer.translation = "Compression for auto saves".Translate(); - } + else + { + PatchSave.EnableForAutoSaves.Value = true; + PatchSave.CompressionTypeForAutoSaves = (CompressionType)idx - 1; + PatchSave.CompressionTypeForAutoSavesConfig.Value = CompressSave.StringFromCompresstionType(PatchSave.CompressionTypeForAutoSaves); + } + }); } } @@ -234,4 +204,16 @@ static class PatchUISaveGame PatchSave.UseCompressSave = false; _context.Window.OSaveGameAs(data); } + + public static Text AddText(string label, int fontSize = 14, string objName = "label") + { + var txt = Object.Instantiate(UIRoot.instance.uiGame.assemblerWindow.stateText); + txt.gameObject.name = objName; + txt.text = label.Translate(); + txt.color = new Color(1f, 1f, 1f, 0.4f); + txt.alignment = TextAnchor.MiddleLeft; + txt.fontSize = fontSize; + txt.rectTransform.sizeDelta = new Vector2(txt.preferredWidth + 8f, txt.preferredHeight + 8f); + return txt; + } } diff --git a/CompressSave/SaveUtil.cs b/CompressSave/SaveUtil.cs index 14695dd..c515ad9 100644 --- a/CompressSave/SaveUtil.cs +++ b/CompressSave/SaveUtil.cs @@ -13,7 +13,7 @@ public static class SaveUtil { Major = 0, Minor = 10, - Release = 28, + Release = 34, }; private static string UnzipToFile(DecompressionStream lzStream, string fullPath) diff --git a/CompressSave/UI/MyComboBox.cs b/CompressSave/UI/MyComboBox.cs new file mode 100644 index 0000000..d5cba31 --- /dev/null +++ b/CompressSave/UI/MyComboBox.cs @@ -0,0 +1,156 @@ +using System; +using System.Linq; +using BepInEx.Configuration; +using UnityEngine; +using UnityEngine.UI; + +namespace CompressSave.UI; + +public class MyComboBox : MonoBehaviour +{ + private RectTransform _rectTrans; + private UIComboBox _comboBox; + public Action OnSelChanged; + + private static GameObject _baseObject; + + public static void InitBaseObject() + { + if (_baseObject) return; + var fontSource = UIRoot.instance.uiGame.buildMenu.uxFacilityCheck.transform.Find("text")?.GetComponent(); + var go = Instantiate(UIRoot.instance.optionWindow.resolutionComp.gameObject); + go.name = "my-combobox"; + go.SetActive(false); + + var rect = (RectTransform)go.transform; + var cbctrl = rect.GetComponent(); + foreach (var button in cbctrl.ItemButtons) + { + Destroy(button.gameObject); + } + cbctrl.Items.Clear(); + cbctrl.ItemButtons.Clear(); + if (fontSource) + { + var txtComp = cbctrl.m_ListItemRes.GetComponentInChildren(); + if (txtComp) + { + txtComp.font = fontSource.font; + txtComp.fontSize = fontSource.fontSize; + txtComp.fontStyle = fontSource.fontStyle; + } + txtComp = rect.Find("Main Button/Text")?.GetComponent(); + if (txtComp) + { + txtComp.font = fontSource.font; + txtComp.fontSize = fontSource.fontSize; + txtComp.fontStyle = fontSource.fontStyle; + } + } + cbctrl.onSubmit.RemoveAllListeners(); + cbctrl.onItemIndexChange.RemoveAllListeners(); + _baseObject = go; + } + + public static GameObject CreateComboBox(string name) + { + if (_baseObject == null) InitBaseObject(); + var gameObject = Instantiate(_baseObject); + gameObject.name = name; + gameObject.SetActive(true); + var cb = gameObject.AddComponent(); + cb._rectTrans = (RectTransform)gameObject.transform; + var box = cb._rectTrans.GetComponent(); + cb._comboBox = box; + box.onItemIndexChange.AddListener(() => { cb.OnSelChanged?.Invoke(box.itemIndex); }); + + return gameObject; + } + + protected void OnDestroy() + { + _config.SettingChanged -= _configChanged; + } + + public void SetFontSize(int size) + { + _comboBox.ItemButtons.ForEach(b => b.GetComponentInChildren().fontSize = size); + _comboBox.m_ListItemRes.GetComponentInChildren().fontSize = size; + var txtComp = _comboBox.transform.Find("Main Button")?.GetComponentInChildren(); + if (txtComp) txtComp.fontSize = size; + } + + public void SetItems(params string[] items) + { + _comboBox.Items = [.. items.Select(s => s.Translate())]; + _comboBox.StartItemIndex = 0; + _comboBox.DropDownCount = Math.Min(items.Length, 8); + } + + public void SetIndex(int index) => _comboBox.itemIndex = index; + + public void SetSize(float width, float height) + { + var rtrans = (RectTransform)_comboBox.transform; + rtrans.sizeDelta = new Vector2(width > 0f ? width : rtrans.sizeDelta.x, height > 0f ? height : rtrans.sizeDelta.y); + _rectTrans.sizeDelta = new Vector2(rtrans.localPosition.x + rtrans.sizeDelta.x, _rectTrans.sizeDelta.y); + } + + public void AddOnSelChanged(Action action) => OnSelChanged += action; + + private EventHandler _configChanged; + private Action _selChanged; + private ConfigEntry _config; + public void SetConfigEntry(ConfigEntry config) + { + if (_selChanged != null) OnSelChanged -= _selChanged; + if (_configChanged != null) config.SettingChanged -= _configChanged; + + _comboBox.itemIndex = config.Value; + _config = config; + _selChanged = value => config.Value = value; + OnSelChanged += _selChanged; + _configChanged = (_, _) => SetIndex(config.Value); + config.SettingChanged += _configChanged; + } + + public MyComboBox WithFontSize(int size) + { + SetFontSize(size); + return this; + } + + public MyComboBox WithItems(params string[] items) + { + SetItems(items); + return this; + } + + public MyComboBox WithIndex(int index) + { + SetIndex(index); + return this; + } + + public MyComboBox WithSize(float width, float height) + { + SetSize(width, height); + return this; + } + + public MyComboBox WithOnSelChanged(params Action[] action) + { + foreach (var act in action) + AddOnSelChanged(act); + return this; + } + + public MyComboBox WithConfigEntry(ConfigEntry config) + { + SetConfigEntry(config); + return this; + } + + public float Width => _rectTrans.sizeDelta.x; + public float Height => _rectTrans.sizeDelta.y; +} \ No newline at end of file diff --git a/CompressSave/package/manifest.json b/CompressSave/package/manifest.json index 0c8dcef..79ab0a5 100644 --- a/CompressSave/package/manifest.json +++ b/CompressSave/package/manifest.json @@ -1,6 +1,6 @@ { "name": "CompressSave", - "version_number": "1.3.7", + "version_number": "1.3.8", "website_url": "https://github.com/soarqin/DSP_Mods_TO/tree/master/CompressSave", "description": "Compress game saves to reduce space use and boost save speed / 压缩游戏存档以降低空间使用并提升保存速度", "dependencies": ["xiaoye97-BepInEx-5.4.17"]