Skip to content

Commit bfd7749

Browse files
committed
library should load mac and windows file together
in one operation system
1 parent 5a61788 commit bfd7749

File tree

3 files changed

+64
-25
lines changed

3 files changed

+64
-25
lines changed

GeometryDashAPI.sln.DotSettings

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
2-
<s:Boolean x:Key="/Default/UserDictionary/Words/=LEVELCONTENT/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
2+
<s:Boolean x:Key="/Default/UserDictionary/Words/=LEVELCONTENT/@EntryIndexedValue">True</s:Boolean>
3+
<s:Boolean x:Key="/Default/UserDictionary/Words/=xored/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace GeometryDashAPI.Data;
2+
3+
public enum DatFileFormat
4+
{
5+
Windows,
6+
Mac
7+
}

GeometryDashAPI/Data/GameData.cs

Lines changed: 55 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ namespace GeometryDashAPI.Data
1010
{
1111
public class GameData
1212
{
13+
// This is xored gzip magick bytes: 'C?'
14+
// see more https://en.wikipedia.org/wiki/Gzip
15+
private static readonly byte[] XorDatFileMagickBytes = [ 0x43, 0x3f ];
16+
1317
public Plist DataPlist { get; set; }
1418

1519
private readonly GameDataType? type;
@@ -34,36 +38,41 @@ public virtual async Task LoadAsync(string fileName)
3438
using var file = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, useAsync: true);
3539
#endif
3640
var data = new byte[file.Length];
37-
await file.ReadAsync(data, 0, data.Length);
41+
_ = await file.ReadAsync(data, 0, data.Length);
3842

39-
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
43+
if (data.AsSpan().Slice(0, XorDatFileMagickBytes.Length).IndexOf(XorDatFileMagickBytes) != 0)
4044
{
41-
string decryptedData = Crypt.LoadSaveAsMacOS(data);
45+
// mac files
46+
var decryptedData = Crypt.LoadSaveAsMacOS(data);
4247
DataPlist = new Plist(Encoding.ASCII.GetBytes(decryptedData));
48+
return;
4349
}
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-
}
50+
51+
// windows files
52+
var xor = Crypt.XOR(data, 0xB);
53+
var index = xor.AsSpan().IndexOf((byte)0);
54+
var gZipDecompress =
55+
Crypt.GZipDecompress(
56+
GameConvert.FromBase64(Encoding.ASCII.GetString(xor, 0, index >= 0 ? index : xor.Length)));
57+
DataPlist = new Plist(Encoding.ASCII.GetBytes(gZipDecompress));
5358
}
54-
59+
5560
/// <summary>
5661
/// Saves class data to a file as a game save<br/><br/>
5762
/// Before saving, make sure that you have closed the game. Otherwise, after closing, the game will overwrite the file<br/>
5863
/// </summary>
5964
/// <param name="fullName">File to write the data.<br />
6065
/// use <b>null</b> value for default resolving
6166
/// </param>
62-
public void Save(string? fullName = null)
67+
/// <param name="format">
68+
/// Specify if you want to save the file in a format specific to another operating system.<br />
69+
/// Leave <b>null</b> to save the file for the current operating system
70+
/// </param>
71+
public void Save(string? fullName = null, DatFileFormat? format = null)
6372
{
6473
using var memory = new MemoryStream();
6574
DataPlist.SaveToStream(memory);
66-
File.WriteAllBytes(fullName ?? ResolveFileName(type), GetFileContent(memory));
75+
File.WriteAllBytes(fullName ?? ResolveFileName(type), GetFileContent(memory, format ?? ResolveFileFormat()));
6776
}
6877

6978
/// <summary>
@@ -73,15 +82,19 @@ public void Save(string? fullName = null)
7382
/// <param name="fileName">File to write the data.<br />
7483
/// use <b>null</b> value for default resolving
7584
/// </param>
76-
public async Task SaveAsync(string? fileName = null)
85+
/// <param name="format">
86+
/// Specify if you want to save the file in a format specific to another operating system.<br />
87+
/// Leave <b>null</b> to save the file for the current operating system
88+
/// </param>
89+
public async Task SaveAsync(string? fileName = null, DatFileFormat? format = null)
7790
{
7891
using var memory = new MemoryStream();
7992
await DataPlist.SaveToStreamAsync(memory);
8093
#if NETSTANDARD2_1
81-
await File.WriteAllBytesAsync(fileName ?? ResolveFileName(type), GetFileContent(memory));
94+
await File.WriteAllBytesAsync(fileName ?? ResolveFileName(type), GetFileContent(memory, format ?? ResolveFileFormat()));
8295
#else
8396
using var file = new FileStream(fileName ?? ResolveFileName(type), FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 4096, useAsync: true);
84-
var data = GetFileContent(memory);
97+
var data = GetFileContent(memory, format ?? ResolveFileFormat());
8598
await file.WriteAsync(data, 0, data.Length);
8699
#endif
87100
}
@@ -93,21 +106,39 @@ public static string ResolveFileName(GameDataType? type)
93106
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
94107
return $@"{Environment.GetEnvironmentVariable("LocalAppData")}\GeometryDash\CC{type}.dat";
95108
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
96-
return $@"/Users/{Environment.GetEnvironmentVariable("USER")}/Library/Application Support/GeometryDash/CC{type}.dat";
109+
return $"/Users/{Environment.GetEnvironmentVariable("USER")}/Library/Application Support/GeometryDash/CC{type}.dat";
97110
throw new InvalidOperationException($"can't resolve the directory with the saves on your operating system: '{RuntimeInformation.OSDescription}'. Use certain file name");
98111
}
99112

100-
private static byte[] GetFileContent(MemoryStream memory)
113+
public static DatFileFormat ResolveFileFormat()
101114
{
102115
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
103-
{
104-
var encrypted = Crypt.SavingSaveAsMacOS(memory.ToArray());
105-
return encrypted;
106-
}
116+
return DatFileFormat.Mac;
117+
return DatFileFormat.Windows;
118+
}
119+
120+
private static byte[] GetFileContent(MemoryStream memory, DatFileFormat format)
121+
{
122+
if (format == DatFileFormat.Mac)
123+
return Crypt.SavingSaveAsMacOS(memory.ToArray());
107124

108125
var base64 = GameConvert.ToBase64(Crypt.GZipCompress(memory.ToArray()));
109126
return Crypt.XOR(Encoding.ASCII.GetBytes(base64), 0xB);
127+
}
110128

129+
private static bool StartsWith(Stream stream, ReadOnlySpan<byte> prefix)
130+
{
131+
if (!stream.CanSeek)
132+
throw new ArgumentException($"{nameof(stream)} is not seekable. This can lead to bugs.");
133+
if (stream.Length < prefix.Length)
134+
return false;
135+
var position = stream.Position;
136+
var buffer = new byte[prefix.Length];
137+
var read = 0;
138+
while (read != buffer.Length)
139+
read += stream.Read(buffer, read, buffer.Length - read);
140+
stream.Seek(position, SeekOrigin.Begin);
141+
return buffer.AsSpan().IndexOf(prefix) == 0;
111142
}
112143
}
113144
}

0 commit comments

Comments
 (0)