2026-04-23 17:08:47 +02:00
|
|
|
// Modules
|
|
|
|
|
using ModMenu;
|
|
|
|
|
using System.Net.Http;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using System.Xml.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Drawing;
|
|
|
|
|
using DrawingColor = System.Drawing.Color;
|
|
|
|
|
using System.Net;
|
|
|
|
|
using System.Dynamic;
|
|
|
|
|
using System.Text.Json;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
using SixLabors.ImageSharp;
|
|
|
|
|
using SixLabors.ImageSharp.Formats.Tga;
|
|
|
|
|
using NativeFileDialogSharp;
|
|
|
|
|
|
|
|
|
|
namespace Func {
|
|
|
|
|
public class Player {
|
|
|
|
|
public string nnidHex { get; set; }
|
|
|
|
|
public uint PID { get; set; }
|
|
|
|
|
public string NNID { get; set; }
|
|
|
|
|
public string Name { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class SessionData {
|
|
|
|
|
public uint SessionID { get; set; }
|
|
|
|
|
public List<Player> Players { get; set; } = new List<Player>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class ExtFunc {
|
|
|
|
|
public static async Task<string> GetNNID(int pid, bool isPretendo = true) {
|
|
|
|
|
using (HttpClient client = new HttpClient()) {
|
|
|
|
|
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0");
|
|
|
|
|
// Thanks @tombun2 for pointing this out
|
|
|
|
|
// src: https://github.com/Milk-Cool/PretendoLookup/blob/932263e2c5e7f2e7527ff5dc690a8701dbf39885/miidata.js
|
|
|
|
|
client.DefaultRequestHeaders.Add("X-Nintendo-Client-ID", "a2efa818a34fa16b8afbc8a74eba3eda");
|
|
|
|
|
client.DefaultRequestHeaders.Add("X-Nintendo-Client-Secret", "c91cdb5658bd4954ade78533a339cf9a");
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// src: https://github.com/kinnay/NintendoClients/wiki/Account-Server
|
|
|
|
|
if (isPretendo) {
|
|
|
|
|
HttpResponseMessage response = await client.GetAsync($"http://account.pretendo.cc/v1/api/miis?pids={pid}");
|
|
|
|
|
if (response.IsSuccessStatusCode) {
|
|
|
|
|
string content = await response.Content.ReadAsStringAsync();
|
|
|
|
|
XDocument doc = XDocument.Parse(content);
|
|
|
|
|
string userId = doc.Root?.Element("mii")?.Element("user_id")?.Value;
|
|
|
|
|
return userId;
|
|
|
|
|
} else {
|
|
|
|
|
return "0";
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2026-08-08 08:37:46 +02:00
|
|
|
HttpResponseMessage response = await client.GetAsync($"https://account.spbr.net/v1/api/admin/mapped_ids?input_type=pid&output_type=user_id&input={pid}");
|
2026-04-23 17:08:47 +02:00
|
|
|
if (response.IsSuccessStatusCode) {
|
|
|
|
|
string content = await response.Content.ReadAsStringAsync();
|
|
|
|
|
XDocument doc = XDocument.Parse(content);
|
|
|
|
|
string userId = doc.Root?.Element("mapped_id")?.Element("out_id")?.Value;
|
|
|
|
|
return userId;
|
|
|
|
|
} else {
|
|
|
|
|
return "0";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex) {
|
|
|
|
|
Console.WriteLine(ex.Message);
|
|
|
|
|
return "0";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public static int FindPattern(byte[] data, byte[] pattern) {
|
|
|
|
|
for (int i = 0; i <= data.Length - pattern.Length; i++) {
|
|
|
|
|
bool match = true;
|
|
|
|
|
for (int j = 0; j < pattern.Length; j++) {
|
|
|
|
|
if (data[i + j] != pattern[j]) {
|
|
|
|
|
match = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (match)
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class Codes {
|
|
|
|
|
public static void DisconnectFromLobby() {
|
|
|
|
|
Int16 timer = 0;
|
|
|
|
|
var client_time_ptr = Program.readUInt32((uint)(Program.readUInt32((uint)(Program.readUInt32((uint)(Program.readUInt32(0x101E5660) + 0x28)) + 0x24)) + 0x40));
|
|
|
|
|
Program.writeInt16((uint)(client_time_ptr + 0x1F8), timer);
|
|
|
|
|
}
|
|
|
|
|
public static void NameChanger(string name) {
|
|
|
|
|
Console.WriteLine($"Changing Name to: {name}...");
|
|
|
|
|
var name_pointer = Program.readUInt32(0x101E80A4);
|
|
|
|
|
Program.writeBytes((uint)(name_pointer + 0x8C), new byte[32]);
|
|
|
|
|
Program.writeBytes((uint)(name_pointer + 0x8C), Encoding.BigEndianUnicode.GetBytes(name));
|
|
|
|
|
}
|
|
|
|
|
public static string GetSessionData() {
|
|
|
|
|
bool isPretendo = true;
|
|
|
|
|
bool isSpfnTried = false;
|
|
|
|
|
string nnidStr = null;
|
|
|
|
|
httpMenu.session.SessionID = 0;
|
|
|
|
|
httpMenu.session.Players.Clear();
|
|
|
|
|
httpMenu.session = new SessionData();
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < 8; i++) {
|
|
|
|
|
var ptrToPlayerInfo = Program.readUInt32((uint)(Program.readUInt32((uint)(Program.readUInt32(0x101DD330) + 0x10)) + (uint)(i * 4))); // PlayerInfo Pointer
|
|
|
|
|
var name = Encoding.BigEndianUnicode.GetString(Program.readBytes(ptrToPlayerInfo + 0x6, 32)).Replace("\n", "").Replace("\r", "").TrimEnd('\0');
|
|
|
|
|
var pidRaw = Program.readUInt32(ptrToPlayerInfo + 0xd0);
|
|
|
|
|
if (pidRaw != 0) {
|
|
|
|
|
nnidStr = ExtFunc.GetNNID((int)pidRaw, isPretendo).GetAwaiter().GetResult();
|
|
|
|
|
} else {
|
|
|
|
|
nnidStr = "0";
|
|
|
|
|
}
|
|
|
|
|
if (nnidStr == "0" && !isSpfnTried && pidRaw != 0) {
|
|
|
|
|
nnidStr = ExtFunc.GetNNID((int)pidRaw, false).GetAwaiter().GetResult();
|
|
|
|
|
if (nnidStr != "0") {
|
|
|
|
|
isPretendo = false;
|
|
|
|
|
} else {
|
|
|
|
|
isSpfnTried = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
string nnidHex = BitConverter.ToString(BitConverter.GetBytes(pidRaw)).Replace("-", "");
|
|
|
|
|
|
|
|
|
|
httpMenu.session.Players.Add(new Player {
|
|
|
|
|
nnidHex = nnidHex,
|
|
|
|
|
PID = pidRaw,
|
|
|
|
|
NNID = nnidStr,
|
|
|
|
|
Name = name
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
var ptr = Program.readUInt32(0x101E8980);
|
|
|
|
|
uint sessionID;
|
|
|
|
|
if (ptr != 0) {
|
|
|
|
|
var index = Program.readBytes(ptr + 0xBD, 1)[0];
|
|
|
|
|
sessionID = Program.readUInt32(ptr + index + 0xCC);
|
|
|
|
|
} else {
|
|
|
|
|
sessionID = 0;
|
|
|
|
|
}
|
|
|
|
|
httpMenu.session.SessionID = sessionID;
|
|
|
|
|
|
|
|
|
|
string json = JsonSerializer.Serialize(httpMenu.session, new JsonSerializerOptions { WriteIndented = true });
|
|
|
|
|
return json;
|
|
|
|
|
}
|
|
|
|
|
public static void forceSessiondId(uint sessionId) {
|
|
|
|
|
// Console.WriteLine(sessionId);
|
|
|
|
|
var p = Program.readUInt32(0x101DD330);
|
|
|
|
|
Program.writeUInt32((uint)(p + 0x260), (uint)sessionId);
|
|
|
|
|
}
|
|
|
|
|
public static void faceimgtga(int format) {
|
|
|
|
|
var p = Program.readUInt32(0x101DCDB0) + 0x150;
|
|
|
|
|
var localPlayer = Program.readBytes(p, 5376);
|
|
|
|
|
byte[] pattern = new byte[]{ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00 };
|
|
|
|
|
int faceimg_offset = ExtFunc.FindPattern(localPlayer, pattern);
|
|
|
|
|
if (faceimg_offset == -1) {
|
|
|
|
|
Console.WriteLine("Could not find FaceImg.tga pattern!");
|
|
|
|
|
} else {
|
|
|
|
|
var faceimg = Program.readBytes((uint)(p + faceimg_offset), 65580);
|
|
|
|
|
string path = null;
|
|
|
|
|
string defaultPath = null;
|
|
|
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
|
|
|
|
|
defaultPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
|
|
|
|
|
} else {
|
|
|
|
|
defaultPath = Path.GetDirectoryName(Environment.ProcessPath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (format == 0) {
|
|
|
|
|
path = Dialog.FileSave("tga", defaultPath).Path;
|
|
|
|
|
if (string.IsNullOrEmpty(path)) return;
|
|
|
|
|
File.WriteAllBytes(path, faceimg);
|
|
|
|
|
} else if (format == 1) {
|
|
|
|
|
path = Dialog.FileSave("png", defaultPath).Path;
|
|
|
|
|
if (string.IsNullOrEmpty(path)) return;
|
|
|
|
|
using (Image image = Image.Load(faceimg)) {
|
|
|
|
|
image.SaveAsPng(path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (string.IsNullOrEmpty(path)) return;
|
|
|
|
|
// on *nix systems set file to be owned by everyone.
|
|
|
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
|
|
|
|
|
File.SetUnixFileMode(path, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute | UnixFileMode.GroupRead | UnixFileMode.GroupWrite | UnixFileMode.GroupExecute | UnixFileMode.OtherRead | UnixFileMode.OtherWrite | UnixFileMode.OtherExecute);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public static string TotalWins() {
|
|
|
|
|
try {
|
|
|
|
|
var p_SaveDataMgr = Program.readUInt32((uint)(Program.readUInt32(SaveEditor.SAVEDATAMGR_ROOT_PTR) + 0xC));
|
|
|
|
|
return $"{Program.readUInt32((uint)(p_SaveDataMgr + 0xCB38))}";
|
|
|
|
|
} catch {
|
|
|
|
|
return "Game not fully loaded yet...";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public static string TotalLoss() {
|
|
|
|
|
try {
|
|
|
|
|
var p_SaveDataMgr = Program.readUInt32((uint)(Program.readUInt32(SaveEditor.SAVEDATAMGR_ROOT_PTR) + 0xC));
|
|
|
|
|
return $"{Program.readUInt32((uint)(p_SaveDataMgr + 0xCB3C))}";
|
|
|
|
|
} catch {
|
|
|
|
|
return "Game not fully loaded yet...";
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-06-27 19:46:06 +02:00
|
|
|
|
|
|
|
|
|
2026-07-03 01:38:36 +02:00
|
|
|
public static string EquippedGear(string slot) {
|
2026-06-27 19:46:06 +02:00
|
|
|
try {
|
|
|
|
|
var p_SaveDataMgr = Program.readUInt32((uint)(Program.readUInt32(SaveEditor.SAVEDATAMGR_ROOT_PTR) + 0xC));
|
2026-07-03 01:38:36 +02:00
|
|
|
switch (slot.ToLower()) {
|
|
|
|
|
case "headgear":
|
|
|
|
|
return $"{Program.readUInt32((uint)(p_SaveDataMgr + 0x188))}";
|
|
|
|
|
|
|
|
|
|
case "clothes":
|
|
|
|
|
return $"{Program.readUInt32((uint)(p_SaveDataMgr + 0x180))}";
|
|
|
|
|
|
|
|
|
|
case "shoes":
|
|
|
|
|
return $"{Program.readUInt32((uint)(p_SaveDataMgr + 0x184))}";
|
|
|
|
|
|
|
|
|
|
case "weapon":
|
|
|
|
|
return $"{Program.readUInt32((uint)(p_SaveDataMgr + 0x18C))}";
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-27 08:36:30 +02:00
|
|
|
} catch {
|
|
|
|
|
return "Game not fully loaded yet...";
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-23 17:08:47 +02:00
|
|
|
}
|
|
|
|
|
}
|