using ModMenu; using Func; using System; using System.Runtime.InteropServices; using System.Text.Json; using System.Collections.Generic; using System.Linq; namespace ModMenu { [StructLayout(LayoutKind.Sequential)] public struct Gear { public uint GearID; public uint UnlockedSlots; public uint AvailableSlots; public uint Slot1; public uint Slot2; public uint Slot3; public uint Unk1; // Default: 0x00000000 public uint Unk2; // Default: 0x00000000 public uint Unk3; // Default: 0x00000000 public uint TimestampLastUsed; public uint HideNewIcon; // Enabled: 0x00010000 | Disabled: 0x00000000 public uint Unk4; // Default: 0x00000000 } public class GearQueueSlot { public string Class { get; set; } public uint Slot { get; set; } public byte[] Data { get; set; } } public class SaveEditor { // [[0x106E975C] + 0xC] + 0x61A0 # Start of Headgear Definitions // [[0x106E975C] + 0xC] + 0x31A0 # Start of Clothes Definitions // [[0x106E975C] + 0xC] + 0x1A0 # Start of Shoes Definitions // Each Gear class is 0x3000 big and each indivdual gear is 0x30 bytes (48 Bytes) // 0x3000 / 0x30 = 0x100 Slots (256 Slots) // 48 Bytes are split evenly according to struct in order above. public static uint SAVEDATAMGR_ROOT_PTR = 0x101E675C; // Begin of Save Data [0x101E675C] + 0xC public static string FetchAllGear() { var p_save_start = Program.readUInt32((uint)(Program.readUInt32(SAVEDATAMGR_ROOT_PTR) + 0xC)); uint GearSectionSize = 0x3000; var results = new Dictionary>(); int structSize = Marshal.SizeOf(); var categories = new (string Name, uint Offset)[] { ("headgear", 0x61A0), ("clothing", 0x31A0), ("shoes", 0x1A0) }; foreach (var cat in categories) { var gearList = new List(); byte[] sectionRaw = Program.readBytes(p_save_start + cat.Offset, GearSectionSize); for (int i = 0; i < GearSectionSize; i += structSize) { byte[] structBuffer = new byte[structSize]; Buffer.BlockCopy(sectionRaw, i, structBuffer, 0, structSize); for (int j = 0; j < structBuffer.Length; j += 4) { Array.Reverse(structBuffer, j, 4); } Gear item = ByteArrayToStructure(structBuffer); gearList.Add(item); } results.Add(cat.Name, gearList); } return JsonSerializer.Serialize(results, new JsonSerializerOptions { WriteIndented = true, IncludeFields = true }); } public static void QueueGearSlot(uint Slot, string Class, uint GearID, uint Slot1, uint Slot2, uint Slot3) { if (Slot > 255) { Console.WriteLine("You can only write to Slot 0-255..."); return; } Gear newGear = new Gear { GearID = GearID, UnlockedSlots = 4, AvailableSlots = 4, Slot1 = Slot1, Slot2 = Slot2, Slot3 = Slot3, Unk1 = 0, Unk2 = 0, Unk3 = 0, TimestampLastUsed = (uint)DateTimeOffset.UtcNow.ToUnixTimeSeconds(), HideNewIcon = 0x00010000, Unk4 = 0 }; byte[] rawData = StructureToByteArray(newGear); for (int i = 0; i < rawData.Length; i += 4) { Array.Reverse(rawData, i, 4); } httpMenu.GearQueue.Add(new GearQueueSlot { Class = Class, Slot = Slot, Data = rawData }); } public static void WriteGearQueue() { var p_save_start = Program.readUInt32((uint)(Program.readUInt32(SAVEDATAMGR_ROOT_PTR) + 0xC)); uint start_offset = 0; foreach (var Gear in httpMenu.GearQueue.ToList()) { switch (Gear.Class.ToLower()) { case "headgear": start_offset = 0x61A0; break; case "clothing": start_offset = 0x31A0; break; case "shoes": start_offset = 0x1A0; break; default: Console.WriteLine("Invalid Class selected"); return; } Program.writeBytes((uint)(p_save_start + start_offset + (Gear.Slot * 0x30)), Gear.Data); } } public static void ChangeGender(uint gender) { if (gender > 2) { gender = 0; } var p_save_start = Program.readUInt32((uint)(Program.readUInt32(SAVEDATAMGR_ROOT_PTR) + 0xC)); Program.writeUInt32((uint)(p_save_start + 0x190), gender); } public static void ChangeLevel(uint level) { uint ActualLevel = level - 1; var p_save_start = Program.readUInt32((uint)(Program.readUInt32(SAVEDATAMGR_ROOT_PTR) + 0xC)); Program.writeUInt32((uint)(p_save_start + 0xA5A8), ActualLevel); } public static void ChangeCash(uint amount) { if (amount > 9999999) { amount = 9999999; } var p_save_start = Program.readUInt32((uint)(Program.readUInt32(SAVEDATAMGR_ROOT_PTR) + 0xC)); Program.writeUInt32((uint)(p_save_start + 0xA5A0), amount); } public static void ChangeSeaSnails(uint amount) { if (amount > 999) { amount = 999; } var p_save_start = Program.readUInt32((uint)(Program.readUInt32(SAVEDATAMGR_ROOT_PTR) + 0xC)); Program.writeUInt32((uint)(p_save_start + 0xA5B4), amount); } public static void ChangeEquippedWeapon(uint WeaponSetID) { var p_save_start = Program.readUInt32((uint)(Program.readUInt32(SAVEDATAMGR_ROOT_PTR) + 0xC)); Program.writeUInt32((uint)(p_save_start + 0x18C), WeaponSetID); } private static T ByteArrayToStructure(byte[] bytes) where T : struct { GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); try { return Marshal.PtrToStructure(handle.AddrOfPinnedObject()); } finally { handle.Free(); } } private static byte[] StructureToByteArray(object obj) { int len = Marshal.SizeOf(obj); byte[] arr = new byte[len]; IntPtr ptr = Marshal.AllocHGlobal(len); try { Marshal.StructureToPtr(obj, ptr, true); Marshal.Copy(ptr, arr, 0, len); } finally { Marshal.FreeHGlobal(ptr); } return arr; } } }