Decompiled source of SaveFileSharingMod v1.1.2

Mods/SaveFileSharing.dll

Decompiled 5 months ago
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using MelonLoader;
using MelonLoader.Preferences;
using SaveFileSharing;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(SaveFileSharingMod), "SaveFileSharingMod", "1.0", "Sour420", null)]
[assembly: MelonColor]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("SaveFileSharing")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("SaveFileSharing")]
[assembly: AssemblyTitle("SaveFileSharing")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace SaveFileSharing;

public static class BuildInfo
{
	public const string Name = "SaveFileSharingMod";

	public const string Description = "Share and get save files from your friends so you dont need one person to always be on to play!";

	public const string Author = "Sour420";

	public const string Company = null;

	public const string Version = "1.0";

	public const string DownloadLink = null;
}
public class SaveFileSharingMod : MelonMod
{
	private static readonly HttpClient httpClient = new HttpClient();

	private static string downloadUrl;

	private static string uploadUrl;

	private static string apiKey;

	private static int saveSlotNumber;

	private static string saveFolderPath;

	private static string zipFilePath;

	public override void OnApplicationStart()
	{
		MelonPreferences_Category obj = MelonPreferences.CreateCategory("SaveFileSharingMod");
		obj.SetFilePath("UserData/SaveFileSharing.cfg");
		MelonPreferences_Entry<int> val = obj.CreateEntry<int>("SaveSlotNumber", 1, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		MelonPreferences_Entry<string> val2 = obj.CreateEntry<string>("DownloadUrl", "https://[ID].supabase.co/storage/v1/object/public/save-sync/SaveGame_", (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		MelonPreferences_Entry<string> val3 = obj.CreateEntry<string>("UploadUrl", "https://[ID].supabase.co/storage/v1/object/save-sync/SaveGame_", (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		MelonPreferences_Entry<string> val4 = obj.CreateEntry<string>("ApiKey", "", (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		MelonPreferences_Entry<string> val5 = obj.CreateEntry<string>("SteamID", "", (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		obj.SaveToFile(true);
		if (string.IsNullOrEmpty(val5.Value) || !val5.Value.All(char.IsDigit))
		{
			MelonLogger.Error("[SaveFileSharingMod] SteamID is invalid! Please make sure it only contains numbers.");
			return;
		}
		string path = "Saves\\" + val5.Value;
		string path2 = Path.Combine(Application.persistentDataPath, path);
		saveSlotNumber = val.Value;
		downloadUrl = $"{val2.Value}{saveSlotNumber}.zip";
		uploadUrl = $"{val3.Value}{saveSlotNumber}.zip";
		apiKey = val4.Value;
		saveFolderPath = Path.Combine(path2, $"SaveGame_{saveSlotNumber}");
		zipFilePath = Path.Combine(path2, $"SaveGame_{saveSlotNumber}.zip");
		MelonLogger.Msg($"SaveFileMod initialized for SaveGame_{saveSlotNumber}");
		MelonLogger.Msg("Save Path: " + saveFolderPath);
		Task.Run((Func<Task?>)DownloadAndExtractSave).Wait();
	}

	public override void OnApplicationQuit()
	{
		Task.Run((Func<Task?>)ZipAndUploadSave).Wait();
	}

	private async Task DownloadAndExtractSave()
	{
		try
		{
			MelonLogger.Msg("Downloading save file...");
			byte[] bytes = await httpClient.GetByteArrayAsync(downloadUrl);
			File.WriteAllBytes(zipFilePath, bytes);
			if (Directory.Exists(saveFolderPath))
			{
				Directory.Delete(saveFolderPath, recursive: true);
			}
			ZipFile.ExtractToDirectory(zipFilePath, saveFolderPath);
			MelonLogger.Msg("Save file downloaded and extracted successfully!");
		}
		catch (Exception ex)
		{
			MelonLogger.Warning("Downloading save file failed: " + ex.Message);
		}
	}

	private async Task ZipAndUploadSave()
	{
		_ = 1;
		try
		{
			MelonLogger.Msg("Zipping save folder...");
			if (Directory.Exists(saveFolderPath))
			{
				if (File.Exists(zipFilePath))
				{
					File.Delete(zipFilePath);
				}
				using (ZipArchive destination = ZipFile.Open(zipFilePath, ZipArchiveMode.Create))
				{
					string[] files = Directory.GetFiles(saveFolderPath, "*", SearchOption.AllDirectories);
					foreach (string text in files)
					{
						string entryName = text.Substring(saveFolderPath.Length + 1);
						destination.CreateEntryFromFile(text, entryName);
					}
				}
				MelonLogger.Msg("Zip created! Uploading to Supabase...");
				byte[] content = File.ReadAllBytes(zipFilePath);
				HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Put, uploadUrl)
				{
					Content = new ByteArrayContent(content)
				};
				httpRequestMessage.Headers.Add("Authorization", "Bearer " + apiKey);
				httpRequestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
				HttpResponseMessage response = await httpClient.SendAsync(httpRequestMessage);
				if (response.IsSuccessStatusCode)
				{
					MelonLogger.Msg("Save file uploaded successfully to Supabase!");
					return;
				}
				string value = await response.Content.ReadAsStringAsync();
				MelonLogger.Warning($"Upload failed: {response.StatusCode}. Response: {value}");
			}
			else
			{
				MelonLogger.Warning("Save folder does not exist, aborting zip operation.");
			}
		}
		catch (Exception ex)
		{
			MelonLogger.Warning("Upload failed: " + ex.Message);
		}
	}
}