ChocolateBird/scripts/loaders/RomLoader.gd

160 lines
6.4 KiB
GDScript3
Raw Normal View History

2023-07-25 16:33:45 +09:30
extends Node
#warning-ignore-all:return_value_discarded
signal loading_stage_updated(stage) # String
signal rom_loaded
const THREADED_LOADING := false
const STRUCT := preload('res://scripts/struct.gd')
const STRUCT_SNES := preload('res://scripts/loaders/snes/structs.gd')
var structdefs := {}
2023-08-01 23:22:31 +09:30
const loader_cd_image := preload('res://scripts/loaders/cd/image.gd')
var psx_productcode_regex := RegEx.new()
const psx_ff5_productcodes = [
'SLUS_008.79', # US Anthology, both 1.0 and 1.1
'SCES_138.40', # EU/Aus Anthology
'SLPM_860.81', # JP
'SCPS_452.14', # JP original, untested
]
2023-07-25 16:33:45 +09:30
var rom_snes := File.new()
var snes_data := {}
var snes_bytes: PoolByteArray
2023-08-23 17:26:16 +09:30
var snes_buffer: StreamPeerBuffer
var thread: Thread
2023-07-25 16:33:45 +09:30
func load_snes_structs(buffer: StreamPeerBuffer) -> Dictionary:
var data := {}
var rom_size := len(buffer.data_array)
for key in Common.SNES_PSX_addresses:
var d = Common.SNES_PSX_addresses[key]
if d.format and (d.SNES < rom_size): # Don't try to grab RPGe bank E0-E7 stuff from a smaller JP ROM
var s: STRUCT.StructType
if d.format in structdefs:
s = structdefs[d.format]
else:
s = STRUCT.get_structarraytype(d.format, structdefs)
structdefs[d.format] = s
if not s:
assert(false, 'Invalid StructType: "%s"' % d.format)
buffer.seek(d.SNES)
if '.' in key:
var keysplit: PoolStringArray = key.split('.', 1)
var k0 := keysplit[0]
var k1 = keysplit[1]
if k1.is_valid_integer():
k1 = int(k1)
if not (k0 in data):
data[k0] = {k1: s.get_value(buffer, [0, 0])}
else:
data[k0][k1] = s.get_value(buffer, [0, 0])
else:
data[key] = s.get_value(buffer, [0, 0])
data['job_levels'] = []
for job_id in 21:
var ability_list_ptr: int = data.ptrs_job_ability_lists[job_id]
var num_abilities: int = data.job_ability_counts[job_id]
var ability_list := []
buffer.seek(0x110000 + ability_list_ptr)
for i in num_abilities:
var abp_requirement: int = buffer.get_u16()
var ability_learned: int = buffer.get_u8()
ability_list.append({'ABP': abp_requirement, 'ability': ability_learned})
data.job_levels.append(ability_list)
return data
2023-12-11 00:53:01 +10:30
var load_start_tick := 0
func _on_loader_loading_stage_updated(stage: String, loader: String) -> void:
var output := '%s: %s' % [loader, stage]
2023-12-11 00:53:01 +10:30
#print(Time.get_time_string_from_system() + ' ' + output)
print('@%dms - %s' % [Time.get_ticks_msec() - load_start_tick, output])
emit_signal('loading_stage_updated', output)
var scenetree := get_tree()
if scenetree:
yield(scenetree, 'idle_frame')
func _load_snes_audio(data_and_buffer: Array):
yield(_on_loader_loading_stage_updated('Loading sound samples and music data', 'SoundLoader'), 'completed')
SoundLoader.parse_rom(data_and_buffer[0], data_and_buffer[1])
func load_snes_rom_from_bytes(bytes: PoolByteArray) -> void:
2023-12-11 00:53:01 +10:30
load_start_tick = Time.get_ticks_msec()
2023-12-06 18:52:45 +10:30
if (len(bytes) % 1024) == 512: # Naive header strip
bytes = bytes.subarray(512, -1)
self.snes_bytes = bytes
self.snes_buffer = StreamPeerBuffer.new()
self.snes_buffer.data_array = bytes
yield(_on_loader_loading_stage_updated('Loading struct definitions', 'StructLoader'), 'completed')
self.snes_data = load_snes_structs(self.snes_buffer)
if THREADED_LOADING:
# Give this its own buffer if threaded, avoid file pointer conflicts
var _thread_error = thread.start(self, '_load_snes_audio', [self.snes_data, self.snes_buffer.duplicate()])
else:
2023-12-11 00:53:01 +10:30
yield(self._load_snes_audio([self.snes_data, self.snes_buffer]), 'completed')
yield(_on_loader_loading_stage_updated('Loading strings', 'StringLoader'), 'completed')
yield(StringLoader.load_snes_rom(self.snes_buffer, true), 'completed')
SpriteLoader.reset()
yield(_on_loader_loading_stage_updated('Loading sprites', 'SpriteLoader'), 'completed')
SpriteLoader.load_from_structs(self.snes_data)
yield(_on_loader_loading_stage_updated('Loading enemy battle sprites', 'SpriteLoader'), 'completed')
SpriteLoader.load_enemy_battle_sprites(self.snes_data, self.snes_buffer)
yield(_on_loader_loading_stage_updated('Loading battle backgrounds', 'SpriteLoader'), 'completed')
SpriteLoader.load_battle_bgs(self.snes_data, self.snes_buffer)
yield(_on_loader_loading_stage_updated('Loading map data', 'MapLoader'), 'completed')
MapLoader.load_snes_rom(self.snes_data, self.snes_buffer)
yield(_on_loader_loading_stage_updated('Parsing music data', 'MusicManager'), 'completed')
MusicManager.load_snes_rom(self.snes_buffer)
yield(_on_loader_loading_stage_updated('Finished loading!', 'RomLoader'), 'completed')
emit_signal('rom_loaded')
2024-07-17 23:20:02 +09:30
var music = load('res://scripts/loaders/snes/music_ff5.gd').new()
music.disassemble_sfx(self.snes_buffer)
func load_snes_rom(filename: String) -> void:
2023-07-25 16:33:45 +09:30
var error := rom_snes.open(filename, File.READ)
if error == OK:
# Copy entire SNES ROM to a buffer for StreamPeerBuffer usage.
# Unfortunately, the File API is different and slightly worse than the StreamPeer API.
var rom_size := rom_snes.get_len()
var bytes := rom_snes.get_buffer(rom_size)
load_snes_rom_from_bytes(bytes)
2023-07-25 16:33:45 +09:30
2023-08-01 23:22:31 +09:30
func load_psx_folder(_dirname: String):
pass
func load_psx_image(filename: String):
# While it would technically be possible to load everything with no temporary files,
# It is more convenient to unpack the small files we care about to the user:// directory
var rom_psx := File.new()
var error := rom_psx.open(filename, File.READ)
if error == OK:
var cd := loader_cd_image.new(rom_psx)
for key in cd.directory:
var s = key.trim_prefix('./')
var re_match := psx_productcode_regex.search(s)
if re_match:
print(re_match.get_string(0))
print(cd.directory)
2023-07-25 16:33:45 +09:30
func _ready():
if THREADED_LOADING:
thread = Thread.new()
structdefs.merge(STRUCT.get_base_structarraytypes())
STRUCT.parse_struct_definitions_from_tsv_filename('res://data/structs_SNES_stubs.tsv', structdefs)
2024-06-26 20:43:37 +09:30
STRUCT.parse_struct_definitions_from_tsv_filename('res://data/5/structs/SNES_stubs.tsv', structdefs)
structdefs.merge(STRUCT_SNES.get_structtypes(), true) # Overwrite the stubs!
2024-06-26 20:43:37 +09:30
STRUCT.parse_struct_definitions_from_tsv_filename('res://data/5/structs/SNES_save.tsv', structdefs)
STRUCT.parse_struct_definitions_from_tsv_filename('res://data/5/structs/SNES.tsv', structdefs)
2023-08-01 23:22:31 +09:30
var _error := psx_productcode_regex.compile('(S[A-Z]{3}_\\d{3}\\.\\d{2});(\\d)')
StringLoader.connect('loading_stage_updated', self, '_on_loader_loading_stage_updated', ['StringLoader'])
2023-08-07 19:34:46 +09:30
# Debugging breakpoint
pass
2023-07-25 16:33:45 +09:30
func _exit_tree() -> void:
pass
if THREADED_LOADING:
thread.wait_to_finish()