Skip to content

Commit 5a61788

Browse files
author
Delynith
committed
support for macos save files
1 parent 66bed70 commit 5a61788

File tree

2 files changed

+79
-5
lines changed

2 files changed

+79
-5
lines changed

GeometryDashAPI/Crypt.cs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,23 @@
11
using System.IO;
22
using System.IO.Compression;
3+
using System.Security.Cryptography;
34
using System.Text;
45
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
56

67
namespace GeometryDashAPI
78
{
89
public class Crypt
910
{
11+
// The MacOS save file is not encoded like the Windows one - instead, it uses AES ECB encryption.
12+
// Huge thanks to: https://github.com/qimiko/gd-save-tools/blob/b5176eb2c805ca65da3e51701409b72b90bdd497/assets/js/savefile.mjs#L43
13+
private static byte[] MAC_SAVE_KEY =
14+
[
15+
0x69, 0x70, 0x75, 0x39, 0x54, 0x55, 0x76, 0x35,
16+
0x34, 0x79, 0x76, 0x5D, 0x69, 0x73, 0x46, 0x4D,
17+
0x68, 0x35, 0x40, 0x3B, 0x74, 0x2E, 0x35, 0x77,
18+
0x33, 0x34, 0x45, 0x32, 0x52, 0x79, 0x40, 0x7B
19+
];
20+
1021
public static byte[] XOR(byte[] data, int key)
1122
{
1223
var result = new byte[data.Length];
@@ -53,5 +64,50 @@ public static byte[] GZipCompress(byte[] data)
5364
}
5465
return memory.ToArray();
5566
}
67+
68+
public static byte[] SavingSaveAsMacOS(byte[] data)
69+
{
70+
using (Aes aesAlg = Aes.Create())
71+
{
72+
aesAlg.Key = MAC_SAVE_KEY;
73+
aesAlg.Mode = CipherMode.ECB;
74+
75+
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
76+
77+
using (MemoryStream msEncrypt = new MemoryStream())
78+
{
79+
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
80+
{
81+
csEncrypt.Write(data, 0, data.Length);
82+
}
83+
return msEncrypt.ToArray();
84+
}
85+
}
86+
}
87+
88+
public static string LoadSaveAsMacOS(byte[] data)
89+
{
90+
using (Aes aesAlg = Aes.Create())
91+
{
92+
aesAlg.Key = MAC_SAVE_KEY;
93+
aesAlg.Mode = CipherMode.ECB;
94+
95+
// Create a decryptor to perform the stream transform.
96+
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
97+
98+
// Create the streams used for decryption.
99+
using (MemoryStream msDecrypt = new MemoryStream(data))
100+
{
101+
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
102+
{
103+
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
104+
{
105+
// Read the decrypted bytes from the decrypting stream and place them in a string.
106+
return srDecrypt.ReadToEnd();
107+
}
108+
}
109+
}
110+
}
111+
}
56112
}
57113
}

GeometryDashAPI/Data/GameData.cs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,20 @@ public virtual async Task LoadAsync(string fileName)
3636
var data = new byte[file.Length];
3737
await file.ReadAsync(data, 0, data.Length);
3838

39-
var xor = Crypt.XOR(data, 0xB);
40-
var index = xor.AsSpan().IndexOf((byte)0);
41-
var gZipDecompress = Crypt.GZipDecompress(GameConvert.FromBase64(Encoding.ASCII.GetString(xor, 0, index >= 0 ? index : xor.Length)));
42-
43-
DataPlist = new Plist(Encoding.ASCII.GetBytes(gZipDecompress));
39+
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
40+
{
41+
string decryptedData = Crypt.LoadSaveAsMacOS(data);
42+
DataPlist = new Plist(Encoding.ASCII.GetBytes(decryptedData));
43+
}
44+
else
45+
{
46+
var xor = Crypt.XOR(data, 0xB);
47+
var index = xor.AsSpan().IndexOf((byte)0);
48+
var gZipDecompress =
49+
Crypt.GZipDecompress(
50+
GameConvert.FromBase64(Encoding.ASCII.GetString(xor, 0, index >= 0 ? index : xor.Length)));
51+
DataPlist = new Plist(Encoding.ASCII.GetBytes(gZipDecompress));
52+
}
4453
}
4554

4655
/// <summary>
@@ -83,13 +92,22 @@ public static string ResolveFileName(GameDataType? type)
8392
throw new InvalidOperationException("can't resolve the directory with the saves for undefined file type. Use certain file name");
8493
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
8594
return $@"{Environment.GetEnvironmentVariable("LocalAppData")}\GeometryDash\CC{type}.dat";
95+
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
96+
return $@"/Users/{Environment.GetEnvironmentVariable("USER")}/Library/Application Support/GeometryDash/CC{type}.dat";
8697
throw new InvalidOperationException($"can't resolve the directory with the saves on your operating system: '{RuntimeInformation.OSDescription}'. Use certain file name");
8798
}
8899

89100
private static byte[] GetFileContent(MemoryStream memory)
90101
{
102+
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
103+
{
104+
var encrypted = Crypt.SavingSaveAsMacOS(memory.ToArray());
105+
return encrypted;
106+
}
107+
91108
var base64 = GameConvert.ToBase64(Crypt.GZipCompress(memory.ToArray()));
92109
return Crypt.XOR(Encoding.ASCII.GetBytes(base64), 0xB);
110+
93111
}
94112
}
95113
}

0 commit comments

Comments
 (0)