SplatTool-public/SaveEditor.cs

183 lines
6.9 KiB
C#

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<string, List<Gear>>();
int structSize = Marshal.SizeOf<Gear>();
var categories = new (string Name, uint Offset)[] {
("headgear", 0x61A0),
("clothing", 0x31A0),
("shoes", 0x1A0)
};
foreach (var cat in categories) {
var gearList = new List<Gear>();
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<Gear>(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<T>(byte[] bytes) where T : struct {
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
try {
return Marshal.PtrToStructure<T>(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;
}
}
}