mirror of
https://git.crafterpika.cc/crafterpika/SplatTool-public.git
synced 2026-08-17 10:42:15 +02:00
253 lines
11 KiB
C#
253 lines
11 KiB
C#
using ProcessWrapper;
|
|
using Func;
|
|
using System.Diagnostics;
|
|
using System.Runtime.InteropServices;
|
|
using System.IO;
|
|
using Photino.NET;
|
|
using System.Text.Json;
|
|
|
|
namespace ModMenu {
|
|
public class Program {
|
|
public static int pid = 0;
|
|
public static int OsType = 0;
|
|
public static IProcessWrapper OsProcessWrapper = null;
|
|
public static ulong cbase = 0;
|
|
public static ulong cbaseCorrected = 0;
|
|
public static Dictionary<string, string> config = null;
|
|
|
|
public static byte[] readBytes(uint address, uint length) {
|
|
return OsProcessWrapper.ReadProcessMemory(pid, (ulong)(cbase + 0xE000000) + address - 0x10000000, length);
|
|
}
|
|
|
|
public static uint readUInt32(uint address) {
|
|
return BitConverter.ToUInt32(OsProcessWrapper.ReadProcessMemory(pid, (ulong)(cbase + 0xE000000) + address - 0x10000000, 4).Reverse().ToArray(), 0);
|
|
}
|
|
|
|
public static float readFloat(uint address) {
|
|
var b = OsProcessWrapper.ReadProcessMemory(pid, (ulong)(cbase + 0xE000000) + address - 0x10000000, 4);
|
|
Array.Reverse(b, 0, b.Length);
|
|
return BitConverter.ToSingle(b, 0);
|
|
}
|
|
|
|
public static void writeBytes(uint address, byte[] bytes) {
|
|
OsProcessWrapper.WriteProcessMemory(pid, (ulong)(cbase + 0xE000000) + address - 0x10000000, bytes);
|
|
}
|
|
|
|
public static void writeUInt32(uint address, uint value) {
|
|
byte[] b = BitConverter.GetBytes(value);
|
|
Array.Reverse(b, 0, b.Length);
|
|
OsProcessWrapper.WriteProcessMemory(pid, (ulong)(cbase + 0xE000000) + address - 0x10000000, b);
|
|
}
|
|
|
|
public static void writeInt16(uint address, Int16 value) {
|
|
byte[] b = BitConverter.GetBytes(value);
|
|
Array.Reverse(b, 0, b.Length);
|
|
OsProcessWrapper.WriteProcessMemory(pid, (ulong)(cbase + 0xE000000) + address - 0x10000000, b);
|
|
}
|
|
|
|
public static void writeFloat(uint address, float value) {
|
|
byte[] b = BitConverter.GetBytes(value);
|
|
Array.Reverse(b, 0, b.Length);
|
|
OsProcessWrapper.WriteProcessMemory(pid, (ulong)(cbase + 0xE000000) + address - 0x10000000, b);
|
|
}
|
|
|
|
public static string FindExecutable(string name) {
|
|
// Not required for Mac/Windows
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return null;
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return null;
|
|
|
|
var pathEnv = Environment.GetEnvironmentVariable("PATH");
|
|
if (string.IsNullOrEmpty(pathEnv)) return null;
|
|
var paths = pathEnv.Split(':');
|
|
|
|
foreach (var path in paths) {
|
|
var fullPath = Path.Combine(path, name);
|
|
if (File.Exists(fullPath)) {
|
|
return fullPath;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static bool IsWayland() {
|
|
string runtimeDir = Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR");
|
|
if (string.IsNullOrEmpty(runtimeDir)) return false;
|
|
|
|
return File.Exists(Path.Combine(runtimeDir, "wayland-0"));
|
|
}
|
|
|
|
[STAThread]
|
|
public static void Main(string[] args) {
|
|
Console.OutputEncoding = System.Text.Encoding.UTF8;
|
|
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
|
|
OsType = 1;
|
|
OsProcessWrapper = new LinuxProcessWrapper();
|
|
if (OsProcessWrapper.getuid() != 0) {
|
|
var pkexecPath = FindExecutable("pkexec");
|
|
|
|
if (!string.IsNullOrEmpty(pkexecPath)) {
|
|
string exePath = Process.GetCurrentProcess().MainModule.FileName;
|
|
string display;
|
|
string runtimeDir;
|
|
string command = null;
|
|
|
|
Console.WriteLine($"Wayland is: {IsWayland()}");
|
|
|
|
if (IsWayland()) {
|
|
display = Environment.GetEnvironmentVariable("WAYLAND_DISPLAY");
|
|
runtimeDir = Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR");
|
|
command = $"--disable-internal-agent env XDG_RUNTIME_DIR={runtimeDir} WAYLAND_DISPLAY={display} \"{exePath}\"";
|
|
} else {
|
|
display = Environment.GetEnvironmentVariable("DISPLAY");
|
|
command = $"--disable-internal-agent env DISPLAY={display} \"{exePath}\"";
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(command)) {
|
|
Console.WriteLine("Please run as root...");
|
|
System.Environment.Exit(1);
|
|
}
|
|
|
|
var startInfo = new ProcessStartInfo {
|
|
FileName = pkexecPath,
|
|
Arguments = command,
|
|
UseShellExecute = false
|
|
};
|
|
|
|
try {
|
|
using (Process elevatedProcess = Process.Start(startInfo)) {
|
|
elevatedProcess?.WaitForExit();
|
|
Environment.Exit(elevatedProcess?.ExitCode ?? 0);
|
|
};
|
|
} catch (Exception ex) {
|
|
Console.WriteLine($"Elevation failed: {ex.Message}");
|
|
Environment.Exit(1);
|
|
}
|
|
} else {
|
|
Console.WriteLine("Please run as root...");
|
|
System.Environment.Exit(1);
|
|
}
|
|
}
|
|
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
|
|
OsType = 2;
|
|
OsProcessWrapper = new MacProcessWrapper();
|
|
if (OsProcessWrapper.getuid() != 0) {
|
|
string basePath = AppContext.BaseDirectory;
|
|
string rootHelperPath = Path.Combine(basePath, "root_helper");
|
|
|
|
if (Path.Exists(rootHelperPath)) {
|
|
var startInfo = new ProcessStartInfo {
|
|
FileName = rootHelperPath,
|
|
UseShellExecute = false
|
|
};
|
|
|
|
try {
|
|
Process.Start(startInfo);
|
|
Environment.Exit(0);
|
|
} catch (Exception ex) {
|
|
Console.WriteLine($"Elevation failed: {ex.Message}");
|
|
Environment.Exit(1);
|
|
}
|
|
} else {
|
|
Console.WriteLine("Please run as root...");
|
|
System.Environment.Exit(1);
|
|
}
|
|
}
|
|
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
|
|
OsType = 3;
|
|
OsProcessWrapper = new WindowsProcessWrapper();
|
|
} else {
|
|
Console.WriteLine("Unsupported Operating System exiting...");
|
|
return;
|
|
}
|
|
|
|
// Root access needed past here for Linux/Mac
|
|
string[] targetNames = { "cemu", "xapfish", ".cemu-wrapped", "cemu_release" };
|
|
Process targetProcess = Process.GetProcesses()
|
|
.FirstOrDefault(p => targetNames.Any(name => p.ProcessName.Equals(name, StringComparison.OrdinalIgnoreCase)));
|
|
|
|
if (targetProcess == null) {
|
|
new PhotinoWindow()
|
|
.SetSize(0, 0)
|
|
.SetMinimized(true)
|
|
.SetNotificationRegistrationId("8FDF1B15-3408-47A6-8EF5-2B0676B76277")
|
|
.SetNotificationsEnabled(false)
|
|
.RegisterWindowCreatedHandler((sender, e) => {
|
|
var window = sender as PhotinoWindow;
|
|
window.ShowMessage("Error", "Could not find Cemu process.", PhotinoDialogButtons.Ok, PhotinoDialogIcon.Error);
|
|
window.Close();
|
|
System.Environment.Exit(0);
|
|
})
|
|
.LoadRawString("<html></html>")
|
|
.WaitForClose();
|
|
return;
|
|
}
|
|
pid = targetProcess.Id;
|
|
cbase = OsProcessWrapper.FindCemuBase(pid, 1308622848);
|
|
Console.WriteLine($"Found target process: {targetProcess.ProcessName} (PID: {pid})");
|
|
|
|
if (cbase != 0) {
|
|
Console.WriteLine("SplatTool v3.2.0 by CrafterPika");
|
|
Console.WriteLine("Special Thanks: Winterberry, Javi.ig and Tombuntu, KittenTM.");
|
|
|
|
cbaseCorrected = (Program.cbase + 0xE000000) - 0x10000000;
|
|
|
|
string indexPath;
|
|
string iconPath;
|
|
string configPath;
|
|
|
|
if (OsType == 2) {
|
|
indexPath = Path.Combine(AppContext.BaseDirectory, "..", "Resources", "modmenu", "index.html");
|
|
iconPath = Path.Combine(AppContext.BaseDirectory, "..", "Resources", "icon.ico");
|
|
configPath = Path.Combine(AppContext.BaseDirectory, "..", "Resources", "config.json");
|
|
} else {
|
|
indexPath = Path.Combine(AppContext.BaseDirectory, "modmenu", "index.html");
|
|
iconPath = Path.Combine(AppContext.BaseDirectory, "icon.ico");
|
|
configPath = Path.Combine(AppContext.BaseDirectory, "config.json");
|
|
}
|
|
|
|
string configJson = File.ReadAllText(configPath);
|
|
config = JsonSerializer.Deserialize<Dictionary<string, string>>(configJson);
|
|
|
|
var window = new PhotinoWindow()
|
|
.SetTitle("SplatTool Mod Menu")
|
|
.SetNotificationRegistrationId("8FDF1B15-3408-47A6-8EF5-2B0676B76277")
|
|
.SetNotificationsEnabled(false)
|
|
.SetUseOsDefaultSize(false)
|
|
.SetSize(900, 700)
|
|
.Center()
|
|
.RegisterWebMessageReceivedHandler((object sender, string message) => {
|
|
var w = sender as PhotinoWindow;
|
|
var data = JsonSerializer.Deserialize<ModMenuRequest>(message);
|
|
if (data != null) {
|
|
string result = httpMenu.RunCode(data);
|
|
w.SendWebMessage(result);
|
|
}
|
|
});
|
|
|
|
if (File.Exists(iconPath)) {
|
|
window.SetIconFile(iconPath);
|
|
}
|
|
|
|
window.Load(indexPath).WaitForClose();
|
|
|
|
} else {
|
|
new PhotinoWindow()
|
|
.SetSize(0, 0)
|
|
.SetNotificationRegistrationId("8FDF1B15-3408-47A6-8EF5-2B0676B76277")
|
|
.SetNotificationsEnabled(false)
|
|
.SetMinimized(true)
|
|
.RegisterWindowCreatedHandler((sender, e) => {
|
|
var window = sender as PhotinoWindow;
|
|
window.ShowMessage("Error", "Cemu base not found! Is Splatoon running?!", PhotinoDialogButtons.Ok, PhotinoDialogIcon.Error);
|
|
window.Close();
|
|
System.Environment.Exit(0);
|
|
})
|
|
.LoadRawString("<html></html>")
|
|
.WaitForClose();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|