2023-07-28 23:11:26 +09:30
extends Node
2023-08-03 19:32:52 +09:30
const STRUCT : = preload ( ' res://scripts/struct.gd ' )
2023-07-28 23:11:26 +09:30
const SLOT_IN_USE : = 0xE41B
2023-08-03 19:32:52 +09:30
var struct_types : = STRUCT . get_base_structarraytypes ( )
2023-07-29 01:53:03 +09:30
2023-07-28 23:11:26 +09:30
# FFV SRAM is 4 slots of 0x700byte save files
# $0000-$06FF - Save Slot 1
# $0700-$0DFF - Save Slot 2
# $0E00-$14FF - Save Slot 3
# $1500-$1BFF - Save Slot 4
# $1C00-$1FEF - Empty
# $1FF0-$1FF7 - Checksum (2 bytes per slot)
# $1FF8-$1FFF - use table - contains $E41B if a slot is in use (deleting a save will just change this)
# Checksum just sums up every 16bit word from 0x000-0x600 of the save slot, using the carry flag.
# The carry flag means that it is not strictly modulo arithmetic, but you have to add 1 every time you pass 0xFFFF
# Offsets within save slot
# $000:600 = $500:B00 in WRAM (0x7E0500:0x7E0B00) only values that don't match are things like game frame timer which increments immediately after load
#
# $000-$04F - Character Slot 1
# $050=$09F - Character Slot 2
# $0A0=$0EF - Character Slot 3
# $0F0=$13F - Character Slot 4
# $447-$449 - Current Gil
#
# $5D8 - Current Map X position
# $5D9 - Current Map Y position
#
# $600-$6FF - Appears to be unused and zeroed out, not used for checksum. Maybe we can use it for cool metadata? :)
func get_slot_checksum ( slot : StreamPeerBuffer ) - > int :
slot . seek ( 0 )
var checksum : = 0
for i in 0x600 : # Last 0x100 bytes aren't checksummed
2023-07-29 01:53:03 +09:30
checksum += slot . get_u16 ( )
2023-07-28 23:11:26 +09:30
if checksum > 0xFFFF : # Addition of shorts can only overflow
checksum &= 0xFFFF
if i < 0x5FF :
checksum += 1 # TODO: confirm this carry flag edge case is correct!
return checksum
2023-07-29 01:53:03 +09:30
func get_struct ( buffer : StreamPeer , struct_name : String ) - > Dictionary :
2023-08-03 19:32:52 +09:30
# Does not seek to the start of the buffer.
2023-07-29 01:53:03 +09:30
# Make sure it is set to where you want to start reading.
2023-08-03 19:32:52 +09:30
if not ( struct_name in struct_types ) :
2023-07-29 01:53:03 +09:30
print_debug ( ' Attempted to get undeclared struct: " %s " ' % struct_name )
return { }
2023-08-03 19:32:52 +09:30
return struct_types [ struct_name ] . get_value ( buffer , [ 0 , 0 ] )
2023-07-29 01:53:03 +09:30
2023-08-03 21:02:41 +09:30
func put_struct ( buffer : StreamPeer , struct_name : String , data : Dictionary ) :
if not ( struct_name in struct_types ) :
print_debug ( ' Attempted to put undeclared struct: " %s " ' % struct_name )
return
struct_types [ struct_name ] . put_value ( buffer , data , [ 0 , 0 ] )
func deserialize_save_slot ( bytes : PoolByteArray ) - > Dictionary :
var buffer = StreamPeerBuffer . new ( )
buffer . data_array = bytes
return struct_types [ ' Save_slot ' ] . get_value ( buffer , [ 0 , 0 ] )
func serialize_save_slot ( data : Dictionary ) - > PoolByteArray :
var buffer = StreamPeerBuffer . new ( )
struct_types [ ' Save_slot ' ] . put_value ( buffer , data , [ 0 , 0 ] )
var padding : = PoolByteArray ( )
padding . resize ( 0x100 )
padding . fill ( 0 )
buffer . put_data ( padding )
return buffer . data_array
func get_save_slot ( sram : File , slot_id : int ) - > StreamPeerBuffer :
2023-07-28 23:11:26 +09:30
var buffer : = StreamPeerBuffer . new ( )
sram . seek ( 0x700 * slot_id )
buffer . set_data_array ( sram . get_buffer ( 0x700 ) )
return buffer
func save_slot ( sram : File , slot_id : int , slot : StreamPeerBuffer ) :
sram . seek ( 0x700 * slot_id )
sram . store_buffer ( slot . data_array )
sram . seek ( 0x1FF0 + ( slot_id * 2 ) )
sram . store_16 ( get_slot_checksum ( slot ) )
sram . seek ( 0x1FF8 + ( slot_id * 2 ) )
sram . store_16 ( SLOT_IN_USE )
func delete_save_slot ( sram : File , slot_id : int ) :
sram . seek ( 0x1FF8 + ( slot_id * 2 ) )
sram . store_16 ( 0 )
2023-07-29 01:53:03 +09:30
func _ready ( ) :
var file : = File . new ( )
2023-08-03 19:32:52 +09:30
var error = file . open ( ' res://data/SNES_save.tsv ' , File . READ )
2023-07-29 01:53:03 +09:30
if error == OK :
2023-08-03 19:32:52 +09:30
var current_struct : STRUCT . Struct
var line_num : = 0 # Currently only used for step-through debugging
2023-07-29 01:53:03 +09:30
while file . get_position ( ) < file . get_len ( ) :
var line : = file . get_csv_line ( ' \t ' )
line_num += 1
var size = line . size ( )
if size < 2 :
continue
# Size is at least 2
2023-08-03 19:32:52 +09:30
var type : = line [ 0 ]
var label : = line [ 1 ]
if type == ' struct ' :
2023-07-29 01:53:03 +09:30
# New struct declaration
2023-08-03 19:32:52 +09:30
current_struct = STRUCT . Struct . new ( )
struct_types [ label ] = current_struct
elif type and label :
2023-07-29 01:53:03 +09:30
# TODO: Maybe store the trailing comments somewhere?
2023-08-03 19:32:52 +09:30
current_struct . members . append ( [ label , STRUCT . get_structarraytype ( type , struct_types ) ] )
2023-07-29 01:53:03 +09:30
file . close ( )