nice thanks!!!10900 green shards
Your gonna hate me for this but unfortunately its still not enough...10900 green shards
Would maxing out all green shards give me issues?10900 green shards
ok ill check it out thanksthe max is 22400 and 10900 should be just under half. i set this to 14000.
ok ill check it out thanks
Ok its just above the threshold for the green crystal locator thanks!the max is 22400 and 10900 should be just under half. i set this to 14000.
Do you think there's a save game edit you can do to make it that save stations refil ammo on hard mode?i added bat files to first post that edit green shards in the first 2 instances it finds id. you can just drop save on to them and it will edit them, if it not enough you can edit bat files in text editor
Or is that more of a mod?Do you think there's a save game edit you can do to make it that save stations refil ammo on hard mode?
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import struct
import zlib
import binascii
import copy
ITEM_DATABASE = {
# Psychic Items
b'\x10\xC0\xF5\x03': "Psychic Glove (1)",
b'\x07\x66\xA5\x04': "Psychic Glove (2)",
b'\x6D\x69\x68\x18': "Psychic Shot",
b'\x05\x7C\xB5\x29': "Psychic Boots (1)",
b'\xA8\x2E\xFE\x2A': "Psychic Boots (2)",
b'\xDF\xD2\x69\x2C': "Psychic Crystal/Visor (1)",
b'\xA0\x3C\x6C\x2D': "Psychic Crystal/Visor (2)",
b'\x52\x21\x65\x73': "Psychic Charge Shot",
b'\xF4\xB9\xC7\x84': "Psychic Power Bomb",
b'\x84\x7F\x51\x8C': "Psychic Grapple (1)",
b'\xDA\x68\xCA\x8E': "Psychic Grapple (2)",
# Teleporter
b'\x1C\x2B\x9F\x08': "Teleporter Patch (1)",
b'\x79\x6A\xEA\x0C': "Teleporter Patch (2)",
b'\x35\xAA\x9D\x0E': "Teleporter Patch (3)",
b'\x22\x44\x44\x10': "Teleporter Patch (4)",
b'\xF8\x1B\xC6\x7D': "Teleporter Chip",
# Weapons & Upgrades
b'\xBD\xA6\xE8\x13': "Super Thunder Shot",
b'\x04\x97\x8C\x1E': "Thunder Chip",
b'\x4F\x53\x59\x8A': "Thunder Shot",
b'\x71\x94\xD8\xB0': "Charged Thunder Shot (1)",
b'\x5B\x01\x94\xB5': "Charged Thunder Shot (2)",
b'\x97\x42\xB4\x27': "Charged Ice Shot",
b'\x71\x64\x03\x2B': "Ice Shot",
b'\xD3\x2E\xC3\x5F': "Vi-O-La IC Suit (1)",
b'\xFE\x04\xE3\x6A': "Vi-O-La IC Suit (2)",
b'\x99\x52\x4F\x74': "Vi-O-La Suit",
b'\x82\x96\xDD\x6C': "Fire Chip",
b'\x85\x40\x45\x6F': "Fire Chip (2)",
b'\x0A\x97\x20\x71': "Fire Chip (3)",
b'\xF7\xC2\xD8\x85': "Fire Shot",
# Movement
b'\x77\x1D\x12\x2E': "Spring Ball",
b'\xE6\xBB\x27\x35': "Space Jump Boots (1)",
b'\xCB\x91\x7F\x35': "Space Jump Boots (2)",
b'\x5A\xD7\x7F\x3D': "Space Jump Boots (3)",
b'\xBA\x15\xD4\x44': "Space Jump Boots (4)",
b'\xC7\x7C\xDB\x48': "Space Jump Boots (5)",
b'\xE5\xDC\x3B\xD9': "Boost Ball (1)",
b'\xF2\x6C\x79\xDC': "Boost Ball (2)",
b'\x06\x06\xCD\xDE': "Boost Ball (3)",
b'\xDD\x01\xB1\xE8': "Boost Ball (4)",
b'\xD6\x0A\x10\xED': "Boost Ball (5)",
b'\x1B\x4E\x40\xF0': "Boost Ball (6)",
b'\xB9\xA4\x4D\xF3': "Boost Ball (7)",
b'\xCB\x9C\x03\xF9': "Boost Ball (8)",
b'\xF5\xB8\x46\xFB': "Boost Ball (9)",
# Morph Ball
b'\x76\x0C\xC9\x76': "Morph Bomb (1)",
b'\x0B\x58\x59\x7A': "Morph Bomb (2)",
# Resources
b'\x66\x51\x03\x1A': "Green Shards",
b'\x06\x9C\x51\x49': "Missiles (1)",
b'\xCE\x41\x51\x4D': "Missiles (2)",
b'\x1C\x5D\x23\x53': "Missiles (3)",
b'\xDD\x54\x99\x5E': "Missiles (4)",
b'\xA0\x0B\x34\xBC': "Super Missile (1)",
b'\x2D\x56\x4E\xBC': "Super Missile (2)",
b'\x61\xC3\xD8\xBC': "Super Missile (3)",
b'\x5F\xEE\xC5\xBD': "Super Missile (4)",
b'\x76\x41\xDC\xBF': "Super Missile (5)",
b'\xFA\x8D\xEF\xC1': "Super Missile (6)",
b'\x35\x05\x1F\xC2': "Super Missile (7)",
b'\x16\xA7\x38\xC5': "Super Missile (8)",
b'\x74\xE4\xB3\xC7': "Super Missile (9)",
b'\x34\xAC\x2C\xCF': "Super Missile (10)",
b'\x57\x48\x75\xD0': "Super Missile (11)",
b'\xFC\xC4\x40\xD5': "Super Missile (12)",
b'\xA6\x12\xFB\x97': "Energy Tank (1)",
b'\xB1\x13\x03\x9D': "Energy Tank (2)",
b'\x1F\x6C\xD8\x9D': "Energy Tank (3)",
b'\x31\x21\xC0\xA0': "Energy Tank (4)",
b'\xE3\x6B\x49\xA4': "Energy Tank (5)",
b'\xA3\x29\xD5\xA6': "Shot Ammo (1)",
b'\x6A\x7C\xA4\xAB': "Shot Ammo (2)",
# Misc
b'\x64\xA9\x21\x23': "Gal Fed Mech Parts (1)",
b'\xBD\x82\xFE\x23': "Gal Fed Mech Parts (2)",
b'\xE5\x77\x80\x25': "Gal Fed Mech Parts (3)",
b'\xFD\x04\x8F\x25': "Gal Fed Mech Parts (4)",
b'\x07\xBC\x73\x26': "Gal Fed Mech Parts (5)",
}
# --- CORE LOGIC ---
class SaveEditor:
def __init__(self, master):
self.master = master
self.master.title("Metroid Prime 4 Beyond - Save Editor")
self.master.geometry("750x600")
self.file_path = None
self.data = bytearray()
self.items_found = [] # List of dicts: {'name', 'offset', 'id', 'val1', 'val2', 'val3'}
# UI Setup
self._setup_ui()
def _setup_ui(self):
# Top Frame: File Operations
top_frame = tk.Frame(self.master, padx=10, pady=10)
top_frame.pack(fill=tk.X)
self.btn_load = tk.Button(top_frame, text="Load Save File", command=self.load_file, width=20)
self.btn_load.pack(side=tk.LEFT, padx=5)
self.btn_save = tk.Button(top_frame, text="Save Changes", command=self.save_file, width=20, state=tk.DISABLED)
self.btn_save.pack(side=tk.LEFT, padx=5)
self.lbl_status = tk.Label(top_frame, text="No file loaded", fg="gray")
self.lbl_status.pack(side=tk.LEFT, padx=20)
# Main Frame: Item List
main_frame = tk.Frame(self.master, padx=10, pady=5)
main_frame.pack(fill=tk.BOTH, expand=True)
# Header for columns
cols_frame = tk.Frame(main_frame)
cols_frame.pack(fill=tk.X)
tk.Label(cols_frame, text="Item Name", width=25, anchor="w", font=('bold')).pack(side=tk.LEFT)
tk.Label(cols_frame, text="Value 1 (Amt)", width=12, font=('bold')).pack(side=tk.LEFT)
tk.Label(cols_frame, text="Value 2", width=12, font=('bold')).pack(side=tk.LEFT)
tk.Label(cols_frame, text="Value 3 (Cap)", width=12, font=('bold')).pack(side=tk.LEFT)
# Canvas for scrolling
self.canvas = tk.Canvas(main_frame)
self.scrollbar = ttk.Scrollbar(main_frame, orient="vertical", command=self.canvas.yview)
self.scrollable_frame = tk.Frame(self.canvas)
self.scrollable_frame.bind(
"<Configure>",
lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))
)
self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
self.canvas.configure(yscrollcommand=self.scrollbar.set)
self.canvas.pack(side="left", fill="both", expand=True)
self.scrollbar.pack(side="right", fill="y")
# Bottom Frame: Bulk Actions
btm_frame = tk.Frame(self.master, padx=10, pady=10)
btm_frame.pack(fill=tk.X)
tk.Label(btm_frame, text="Note: Backup your save before editing!", fg="red").pack(side=tk.LEFT)
def load_file(self):
path = filedialog.askopenfilename(filetypes=[("All Files", "*.*")])
if not path:
return
try:
with open(path, "rb") as f:
self.data = bytearray(f.read())
self.file_path = path
# 1. Header Parsing
if len(self.data) < 8:
raise ValueError("File too small to be a valid save.")
stored_crc = struct.unpack('<I', self.data[0:4])[0]
stored_size = struct.unpack('<I', self.data[4:8])[0]
# 2. Check Size
actual_size = len(self.data) - 8
checksum_data = self.data[8:]
calculated_crc = zlib.crc32(checksum_data) ^ 0xFFFFFFFF
status_msg = f"Loaded. Size: {actual_size}"
if calculated_crc == stored_crc:
status_msg += " | CRC: Valid"
self.lbl_status.config(text=status_msg, fg="green")
else:
status_msg += f" | CRC: INVALID (Exp {hex(calculated_crc)}, Got {hex(stored_crc)})"
self.lbl_status.config(text=status_msg, fg="orange")
self.parse_items()
self.btn_save.config(state=tk.NORMAL)
except Exception as e:
messagebox.showerror("Error", f"Failed to load file: {str(e)}")
def parse_items(self):
# Clear existing GUI items
for widget in self.scrollable_frame.winfo_children():
widget.destroy()
self.items_found = []
# Search for IDs
# Structure: [ID 4bytes] [Val1 Float] [Val2 Float] [Val3 Float] = 16 bytes total
# We search the data from offset 8 onwards
row_idx = 0
for id_bytes, name in ITEM_DATABASE.items():
start = 0
while True:
offset = self.data.find(id_bytes, start)
if offset == -1:
break
# Check if we have room for the floats
if offset + 16 > len(self.data):
break
# Read floats
try:
v1 = struct.unpack('<f', self.data[offset+4:offset+8])[0]
v2 = struct.unpack('<f', self.data[offset+8:offset+12])[0]
v3 = struct.unpack('<f', self.data[offset+12:offset+16])[0]
item_entry = {
'name': name,
'offset': offset,
'id_bytes': id_bytes,
'v1_var': tk.StringVar(value=str(v1)),
'v2_var': tk.StringVar(value=str(v2)),
'v3_var': tk.StringVar(value=str(v3))
}
self.items_found.append(item_entry)
self._create_item_row(item_entry, row_idx)
row_idx += 1
except:
pass # Parse error, skip instance
start = offset + 1
def _create_item_row(self, item, row):
# Create widgets for this item
f = tk.Frame(self.scrollable_frame)
f.pack(fill=tk.X, pady=2)
# Name
tk.Label(f, text=item['name'], width=25, anchor="w").pack(side=tk.LEFT)
# Value 1
tk.Entry(f, textvariable=item['v1_var'], width=12).pack(side=tk.LEFT, padx=2)
# Value 2
tk.Entry(f, textvariable=item['v2_var'], width=12).pack(side=tk.LEFT, padx=2)
# Value 3
tk.Entry(f, textvariable=item['v3_var'], width=12).pack(side=tk.LEFT, padx=2)
def save_file(self):
if not self.file_path:
return
try:
# 1. Update Data from GUI
for item in self.items_found:
off = item['offset']
try:
v1 = float(item['v1_var'].get())
v2 = float(item['v2_var'].get())
v3 = float(item['v3_var'].get())
# Pack floats back to bytes (Little Endian)
b_v1 = struct.pack('<f', v1)
b_v2 = struct.pack('<f', v2)
b_v3 = struct.pack('<f', v3)
# Write to bytearray
self.data[off+4:off+8] = b_v1
self.data[off+8:off+12] = b_v2
self.data[off+12:off+16] = b_v3
except ValueError:
messagebox.showwarning("Value Error", f"Invalid number for {item['name']}. Skipping.")
# The size header is len(data) - 8
new_size = len(self.data) - 8
self.data[4:8] = struct.pack('<I', new_size)
# 3. Recalculate CRC
# Checksum covers from offset 8 to end
checksum_data = self.data[8:]
new_crc = zlib.crc32(checksum_data) ^ 0xFFFFFFFF
self.data[0:4] = struct.pack('<I', new_crc)
# 4. Write to disk
new_path = filedialog.asksaveasfilename(defaultextension=".sav", initialfile=self.file_path)
if new_path:
with open(new_path, "wb") as f:
f.write(self.data)
messagebox.showinfo("Success", "File saved successfully!")
except Exception as e:
messagebox.showerror("Error", f"Failed to save: {str(e)}")
if __name__ == "__main__":
root = tk.Tk()
app = SaveEditor(root)
root.mainloop()
Why are there multiple copies of the same item?Its rough but if you want i made a small editor using what you have? modify it if you will to your findings. hope it helps. it will correct your checksum too.
View attachment 545399Python:import tkinter as tk from tkinter import filedialog, messagebox, ttk import struct import zlib import binascii import copy ITEM_DATABASE = { # Psychic Items b'\x10\xC0\xF5\x03': "Psychic Glove (1)", b'\x07\x66\xA5\x04': "Psychic Glove (2)", b'\x6D\x69\x68\x18': "Psychic Shot", b'\x05\x7C\xB5\x29': "Psychic Boots (1)", b'\xA8\x2E\xFE\x2A': "Psychic Boots (2)", b'\xDF\xD2\x69\x2C': "Psychic Crystal/Visor (1)", b'\xA0\x3C\x6C\x2D': "Psychic Crystal/Visor (2)", b'\x52\x21\x65\x73': "Psychic Charge Shot", b'\xF4\xB9\xC7\x84': "Psychic Power Bomb", b'\x84\x7F\x51\x8C': "Psychic Grapple (1)", b'\xDA\x68\xCA\x8E': "Psychic Grapple (2)", # Teleporter b'\x1C\x2B\x9F\x08': "Teleporter Patch (1)", b'\x79\x6A\xEA\x0C': "Teleporter Patch (2)", b'\x35\xAA\x9D\x0E': "Teleporter Patch (3)", b'\x22\x44\x44\x10': "Teleporter Patch (4)", b'\xF8\x1B\xC6\x7D': "Teleporter Chip", # Weapons & Upgrades b'\xBD\xA6\xE8\x13': "Super Thunder Shot", b'\x04\x97\x8C\x1E': "Thunder Chip", b'\x4F\x53\x59\x8A': "Thunder Shot", b'\x71\x94\xD8\xB0': "Charged Thunder Shot (1)", b'\x5B\x01\x94\xB5': "Charged Thunder Shot (2)", b'\x97\x42\xB4\x27': "Charged Ice Shot", b'\x71\x64\x03\x2B': "Ice Shot", b'\xD3\x2E\xC3\x5F': "Vi-O-La IC Suit (1)", b'\xFE\x04\xE3\x6A': "Vi-O-La IC Suit (2)", b'\x99\x52\x4F\x74': "Vi-O-La Suit", b'\x82\x96\xDD\x6C': "Fire Chip", b'\x85\x40\x45\x6F': "Fire Chip (2)", b'\x0A\x97\x20\x71': "Fire Chip (3)", b'\xF7\xC2\xD8\x85': "Fire Shot", # Movement b'\x77\x1D\x12\x2E': "Spring Ball", b'\xE6\xBB\x27\x35': "Space Jump Boots (1)", b'\xCB\x91\x7F\x35': "Space Jump Boots (2)", b'\x5A\xD7\x7F\x3D': "Space Jump Boots (3)", b'\xBA\x15\xD4\x44': "Space Jump Boots (4)", b'\xC7\x7C\xDB\x48': "Space Jump Boots (5)", b'\xE5\xDC\x3B\xD9': "Boost Ball (1)", b'\xF2\x6C\x79\xDC': "Boost Ball (2)", b'\x06\x06\xCD\xDE': "Boost Ball (3)", b'\xDD\x01\xB1\xE8': "Boost Ball (4)", b'\xD6\x0A\x10\xED': "Boost Ball (5)", b'\x1B\x4E\x40\xF0': "Boost Ball (6)", b'\xB9\xA4\x4D\xF3': "Boost Ball (7)", b'\xCB\x9C\x03\xF9': "Boost Ball (8)", b'\xF5\xB8\x46\xFB': "Boost Ball (9)", # Morph Ball b'\x76\x0C\xC9\x76': "Morph Bomb (1)", b'\x0B\x58\x59\x7A': "Morph Bomb (2)", # Resources b'\x66\x51\x03\x1A': "Green Shards", b'\x06\x9C\x51\x49': "Missiles (1)", b'\xCE\x41\x51\x4D': "Missiles (2)", b'\x1C\x5D\x23\x53': "Missiles (3)", b'\xDD\x54\x99\x5E': "Missiles (4)", b'\xA0\x0B\x34\xBC': "Super Missile (1)", b'\x2D\x56\x4E\xBC': "Super Missile (2)", b'\x61\xC3\xD8\xBC': "Super Missile (3)", b'\x5F\xEE\xC5\xBD': "Super Missile (4)", b'\x76\x41\xDC\xBF': "Super Missile (5)", b'\xFA\x8D\xEF\xC1': "Super Missile (6)", b'\x35\x05\x1F\xC2': "Super Missile (7)", b'\x16\xA7\x38\xC5': "Super Missile (8)", b'\x74\xE4\xB3\xC7': "Super Missile (9)", b'\x34\xAC\x2C\xCF': "Super Missile (10)", b'\x57\x48\x75\xD0': "Super Missile (11)", b'\xFC\xC4\x40\xD5': "Super Missile (12)", b'\xA6\x12\xFB\x97': "Energy Tank (1)", b'\xB1\x13\x03\x9D': "Energy Tank (2)", b'\x1F\x6C\xD8\x9D': "Energy Tank (3)", b'\x31\x21\xC0\xA0': "Energy Tank (4)", b'\xE3\x6B\x49\xA4': "Energy Tank (5)", b'\xA3\x29\xD5\xA6': "Shot Ammo (1)", b'\x6A\x7C\xA4\xAB': "Shot Ammo (2)", # Misc b'\x64\xA9\x21\x23': "Gal Fed Mech Parts (1)", b'\xBD\x82\xFE\x23': "Gal Fed Mech Parts (2)", b'\xE5\x77\x80\x25': "Gal Fed Mech Parts (3)", b'\xFD\x04\x8F\x25': "Gal Fed Mech Parts (4)", b'\x07\xBC\x73\x26': "Gal Fed Mech Parts (5)", } # --- CORE LOGIC --- class SaveEditor: def __init__(self, master): self.master = master self.master.title("Metroid Prime 4 Beyond - Save Editor") self.master.geometry("750x600") self.file_path = None self.data = bytearray() self.items_found = [] # List of dicts: {'name', 'offset', 'id', 'val1', 'val2', 'val3'} # UI Setup self._setup_ui() def _setup_ui(self): # Top Frame: File Operations top_frame = tk.Frame(self.master, padx=10, pady=10) top_frame.pack(fill=tk.X) self.btn_load = tk.Button(top_frame, text="Load Save File", command=self.load_file, width=20) self.btn_load.pack(side=tk.LEFT, padx=5) self.btn_save = tk.Button(top_frame, text="Save Changes", command=self.save_file, width=20, state=tk.DISABLED) self.btn_save.pack(side=tk.LEFT, padx=5) self.lbl_status = tk.Label(top_frame, text="No file loaded", fg="gray") self.lbl_status.pack(side=tk.LEFT, padx=20) # Main Frame: Item List main_frame = tk.Frame(self.master, padx=10, pady=5) main_frame.pack(fill=tk.BOTH, expand=True) # Header for columns cols_frame = tk.Frame(main_frame) cols_frame.pack(fill=tk.X) tk.Label(cols_frame, text="Item Name", width=25, anchor="w", font=('bold')).pack(side=tk.LEFT) tk.Label(cols_frame, text="Value 1 (Amt)", width=12, font=('bold')).pack(side=tk.LEFT) tk.Label(cols_frame, text="Value 2", width=12, font=('bold')).pack(side=tk.LEFT) tk.Label(cols_frame, text="Value 3 (Cap)", width=12, font=('bold')).pack(side=tk.LEFT) # Canvas for scrolling self.canvas = tk.Canvas(main_frame) self.scrollbar = ttk.Scrollbar(main_frame, orient="vertical", command=self.canvas.yview) self.scrollable_frame = tk.Frame(self.canvas) self.scrollable_frame.bind( "<Configure>", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all")) ) self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw") self.canvas.configure(yscrollcommand=self.scrollbar.set) self.canvas.pack(side="left", fill="both", expand=True) self.scrollbar.pack(side="right", fill="y") # Bottom Frame: Bulk Actions btm_frame = tk.Frame(self.master, padx=10, pady=10) btm_frame.pack(fill=tk.X) tk.Label(btm_frame, text="Note: Backup your save before editing!", fg="red").pack(side=tk.LEFT) def load_file(self): path = filedialog.askopenfilename(filetypes=[("All Files", "*.*")]) if not path: return try: with open(path, "rb") as f: self.data = bytearray(f.read()) self.file_path = path # 1. Header Parsing if len(self.data) < 8: raise ValueError("File too small to be a valid save.") stored_crc = struct.unpack('<I', self.data[0:4])[0] stored_size = struct.unpack('<I', self.data[4:8])[0] # 2. Check Size actual_size = len(self.data) - 8 checksum_data = self.data[8:] calculated_crc = zlib.crc32(checksum_data) ^ 0xFFFFFFFF status_msg = f"Loaded. Size: {actual_size}" if calculated_crc == stored_crc: status_msg += " | CRC: Valid" self.lbl_status.config(text=status_msg, fg="green") else: status_msg += f" | CRC: INVALID (Exp {hex(calculated_crc)}, Got {hex(stored_crc)})" self.lbl_status.config(text=status_msg, fg="orange") self.parse_items() self.btn_save.config(state=tk.NORMAL) except Exception as e: messagebox.showerror("Error", f"Failed to load file: {str(e)}") def parse_items(self): # Clear existing GUI items for widget in self.scrollable_frame.winfo_children(): widget.destroy() self.items_found = [] # Search for IDs # Structure: [ID 4bytes] [Val1 Float] [Val2 Float] [Val3 Float] = 16 bytes total # We search the data from offset 8 onwards row_idx = 0 for id_bytes, name in ITEM_DATABASE.items(): start = 0 while True: offset = self.data.find(id_bytes, start) if offset == -1: break # Check if we have room for the floats if offset + 16 > len(self.data): break # Read floats try: v1 = struct.unpack('<f', self.data[offset+4:offset+8])[0] v2 = struct.unpack('<f', self.data[offset+8:offset+12])[0] v3 = struct.unpack('<f', self.data[offset+12:offset+16])[0] item_entry = { 'name': name, 'offset': offset, 'id_bytes': id_bytes, 'v1_var': tk.StringVar(value=str(v1)), 'v2_var': tk.StringVar(value=str(v2)), 'v3_var': tk.StringVar(value=str(v3)) } self.items_found.append(item_entry) self._create_item_row(item_entry, row_idx) row_idx += 1 except: pass # Parse error, skip instance start = offset + 1 def _create_item_row(self, item, row): # Create widgets for this item f = tk.Frame(self.scrollable_frame) f.pack(fill=tk.X, pady=2) # Name tk.Label(f, text=item['name'], width=25, anchor="w").pack(side=tk.LEFT) # Value 1 tk.Entry(f, textvariable=item['v1_var'], width=12).pack(side=tk.LEFT, padx=2) # Value 2 tk.Entry(f, textvariable=item['v2_var'], width=12).pack(side=tk.LEFT, padx=2) # Value 3 tk.Entry(f, textvariable=item['v3_var'], width=12).pack(side=tk.LEFT, padx=2) def save_file(self): if not self.file_path: return try: # 1. Update Data from GUI for item in self.items_found: off = item['offset'] try: v1 = float(item['v1_var'].get()) v2 = float(item['v2_var'].get()) v3 = float(item['v3_var'].get()) # Pack floats back to bytes (Little Endian) b_v1 = struct.pack('<f', v1) b_v2 = struct.pack('<f', v2) b_v3 = struct.pack('<f', v3) # Write to bytearray self.data[off+4:off+8] = b_v1 self.data[off+8:off+12] = b_v2 self.data[off+12:off+16] = b_v3 except ValueError: messagebox.showwarning("Value Error", f"Invalid number for {item['name']}. Skipping.") # The size header is len(data) - 8 new_size = len(self.data) - 8 self.data[4:8] = struct.pack('<I', new_size) # 3. Recalculate CRC # Checksum covers from offset 8 to end checksum_data = self.data[8:] new_crc = zlib.crc32(checksum_data) ^ 0xFFFFFFFF self.data[0:4] = struct.pack('<I', new_crc) # 4. Write to disk new_path = filedialog.asksaveasfilename(defaultextension=".sav", initialfile=self.file_path) if new_path: with open(new_path, "wb") as f: f.write(self.data) messagebox.showinfo("Success", "File saved successfully!") except Exception as e: messagebox.showerror("Error", f"Failed to save: {str(e)}") if __name__ == "__main__": root = tk.Tk() app = SaveEditor(root) root.mainloop()
Your description of data was a lil weird so i didnt use any specific patterns atm aside from the exact AOB you gave for each in your list lol
Just the way i formatted it based on what they provided in their blurb. can fix if you wantWhy are there multiple copies of the same item?
Are you still working on the save editor? Will you be adding more items to it like energy tanks and green shards?Just the way i formatted it based on what they provided in their blurb. can fix if you want
Cause im on hard mode right now and I could really use the upgradesAre you still working on the save editor? Will you be adding more items to it like energy tanks and green shards?
If you guys find more values in the save data then i can add and correct things. i didnt bother to parse the save data myself. just quickly put this together based on what op has put upAre you still working on the save editor? Will you be adding more items to it like energy tanks and green shards?
Post automatically merged:
Cause im on hard mode right now and I could really use the upgrades
What items are safe to add to an early game save? For example volt forge tower 2 before turning on the second generator?10900 green shards
Im wondering this because I need help in hard modeWhat items are safe to add to an early game save? For example volt forge tower 2 before turning on the second generator?
What items are safe to add to an early game save? For example volt forge tower 2 before turning on the second generator?
Post automatically merged:
Im wondering this because I need help in hard mode
im having some trouble in hard mode can you give me some items to help me out?This how to edit Metroid Prime 4 saves. I am not sure save order other than it might be all the auto saves first then all the manual. if you have only used one save slot it should be easy to work out. searching for VSM in text might be the start of each save.
Saves Have Checksum
There a Checksum fixer in zip file unzip and drop save onto bat file it will fix the checksum so won't need to use Hxd to fix it. The Checksum fixer uses Bucanero's cli app and a bat file. Bat file is a patch file if you have experience with ps4 save wizard you can add quick codes to it.
Fix Checksum With Hxd
The saves have a checksum it is the first 4 bytes. the next 4 bytes is the whole size of save minus the first 8 bytes if your not changing save size you can ignore this. the checksum covers from after 8th byte or from 35 to end of save. the checksum is CRC-32/JAMCRC. i used hxd to fix checksum. i selected the whole save minus the first 8 bytes you can go into edit and select block. start is 8 and end you can just type ffffff. you then go into analysis select checksum. in generate checksums box select custom CRC (32bit) then select custom crc... in the custom CRC box you need to put 04C11DB7 in poly, FFFFFFFF in intial and in output xor 00000000. also need to tick in and out for reflection. click ok for this and previous box and you should have checksum. the bytes in checksum needs to be reversed you can see in picture below what i mean. both checksum and size are in little endian meaning bytes are reversed.
View attachment 544780
Editing
To find green shards search for 66 51 03 1A shards will be after that in reverse float. max green shards is 0000af46. also max amount you can have is also after the amount you have so if the id doesn't work you can search for max shard value and go back 4 bytes
View attachment 544774
it looks like for items its id then amount then cap total repeated twice (so thats 4 byte id and 3 reverse float values). any id with 00 00 80 3F repeated 3 times is probably something unlocked. will need a completed save to see what the right floats should be after the ids.
I don't think id change order so not a problem but if the ids are like the scan section it would be float values then Id
To find scan section search for AF 05 00 00 00 you should find a section similar to whats in picture. all you need to do is put 00 3F in front of the Ids the ids range from 00 to 0A. the 00 ones are easy workout after filling the others in.
As mentioned below changing all might cause a softlock it could be the 0A ones. in picture below in the boxes is the ids
View attachment 544970
here are some ids i got from a end game save with their float values. I added names that genesis nova found
you can use this site if you want to edit float values just enter decimal value in float value box and tick the box Swap to use big-endian https://gregstoll.com/~gregstoll/floattohex/Code:Name ID Float 1 Float 2 Float 3 Float 1 to dec Float 3 to dec Psychic Glove 10 C0 F5 03 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 07 66 A5 04 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 Teleporter Patches 1C 2B 9F 08 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 79 6A EA 0C 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 35 AA 9D 0E 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 22 44 44 10 00 00 00 00 00 00 00 00 00 00 80 3F 1 Super Thunder Shot BD A6 E8 13 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 Psychic Shot 6D 69 68 18 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 Green Shards 66 51 03 1A 00 00 AF 46 00 00 AF 46 F0 23 74 49 22400 999999 Thunder Chip 04 97 8C 1E 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 Gal Fed Mech Parts 64 A9 21 23 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 BD 82 FE 23 00 00 C8 42 00 00 C8 42 00 00 C8 42 100 100 E5 77 80 25 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 FD 04 8F 25 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 07 BC 73 26 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 charged ice Shot 97 42 B4 27 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 Psychic Boots 05 7C B5 29 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 A8 2E FE 2A 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 Ice shot 71 64 03 2B 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 Psychic Crystal/Visor DF D2 69 2C 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 A0 3C 6C 2D 00 00 80 40 00 00 80 40 00 00 80 40 4 4 Spring Ball 77 1D 12 2E 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 Space Jump Boots E6 BB 27 35 00 00 40 40 00 00 40 40 00 00 40 40 3 3 CB 91 7F 35 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 5A D7 7F 3D 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 BA 15 D4 44 00 00 00 00 00 00 00 00 00 00 80 3F 1 C7 7C DB 48 00 00 80 3F 00 00 80 3F 00 00 00 40 1 2 Missiles 06 9C 51 49 00 00 7A 43 00 00 7A 43 00 00 7A 43 250 250 CE 41 51 4D 00 00 80 3F 00 00 80 3F 00 00 80 42 1 64 1C 5D 23 53 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 DD 54 99 5E 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 Vi-O-La IC Suit/Vi-O-La IC D3 2E C3 5F 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 FE 04 E3 6A 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 Fire Chip 82 96 DD 6C 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 85 40 45 6F 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 0A 97 20 71 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 Psychic Charge Shot 52 21 65 73 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 Vi-O-La Suit 99 52 4F 74 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 Morph Bomb 76 0C C9 76 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 0B 58 59 7A 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 Teleporter Chip F8 1B C6 7D 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 Psychic Power Bomb F4 B9 C7 84 00 00 00 41 00 00 00 41 00 00 00 41 8 8 Fire Shot F7 C2 D8 85 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 Thunder Shot 4F 53 59 8A 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 Psychic Grapple 84 7F 51 8C 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 DA 68 CA 8E 00 00 A0 40 00 00 A0 40 00 00 A0 40 5 5 Tanks A6 12 FB 97 00 60 BB 44 00 60 BB 44 00 60 BB 44 1499 1499 B1 13 03 9D 00 00 00 00 00 00 96 43 00 00 96 43 300 1F 6C D8 9D 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 31 21 C0 A0 00 00 80 3F 00 00 80 3F 00 00 00 41 1 8 E3 6B 49 A4 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 Shot Ammo A3 29 D5 A6 00 80 04 44 00 80 04 44 00 80 04 44 530 530 6A 7C A4 AB 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 Charged Thunder Shot 71 94 D8 B0 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 5B 01 94 B5 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 Super Missile A0 0B 34 BC 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 2D 56 4E BC 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 61 C3 D8 BC 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 5F EE C5 BD 00 00 00 00 00 00 00 00 00 00 A0 40 5 76 41 DC BF 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 FA 8D EF C1 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 35 05 1F C2 00 00 00 00 00 00 C6 42 00 00 C6 42 99 16 A7 38 C5 00 00 00 00 00 00 00 00 00 00 40 40 3 74 E4 B3 C7 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 34 AC 2C CF 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 57 48 75 D0 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 FC C4 40 D5 00 00 C0 40 00 00 C0 40 00 00 C6 42 6 99 Boost Ball E5 DC 3B D9 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 F2 6C 79 DC 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 06 06 CD DE 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 DD 01 B1 E8 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 D6 0A 10 ED 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 1B 4E 40 F0 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 B9 A4 4D F3 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 CB 9C 03 F9 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1 F5 B8 46 FB 00 00 80 3F 00 00 80 3F 00 00 80 3F 1 1
There a zip file with bat files that edit green shards it should edit the first auto and manual save you just drop save on to them. you can open them in text editor and change amount they write use the gregstoll site to create hex values you don't have to tick swap.
the checksum works but stuff without a name might not be connected to what comes before, i have added genesis nova info to first page. I converted py into app using pyinstaller as well as use the updated ids. https://www.mediafire.com/file/6a6anzvua3zcpmg/editor.zip/fileIf you guys find more values in the save data then i can add and correct things. i didnt bother to parse the save data myself. just quickly put this together based on what op has put up
Have you tried editing all 3 values
0674 Galactic federation
1974 Galactic federation
1E3C Galactic federation
2024 Galactic federation
2184 Galactic federation
2BF4 Galactic federation
0104 Larmonian Records
0334 Larmonian Records
037C Larmonian Records
050C Larmonian Records
0BEC Larmonian Records
0EA4 Larmonian Records
0FEC Larmonian Records
101C Larmonian Records
1094 Larmonian Records
119C Larmonian Records
217C Larmonian Records
256C Larmonian Records
270C Larmonian Records
2884 Larmonian Records
28C4 Larmonian Records
29BC Larmonian Records
2CBC Larmonian Records
01A4 Larmonian Writings
0544 Larmonian Writings
0754 Larmonian Writings
0B54 Larmonian Writings
0E4C Larmonian Writings
108C Larmonian Writings
16C4 Larmonian Writings
1C3C Larmonian Writings
2014 Larmonian Writings
2A94 Larmonian Writings
0114 Machines
04D4 Machines
0624 Machines
083C Machines
0A04 Machines
0AE4 Machines
0B4C Machines
0E5C Machines
0F4C Machines
10E4 Machines
12C4 Machines
13BC Machines
145C Machines
15BC Machines
1944 Machines
200C Machines
250C Machines
2CFC Machines
2D24 Machines
001C Research
0054 Research
02DC Research
0684 Research
0774 Research
0894 Research
0C44 Research
0CD4 Research
0D84 Research
0EFC Research
13FC Research
16CC Research
179C Research
181C Research
1994 Research
1C04 Research
2464 Research
2824 Research
011C Sylux Troops
07EC Sylux Troops
09AC Sylux Troops
0BC4 Sylux Troops
008C Viewros
00F4 Viewros
046C Viewros
052C Viewros
05AC Viewros
05DC Viewros
084C Viewros
08BC Viewros
0914 Viewros
0A44 Viewros
0A6C Viewros
0A9C Viewros
0AF4 Viewros
0B94 Viewros
0C24 Viewros
0C9C Viewros
0CAC Viewros
0E1C Viewros
10A4 Viewros
115C Viewros
124C Viewros
12AC Viewros
12D4 Viewros
133C Viewros
13F4 Viewros
154C Viewros
155C Viewros
1704 Viewros
1744 Viewros
195C Viewros
19EC Viewros
1A24 Viewros
1A84 Viewros
1B34 Viewros
1C8C Viewros
1CFC Viewros
1D14 Viewros
1EDC Viewros
1F2C Viewros
1F9C Viewros
2074 Viewros
20C4 Viewros
2114 Viewros
2124 Viewros
2214 Viewros
226C Viewros
235C Viewros
261C Viewros
267C Viewros
27AC Viewros
291C Viewros
29D4 Viewros
2D14 Viewros
the checksum works but stuff without a name might not be connected to what comes before, i have added genesis nova info to first page. I converted py into app using pyinstaller as well as use the updated ids. https://www.mediafire.com/file/6a6anzvua3zcpmg/editor.zip/file
depending on how many slots are filled there will be 2 instances per slot so there will either be 2, 4 or 6 of each. also here is the py file. is it possible to search for a value then have multiple offsets from it.
yes editor is updated it now shows different saves and you can edit log scans, check the first post. i used claude.ai to add stuff to original py file like offsets and separate stuff into individual saves. it dose list saves as save 1, save 2 and so on. the odd numbers should be manual saves and even auto saves. Save editor skips completed saves.Is this editor updated from the one you showed me yesterday? Does it include other things like log book entries?
Btw the psychic grapple i added into my inventory doesn't work apparently there's probably another code to activate it like power bombsyes editor is updated it now shows different saves and you can edit log scans, check the first post. i used claude.ai to add stuff to original py file like offsets and separate stuff into individual saves. it dose list saves as save 1, save 2 and so on. the odd numbers should be manual saves and even auto saves. Save editor skips completed saves.
Does the new enhanced editor contain all the logbook entries?yes editor is updated it now shows different saves and you can edit log scans, check the first post. i used claude.ai to add stuff to original py file like offsets and separate stuff into individual saves. it dose list saves as save 1, save 2 and so on. the odd numbers should be manual saves and even auto saves. Save editor skips completed saves.