Small additions/changes I forgot to commit

This commit is contained in:
Luke Hubmayer-Werner 2019-06-16 00:07:26 +09:30
parent 486880262b
commit bbcd64b0f2
3 changed files with 146 additions and 6 deletions

View File

@ -273,6 +273,8 @@ class FF5Reader(QMainWindow):
#zone_pxs += make_zone_pxs2(blocks, miniblocks, blockmaps, z, zone_px_cache) #zone_pxs += make_zone_pxs2(blocks, miniblocks, blockmaps, z, zone_px_cache)
print('Zone pixmap cache results: {misses} misses, {hits} hits'.format(**zone_px_cache)) print('Zone pixmap cache results: {misses} misses, {hits} hits'.format(**zone_px_cache))
perfcount() perfcount()
del block_cache
del zone_px_cache
print('Generating Battle backgrounds') print('Generating Battle backgrounds')
battle_bgs = make_battle_backgrounds(ROM_jp) battle_bgs = make_battle_backgrounds(ROM_jp)

View File

@ -17,18 +17,21 @@
HEX_PREFIX = '#' # '#' '$' or '0x' are also nice HEX_PREFIX = '#' # '#' '$' or '0x' are also nice
def divceil(numerator, denominator): def divceil(numerator, denominator):
''' '''
Reverse floor division for fast ceil Reverse floor division for fast ceil
''' '''
return -(-numerator // denominator) return -(-numerator // denominator)
def hex_length(i): def hex_length(i):
''' '''
String length of hexadecimal representation of integer String length of hexadecimal representation of integer
''' '''
return divceil(i.bit_length(), 4) return divceil(i.bit_length(), 4)
def hex(num, digits=2): def hex(num, digits=2):
''' '''
Consolidate hex formatting for consistency Consolidate hex formatting for consistency
@ -36,11 +39,13 @@ def hex(num, digits=2):
#return '{:0{}X}₁₆'.format(num, digits) #return '{:0{}X}₁₆'.format(num, digits)
return HEX_PREFIX + '{:0{}X}'.format(num, digits) return HEX_PREFIX + '{:0{}X}'.format(num, digits)
def indirect(rom, start, length=2):
def indirect(rom, start, length=2, endian='little'):
''' '''
Read little-endian value at start address in rom Read little-endian value at start address in rom
''' '''
return int.from_bytes(rom[start:start+length], 'little') return int.from_bytes(rom[start:start+length], endian)
def parse_struct(rom, offset, structure): def parse_struct(rom, offset, structure):
''' '''
@ -97,3 +102,81 @@ def decompress_lzss(rom, start, header=False, length=None):
buffer_p = (buffer_p+1) % 0x800 buffer_p = (buffer_p+1) % 0x800
offset = (offset+1) % 0x800 offset = (offset+1) % 0x800
return bytes(output[:uncompressed_length]) return bytes(output[:uncompressed_length])
def decompress_lzss_FFVa(rom, start, header=False, length=None):
'''
Oops, it's just GBA BIOS decompression functions
see https://web.archive.org/web/20130323133944/http://nocash.emubase.de/gbatek.htm#biosdecompressionfunctions
'''
ptr = start
if length:
uncompressed_length = length
else:
uncompressed_length = indirect(rom, start, endian='big')
ptr += 2
output = []
while len(output) < uncompressed_length:
bitmap_byte = rom[ptr]
ptr += 1
for i in reversed(range(8)):
bit = (bitmap_byte >> i) & 1
if not bit:
b = rom[ptr]
ptr += 1
output.append(b)
else:
b1 = rom[ptr]
b2 = rom[ptr+1]
ptr += 2
length = ((b1 & 0xF0) >> 4) + 3
trackback = -1 - (b2 + ((b1 & 0x0F) << 8))
try:
for j in range(length):
output.append(output[trackback])
except:
print(len(output), f'0x{ptr:X}', f'0x{b1:02X}{b2:02X}', trackback)
raise
print(f'0x{ptr:X}')
return bytes(output[:uncompressed_length])
def findall(rom, string):
results = []
start = 0
while True:
val = rom.find(string, start)
if val < 0:
return results
results.append(val)
start = val + 1
def parse_ips(data):
assert data[:5] == b'PATCH' and data[-3:] == b'EOF', 'File header and footer missing!'
patches = {}
ptr = 5
while ptr < len(data)-6:
address = int.from_bytes(data[ptr:ptr+3], 'big')
length = int.from_bytes(data[ptr+3:ptr+5], 'big')
if length > 0:
payload = data[ptr+5:ptr+5+length]
ptr += 5 + length
else:
repeats = data[ptr+5:ptr+7]
payload = data[ptr+7] * repeats
ptr += 8
patches[address] = payload
return patches
if __name__ == '__main__':
with open('2564 - Final Fantasy V Advance (U)(Independent).gba', 'rb') as file:
ROM = file.read()
landmark = ROM.find(b'FINAL FANTASY V ADVANCE SYGMAB')
try:
with open('Final Fantasy V Advance (Europe) (En,Fr,De,Es,It)-spritehack.ips', 'rb') as file:
spritehack_ips = file.read()
print('spritehack_ips loaded')
except:
pass

View File

@ -26,6 +26,8 @@ import sys
from midiutil import MIDIFile from midiutil import MIDIFile
from includes.helpers import indirect, hex from includes.helpers import indirect, hex
from includes.const import BGM_Tracks_Safe from includes.const import BGM_Tracks_Safe
import struct
import wave
def generate_pointer_set(data): def generate_pointer_set(data):
''' '''
@ -51,14 +53,27 @@ def analyse_sample(data, pointer):
def decode_brr(data): def decode_brr(data):
range = data[0] >> 4 '''
Decodes a single 9byte BRR packet
'''
_range = data[0] >> 4
filter_designation = (data[0] & 0x0C) >> 2 filter_designation = (data[0] & 0x0C) >> 2
loop = bool(data[0] & 0x02) loop = bool(data[0] & 0x02)
end = bool(data[0] & 0x01) end = bool(data[0] & 0x01)
samples = [] samples = []
for i in range(8): for i in data[1:]:
samples.append((data[1+range] >> 4) << range) b1 = (i >> 4)
samples.append((data[1+range] & 0x0F) << range) b2 = (i & 0x0F)
# Sign-extend
if b1 >= 8:
b1 |= 0xFFF0
if b2 >= 8:
b2 |= 0xFFF0
samples.append((b1 << _range) & 0xFFFF)
samples.append((b2 << _range) & 0xFFFF)
# For filter arithmetic the samples need to be in signed form.
sample_bytes = struct.pack('<'+'H'*16, *samples)
samples = struct.unpack('<'+'h'*16, sample_bytes)
return (samples, loop, end, filter_designation) return (samples, loop, end, filter_designation)
@ -275,6 +290,46 @@ def get_song_data(rom, id):
#data = rom[offset+2:offset+2+size] #data = rom[offset+2:offset+2+size]
return tracks return tracks
def get_sample_data(rom, id):
lookup_offset = 0x043C6F + (id*3)
offset = indirect(rom, lookup_offset, 3)-0xC00000
size = indirect(rom, offset)
data = rom[offset+2:offset+2+size]
return data
def clamp_short(num):
return min(max(num, -0x7FFF), 0x7FFF)
def make_sample(rom, id):
data = get_sample_data(rom, id)
packets = [data[i:i+9] for i in range(0, len(data), 9)]
samples = [0, 0] # Two zero samples for filter purposes, strip them from the actual output
for p in packets:
c_samples, loop, end, filter = decode_brr(p)
samples += c_samples
if filter == 1:
for i in range(-8, 0, 1):
samples[i] = clamp_short(samples[i] + (samples[i-1]*15)//16)
elif filter == 2:
for i in range(-8, 0, 1):
samples[i] = clamp_short(samples[i] + (samples[i-1]*61)//32 - (samples[i-2]*15)//16 )
elif filter == 3:
for i in range(-8, 0, 1):
samples[i] = clamp_short(samples[i] + (samples[i-1]*115)//64 - (samples[i-2]*13)//16 )
if end:
break
return samples[2:]
def make_sample_wav(rom, id):
samples = make_sample(rom, id)
filename = 'Sample{}.wav'.format(id)
with wave.open(filename, 'wb') as file:
file.setnchannels(1)
file.setframerate(8000)
file.setsampwidth(2)
sample_bytes = struct.pack('<'+'h'*len(samples), *samples)
file.writeframes(sample_bytes)
def make_midi_file(tracks, filename='test.mid'): def make_midi_file(tracks, filename='test.mid'):
m = SPCParser().parse(tracks) m = SPCParser().parse(tracks)
with open(filename, 'wb') as file: with open(filename, 'wb') as file: