Fix save serialization

This commit is contained in:
Luke Hubmayer-Werner 2023-08-03 21:02:41 +09:30
parent 724f48a62e
commit 36d025e18c
3 changed files with 59 additions and 14 deletions

View File

@ -47,7 +47,27 @@ func get_struct(buffer: StreamPeer, struct_name: String) -> Dictionary:
return {} return {}
return struct_types[struct_name].get_value(buffer, [0, 0]) return struct_types[struct_name].get_value(buffer, [0, 0])
func get_save_slot(sram: File, slot_id: int): 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:
var buffer := StreamPeerBuffer.new() var buffer := StreamPeerBuffer.new()
sram.seek(0x700 * slot_id) sram.seek(0x700 * slot_id)
buffer.set_data_array(sram.get_buffer(0x700)) buffer.set_data_array(sram.get_buffer(0x700))

View File

@ -78,7 +78,7 @@ class UBits extends StructType:
return value return value
func put_value(buffer: StreamPeer, value, leftover_bits: Array): func put_value(buffer: StreamPeer, value, leftover_bits: Array):
leftover_bits[1] |= value << bits leftover_bits[1] |= value << leftover_bits[0]
leftover_bits[0] += bits leftover_bits[0] += bits
while leftover_bits[0] >= 8: while leftover_bits[0] >= 8:
buffer.put_8(leftover_bits[1] & 0xFF) buffer.put_8(leftover_bits[1] & 0xFF)

View File

@ -2,6 +2,15 @@ extends Control
var dir_user := Directory.new() var dir_user := Directory.new()
const P_TESTDATA := 'user://test_data/' const P_TESTDATA := 'user://test_data/'
# Shared state between tests
var save_slot_buffers = []
var save_slot_dicts = []
func test(label, input):
if input:
print('SUCCESS: ' + label)
else:
print('FAILURE: ' + label)
func load_snes_savefile(filename: String = 'res://test.srm'): func load_snes_savefile(filename: String = 'res://test.srm'):
var save_file := File.new() var save_file := File.new()
@ -11,17 +20,14 @@ func load_snes_savefile(filename: String = 'res://test.srm'):
var error: var error:
print_debug('Failed to open test.srm for reading: %d' % error) print_debug('Failed to open test.srm for reading: %d' % error)
return return
var save_slots = []
var save_slot_dicts = []
for i in 4: for i in 4:
save_slots.append(SaveLoader.get_save_slot(save_file, i)) self.save_slot_buffers.append(SaveLoader.get_save_slot(save_file, i))
save_slot_dicts.append(SaveLoader.get_struct(save_slots[i], 'Save_slot')) self.save_slot_dicts.append(SaveLoader.deserialize_save_slot(self.save_slot_buffers[i].data_array))
print('Loaded test save file') print('Loaded test save file')
return save_slot_dicts
func generate_known_good_results(): func generate_known_good_results():
var save_slot_dicts = load_snes_savefile() load_snes_savefile()
if not save_slot_dicts: if not self.save_slot_dicts:
return return
match dir_user.make_dir_recursive(P_TESTDATA): match dir_user.make_dir_recursive(P_TESTDATA):
OK: OK:
@ -30,7 +36,7 @@ func generate_known_good_results():
print_debug('Failed to create "%s" with error code %d' % [P_TESTDATA, error]) print_debug('Failed to create "%s" with error code %d' % [P_TESTDATA, error])
return return
var filename := P_TESTDATA + 'test.srm.json' var filename := P_TESTDATA + 'test.srm.json'
match Common.save_json(filename, save_slot_dicts): match Common.save_json(filename, self.save_slot_dicts):
OK: OK:
pass pass
var error: var error:
@ -38,8 +44,8 @@ func generate_known_good_results():
return return
func test_save_loading() -> bool: func test_save_loading() -> bool:
var save_slot_dicts = load_snes_savefile() load_snes_savefile()
if not save_slot_dicts: if not self.save_slot_dicts:
print_debug('Failed to load test savefile') print_debug('Failed to load test savefile')
return false return false
var filename := P_TESTDATA + 'test.srm.json' var filename := P_TESTDATA + 'test.srm.json'
@ -47,7 +53,7 @@ func test_save_loading() -> bool:
match typeof(known_good): match typeof(known_good):
TYPE_ARRAY: TYPE_ARRAY:
print_debug('Comparing known savefile results') print_debug('Comparing known savefile results')
return Common.are_arrays_equal(save_slot_dicts, known_good) return Common.are_arrays_equal(self.save_slot_dicts, known_good)
TYPE_DICTIONARY: TYPE_DICTIONARY:
print_debug('Known savefile results "%s" is a dict instead of an array. Did we change formats?' % filename) print_debug('Known savefile results "%s" is a dict instead of an array. Did we change formats?' % filename)
return false return false
@ -55,6 +61,24 @@ func test_save_loading() -> bool:
print_debug('Failed to load known savefile results "%s"' % filename) print_debug('Failed to load known savefile results "%s"' % filename)
return false return false
func test_save_serialization() -> bool:
if not self.save_slot_dicts:
print_debug('test savefile not loaded')
return false
for i in 4:
var bytes = SaveLoader.serialize_save_slot(self.save_slot_dicts[i])
if bytes != self.save_slot_buffers[i].data_array:
print_debug('Slot %d failed to serialize correctly, rescanning' % i)
for j in 0x700:
var b1: int = bytes[j]
var b2: int = self.save_slot_buffers[i].data_array[j]
if b1 != b2:
print_debug('Mismatch occurs at byte %d: %d vs %d' % [j, b1, b2])
return false
return true
# Called when the node enters the scene tree for the first time. # Called when the node enters the scene tree for the first time.
func _ready() -> void: func _ready() -> void:
@ -64,5 +88,6 @@ func _ready() -> void:
var error: var error:
print_debug('Failed to open user directory') print_debug('Failed to open user directory')
# generate_known_good_results() # Uncomment this to get your sample on first run # generate_known_good_results() # Uncomment this to get your sample on first run
print(test_save_loading()) test('SNES save file loaded to array of dictionaries', test_save_loading())
test('SNES save file slots serialized from dictionaries', test_save_serialization())
get_tree().quit() get_tree().quit()