Small additions/changes I forgot to commit
This commit is contained in:
parent
486880262b
commit
bbcd64b0f2
|
@ -273,6 +273,8 @@ class FF5Reader(QMainWindow):
|
|||
#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))
|
||||
perfcount()
|
||||
del block_cache
|
||||
del zone_px_cache
|
||||
|
||||
print('Generating Battle backgrounds')
|
||||
battle_bgs = make_battle_backgrounds(ROM_jp)
|
||||
|
|
|
@ -17,18 +17,21 @@
|
|||
|
||||
HEX_PREFIX = '#' # '#' '$' or '0x' are also nice
|
||||
|
||||
|
||||
def divceil(numerator, denominator):
|
||||
'''
|
||||
Reverse floor division for fast ceil
|
||||
'''
|
||||
return -(-numerator // denominator)
|
||||
|
||||
|
||||
def hex_length(i):
|
||||
'''
|
||||
String length of hexadecimal representation of integer
|
||||
'''
|
||||
return divceil(i.bit_length(), 4)
|
||||
|
||||
|
||||
def hex(num, digits=2):
|
||||
'''
|
||||
Consolidate hex formatting for consistency
|
||||
|
@ -36,11 +39,13 @@ def hex(num, digits=2):
|
|||
#return '{: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
|
||||
'''
|
||||
return int.from_bytes(rom[start:start+length], 'little')
|
||||
return int.from_bytes(rom[start:start+length], endian)
|
||||
|
||||
|
||||
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
|
||||
offset = (offset+1) % 0x800
|
||||
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
|
||||
|
|
|
@ -26,6 +26,8 @@ import sys
|
|||
from midiutil import MIDIFile
|
||||
from includes.helpers import indirect, hex
|
||||
from includes.const import BGM_Tracks_Safe
|
||||
import struct
|
||||
import wave
|
||||
|
||||
def generate_pointer_set(data):
|
||||
'''
|
||||
|
@ -51,14 +53,27 @@ def analyse_sample(data, pointer):
|
|||
|
||||
|
||||
def decode_brr(data):
|
||||
range = data[0] >> 4
|
||||
'''
|
||||
Decodes a single 9byte BRR packet
|
||||
'''
|
||||
_range = data[0] >> 4
|
||||
filter_designation = (data[0] & 0x0C) >> 2
|
||||
loop = bool(data[0] & 0x02)
|
||||
end = bool(data[0] & 0x01)
|
||||
samples = []
|
||||
for i in range(8):
|
||||
samples.append((data[1+range] >> 4) << range)
|
||||
samples.append((data[1+range] & 0x0F) << range)
|
||||
for i in data[1:]:
|
||||
b1 = (i >> 4)
|
||||
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)
|
||||
|
||||
|
||||
|
@ -275,6 +290,46 @@ def get_song_data(rom, id):
|
|||
#data = rom[offset+2:offset+2+size]
|
||||
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'):
|
||||
m = SPCParser().parse(tracks)
|
||||
with open(filename, 'wb') as file:
|
||||
|
|
Loading…
Reference in New Issue