diff --git a/scripts/loaders/MapLoader.gd b/scripts/loaders/MapLoader.gd index 59c3eb5..33bce99 100644 --- a/scripts/loaders/MapLoader.gd +++ b/scripts/loaders/MapLoader.gd @@ -96,13 +96,13 @@ var worldmaps = [WorldMap.new(), WorldMap.new(), WorldMap.new(), WorldMap.new(), var worldmap_block_properties = [] var worldmap_block_pathings = [] -func load_worldmap_block_properties(rom: File): - rom.seek(0x0FEA00) +func load_worldmap_block_properties(buffer: StreamPeerBuffer): + buffer.seek(0x0FEA00) for world_ts in 3: var ts_properties = PoolIntArray() var ts_pathings = PoolIntArray() for block in 0xC0: - var properties := rom.get_16() + (rom.get_8() << 16) + var properties := buffer.get_u16() + (buffer.get_u8() << 16) ts_properties.append(properties) var pathings := properties >> 16 # First 8 pathable flags map directly pathings |= (((properties >> 12) & 0xF) ^ 0xF) << 8 # Next 4 flags (can land) are taken from high bits of second byte and inverted @@ -116,14 +116,14 @@ func load_worldmap_block_properties(rom: File): worldmaps[4].block_pathing = worldmap_block_pathings[2] -func load_worldmaps(rom: File): +func load_worldmaps(buffer: StreamPeerBuffer): var chunk_addresses = PoolIntArray() chunk_addresses.resize(0x500) # 5 worldmaps * 256 chunks - rom.seek(0x0FE000) + buffer.seek(0x0FE000) for id in range(0, 0x434): - chunk_addresses[id] = rom.get_16() + 0x070000 + chunk_addresses[id] = buffer.get_u16() + 0x070000 for id in range(0x434, 0x500): - chunk_addresses[id] = rom.get_16() + 0x080000 + chunk_addresses[id] = buffer.get_u16() + 0x080000 for worldmap_id in 5: # Bartz World, Galuf World, Combined World, Underwater Galuf World, Underwater Combined World # Worldmap chunks have a basic compression. @@ -132,14 +132,14 @@ func load_worldmaps(rom: File): var blockmap = PoolByteArray() # blockmap.resize(WorldMap.block_height * WorldMap.block_width) # Try this later if performance is a problem for chunk_id in range(worldmap_id*0x100, (worldmap_id+1)*0x100): - rom.seek(chunk_addresses[chunk_id]) + buffer.seek(chunk_addresses[chunk_id]) var chunk_size := 0 while chunk_size < 256: # var b: int = (blockmap.size() % 16) + (16 * (chunk_id % 12)); # For debugging the map shader against blocks - var b := rom.get_8() + var b := buffer.get_u8() if b >= 0xC0: # RLE var count := b-0xBF - var block = rom.get_8() + var block = buffer.get_u8() for i in count: blockmap.append(block) chunk_size += count @@ -161,6 +161,6 @@ func update_worldmap_block_tile_ids(worldmap_block_tile_ids: Array): worldmaps[3].block_tile_ids = worldmap_block_tile_ids[2] worldmaps[4].block_tile_ids = worldmap_block_tile_ids[2] -func load_snes_rom(rom: File): - load_worldmap_block_properties(rom) - load_worldmaps(rom) +func load_snes_rom(buffer: StreamPeerBuffer): + load_worldmap_block_properties(buffer) + load_worldmaps(buffer) diff --git a/scripts/loaders/RomLoader.gd b/scripts/loaders/RomLoader.gd index 62a656f..52b8bba 100644 --- a/scripts/loaders/RomLoader.gd +++ b/scripts/loaders/RomLoader.gd @@ -20,6 +20,46 @@ var snes_data := {} var snes_bytes: PoolByteArray var thread := Thread.new() +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 + func load_snes_rom(filename: String): var error := rom_snes.open(filename, File.READ) if error == OK: @@ -30,52 +70,16 @@ func load_snes_rom(filename: String): self.snes_bytes = bytes var buffer = StreamPeerBuffer.new() buffer.data_array = bytes - # SpriteLoader.load_snes_rom(rom_snes) - # MapLoader.load_snes_rom(rom_snes) - StringLoader.load_snes_rom(rom_snes, true) - # Don't do this concurrently, avoid file pointer conflicts - #var _thread_error = thread.start(SoundLoader, 'parse_rom', rom_snes) - # Can concurrently work with the preloaded StreamPeerBuffer though - 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 snes_data): - snes_data[k0] = {k1: s.get_value(buffer, [0, 0])} - else: - snes_data[k0][k1] = s.get_value(buffer, [0, 0]) - else: - snes_data[key] = s.get_value(buffer, [0, 0]) - snes_data['job_levels'] = [] - for job_id in 21: - var ability_list_ptr: int = snes_data.ptrs_job_ability_lists[job_id] - var num_abilities: int = snes_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}) - snes_data.job_levels.append(ability_list) + + StringLoader.load_snes_rom(buffer, true) + # Give this its own buffer, avoid file pointer conflicts + var _thread_error = thread.start(SoundLoader, 'parse_rom', buffer.duplicate()) + self.snes_data = load_snes_structs(buffer) #print(snes_data.job_levels) - SpriteLoader.load_from_structs(snes_data) - SpriteLoader.load_enemy_battle_sprites(snes_data, buffer) - SpriteLoader.load_battle_bgs(snes_data, buffer) - MapLoader.load_snes_rom(rom_snes) + SpriteLoader.load_from_structs(self.snes_data) + SpriteLoader.load_enemy_battle_sprites(self.snes_data, buffer) + SpriteLoader.load_battle_bgs(self.snes_data, buffer) + MapLoader.load_snes_rom(buffer) func load_psx_folder(_dirname: String): pass diff --git a/scripts/loaders/SoundLoader.gd b/scripts/loaders/SoundLoader.gd index 2d3269d..34ab993 100644 --- a/scripts/loaders/SoundLoader.gd +++ b/scripts/loaders/SoundLoader.gd @@ -21,9 +21,9 @@ var bgm_tracks = [] var instrument_samples = [] var sfx_samples = [] -func read_rom_address(rom: File) -> int: +func read_rom_address(buffer: StreamPeerBuffer) -> int: # Read a 3-byte little-endian address and wrap the bank to ROM space - return rom.get_16() + ((rom.get_8() & 0x3F) << 16) + return buffer.get_u16() + ((buffer.get_u8() & 0x3F) << 16) const MAX_15B = 1 << 15 const MAX_16B = 1 << 16 @@ -55,14 +55,14 @@ func clamp_short(i: int) -> int: return 0x7FFF return i -func make_sample(rom: File, size: int, sample_rate: int) -> AudioStreamSample: +func make_sample(buffer: StreamPeerBuffer, size: int, sample_rate: int) -> AudioStreamSample: var audio := AudioStreamSample.new() audio.mix_rate = sample_rate audio.stereo = false audio.set_format(AudioStreamSample.FORMAT_16_BITS) if (size % 9) != 0: - print_debug('Oh no! An instrument sample has an invalid size of %d! at $%06X' % [size, rom.get_position()-2]) + print_debug('Oh no! An instrument sample has an invalid size of %d! at $%06X' % [size, buffer.get_position()-2]) return audio var num_packets := size/9 @@ -70,13 +70,13 @@ func make_sample(rom: File, size: int, sample_rate: int) -> AudioStreamSample: var i := 2 for pkt in num_packets: # Decode a single 9byte BRR packet - var header_byte := rom.get_8() + var header_byte := buffer.get_u8() var exponent := header_byte >> 4 var filter := (header_byte >> 2) & 0x03 var loop := bool(header_byte & 0x02) var end := bool(header_byte & 0x01) for sample in 8: - var b := rom.get_8() + var b := buffer.get_u8() samples.append(process_sample(b >> 4, exponent)) samples.append(process_sample(b & 0x0F, exponent)) # Apply filter @@ -103,75 +103,75 @@ func make_sample(rom: File, size: int, sample_rate: int) -> AudioStreamSample: audio.data = audio_data return audio -func get_inst_sample_data(rom: File, id: int) -> AudioStreamSample: - rom.seek(INST_SR + (id*2)) - var tuning1 := rom.get_8() - var tuning2 := rom.get_8() +func get_inst_sample_data(buffer: StreamPeerBuffer, id: int) -> AudioStreamSample: + buffer.seek(INST_SR + (id*2)) + var tuning1 := buffer.get_u8() + var tuning2 := buffer.get_u8() var sample_rate := get_reference_pitch_samplerate(tuning1) - rom.seek(INST_LOOP + (id*2)) - var loop_start_packet := rom.get_16()/9 # Note that Instrument $1F Steel Guitar has a length of $088B but a loop point of $088D which is 243.22... packets. Luckily it doesn't matter. + buffer.seek(INST_LOOP + (id*2)) + var loop_start_packet := buffer.get_u16()/9 # Note that Instrument $1F Steel Guitar has a length of $088B but a loop point of $088D which is 243.22... packets. Luckily it doesn't matter. var lookup_offset := INST_BRR_LOOKUP + (id*3) - rom.seek(lookup_offset) - var brr_offset := read_rom_address(rom) - rom.seek(brr_offset) - var size := rom.get_16() + buffer.seek(lookup_offset) + var brr_offset := read_rom_address(buffer) + buffer.seek(brr_offset) + var size := buffer.get_u16() var num_samples := (size/9)*16 - var audio := make_sample(rom, size, sample_rate) + var audio := make_sample(buffer, size, sample_rate) audio.loop_mode = AudioStreamSample.LOOP_FORWARD audio.loop_begin = loop_start_packet * 16 # Each 9byte packet is 16 samples audio.loop_end = num_samples-1 # print_debug('Loaded instrument #%02X with lookup offset $%06X, BRR data offset $%06X, length $%04X (%f packets, %d samples) and loop point %d samples' % [id, lookup_offset, brr_offset, size, size/9.0, num_samples, audio.loop_begin]) return audio -func load_sfx_samples_data(rom: File): +func load_sfx_samples_data(buffer: StreamPeerBuffer): var sample_rates = [] - rom.seek(SFX_SR) + buffer.seek(SFX_SR) for i in SFX_NUM: - var tuning1 := rom.get_8() - var tuning2 := rom.get_8() + var tuning1 := buffer.get_u8() + var tuning2 := buffer.get_u8() sample_rates.append(get_reference_pitch_samplerate(tuning1)) var brr_spc_addrs = [] var brr_spc_loop_addrs = [] - rom.seek(SFX_BRR_SPC_TABLE) + buffer.seek(SFX_BRR_SPC_TABLE) for i in SFX_NUM: - brr_spc_addrs.append(rom.get_16()) - brr_spc_loop_addrs.append(rom.get_16()) + brr_spc_addrs.append(buffer.get_u16()) + brr_spc_loop_addrs.append(buffer.get_u16()) var brr_spc_start = brr_spc_addrs[0] for i in SFX_NUM: brr_spc_addrs[i] += SFX_BRR_START - brr_spc_start for i in SFX_NUM: - rom.seek(brr_spc_addrs[i]) - # print('Loading sfx sample #%X with BRR data offset $%06X' % [i, rom.get_position()]) - sfx_samples.append(make_sample(rom, 900, sample_rates[i])) # Use 900 as a limit, it won't be hit, parser stops after End packet anyway + buffer.seek(brr_spc_addrs[i]) + # print('Loading sfx sample #%X with BRR data offset $%06X' % [i, buffer.get_position()]) + sfx_samples.append(make_sample(buffer, 900, sample_rates[i])) # Use 900 as a limit, it won't be hit, parser stops after End packet anyway emit_signal('audio_sfx_sample_loaded', i) # print('size of %d samples' % sfx_samples[i].data.size()) # Called when the node enters the scene tree for the first time. -func load_samples(rom: File): - load_sfx_samples_data(rom) +func load_samples(buffer: StreamPeerBuffer): + load_sfx_samples_data(buffer) # For some reason, this is a bit slow currently. Might optimize later. for i in INST_NUM: - instrument_samples.append(get_inst_sample_data(rom, i)) + instrument_samples.append(get_inst_sample_data(buffer, i)) emit_signal('audio_inst_sample_loaded', i) -func get_song_data(rom: File, id: int): +func get_song_data(buffer: StreamPeerBuffer, id: int): var lookup_offset := BGM_LOOKUP + (id*3) - rom.seek(lookup_offset) - var offset := read_rom_address(rom) + buffer.seek(lookup_offset) + var offset := read_rom_address(buffer) var bank := offset & 0xFF0000 - rom.seek(offset) - var _block_size := rom.get_16() # Unused since we pull the individual tracks + buffer.seek(offset) + var _block_size := buffer.get_u16() # Unused since we pull the individual tracks var track_ptrs = [] for i in 10: - var a := bank + rom.get_16() + var a := bank + buffer.get_u16() if a < offset: a += 0x010000 # Bank shift track_ptrs.append(a) @@ -181,12 +181,12 @@ func get_song_data(rom: File, id: int): var tracks = [] for i in range(1, track_ptrs.size()-1): var length = track_ptrs[i+1] - track_ptrs[i] - tracks.append(rom.get_buffer(length)) + tracks.append(buffer.get_data(length)[0]) return tracks -func load_bgms(rom: File): +func load_bgms(buffer: StreamPeerBuffer): for i in BGM_NUM: - bgm_tracks.append(get_song_data(rom, i)) + bgm_tracks.append(get_song_data(buffer, i)) @@ -204,9 +204,9 @@ func play_sfx(id: int): player.stream = sfx_samples[id] player.play() -func parse_rom(rom: File): - load_samples(rom) - #load_bgms(rom) +func parse_rom(buffer: StreamPeerBuffer): + load_samples(buffer) + #load_bgms(buffer) has_loaded_audio_samples = true emit_signal('audio_samples_loaded') diff --git a/scripts/loaders/StringLoader.gd b/scripts/loaders/StringLoader.gd index 891c7f4..e4a22e0 100644 --- a/scripts/loaders/StringLoader.gd +++ b/scripts/loaders/StringLoader.gd @@ -28,7 +28,7 @@ func decode_array(array, glyph_table, trim_trailing_whitespace: bool = true) -> output.append(decode_string(s, glyph_table, trim_trailing_whitespace)) return output -func load_snes_rom(rom: File, is_RPGe: bool = false) -> void: +func load_snes_rom(buffer: StreamPeerBuffer, is_RPGe: bool = false) -> void: for block_name in SNES_block_addresses: var block: Dictionary = SNES_block_addresses[block_name] var glyph_table_small: PoolStringArray = glyph_tables.RPGe_small if is_RPGe else glyph_tables.SNES_small @@ -41,7 +41,7 @@ func load_snes_rom(rom: File, is_RPGe: bool = false) -> void: var l1_address: int = block.address if (not is_RPGe) and block.snes_address: l1_address = block.snes_address - rom.seek(l1_address) + buffer.seek(l1_address) var ptr_offset = block.rpge_ptr_offset if is_RPGe else block.snes_ptr_offset if ptr_offset is int: @@ -49,33 +49,33 @@ func load_snes_rom(rom: File, is_RPGe: bool = false) -> void: match l1_width: 1: for i in num_entries: - ptrs.append((ptr_offset + rom.get_8()) & 0x3FFFFF) # Bank wrapping + ptrs.append((ptr_offset + buffer.get_u8()) & 0x3FFFFF) # Bank wrapping 2: for i in num_entries: - ptrs.append((ptr_offset + rom.get_16()) & 0x3FFFFF) # Bank wrapping + ptrs.append((ptr_offset + buffer.get_u16()) & 0x3FFFFF) # Bank wrapping 3: for i in num_entries: - ptrs.append((ptr_offset + rom.get_16() + (rom.get_8() << 16)) & 0x3FFFFF) # Bank wrapping + ptrs.append((ptr_offset + buffer.get_u16() + (buffer.get_u8() << 16)) & 0x3FFFFF) # Bank wrapping _: assert(false, 'Indirect l1_width of %d is not possible' % l1_width) if block.null_terminated: for i in num_entries: - rom.seek(ptrs[i]) + buffer.seek(ptrs[i]) var bytes = PoolByteArray() while true: - var b = rom.get_8() + var b = buffer.get_u8() if b == 0: break bytes.append(b) raw_strings.append(bytes) else: for i in num_entries-1: - rom.seek(ptrs[i]) - raw_strings.append(rom.get_buffer(ptrs[i+1] - ptrs[i])) + buffer.seek(ptrs[i]) + raw_strings.append(buffer.get_data(ptrs[i+1] - ptrs[i])[0]) else: # Get first level of data for i in num_entries: - raw_strings.append(rom.get_buffer(l1_width)) + raw_strings.append(buffer.get_data(l1_width)[0]) # Decode if block.dialog: