Browse Source

Add game save compression

preview
NGnius (Graham) 3 years ago
parent
commit
4f64cf893c
5 changed files with 649 additions and 453 deletions
  1. +53
    -0
      Kompressor/FileReadAllBytesPatch.cs
  2. +35
    -0
      Kompressor/FileWriteAllBytesPatch.cs
  3. +546
    -437
      Kompressor/Kompressor.csproj
  4. +14
    -15
      Kompressor/MyPlugin.cs
  5. +1
    -1
      Kompressor/QualitySettings.cs

+ 53
- 0
Kompressor/FileReadAllBytesPatch.cs View File

@@ -0,0 +1,53 @@
using System.IO;
using System.IO.Compression;
using HarmonyLib;

namespace Kompressor
{
[HarmonyPatch(typeof(File), "ReadAllBytes")]
public class FileReadAllBytesPatch
{
public static void Postfix(string path, ref byte[] __result)
{
if (!path.EndsWith("GameSave.GC")) return; // not a save file; do nothing
for (byte i = 0; i < MyPlugin.COMPRESSION_FRAME_START.Length; i++)
{
if (__result[i] != MyPlugin.COMPRESSION_FRAME_START[i])
{
// Don't do anything; not compressed
return;
}
}
GamecraftModdingAPI.Utility.Logging.MetaLog($"Reading compressed save from {path}");
// remove frame at start of file
byte[] compressedResult = new byte[__result.Length - MyPlugin.COMPRESSION_FRAME_START.Length];
for (long i = MyPlugin.COMPRESSION_FRAME_START.Length; i < __result.Length; i++)
{
compressedResult[i - MyPlugin.COMPRESSION_FRAME_START.Length] = __result[i];
}
MemoryStream compressedData = new MemoryStream(compressedResult);
// decompress
GZipStream gzip = new GZipStream(compressedData, CompressionMode.Decompress, true);
// update result
MemoryStream decompressedData = new MemoryStream();
byte[] buffer = new byte[1];
int readCount = gzip.Read(buffer, 0, 1);
while (readCount != 0)
{
decompressedData.Write(buffer, 0, 1);
readCount = gzip.Read(buffer, 0, 1);
}
gzip.Close();
byte[] newResult = new byte[decompressedData.Length];
decompressedData.Seek(0, 0);
decompressedData.Read(newResult, 0, (int)decompressedData.Length);
GamecraftModdingAPI.Utility.Logging.MetaLog($"compressedData.Length: {compressedData.Length}");
GamecraftModdingAPI.Utility.Logging.MetaLog($"decompressedData.Length: {decompressedData.Length}");
GamecraftModdingAPI.Utility.Logging.MetaLog($"newResult: \"{System.Text.Encoding.UTF8.GetString(newResult)}\"");
GamecraftModdingAPI.Utility.Logging.MetaLog($"Completed decompression of save");
__result = newResult;
compressedData.Close();
decompressedData.Close();
}
}
}

+ 35
- 0
Kompressor/FileWriteAllBytesPatch.cs View File

@@ -0,0 +1,35 @@
using System.IO;
using System.IO.Compression;
using HarmonyLib;

namespace Kompressor
{
[HarmonyPatch(typeof(File), "WriteAllBytes")]
public class FileWriteAllBytesPatch
{
public static void Prefix(string path, ref byte[] bytes)
{
if (!path.EndsWith("GameSave.GC")) return; // not a save file; do nothing
GamecraftModdingAPI.Utility.Logging.MetaLog($"Writing compressed save to {path}");
MemoryStream data = new MemoryStream();
// compress files
GZipStream gzip = new GZipStream(data, CompressionLevel.Optimal, true);
gzip.Write(bytes, 0, bytes.Length);
//GamecraftModdingAPI.Utility.Logging.MetaLog($"bytes.Length: {bytes.Length}");
gzip.Flush();
gzip.Close();
gzip.Dispose();
byte[] compressedData = new byte[data.Length + MyPlugin.COMPRESSION_FRAME_START.Length];
// copy frame into start of compressedData
for (byte i = 0; i < MyPlugin.COMPRESSION_FRAME_START.Length; i++)
{
compressedData[i] = MyPlugin.COMPRESSION_FRAME_START[i];
}
//GamecraftModdingAPI.Utility.Logging.MetaLog($"compressedData.Length: {data.Length}");
data.Seek(0, 0);
data.Read(compressedData, MyPlugin.COMPRESSION_FRAME_START.Length, (int) data.Length);
// update stuff to write
bytes = compressedData;
}
}
}

+ 546
- 437
Kompressor/Kompressor.csproj
File diff suppressed because it is too large
View File


+ 14
- 15
Kompressor/MyPlugin.cs View File

@@ -1,5 +1,8 @@
using System;
using System.Reflection;
using System.Text;

using HarmonyLib;

//using UnityEngine;

@@ -9,18 +12,22 @@ using GamecraftModdingAPI.Commands;

namespace Kompressor
{
public class MyPlugin : IPlugin // the Illusion Plugin Architecture (IPA) will ignore classes that don't implement IPlugin'
public class MyPlugin : IEnhancedPlugin // the Illusion Plugin Architecture (IPA) will ignore classes that don't implement IPlugin'
{
public string Name { get; } = Assembly.GetExecutingAssembly().GetName().Name; // Kompressor by default
public override string Name { get; } = Assembly.GetExecutingAssembly().GetName().Name; // Kompressor by default
// To change the name, change the project's name

public string Version { get; } = Assembly.GetExecutingAssembly().GetName().Version.ToString(); // 0.0.1.0 by default
public override string Version { get; } = Assembly.GetExecutingAssembly().GetName().Version.ToString(); // 0.0.1.0 by default
// To change the version, change <Version>0.0.1</Version> in Kompressor.csproj

private const string SETTINGS_PATH = "kompressor.json";

internal static byte[] COMPRESSION_FRAME_START = Encoding.UTF8.GetBytes("KOMPRESSED");

private static Harmony harmony = null;

// called when Gamecraft shuts down
public void OnApplicationQuit()
public override void OnApplicationQuit()
{
// Shutdown this mod
GamecraftModdingAPI.Utility.Logging.LogDebug($"{Name} has shutdown");
@@ -30,11 +37,13 @@ namespace Kompressor
}

// called when Gamecraft starts up
public void OnApplicationStart()
public override void OnApplicationStart()
{
// Initialize the Gamecraft modding API first
GamecraftModdingAPI.Main.Init();
// check out the modding API docs here: https://mod.exmods.org/
harmony = new Harmony(Name);
harmony.PatchAll();

// reload command
CommandBuilder.Builder()
@@ -120,15 +129,5 @@ namespace Kompressor

UnityEngine.Camera.main.fieldOfView = qs.FieldOfView;
}

// unused methods

public void OnFixedUpdate() { } // called once per physics update

public void OnLevelWasInitialized(int level) { } // called after a level is initialized

public void OnLevelWasLoaded(int level) { } // called after a level is loaded

public void OnUpdate() { } // called once per rendered frame (frame update)
}
}

+ 1
- 1
Kompressor/QualitySettings.cs View File

@@ -89,7 +89,7 @@ namespace Kompressor

internal static void Save(QualitySettings qs, string filepath)
{
string contents = JsonConvert.SerializeObject(qs);
string contents = JsonConvert.SerializeObject(qs, Formatting.Indented);
File.WriteAllText(filepath, contents);
}