Refactor a bunch of stuff
This commit is contained in:
parent
e87942c731
commit
1533e16c4f
226
ff5reader.py
226
ff5reader.py
|
@ -23,6 +23,7 @@ from itertools import chain
|
|||
from array import array
|
||||
import time
|
||||
import functools
|
||||
from typing import Iterable
|
||||
|
||||
from includes.helpers import *
|
||||
from includes.qthelpers import *
|
||||
|
@ -34,12 +35,13 @@ from includes.snestile import (
|
|||
Canvas, Canvas_Indexed
|
||||
)
|
||||
from includes.snes import *
|
||||
import includes.const as const
|
||||
import includes.ff5.const as const
|
||||
from includes.ff5.strings import StringBlock, RPGe_Dialogue_Width
|
||||
from includes.ff5.strings import Strings as FFVStrings
|
||||
import includes.ff4 as ff4
|
||||
import includes.ff5 as ff5
|
||||
import includes.ff6 as ff6
|
||||
|
||||
filename_en = 'Final Fantasy V (Japan) [En by RPGe v1.1].sfc'
|
||||
filename_jp = 'Final Fantasy V (Japan).sfc'
|
||||
filename_jp_ff4 = 'Final Fantasy IV (Japan) (Rev A).sfc'
|
||||
filename_jp_ff6 = 'Final Fantasy VI (Japan).sfc'
|
||||
|
||||
|
@ -53,17 +55,11 @@ class FF5Reader(QMainWindow):
|
|||
QMainWindow.__init__(self, None)
|
||||
perfcount()
|
||||
print('Reading ROMs')
|
||||
with open(filename_en, 'rb') as file:
|
||||
ROM_en = file.read()
|
||||
print(len(ROM_en), filename_en)
|
||||
with open(filename_jp, 'rb') as file:
|
||||
ROM_jp = file.read()
|
||||
print(len(ROM_jp), filename_jp)
|
||||
with open(filename_jp_ff4, 'rb') as file:
|
||||
ROM_FF4jp = file.read()
|
||||
ROM_en = ff5.files.ROM_RPGe
|
||||
ROM_jp = ff5.files.ROM_SNES
|
||||
ROM_FF4jp = load_raw(filename_jp_ff4)
|
||||
ROM_FF6jp = load_raw(filename_jp_ff6)
|
||||
print(len(ROM_FF4jp), filename_jp_ff4)
|
||||
with open(filename_jp_ff6, 'rb') as file:
|
||||
ROM_FF6jp = file.read()
|
||||
print(len(ROM_FF6jp), filename_jp_ff6)
|
||||
perfcount()
|
||||
|
||||
|
@ -75,61 +71,14 @@ class FF5Reader(QMainWindow):
|
|||
'glyphs_jp_l': generate_glyphs_large(ROM_jp, 0x03E800),
|
||||
'glyphs_kanji': generate_glyphs_large(ROM_jp, 0x1BD000, 0x1AA), # Kanji are unchanged in EN version
|
||||
}
|
||||
make_string_img_list = functools.partial(_make_string_img_list, ROM_en, ROM_jp, **self.glyph_sprites)
|
||||
make_string_img_list = functools.partial(_make_string_img_list, **self.glyph_sprites)
|
||||
perfcount()
|
||||
|
||||
stringlist_headers = ['Address', 'ID', 'Name']
|
||||
imglist_headers = stringlist_headers + ['Img', 'Name JP', 'Img JP']
|
||||
imglist_headers = ['ID', 'EN Pointer', 'EN Address', 'EN String', 'EN Img', 'JP Pointer', 'JP Address', 'JP String', 'JP Img']
|
||||
|
||||
print('Generating Strings')
|
||||
zone_names = make_string_img_list(0x107000, 2, 0x100, start_str=0x270000, start_jp_str=0x107200, indirect=True, large=True)
|
||||
menu_strings = make_string_img_list(0xF987, 2, 139, start_str=0x270000, start_jp_str=0x0000, indirect=True)
|
||||
items = make_string_img_list(0x111380, 9, 256)
|
||||
magics = make_string_img_list(0x111C80, 6, 87)
|
||||
more_magics = make_string_img_list(0x111E8A, 9, 73)
|
||||
enemy_names = make_string_img_list(0x200050, 10, 0x180, 0x105C00, 8)
|
||||
character_names = make_string_img_list(0x115500, 6, 5)
|
||||
job_names = make_string_img_list(0x115600, 8, 22)
|
||||
ability_names = make_string_img_list(0x116200, 8, 33)
|
||||
battle_commands = make_string_img_list(0x201150, 7, 0x60, 0x115800, 5)
|
||||
print('Generating String Images')
|
||||
string_images = {k: make_string_img_list(*FFVStrings.blocks_SNES_RPGe[k], large=config.get('dialog')) for k,config in FFVStrings.config.items()}
|
||||
perfcount()
|
||||
dialogue = make_string_img_list(0x2013F0, 3, 0x900, start_jp=0x082220, len_jp=2, start_str=0x0, start_jp_str=0x0A0000, indirect=True, large=True, macros_en=const.Dialogue_Macros_EN, macros_jp=const.Dialogue_Macros_JP)
|
||||
perfcount()
|
||||
|
||||
def split_tilesets(data):
|
||||
tilesets = [(data & 0x00003F),
|
||||
(data & 0x000FC0) >> 6,
|
||||
(data & 0x03F000) >> 12,
|
||||
(data & 0xFC0000) >> 18]
|
||||
return ' '.join([hex(i,2) for i in tilesets])
|
||||
def split_blockmaps(data):
|
||||
blockmaps = [(data & 0x000003FF) - 1,
|
||||
((data & 0x000FFC00) >> 10) - 1,
|
||||
((data & 0x3FF00000) >> 20) - 1]
|
||||
return ' '.join([hex(i,3) for i in blockmaps])
|
||||
|
||||
zone_structure = [('NPC Layer', 2, None), # 00 01
|
||||
('Name', 1, [z[2] for z in zone_names]), # 02
|
||||
('ShadowFlags', 1, None), # 03
|
||||
('Graphic maths', 1, None), # 04 - MSb animation-related, 6 LSbs are ID for table in 0x005BB8 which writes to $2131-$2133 (Color Math Designation, Subscreen BG color)
|
||||
('Tile properties', 1, None), # 05
|
||||
('Flags '+hex(6), 1, None), # 06
|
||||
(hex(7), 1, None), # 07
|
||||
('Blockset', 1, None), # 08
|
||||
('Tilesets', 3, split_tilesets), # 09 0A 0B
|
||||
('Blockmaps', 4, split_blockmaps), # 0C 0D 0E 0F
|
||||
(hex(16), 1, None), # 10
|
||||
(hex(17), 1, None), # 11
|
||||
(hex(18), 1, None), # 12
|
||||
(hex(19), 1, None), # 13
|
||||
(hex(20), 1, None), # 14
|
||||
(hex(21), 1, None), # 15
|
||||
('Palette', 1, None), # 16
|
||||
(hex(23), 1, None), # 17
|
||||
(hex(24), 1, None), # 18
|
||||
('Music', 1, const.BGM_Tracks)] # 19
|
||||
zone_headers = ['Address'] + [z[0] for z in zone_structure]
|
||||
zone_data = [parse_struct(ROM_en, 0x0E9C00 + (i*0x1A), zone_structure) for i in range(const.zone_count)]
|
||||
|
||||
battle_bg_structure = [('Tileset', 1, None),
|
||||
('Palette 1', 1, None),
|
||||
|
@ -170,7 +119,7 @@ class FF5Reader(QMainWindow):
|
|||
enemy_sprite_headers = ['Address']+[i[0] for i in enemy_sprite_structure]+['EN Name','EN Name']
|
||||
address = 0x14B180
|
||||
for i in range(0x180):
|
||||
enemy_sprite_data.append(parse_struct(ROM_en, address + (i*5), enemy_sprite_structure) + enemy_names[i][2:4])
|
||||
enemy_sprite_data.append(parse_struct(ROM_en, address + (i*5), enemy_sprite_structure) + string_images['enemy_names'][i][3:5])
|
||||
|
||||
perfcount()
|
||||
print('Generating map tiles')
|
||||
|
@ -286,23 +235,22 @@ class FF5Reader(QMainWindow):
|
|||
self.ff6widget.addTab(make_px_table(self.portraits_ff6, cols=19, scale=2), 'Character Portraits')
|
||||
|
||||
|
||||
structs_tab.addTab(make_table(zone_headers, zone_data, True), 'Zones')
|
||||
structs_tab.addTab(make_table(ff5.ZoneData.zone_headers, ff5.ZoneData.get_data(), True), 'Zones')
|
||||
structs_tab.addTab(make_table(tileset_headers, tileset_data, True), 'Tilesets')
|
||||
structs_tab.addTab(make_table(battle_bg_headers, battle_bg_data, True), 'BattleBGs')
|
||||
structs_tab.addTab(make_table(const.npc_layer_headers, npc_layers, True), 'NPC Layers')
|
||||
structs_tab.addTab(make_table(enemy_sprite_headers, enemy_sprite_data, True), 'Enemy Sprites')
|
||||
|
||||
strings_tab.addTab(make_table(imglist_headers, menu_strings, row_labels=False), 'Menu Strings')
|
||||
strings_tab.addTab(make_table(imglist_headers, items, row_labels=False), 'Items')
|
||||
strings_tab.addTab(make_table(imglist_headers, magics+more_magics, row_labels=False), 'Magics')
|
||||
#strings_tab.addTab(make_table(imglist_headers, more_magics, row_labels=False), 'More Magics')
|
||||
strings_tab.addTab(make_table(imglist_headers, enemy_names, row_labels=False), 'Enemy Names')
|
||||
strings_tab.addTab(make_table(imglist_headers, character_names, row_labels=False), 'Character Names')
|
||||
strings_tab.addTab(make_table(imglist_headers, job_names, row_labels=False), 'Job Names')
|
||||
strings_tab.addTab(make_table(imglist_headers, ability_names, row_labels=False), 'Ability Names')
|
||||
strings_tab.addTab(make_table(imglist_headers, battle_commands, row_labels=False), 'Battle Commands')
|
||||
strings_tab.addTab(make_table(imglist_headers, zone_names, True, scale=1), 'Zone Names')
|
||||
strings_tab.addTab(make_table(imglist_headers+['JP address'], dialogue, scale=1), 'Dialogue')
|
||||
strings_tab.addTab(make_table(imglist_headers, string_images['menu_strings'], row_labels=False), 'Menu Strings')
|
||||
strings_tab.addTab(make_table(imglist_headers, string_images['items'], row_labels=False), 'Items')
|
||||
strings_tab.addTab(make_table(imglist_headers, string_images['magics']+string_images['magics2'], row_labels=False), 'Magics')
|
||||
strings_tab.addTab(make_table(imglist_headers, string_images['enemy_names'], row_labels=False), 'Enemy Names')
|
||||
strings_tab.addTab(make_table(imglist_headers, string_images['character_names'], row_labels=False), 'Character Names')
|
||||
strings_tab.addTab(make_table(imglist_headers, string_images['job_names'], row_labels=False), 'Job Names')
|
||||
strings_tab.addTab(make_table(imglist_headers, string_images['ability_names'], row_labels=False), 'Ability Names')
|
||||
strings_tab.addTab(make_table(imglist_headers, string_images['battle_commands'], row_labels=False), 'Battle Commands')
|
||||
strings_tab.addTab(make_table(imglist_headers, string_images['zone_names'], True, scale=1), 'Zone Names')
|
||||
strings_tab.addTab(make_table(imglist_headers, string_images['dialogue'], scale=1), 'Dialogue')
|
||||
|
||||
self.string_decoder = QWidget()
|
||||
self.decoder_input = QLineEdit()
|
||||
|
@ -337,19 +285,17 @@ class FF5Reader(QMainWindow):
|
|||
The painting logic here needs to be moved into includes.snestile at some point.
|
||||
Once that is done, these functions will be moved to includes.snes which should not have qt dependencies.
|
||||
'''
|
||||
def make_string_img_small(bytestring, glyphs, jp=False):
|
||||
def make_string_img_small(bytestring: Iterable[int], glyphs, handle_dakuten=False):
|
||||
'''
|
||||
JP version is not as fancy as this with dakuten, it just puts them on the row above and clips.
|
||||
'''
|
||||
if len(bytestring) < 1:
|
||||
raise ValueError('Empty bytestring was passed')
|
||||
string = ''
|
||||
return None
|
||||
img = QImage(len(bytestring)*8, 10, QImage.Format_RGB16)
|
||||
img.fill(bg_color)
|
||||
painter = QtGui.QPainter(img)
|
||||
if jp:
|
||||
if handle_dakuten:
|
||||
for x, j in enumerate(bytestring):
|
||||
string = string + const.Glyphs_JP2[j]
|
||||
if 0x20 <= j < 0x52:
|
||||
if j > 0x48:
|
||||
painter.drawPixmap(x*8, 2, glyphs[j+0x17])
|
||||
|
@ -361,33 +307,20 @@ def make_string_img_small(bytestring, glyphs, jp=False):
|
|||
painter.drawPixmap(x*8, 2, glyphs[j])
|
||||
else:
|
||||
for x, j in enumerate(bytestring):
|
||||
string = string + const.Glyphs[j]
|
||||
painter.drawPixmap(x*8, 1, glyphs[j])
|
||||
del painter
|
||||
return string, QPixmap.fromImage(img)
|
||||
return QPixmap.fromImage(img)
|
||||
|
||||
|
||||
def make_string_img_large(bytestring, glyphs, glyphs_kanji, macros=None, jp=False):
|
||||
def make_string_img_large(glyph_ids: Iterable[int], glyphs, glyphs_kanji=None):
|
||||
'''
|
||||
This is how we decipher dialogue data, which has multiple lines, macro expansions and kanji.
|
||||
English characters have varying widths. In the japanese version, everything is fullwidth (16px)
|
||||
Kanji aren't used in English dialogue but the cost is likely the same in checking either way.
|
||||
'''
|
||||
if len(bytestring) < 1:
|
||||
raise ValueError('Empty bytestring was passed')
|
||||
if len(glyph_ids) < 1:
|
||||
return None
|
||||
|
||||
newstring = []
|
||||
bytes = iter(bytestring)
|
||||
for b in bytes:
|
||||
if b in const.DoubleChars:
|
||||
b2 = next(bytes)
|
||||
newstring.append((b<<8) + b2)
|
||||
elif macros and b in macros:
|
||||
newstring.extend(macros[b])
|
||||
else:
|
||||
newstring.append(b)
|
||||
|
||||
string = ''
|
||||
# Because the length of the input has little bearing on the size of the image thanks to linebreaks and macros, we overprovision then clip away.
|
||||
max_width = 256 # This seems to check out, but the EN dialogue has linebreaks virtually everywhere anyway
|
||||
max_height = 1024 # I've seen up to 58 rows in EN, 36 in JP. Stay safe.
|
||||
|
@ -396,96 +329,63 @@ def make_string_img_large(bytestring, glyphs, glyphs_kanji, macros=None, jp=Fals
|
|||
painter = QtGui.QPainter(img)
|
||||
|
||||
x = xmax = y = 0
|
||||
for j in newstring:
|
||||
if glyphs_kanji is not None: # jp
|
||||
for j in glyph_ids:
|
||||
if x >= max_width: # Wrap on long line
|
||||
string += '[wr]\n'
|
||||
xmax = max_width # Can't go higher than this anyway
|
||||
x = 0
|
||||
y += 16
|
||||
|
||||
if j == 0x01: # Line break
|
||||
string += '[br]\n'
|
||||
xmax = x if x > xmax else xmax
|
||||
x = 0
|
||||
y += 16
|
||||
elif 0x1E00 <= j < 0x1FAA: # Kanji live in this range
|
||||
string += const.Glyphs_Kanji[j-0x1E00]
|
||||
painter.drawPixmap(x, y+2, glyphs_kanji[j-0x1E00])
|
||||
x += 16
|
||||
elif j < 0x13 or j > 0xFF: # Everything remaining outside this is a control char
|
||||
string += '[0x{:02X}]'.format(j)
|
||||
else:
|
||||
if jp:
|
||||
string += const.Glyphs_JP_large[j]
|
||||
elif 0x13 <= j <= 0xFF: # Everything remaining outside this is a control char
|
||||
painter.drawPixmap(x, y+2, glyphs[j])
|
||||
x += 16
|
||||
else:
|
||||
string += const.Glyphs[j]
|
||||
else: # en
|
||||
for j in glyph_ids:
|
||||
if x >= max_width: # Wrap on long line
|
||||
xmax = max_width # Can't go higher than this anyway
|
||||
x = 0
|
||||
y += 16
|
||||
|
||||
if j == 0x01: # Line break
|
||||
xmax = x if x > xmax else xmax
|
||||
x = 0
|
||||
y += 16
|
||||
elif 0x13 <= j <= 0xFF: # Everything outside this is a control char
|
||||
painter.drawPixmap(x, y+4, glyphs[j])
|
||||
x += const.Dialogue_Width[j]
|
||||
x += RPGe_Dialogue_Width[j]
|
||||
|
||||
del painter
|
||||
xmax = x if x > xmax else xmax
|
||||
return string, QPixmap.fromImage(img.copy(0, 0, xmax, y+16))
|
||||
return QPixmap.fromImage(img.copy(0, 0, xmax, y+16))
|
||||
|
||||
|
||||
def _make_string_img_list(rom_e, rom_j, start, length, num,
|
||||
start_jp=None, len_jp=None, start_str=None, start_jp_str=None,
|
||||
indirect=False, large=False, macros_en=None, macros_jp=None,
|
||||
def _make_string_img_list(jp: StringBlock, en: StringBlock, large=False,
|
||||
glyphs_en_s=None, glyphs_en_l=None,
|
||||
glyphs_jp_s=None, glyphs_jp_l=None, glyphs_kanji=None):
|
||||
start_jp = start if start_jp is None else start_jp
|
||||
len_jp = length if len_jp is None else len_jp
|
||||
start_str = start if start_str is None else start_str
|
||||
start_jp_str = start_str if start_jp_str is None else start_jp_str
|
||||
num = len(en)
|
||||
print(len(en), len(jp))
|
||||
stringlist = []
|
||||
id_digits = hex_length(num-1)
|
||||
|
||||
if indirect:
|
||||
for id in range(num):
|
||||
en = start + (id*length)
|
||||
jp = start_jp + (id*len_jp)
|
||||
en_start = int.from_bytes(rom_e[en:en+length],'little') + start_str
|
||||
en_end = int.from_bytes(rom_e[en+length:en+(length*2)],'little') + start_str
|
||||
if en_start >= 0xC00000: # SNES memory space has the ROM starting at 0xC00000 in HiROM mode.
|
||||
en_start -= 0xC00000
|
||||
en_end -= 0xC00000
|
||||
jp_start = int.from_bytes(rom_j[jp:jp+len_jp],'little') + start_jp_str
|
||||
jp_end = int.from_bytes(rom_j[jp+len_jp:jp+(len_jp*2)],'little') + start_jp_str
|
||||
if jp_start >= 0xC00000: # SNES memory space has the ROM starting at 0xC00000 in HiROM mode.
|
||||
jp_start -= 0xC00000
|
||||
jp_end -= 0xC00000
|
||||
if (en_end == start_str) or (jp_end == start_jp_str):
|
||||
break
|
||||
try: # When dealing with pointer redirection we might end up passing empty strings
|
||||
str_en = en.decoded[id]
|
||||
str_jp = jp.decoded[id]
|
||||
if large:
|
||||
str_en, img_en = make_string_img_large(rom_e[en_start:en_end], glyphs_en_l, glyphs_kanji, macros_en)
|
||||
img_en = make_string_img_large(en.glyphs[id], glyphs_en_l)
|
||||
img_jp = make_string_img_large(jp.glyphs[id], glyphs_jp_l, glyphs_kanji)
|
||||
else:
|
||||
str_en, img_en = make_string_img_small(rom_e[en_start:en_end], glyphs_en_s)
|
||||
except ValueError:
|
||||
str_en = ''
|
||||
img_en = None
|
||||
try:
|
||||
if large:
|
||||
str_jp, img_jp = make_string_img_large(rom_j[jp_start:jp_end], glyphs_jp_l, glyphs_kanji, macros_jp, jp=True)
|
||||
else:
|
||||
str_jp, img_jp = make_string_img_small(rom_j[jp_start:jp_end], glyphs_jp_s, jp=True)
|
||||
except ValueError:
|
||||
str_jp = ''
|
||||
img_jp = None
|
||||
stringlist.append([
|
||||
hex(en, 6), hex(id, id_digits),
|
||||
str_en, img_en, str_jp, img_jp, hex(jp_start, 6)
|
||||
])
|
||||
else:
|
||||
for id in range(num):
|
||||
j1 = start + (id*length)
|
||||
j2 = start_jp + (id*len_jp)
|
||||
if large:
|
||||
str_en, img_en = make_string_img_large(rom_e[j1:j1+length], glyphs_en_l, glyphs_kanji, macros_en)
|
||||
str_jp, img_jp = make_string_img_large(rom_j[j2:j2+len_jp], glyphs_jp_l, glyphs_kanji, macros_jp, jp=True)
|
||||
else:
|
||||
str_en, img_en = make_string_img_small(rom_e[j1:j1+length], glyphs_en_s)
|
||||
str_jp, img_jp = make_string_img_small(rom_j[j2:j2+len_jp], glyphs_jp_s, jp=True)
|
||||
stringlist.append([hex(j1, 6), hex(id, id_digits), str_en, img_en, str_jp, img_jp])
|
||||
img_en = make_string_img_small(en.glyphs[id], glyphs_en_s)
|
||||
img_jp = make_string_img_small(jp.glyphs[id], glyphs_jp_s, handle_dakuten=True)
|
||||
# ['ID', 'EN Pointer', 'EN Address', 'EN String', 'EN Img', 'JP Pointer', 'JP Address', 'JP String', 'JP Img']
|
||||
stringlist.append([hex(id, id_digits), hex(en.pointer_address[id].start, 6), hex(en.string_address[id].start, 6), str_en, img_en,
|
||||
hex(jp.pointer_address[id].start, 6), hex(jp.string_address[id].start, 6), str_jp, img_jp])
|
||||
return stringlist
|
||||
|
||||
last_perfcount = None
|
||||
|
|
|
@ -1,251 +0,0 @@
|
|||
'''
|
||||
This file is part of ff5reader.
|
||||
|
||||
ff5reader is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ff5reader is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with ff5reader. If not, see <http://www.gnu.org/licenses/>.
|
||||
'''
|
||||
small_palette = [0xFF000000, 0x00000080, 0xFF808080, 0xFFFFFFFF]
|
||||
dialogue_palette = [0xFF000080, 0xFFFFFFFF]
|
||||
mono_palette = [0xFF000000, 0xFFFFFFFF]
|
||||
|
||||
|
||||
Glyphs = (
|
||||
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x00
|
||||
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x10
|
||||
'A','B','C','D', 'E','F','G','H', 'I','J','K','L', 'M','N','O','P', # 0x20
|
||||
'Q','R','S','T','U','V','W','X','Y','Z','[stone]','[toad]','[mini]','[float]','[poison]','[KO]', # 0x30
|
||||
'[blind]',' ',' ',' ',' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x40
|
||||
' ',' ',' ','0', '1','2','3','4', '5','6','7','8', '9','_m','_H','_P', # 0x50
|
||||
'A','B','C','D', 'E','F','G','H', 'I','J','K','L', 'M','N','O','P', # 0x60
|
||||
'Q','R','S','T', 'U','V','W','X', 'Y','Z','a','b', 'c','d','e','f', # 0x70
|
||||
'g','h','i','j', 'k','l','m','n', 'o','p','q','r', 's','t','u','v', # 0x80
|
||||
'w','x','y','z', 'il','it',' ','li', 'll','\'','"',':', ';',',','(',')', # 0x90
|
||||
'/','!','?','.', 'ti','fi','Bl','a', 'pe','l','\'','"', 'if','lt','tl','ir', # 0xA0
|
||||
'tt','や','ユ','ゆ', 'ヨ', 'よ', 'ワ', 'わ', 'ン', 'ん', 'ヲ','を', '[key]', '[shoe]', '◆', '[hammer]', # 0xB0
|
||||
'⛺', '[ribbon]', '[potion]', '[shirt]', '♪', '-', '[shuriken]', '‥', '[scroll]', '!', '[claw]', '?', '[glove]', '%', '/', ':', # 0xC0
|
||||
'「', '」', '.', 'A', 'B', 'X', 'Y', 'L', 'R', 'E', 'H', 'M', 'P', 'S', 'C', 'T', # 0xD0
|
||||
'↑', '→', '+', '[sword]', '[wh.mag]', '[blk.mag]', '🕒', '[knife]', '[spear]', '[axe]', '[katana]', '[rod]', '[staff]', '[bow]', '[harp]', '[whip]', # 0xE0
|
||||
'[bell]', '[shield]', '[helmet]', '[armor]', '[ring]', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ') # F0
|
||||
Glyphs_JP = list(Glyphs) # Transcription of the japanese glyph tiles
|
||||
Glyphs_JP[0x60:0xCD] = [
|
||||
'ハ','は','ヒ','ひ', 'フ','ふ','ヘ','へ', 'ホ','ほ','カ','か', 'キ','き','ク','く', # 0x60
|
||||
'ケ','け','コ','こ', 'サ','さ','シ','し', 'ス','す','セ','せ', 'ソ','そ','タ','た', # 0x70
|
||||
'チ','ち','ツ','つ', 'テ','て','ト','と', 'ウ','う','ア','あ', 'イ','い','エ','え', # 0x80
|
||||
'オ','お','ナ','な', 'ニ','に','ヌ','ぬ', 'ネ','ね','ノ','の', 'マ','ま','ミ','み', # 0x90
|
||||
'ム','む','メ','め', 'モ','も','ラ','ら', 'リ','り','ル','る', 'レ','れ','ロ','ろ', # 0xA0
|
||||
'ヤ','や','ユ','ゆ', 'ヨ','よ','ワ','わ', 'ン','ん','ヲ','を', 'ッ','っ','ャ','ゃ', # 0xB0
|
||||
'ュ','ゅ','ョ','ょ', 'ァ','ー','ィ', '‥', 'ぅ','!','ェ','?', 'ォ'] # 0xC0
|
||||
Glyphs_JP[0xD2] = '。'
|
||||
Glyphs_JP[0xE3] = '[洋剣]'
|
||||
Glyphs_JP[0xE7:0xF0] = ['[刂]', '[槍]', '[鉞]', '[刀]', '[棒]', '[杖]', '[弓]', '♪', '[鞭]']
|
||||
Glyphs_JP2 = list(Glyphs_JP) # Japanese glyphs using the dakuten encoding
|
||||
Glyphs_JP2[0x20:0x53] = [
|
||||
'バ','ば','ビ','び', 'ブ','ぶ','ベ','べ', 'ボ','ぼ','ガ','が', 'ギ','ぎ','グ','ぐ', # 0x20
|
||||
'ゲ','げ','ゴ','ご', 'ザ','ざ','ジ','じ', 'ズ','ず','ゼ','ぜ', 'ゾ','ぞ','ダ','だ', # 0x30
|
||||
'ヂ','ぢ','ヅ','づ', 'デ','で','ド','ど', 'ヴ', # 0x40-0x49
|
||||
'パ','ぱ','ピ','ぴ', 'プ','ぷ','ペ','ぺ', 'ポ','ぽ'] # 0x49-0x53
|
||||
Glyphs_JP_large = list(Glyphs_JP2) # Large glyphs are subtly different again
|
||||
Glyphs_JP_large[0xC7] = '⋯'
|
||||
Glyphs_JP_large[0xE0:0xEB] = ['←','→','+','、', '◯', '『', 'F', '°C', '・', '(', ')']
|
||||
Glyphs_JP_large[0xFF] = ' '
|
||||
|
||||
Glyphs_Kanji = (
|
||||
'王', '行', '力', '様', '飛', '世', '界', '封', # 0x000
|
||||
'入', '城', '竜', '士', '船', '印', '海', '父', # 0x008
|
||||
'人', '見', '魔', '物', '大', '者', '本', '何', # 0x010
|
||||
'戦', '出', '気', '手', '言', '石', '守', '辺', # 0x018
|
||||
'無', '心', '間', '前', '風', '帰', '私', '生', # 0x020
|
||||
'年', '武', '器', '兵', '火', '使', '中', '急', # 0x028
|
||||
'時', '森', '来', '砂', '動', '女', '待', '臣', # 0x030
|
||||
'地', '助', '当', '今', '上', '悪', '泉', '騎', # 0x038
|
||||
|
||||
'思', '持', '変', '水', '塔', '草', '仲', '復', # 0x040
|
||||
'目', '作', '分', '知', '機', '一', '姫', '最', # 0x048
|
||||
'聞', '械', '神', '流', '乗', '取', '町', '殿', # 0x050
|
||||
'事', '空', '勇', '村', '早', '子', '格', '納', # 0x058
|
||||
'伝', '消', '書', '長', '話', '合', '所', '場', # 0x060
|
||||
'脱', '北', '後', '全', '忍', '獣', '詩', '吟', # 0x068
|
||||
'落', '自', '階', '説', '残', '親', '活', '休', # 0x070
|
||||
'姉', '破', '土', '度', '記', '発', '赤', '侍', # 0x078
|
||||
|
||||
'死', '国', '旅', '完', '小', '林', '古', '図', # 0x080
|
||||
'礼', '商', '島', '邪', '部', '狩', '精', '姿', # 0x088
|
||||
'防', '向', '先', '解', '板', '下', '台', '賢', # 0x090
|
||||
'対', '木', '成', '命', '配', '法', '飲', '回', # 0x098
|
||||
'願', '門', '東', '開', '貸', '増', '危', '舘', # 0x0A0
|
||||
'道', '身', '老', '西', '近', '層', '第', '青', # 0x0A8
|
||||
'光', '読', '外', '理', '強', '同', '谷', '負', # 0x0B0
|
||||
'意', '学', '攻', '屋', '体', '運', '河', '聖', # 0x0B8
|
||||
|
||||
'必', '南', '方', '黒', '絶', '食', '艇', '山', # 0x0C0
|
||||
'穴', '名', '受', '暁', '傷', '少', '鼻', '倉', # 0x0C8
|
||||
'然', '的', '男', '用', '酒', '安', '現', '代', # 0x0D0
|
||||
'立', '具', '育', '続', '通', '会', '庫', '飼', # 0x0D8
|
||||
'日', '窟', '砲', '広', '化', '博', '以', '兄', # 0x0E0
|
||||
'室', '洞', '別', '番', '昔', '住', '吸', '去', # 0x0E8
|
||||
'宝', '毒', '売', '好', '枝', '踊', '月', '巣', # 0x0F0
|
||||
'底', '明', '感', '宿', '召', '喚', '決', '形', # 0x0F8
|
||||
|
||||
'団', '異', '初', '険', '家', '息', '連', '集', # 0x100
|
||||
'墓', '著', '跡', '遺', '内', '悲', '教', '返', # 0x108
|
||||
'頭', '再', '高', '借', '登', '走', '過', '敵', # 0x110
|
||||
'鏡', '愛', '魚', '燃', '幅', '音', '血', '滝', # 0x118
|
||||
'次', '閉', '求', '声', '箱', '軍', '泣', '港', # 0x120
|
||||
'冊', '秒', '憶', '潜', '爆', '恋', '石', '左', # 0x128
|
||||
'役', '放', '衛', '母', '建', '角', '信', '師', # 0x130
|
||||
'元', '失', '字', '歩', '遊', '頂', '震', '収', # 0x138
|
||||
|
||||
'追', '暗', '橋', '起', '約', '束', '文', '反', # 0x140
|
||||
'絵', '能', '歌', '弱', '境', '波', '針', '千', # 0x148
|
||||
'炎', '望', '雨', '金', '希', '花', '不', '白', # 0x150
|
||||
'剣', '陸', '勝', '烏', '笛', '究', '三', '探', # 0x158
|
||||
'夜', '病', '半', '美', '敗', '友', '研', '予', # 0x160
|
||||
'支', '々', '除', '数', '店', '翼', '充', '填', # 0x168
|
||||
'薬', '弟', '経', '験', '値', '闘', '得', '闇', # 0x170
|
||||
'混', '乱', '点', '速', '閃', '射', '斬', '鉄', # 0x178
|
||||
|
||||
'宣', '告', '電', '磁', '圧', '昇', '重', '倍', # 0x180
|
||||
'妖', '刀', '拡', '散', '影', '御', '正', '漠', # 0x188
|
||||
'特', '胸', '永', '遠', '議', '浮', '眠', '逃', # 0x190
|
||||
'滅', '囗', '極', '湖', '利', '周', '緑', '切', # 0x198
|
||||
'紙', '胸', '平', '和', '宇', '宙', '則', '孤', # 0x1A0
|
||||
'溝', ' ') # 0x1A8
|
||||
|
||||
Dialogue_Macros_EN = {
|
||||
0x02: [0x61, 0x7A, 0x8B, 0x8D, 0x93], # expands to Bartz (or whatever his name is)
|
||||
}
|
||||
|
||||
Dialogue_Macros_JP = {
|
||||
# Is 0x00 a wait for input marker?
|
||||
# 0x01 is linebreak
|
||||
0x02: [0x20, 0xBC, 0x82], # 0x02 expands to Bartz's name バッツ. Used for his dialogue in EN, only used for other chars in JP.
|
||||
0x03: [0x6E, 0xA8, 0x78, 0x7E, 0xAA], # 0x03 is クリスタル
|
||||
0x04: [0x7E, 0x8C, 0x6E, 0xC5, 0xB8], # expands to タイクーン
|
||||
0x06: [0x37, 0xBF], # expands to じゃ
|
||||
0x07: [0x8D, 0xAB], # expands to いる
|
||||
0x08: [0xFF, 0xFF, 0xFF, 0xFF], # 4 spaces
|
||||
0x09: [0xFF, 0xFF, 0xFF], # 3 spaces
|
||||
0x0A: [0xFF, 0xFF], # 2 spaces
|
||||
0x0B: [0x1E12, 0x1E13], # expands to 魔物
|
||||
# 0x0C appears to be a pause in delivery - affects previous char
|
||||
0x0D: [0x1E24, 0x9B, 0x1E52, 0x1E57], # expands to 風の神殿
|
||||
0x0E: [0x1E04, 0x1E0A], # expands to 飛竜
|
||||
# 0x0F - unknown (invisible control char)
|
||||
# 0x10 is a gil substitution
|
||||
# 0x11 and 0x12 appear to be item (obtained) substitutions
|
||||
0x13: [0x1E07, 0x1E0D], # expands to 封印
|
||||
0x14: [0x76, 0x46, 0xD0], # Cid speaking - シド「
|
||||
0x15: [0x9E, 0x46, 0xD0], # Mid speaking - ミド「
|
||||
0x16: [0x1E05, 0x1E06], # expands to 世界
|
||||
# 0x17 uses the next byte for pause duration (seconds?)
|
||||
0x18: [0x8E, 0x6E, 0x78, 0x44, 0x78], # expands to エクスデス
|
||||
0x19: [0xAC, 0x92, 0xD0], # Lenna speaking - レナ「
|
||||
0x1A: [0x2A, 0xA6, 0x64, 0xD0], # Galuf speaking - ガラフ「
|
||||
0x1B: [0x64, 0xC4, 0xA8, 0x78, 0xD0], # Faris speaking - ファリス「
|
||||
0x1C: [0x6E, 0xAA, 0xAA, 0xD0], # Krile/Kara speaking - クルル「
|
||||
0x1D: [0x91, 0x37, 0x8D, 0x81, 0xBF, 0xB9], # expands to おじいちゃん
|
||||
# 0x1E-0x1F form kanji with the next byte
|
||||
# 0x20-0xCC are standard character set
|
||||
0xCD: [0xC9, 0xC9], # % (0xCD) to !!
|
||||
# 0xCE is /
|
||||
0xCF: [0xBD, 0x85], # : (0xCF) appears to expand to って
|
||||
# 0xD0-0xD4 are 「」。AB
|
||||
0xD5: [0x1E1B, 0x95, 0x1E08, 0xAD], # expands to 手に入れ
|
||||
# 0xD6, 0xD7, 0xD8 are YLR
|
||||
0xD9: [0x93, 0x8D], # expands to ない
|
||||
# 0xDA-0xDC are HMP
|
||||
0xDD: [0xC7, 0xC7], # S (0xDD) to ……
|
||||
0xDE: [0x3F, 0x8D, 0x37, 0xC3, 0x89, 0x25], # C (0xDE) to だいじょうぶ
|
||||
0xDF: [0x61, 0xE3], # T (0xDF) to は、
|
||||
0xE0: [0xB9, 0x3F], # expands to んだ
|
||||
0xE1: [0x85, 0x8D], # expands to てい
|
||||
0xE2: [0x77, 0x7F], # expands to した
|
||||
# 0xE3 is 、
|
||||
0xE4: [0x77, 0x85], # ◯ (0xE4) appears to expand to して
|
||||
# 0xE5 is used for Bartz speaking in JP. This only appears as 『
|
||||
0xE6: [0x91, 0x1E0F, 0x1E03], # F (0xE6) appears to expand to otousan (お父様)
|
||||
0xE7: [0xC9, 0xCB], # °C (0xE7) to !? - yes this is the wrong order interrobang
|
||||
0xE8: [0x45, 0x79], # ・ (0xE8) appears to expand to です
|
||||
# 0xE9, 0xEA are ()
|
||||
0xEB: [0x73, 0x9B], # expands to この
|
||||
0xEC: [0x9B, 0x1E02], # expands to の力
|
||||
0xED: [0x70, 0xAA, 0x2A, 0xC5], # expands to ケルガー
|
||||
0xEE: [0x1E86, 0x1ED7, 0x1E87, 0x1E62, 0x1EA7], # expands to 古代図書舘 (ancient library?)
|
||||
0xEF: [0x1E1C, 0xBD, 0x85], # expands to 言って
|
||||
0xF0: [0x1E2B, 0x1E0B, 0xD0], # soldier speaking - 兵士「
|
||||
0xF1: [0x6B, 0xA7], # expands to から
|
||||
0xF2: [0x1E2C, 0x6A, 0x1E0C], # expands to 火カ船
|
||||
0xF3: [0x1E0E, 0x3D, 0x6F], # expands to 海ぞく
|
||||
0xF4: [0x8D, 0x37, 0xC3, 0x89], # expands to いじょう
|
||||
0xF5: [0x2B, 0xE3], # expands to が、
|
||||
0xF6: [0x7F, 0x81], # expands to たち
|
||||
0xF7: [0x7F, 0x9B], # expands to たの
|
||||
0xF8: [0x9D, 0x79], # expands to ます
|
||||
0xF9: [0x6F, 0x3F, 0x75, 0x8D], # expands to ください
|
||||
0xFA: [0x6B, 0xBD, 0x7F], # expands to かった
|
||||
0xFB: [0x7F, 0xC9], # expands to た!
|
||||
0xFC: [0x95, 0xE3], # expands to に、
|
||||
0xFD: [0x8D, 0x93, 0x8D, 0x6B, 0xA7, 0x93, 0xB9, 0x3F], # expands to いないからなんだ
|
||||
0xFE: [0x1F20, 0x1F38, 0x9B, 0x61, 0x35, 0x9D], # expands to 次元のはざま
|
||||
# 0xFF is space
|
||||
}
|
||||
DoubleChars = set([0x17, 0x1E, 0x1F]) # 0x1E and 0x1F are kanji, 0x17 is a pause marker
|
||||
|
||||
Dialogue_Width = [4 for i in range(256)]
|
||||
Dialogue_Width[0x50:0xB1] = [a+1 for a in [
|
||||
5, 2, 6, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6, 8, 8, 8, # 0x50
|
||||
6, 6, 5, 6, 5, 5, 6, 6, 2, 6, 7, 5, 10,7, 6, 6, # 0x60
|
||||
6, 6, 6, 6, 6, 6, 10,6, 6, 6, 6, 6, 5, 6, 6, 5, # 0x70
|
||||
6, 6, 2, 5, 6, 2, 10,6, 6, 6, 6, 5, 5, 4, 6, 6, # 0x80
|
||||
10,6, 6, 6, 5, 7, 6, 5, 5, 2, 5, 2, 2, 2, 3, 3, # 0x90
|
||||
5, 2, 6, 2, 7, 8, 0, 0, 6, 9, 2, 5, 8, 7, 7, 8, # 0xA0
|
||||
9
|
||||
]]
|
||||
|
||||
BGM_Tracks = (
|
||||
"Ahead on our way", "The Fierce Battle", "A Presentiment", "Go Go Boko!",
|
||||
"Pirates Ahoy", "Tenderness in the Air", "Fate in Haze", "Moogle theme",
|
||||
"Prelude/Crystal Room", "The Last Battle", "Requiem", "Nostalgia",
|
||||
"Cursed Earths", "Lenna's Theme", "Victory's Fanfare", "Deception",
|
||||
"The Day Will Come", "Nothing?", "ExDeath's Castle", "My Home, Sweet Home",
|
||||
"Waltz Suomi", "Sealed Away", "The Four Warriors of Dawn", "Danger",
|
||||
"The Fire Powered Ship", "As I Feel, You Feel", "Mambo de Chocobo!", "Music Box",
|
||||
"Intension of the Earth", "The Dragon Spreads its Wings", "Beyond the Deep Blue Sea", "The Prelude of Empty Skies",
|
||||
"Searching the Light", "Harvest", "Battle with Gilgamesh", "Four Valiant Hearts",
|
||||
"The Book of Sealings", "What?", "Hurry! Hurry!", "Unknown Lands",
|
||||
"The Airship", "Fanfare 1", "Fanfare 2", "The Battle",
|
||||
"Walking the Snowy Mountains", "The Evil Lord Exdeath", "The Castle of Dawn", "I'm a Dancer",
|
||||
"Reminiscence", "Run!", "The Ancient Library", "Royal Palace",
|
||||
"Good Night!", "Piano Lesson 1", "Piano Lesson 2", "Piano Lesson 3",
|
||||
"Piano Lesson 4", "Piano Lesson 5", "Piano Lesson 6", "Piano Lesson 7",
|
||||
"Piano Lesson 8", "Musica Machina", "Meteor falling?", "The Land Unknown",
|
||||
"The Decisive Battle", "The Silent Beyond", "Dear Friends", "Final Fantasy",
|
||||
"A New Origin", "Chirping sound"
|
||||
)
|
||||
BGM_Tracks_Safe = [t.replace('/', '-') for t in BGM_Tracks]
|
||||
|
||||
|
||||
|
||||
npc_layer_count = 0x200
|
||||
npc_layer_structure = [
|
||||
("Dialogue/trigger ID", 1, None),
|
||||
("0x01", 1, None),
|
||||
("Sprite ID", 1, None),
|
||||
("X", 1, None),
|
||||
("Y", 1, None),
|
||||
("Move Pattern", 1, None),
|
||||
("Palette", 1, None)
|
||||
]
|
||||
npc_layer_headers = ["Ptr Address", "Layer", "Data Address"] + [x[0] for x in npc_layer_structure]
|
||||
|
||||
zone_count = 0x200
|
|
@ -0,0 +1,70 @@
|
|||
Ahead on our way
|
||||
The Fierce Battle
|
||||
A Presentiment
|
||||
Go Go Boko!
|
||||
Pirates Ahoy
|
||||
Tenderness in the Air
|
||||
Fate in Haze
|
||||
Moogle theme
|
||||
Prelude/Crystal Room
|
||||
The Last Battle
|
||||
Requiem
|
||||
Nostalgia
|
||||
Cursed Earths
|
||||
Lenna's Theme
|
||||
Victory's Fanfare
|
||||
Deception
|
||||
The Day Will Come
|
||||
Nothing?
|
||||
ExDeath's Castle
|
||||
My Home, Sweet Home
|
||||
Waltz Suomi
|
||||
Sealed Away
|
||||
The Four Warriors of Dawn
|
||||
Danger
|
||||
The Fire Powered Ship
|
||||
As I Feel, You Feel
|
||||
Mambo de Chocobo!
|
||||
Music Box
|
||||
Intension of the Earth
|
||||
The Dragon Spreads its Wings
|
||||
Beyond the Deep Blue Sea
|
||||
The Prelude of Empty Skies
|
||||
Searching the Light
|
||||
Harvest
|
||||
Battle with Gilgamesh
|
||||
Four Valiant Hearts
|
||||
The Book of Sealings
|
||||
What?
|
||||
Hurry! Hurry!
|
||||
Unknown Lands
|
||||
The Airship
|
||||
Fanfare 1
|
||||
Fanfare 2
|
||||
The Battle
|
||||
Walking the Snowy Mountains
|
||||
The Evil Lord Exdeath
|
||||
The Castle of Dawn
|
||||
I'm a Dancer
|
||||
Reminiscence
|
||||
Run!
|
||||
The Ancient Library
|
||||
Royal Palace
|
||||
Good Night!
|
||||
Piano Lesson 1
|
||||
Piano Lesson 2
|
||||
Piano Lesson 3
|
||||
Piano Lesson 4
|
||||
Piano Lesson 5
|
||||
Piano Lesson 6
|
||||
Piano Lesson 7
|
||||
Piano Lesson 8
|
||||
Musica Machina
|
||||
Meteor falling?
|
||||
The Land Unknown
|
||||
The Decisive Battle
|
||||
The Silent Beyond
|
||||
Dear Friends
|
||||
Final Fantasy
|
||||
A New Origin
|
||||
Chirping sound
|
|
@ -0,0 +1,256 @@
|
|||
|
||||
\n
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
バ
|
||||
ば
|
||||
ビ
|
||||
び
|
||||
ブ
|
||||
ぶ
|
||||
ベ
|
||||
べ
|
||||
ボ
|
||||
ぼ
|
||||
ガ
|
||||
が
|
||||
ギ
|
||||
ぎ
|
||||
グ
|
||||
ぐ
|
||||
ゲ
|
||||
げ
|
||||
ゴ
|
||||
ご
|
||||
ザ
|
||||
ざ
|
||||
ジ
|
||||
じ
|
||||
ズ
|
||||
ず
|
||||
ゼ
|
||||
ぜ
|
||||
ゾ
|
||||
ぞ
|
||||
ダ
|
||||
だ
|
||||
ヂ
|
||||
ぢ
|
||||
ヅ
|
||||
づ
|
||||
デ
|
||||
で
|
||||
ド
|
||||
ど
|
||||
ヴ
|
||||
パ
|
||||
ぱ
|
||||
ピ
|
||||
ぴ
|
||||
プ
|
||||
ぷ
|
||||
ペ
|
||||
"
|
||||
'
|
||||
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
_m
|
||||
_H
|
||||
_P
|
||||
A
|
||||
B
|
||||
C
|
||||
D
|
||||
E
|
||||
F
|
||||
G
|
||||
H
|
||||
I
|
||||
J
|
||||
K
|
||||
L
|
||||
M
|
||||
N
|
||||
O
|
||||
P
|
||||
Q
|
||||
R
|
||||
S
|
||||
T
|
||||
U
|
||||
V
|
||||
W
|
||||
X
|
||||
Y
|
||||
Z
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
f
|
||||
g
|
||||
h
|
||||
i
|
||||
j
|
||||
k
|
||||
l
|
||||
m
|
||||
n
|
||||
o
|
||||
p
|
||||
q
|
||||
r
|
||||
s
|
||||
t
|
||||
u
|
||||
v
|
||||
w
|
||||
x
|
||||
y
|
||||
z
|
||||
il
|
||||
it
|
||||
|
||||
li
|
||||
ll
|
||||
'
|
||||
"
|
||||
:
|
||||
;
|
||||
,
|
||||
(
|
||||
)
|
||||
/
|
||||
!
|
||||
?
|
||||
.
|
||||
ti
|
||||
fi
|
||||
ラ
|
||||
ら
|
||||
pe
|
||||
l
|
||||
'
|
||||
"
|
||||
if
|
||||
lt
|
||||
tl
|
||||
ir
|
||||
tt
|
||||
や
|
||||
ユ
|
||||
ゆ
|
||||
ヨ
|
||||
よ
|
||||
ワ
|
||||
わ
|
||||
ン
|
||||
ん
|
||||
ヲ
|
||||
を
|
||||
ッ
|
||||
っ
|
||||
ャ
|
||||
ゃ
|
||||
ュ
|
||||
ゅ
|
||||
ョ
|
||||
ょ
|
||||
ァ
|
||||
ー
|
||||
ィ
|
||||
⋯
|
||||
ぅ
|
||||
!
|
||||
ェ
|
||||
?
|
||||
ォ
|
||||
%
|
||||
/
|
||||
:
|
||||
「
|
||||
」
|
||||
。
|
||||
A
|
||||
B
|
||||
X
|
||||
Y
|
||||
L
|
||||
R
|
||||
E
|
||||
H
|
||||
M
|
||||
P
|
||||
S
|
||||
C
|
||||
T
|
||||
←
|
||||
→
|
||||
+
|
||||
、
|
||||
◯
|
||||
『
|
||||
F
|
||||
°C
|
||||
・
|
||||
(
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
|
||||
\n
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
バ
|
||||
ば
|
||||
ビ
|
||||
び
|
||||
ブ
|
||||
ぶ
|
||||
ベ
|
||||
べ
|
||||
ボ
|
||||
ぼ
|
||||
ガ
|
||||
が
|
||||
ギ
|
||||
ぎ
|
||||
グ
|
||||
ぐ
|
||||
ゲ
|
||||
げ
|
||||
ゴ
|
||||
ご
|
||||
ザ
|
||||
ざ
|
||||
ジ
|
||||
じ
|
||||
ズ
|
||||
ず
|
||||
ゼ
|
||||
ぜ
|
||||
ゾ
|
||||
ぞ
|
||||
ダ
|
||||
だ
|
||||
ヂ
|
||||
ぢ
|
||||
ヅ
|
||||
づ
|
||||
デ
|
||||
で
|
||||
ド
|
||||
ど
|
||||
ヴ
|
||||
パ
|
||||
ぱ
|
||||
ピ
|
||||
ぴ
|
||||
プ
|
||||
ぷ
|
||||
ペ
|
||||
ぺ
|
||||
ポ
|
||||
ぽ
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
_m
|
||||
_H
|
||||
_P
|
||||
ハ
|
||||
は
|
||||
ヒ
|
||||
ひ
|
||||
フ
|
||||
ふ
|
||||
ヘ
|
||||
へ
|
||||
ホ
|
||||
ほ
|
||||
カ
|
||||
か
|
||||
キ
|
||||
き
|
||||
ク
|
||||
く
|
||||
ケ
|
||||
け
|
||||
コ
|
||||
こ
|
||||
サ
|
||||
さ
|
||||
シ
|
||||
し
|
||||
ス
|
||||
す
|
||||
セ
|
||||
せ
|
||||
ソ
|
||||
そ
|
||||
タ
|
||||
た
|
||||
チ
|
||||
ち
|
||||
ツ
|
||||
つ
|
||||
テ
|
||||
て
|
||||
ト
|
||||
と
|
||||
ウ
|
||||
う
|
||||
ア
|
||||
あ
|
||||
イ
|
||||
い
|
||||
エ
|
||||
え
|
||||
オ
|
||||
お
|
||||
ナ
|
||||
な
|
||||
ニ
|
||||
に
|
||||
ヌ
|
||||
ぬ
|
||||
ネ
|
||||
ね
|
||||
ノ
|
||||
の
|
||||
マ
|
||||
ま
|
||||
ミ
|
||||
み
|
||||
ム
|
||||
む
|
||||
メ
|
||||
め
|
||||
モ
|
||||
も
|
||||
ラ
|
||||
ら
|
||||
リ
|
||||
り
|
||||
ル
|
||||
る
|
||||
レ
|
||||
れ
|
||||
ロ
|
||||
ろ
|
||||
ヤ
|
||||
や
|
||||
ユ
|
||||
ゆ
|
||||
ヨ
|
||||
よ
|
||||
ワ
|
||||
わ
|
||||
ン
|
||||
ん
|
||||
ヲ
|
||||
を
|
||||
ッ
|
||||
っ
|
||||
ャ
|
||||
ゃ
|
||||
ュ
|
||||
ゅ
|
||||
ョ
|
||||
ょ
|
||||
ァ
|
||||
ー
|
||||
ィ
|
||||
⋯
|
||||
ぅ
|
||||
!
|
||||
ェ
|
||||
?
|
||||
ォ
|
||||
%
|
||||
/
|
||||
:
|
||||
「
|
||||
」
|
||||
。
|
||||
A
|
||||
B
|
||||
X
|
||||
Y
|
||||
L
|
||||
R
|
||||
E
|
||||
H
|
||||
M
|
||||
P
|
||||
S
|
||||
C
|
||||
T
|
||||
←
|
||||
→
|
||||
+
|
||||
、
|
||||
◯
|
||||
『
|
||||
F
|
||||
°C
|
||||
・
|
||||
(
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,426 @@
|
|||
王
|
||||
行
|
||||
力
|
||||
様
|
||||
飛
|
||||
世
|
||||
界
|
||||
封
|
||||
入
|
||||
城
|
||||
竜
|
||||
士
|
||||
船
|
||||
印
|
||||
海
|
||||
父
|
||||
人
|
||||
見
|
||||
魔
|
||||
物
|
||||
大
|
||||
者
|
||||
本
|
||||
何
|
||||
戦
|
||||
出
|
||||
気
|
||||
手
|
||||
言
|
||||
石
|
||||
守
|
||||
辺
|
||||
無
|
||||
心
|
||||
間
|
||||
前
|
||||
風
|
||||
帰
|
||||
私
|
||||
生
|
||||
年
|
||||
武
|
||||
器
|
||||
兵
|
||||
火
|
||||
使
|
||||
中
|
||||
急
|
||||
時
|
||||
森
|
||||
来
|
||||
砂
|
||||
動
|
||||
女
|
||||
待
|
||||
臣
|
||||
地
|
||||
助
|
||||
当
|
||||
今
|
||||
上
|
||||
悪
|
||||
泉
|
||||
騎
|
||||
思
|
||||
持
|
||||
変
|
||||
水
|
||||
塔
|
||||
草
|
||||
仲
|
||||
復
|
||||
目
|
||||
作
|
||||
分
|
||||
知
|
||||
機
|
||||
一
|
||||
姫
|
||||
最
|
||||
聞
|
||||
械
|
||||
神
|
||||
流
|
||||
乗
|
||||
取
|
||||
町
|
||||
殿
|
||||
事
|
||||
空
|
||||
勇
|
||||
村
|
||||
早
|
||||
子
|
||||
格
|
||||
納
|
||||
伝
|
||||
消
|
||||
書
|
||||
長
|
||||
話
|
||||
合
|
||||
所
|
||||
場
|
||||
脱
|
||||
北
|
||||
後
|
||||
全
|
||||
忍
|
||||
獣
|
||||
詩
|
||||
吟
|
||||
落
|
||||
自
|
||||
階
|
||||
説
|
||||
残
|
||||
親
|
||||
活
|
||||
休
|
||||
姉
|
||||
破
|
||||
土
|
||||
度
|
||||
記
|
||||
発
|
||||
赤
|
||||
侍
|
||||
死
|
||||
国
|
||||
旅
|
||||
完
|
||||
小
|
||||
林
|
||||
古
|
||||
図
|
||||
礼
|
||||
商
|
||||
島
|
||||
邪
|
||||
部
|
||||
狩
|
||||
精
|
||||
姿
|
||||
防
|
||||
向
|
||||
先
|
||||
解
|
||||
板
|
||||
下
|
||||
台
|
||||
賢
|
||||
対
|
||||
木
|
||||
成
|
||||
命
|
||||
配
|
||||
法
|
||||
飲
|
||||
回
|
||||
願
|
||||
門
|
||||
東
|
||||
開
|
||||
貸
|
||||
増
|
||||
危
|
||||
舘
|
||||
道
|
||||
身
|
||||
老
|
||||
西
|
||||
近
|
||||
層
|
||||
第
|
||||
青
|
||||
光
|
||||
読
|
||||
外
|
||||
理
|
||||
強
|
||||
同
|
||||
谷
|
||||
負
|
||||
意
|
||||
学
|
||||
攻
|
||||
屋
|
||||
体
|
||||
運
|
||||
河
|
||||
聖
|
||||
必
|
||||
南
|
||||
方
|
||||
黒
|
||||
絶
|
||||
食
|
||||
艇
|
||||
山
|
||||
穴
|
||||
名
|
||||
受
|
||||
暁
|
||||
傷
|
||||
少
|
||||
鼻
|
||||
倉
|
||||
然
|
||||
的
|
||||
男
|
||||
用
|
||||
酒
|
||||
安
|
||||
現
|
||||
代
|
||||
立
|
||||
具
|
||||
育
|
||||
続
|
||||
通
|
||||
会
|
||||
庫
|
||||
飼
|
||||
日
|
||||
窟
|
||||
砲
|
||||
広
|
||||
化
|
||||
博
|
||||
以
|
||||
兄
|
||||
室
|
||||
洞
|
||||
別
|
||||
番
|
||||
昔
|
||||
住
|
||||
吸
|
||||
去
|
||||
宝
|
||||
毒
|
||||
売
|
||||
好
|
||||
枝
|
||||
踊
|
||||
月
|
||||
巣
|
||||
底
|
||||
明
|
||||
感
|
||||
宿
|
||||
召
|
||||
喚
|
||||
決
|
||||
形
|
||||
団
|
||||
異
|
||||
初
|
||||
険
|
||||
家
|
||||
息
|
||||
連
|
||||
集
|
||||
墓
|
||||
著
|
||||
跡
|
||||
遺
|
||||
内
|
||||
悲
|
||||
教
|
||||
返
|
||||
頭
|
||||
再
|
||||
高
|
||||
借
|
||||
登
|
||||
走
|
||||
過
|
||||
敵
|
||||
鏡
|
||||
愛
|
||||
魚
|
||||
燃
|
||||
幅
|
||||
音
|
||||
血
|
||||
滝
|
||||
次
|
||||
閉
|
||||
求
|
||||
声
|
||||
箱
|
||||
軍
|
||||
泣
|
||||
港
|
||||
冊
|
||||
秒
|
||||
憶
|
||||
潜
|
||||
爆
|
||||
恋
|
||||
石
|
||||
左
|
||||
役
|
||||
放
|
||||
衛
|
||||
母
|
||||
建
|
||||
角
|
||||
信
|
||||
師
|
||||
元
|
||||
失
|
||||
字
|
||||
歩
|
||||
遊
|
||||
頂
|
||||
震
|
||||
収
|
||||
追
|
||||
暗
|
||||
橋
|
||||
起
|
||||
約
|
||||
束
|
||||
文
|
||||
反
|
||||
絵
|
||||
能
|
||||
歌
|
||||
弱
|
||||
境
|
||||
波
|
||||
針
|
||||
千
|
||||
炎
|
||||
望
|
||||
雨
|
||||
金
|
||||
希
|
||||
花
|
||||
不
|
||||
白
|
||||
剣
|
||||
陸
|
||||
勝
|
||||
烏
|
||||
笛
|
||||
究
|
||||
三
|
||||
探
|
||||
夜
|
||||
病
|
||||
半
|
||||
美
|
||||
敗
|
||||
友
|
||||
研
|
||||
予
|
||||
支
|
||||
々
|
||||
除
|
||||
数
|
||||
店
|
||||
翼
|
||||
充
|
||||
填
|
||||
薬
|
||||
弟
|
||||
経
|
||||
験
|
||||
値
|
||||
闘
|
||||
得
|
||||
闇
|
||||
混
|
||||
乱
|
||||
点
|
||||
速
|
||||
閃
|
||||
射
|
||||
斬
|
||||
鉄
|
||||
宣
|
||||
告
|
||||
電
|
||||
磁
|
||||
圧
|
||||
昇
|
||||
重
|
||||
倍
|
||||
妖
|
||||
刀
|
||||
拡
|
||||
散
|
||||
影
|
||||
御
|
||||
正
|
||||
漠
|
||||
特
|
||||
胸
|
||||
永
|
||||
遠
|
||||
議
|
||||
浮
|
||||
眠
|
||||
逃
|
||||
滅
|
||||
囗
|
||||
極
|
||||
湖
|
||||
利
|
||||
周
|
||||
緑
|
||||
切
|
||||
紙
|
||||
胸
|
||||
平
|
||||
和
|
||||
宇
|
||||
宙
|
||||
則
|
||||
孤
|
||||
溝
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
A
|
||||
B
|
||||
C
|
||||
D
|
||||
E
|
||||
F
|
||||
G
|
||||
H
|
||||
I
|
||||
J
|
||||
K
|
||||
L
|
||||
M
|
||||
N
|
||||
O
|
||||
P
|
||||
Q
|
||||
R
|
||||
S
|
||||
T
|
||||
U
|
||||
V
|
||||
W
|
||||
X
|
||||
Y
|
||||
Z
|
||||
[stone]
|
||||
[toad]
|
||||
[mini]
|
||||
[float]
|
||||
[poison]
|
||||
[KO]
|
||||
[blind]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
_m
|
||||
_H
|
||||
_P
|
||||
A
|
||||
B
|
||||
C
|
||||
D
|
||||
E
|
||||
F
|
||||
G
|
||||
H
|
||||
I
|
||||
J
|
||||
K
|
||||
L
|
||||
M
|
||||
N
|
||||
O
|
||||
P
|
||||
Q
|
||||
R
|
||||
S
|
||||
T
|
||||
U
|
||||
V
|
||||
W
|
||||
X
|
||||
Y
|
||||
Z
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
f
|
||||
g
|
||||
h
|
||||
i
|
||||
j
|
||||
k
|
||||
l
|
||||
m
|
||||
n
|
||||
o
|
||||
p
|
||||
q
|
||||
r
|
||||
s
|
||||
t
|
||||
u
|
||||
v
|
||||
w
|
||||
x
|
||||
y
|
||||
z
|
||||
il
|
||||
it
|
||||
|
||||
li
|
||||
ll
|
||||
'
|
||||
"
|
||||
:
|
||||
;
|
||||
,
|
||||
(
|
||||
)
|
||||
/
|
||||
!
|
||||
?
|
||||
.
|
||||
ti
|
||||
fi
|
||||
Bl
|
||||
a
|
||||
pe
|
||||
l
|
||||
'
|
||||
"
|
||||
if
|
||||
lt
|
||||
tl
|
||||
ir
|
||||
tt
|
||||
や
|
||||
ユ
|
||||
ゆ
|
||||
ヨ
|
||||
よ
|
||||
ワ
|
||||
わ
|
||||
ン
|
||||
ん
|
||||
ヲ
|
||||
を
|
||||
[key]
|
||||
[shoe]
|
||||
◆
|
||||
[hammer]
|
||||
⛺
|
||||
[ribbon]
|
||||
[potion]
|
||||
[shirt]
|
||||
♪
|
||||
-
|
||||
[shuriken]
|
||||
‥
|
||||
[scroll]
|
||||
!
|
||||
[claw]
|
||||
?
|
||||
[glove]
|
||||
%
|
||||
/
|
||||
:
|
||||
「
|
||||
」
|
||||
.
|
||||
A
|
||||
B
|
||||
X
|
||||
Y
|
||||
L
|
||||
R
|
||||
E
|
||||
H
|
||||
M
|
||||
P
|
||||
S
|
||||
C
|
||||
T
|
||||
↑
|
||||
→
|
||||
+
|
||||
[sword]
|
||||
[wh.mag]
|
||||
[blk.mag]
|
||||
🕒
|
||||
[knife]
|
||||
[spear]
|
||||
[axe]
|
||||
[katana]
|
||||
[rod]
|
||||
[staff]
|
||||
[bow]
|
||||
[harp]
|
||||
[whip]
|
||||
[bell]
|
||||
[shield]
|
||||
[helmet]
|
||||
[armor]
|
||||
[ring]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
A
|
||||
B
|
||||
C
|
||||
D
|
||||
E
|
||||
F
|
||||
G
|
||||
H
|
||||
I
|
||||
J
|
||||
K
|
||||
L
|
||||
M
|
||||
N
|
||||
O
|
||||
P
|
||||
Q
|
||||
R
|
||||
S
|
||||
T
|
||||
U
|
||||
V
|
||||
W
|
||||
X
|
||||
Y
|
||||
Z
|
||||
[stone]
|
||||
[toad]
|
||||
[mini]
|
||||
[float]
|
||||
[poison]
|
||||
[KO]
|
||||
[blind]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
_m
|
||||
_H
|
||||
_P
|
||||
ハ
|
||||
は
|
||||
ヒ
|
||||
ひ
|
||||
フ
|
||||
ふ
|
||||
ヘ
|
||||
へ
|
||||
ホ
|
||||
ほ
|
||||
カ
|
||||
か
|
||||
キ
|
||||
き
|
||||
ク
|
||||
く
|
||||
ケ
|
||||
け
|
||||
コ
|
||||
こ
|
||||
サ
|
||||
さ
|
||||
シ
|
||||
し
|
||||
ス
|
||||
す
|
||||
セ
|
||||
せ
|
||||
ソ
|
||||
そ
|
||||
タ
|
||||
た
|
||||
チ
|
||||
ち
|
||||
ツ
|
||||
つ
|
||||
テ
|
||||
て
|
||||
ト
|
||||
と
|
||||
ウ
|
||||
う
|
||||
ア
|
||||
あ
|
||||
イ
|
||||
い
|
||||
エ
|
||||
え
|
||||
オ
|
||||
お
|
||||
ナ
|
||||
な
|
||||
ニ
|
||||
に
|
||||
ヌ
|
||||
ぬ
|
||||
ネ
|
||||
ね
|
||||
ノ
|
||||
の
|
||||
マ
|
||||
ま
|
||||
ミ
|
||||
み
|
||||
ム
|
||||
む
|
||||
メ
|
||||
め
|
||||
モ
|
||||
も
|
||||
ラ
|
||||
ら
|
||||
リ
|
||||
り
|
||||
ル
|
||||
る
|
||||
レ
|
||||
れ
|
||||
ロ
|
||||
ろ
|
||||
ヤ
|
||||
や
|
||||
ユ
|
||||
ゆ
|
||||
ヨ
|
||||
よ
|
||||
ワ
|
||||
わ
|
||||
ン
|
||||
ん
|
||||
ヲ
|
||||
を
|
||||
ッ
|
||||
っ
|
||||
ャ
|
||||
ゃ
|
||||
ュ
|
||||
ゅ
|
||||
ョ
|
||||
ょ
|
||||
ァ
|
||||
ー
|
||||
ィ
|
||||
‥
|
||||
ぅ
|
||||
!
|
||||
ェ
|
||||
?
|
||||
ォ
|
||||
%
|
||||
/
|
||||
:
|
||||
「
|
||||
」
|
||||
。
|
||||
A
|
||||
B
|
||||
X
|
||||
Y
|
||||
L
|
||||
R
|
||||
E
|
||||
H
|
||||
M
|
||||
P
|
||||
S
|
||||
C
|
||||
T
|
||||
↑
|
||||
→
|
||||
+
|
||||
[洋剣]
|
||||
[wh.mag]
|
||||
[blk.mag]
|
||||
🕒
|
||||
[刂]
|
||||
[槍]
|
||||
[鉞]
|
||||
[刀]
|
||||
[棒]
|
||||
[杖]
|
||||
[弓]
|
||||
♪
|
||||
[鞭]
|
||||
[bell]
|
||||
[shield]
|
||||
[helmet]
|
||||
[armor]
|
||||
[ring]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
バ
|
||||
ば
|
||||
ビ
|
||||
び
|
||||
ブ
|
||||
ぶ
|
||||
ベ
|
||||
べ
|
||||
ボ
|
||||
ぼ
|
||||
ガ
|
||||
が
|
||||
ギ
|
||||
ぎ
|
||||
グ
|
||||
ぐ
|
||||
ゲ
|
||||
げ
|
||||
ゴ
|
||||
ご
|
||||
ザ
|
||||
ざ
|
||||
ジ
|
||||
じ
|
||||
ズ
|
||||
ず
|
||||
ゼ
|
||||
ぜ
|
||||
ゾ
|
||||
ぞ
|
||||
ダ
|
||||
だ
|
||||
ヂ
|
||||
ぢ
|
||||
ヅ
|
||||
づ
|
||||
デ
|
||||
で
|
||||
ド
|
||||
ど
|
||||
ヴ
|
||||
パ
|
||||
ぱ
|
||||
ピ
|
||||
ぴ
|
||||
プ
|
||||
ぷ
|
||||
ペ
|
||||
ぺ
|
||||
ポ
|
||||
ぽ
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
_m
|
||||
_H
|
||||
_P
|
||||
ハ
|
||||
は
|
||||
ヒ
|
||||
ひ
|
||||
フ
|
||||
ふ
|
||||
ヘ
|
||||
へ
|
||||
ホ
|
||||
ほ
|
||||
カ
|
||||
か
|
||||
キ
|
||||
き
|
||||
ク
|
||||
く
|
||||
ケ
|
||||
け
|
||||
コ
|
||||
こ
|
||||
サ
|
||||
さ
|
||||
シ
|
||||
し
|
||||
ス
|
||||
す
|
||||
セ
|
||||
せ
|
||||
ソ
|
||||
そ
|
||||
タ
|
||||
た
|
||||
チ
|
||||
ち
|
||||
ツ
|
||||
つ
|
||||
テ
|
||||
て
|
||||
ト
|
||||
と
|
||||
ウ
|
||||
う
|
||||
ア
|
||||
あ
|
||||
イ
|
||||
い
|
||||
エ
|
||||
え
|
||||
オ
|
||||
お
|
||||
ナ
|
||||
な
|
||||
ニ
|
||||
に
|
||||
ヌ
|
||||
ぬ
|
||||
ネ
|
||||
ね
|
||||
ノ
|
||||
の
|
||||
マ
|
||||
ま
|
||||
ミ
|
||||
み
|
||||
ム
|
||||
む
|
||||
メ
|
||||
め
|
||||
モ
|
||||
も
|
||||
ラ
|
||||
ら
|
||||
リ
|
||||
り
|
||||
ル
|
||||
る
|
||||
レ
|
||||
れ
|
||||
ロ
|
||||
ろ
|
||||
ヤ
|
||||
や
|
||||
ユ
|
||||
ゆ
|
||||
ヨ
|
||||
よ
|
||||
ワ
|
||||
わ
|
||||
ン
|
||||
ん
|
||||
ヲ
|
||||
を
|
||||
ッ
|
||||
っ
|
||||
ャ
|
||||
ゃ
|
||||
ュ
|
||||
ゅ
|
||||
ョ
|
||||
ょ
|
||||
ァ
|
||||
ー
|
||||
ィ
|
||||
‥
|
||||
ぅ
|
||||
!
|
||||
ェ
|
||||
?
|
||||
ォ
|
||||
%
|
||||
/
|
||||
:
|
||||
「
|
||||
」
|
||||
。
|
||||
A
|
||||
B
|
||||
X
|
||||
Y
|
||||
L
|
||||
R
|
||||
E
|
||||
H
|
||||
M
|
||||
P
|
||||
S
|
||||
C
|
||||
T
|
||||
↑
|
||||
→
|
||||
+
|
||||
[洋剣]
|
||||
[wh.mag]
|
||||
[blk.mag]
|
||||
🕒
|
||||
[刂]
|
||||
[槍]
|
||||
[鉞]
|
||||
[刀]
|
||||
[棒]
|
||||
[杖]
|
||||
[弓]
|
||||
♪
|
||||
[鞭]
|
||||
[bell]
|
||||
[shield]
|
||||
[helmet]
|
||||
[armor]
|
||||
[ring]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
from . import const, files, strings
|
||||
from ..helpers import hex, parse_struct
|
||||
|
||||
class ZoneData:
|
||||
def split_tilesets(data):
|
||||
tilesets = [(data & 0x00003F),
|
||||
(data & 0x000FC0) >> 6,
|
||||
(data & 0x03F000) >> 12,
|
||||
(data & 0xFC0000) >> 18]
|
||||
return ' '.join([hex(i,2) for i in tilesets])
|
||||
|
||||
def split_blockmaps(data):
|
||||
blockmaps = [(data & 0x000003FF) - 1,
|
||||
((data & 0x000FFC00) >> 10) - 1,
|
||||
((data & 0x3FF00000) >> 20) - 1]
|
||||
return ' '.join([hex(i,3) for i in blockmaps])
|
||||
|
||||
zone_structure = [('NPC Layer', 2, None), # 00 01
|
||||
('Name', 1, strings.Strings.blocks_RPGe['zone_names'].decoded), # 02
|
||||
('ShadowFlags', 1, None), # 03
|
||||
('Graphic maths', 1, None), # 04 - MSb animation-related, 6 LSbs are ID for table in 0x005BB8 which writes to $2131-$2133 (Color Math Designation, Subscreen BG color)
|
||||
('Tile properties',1, None), # 05
|
||||
('Flags '+hex(6), 1, None), # 06
|
||||
(hex(7), 1, None), # 07
|
||||
('Blockset', 1, None), # 08
|
||||
('Tilesets', 3, split_tilesets), # 09 0A 0B
|
||||
('Blockmaps', 4, split_blockmaps), # 0C 0D 0E 0F
|
||||
(hex(16), 1, None), # 10
|
||||
(hex(17), 1, None), # 11
|
||||
(hex(18), 1, None), # 12
|
||||
(hex(19), 1, None), # 13
|
||||
(hex(20), 1, None), # 14
|
||||
(hex(21), 1, None), # 15
|
||||
('Palette', 1, None), # 16
|
||||
(hex(23), 1, None), # 17
|
||||
(hex(24), 1, None), # 18
|
||||
('Music', 1, const.BGM_Tracks)] # 19
|
||||
zone_headers = ['Address'] + [z[0] for z in zone_structure]
|
||||
|
||||
@classmethod
|
||||
def get_data(cls):
|
||||
return [parse_struct(files.ROM_RPGe, 0x0E9C00 + (i*0x1A), cls.zone_structure) for i in range(const.zone_count)]
|
|
@ -0,0 +1,47 @@
|
|||
'''
|
||||
This file is part of ff5reader.
|
||||
|
||||
ff5reader is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ff5reader is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with ff5reader. If not, see <http://www.gnu.org/licenses/>.
|
||||
'''
|
||||
from ..helpers import load_table
|
||||
from pathlib import Path
|
||||
|
||||
dir = Path(__file__).parent
|
||||
# print(dir)
|
||||
|
||||
|
||||
# Font palettes (probably move these later)
|
||||
small_palette = [0xFF000000, 0x00000080, 0xFF808080, 0xFFFFFFFF]
|
||||
dialogue_palette = [0xFF000080, 0xFFFFFFFF]
|
||||
mono_palette = [0xFF000000, 0xFFFFFFFF]
|
||||
|
||||
|
||||
BGM_Tracks = load_table(dir/'BGM_Titles.txt')
|
||||
BGM_Tracks_Safe = [t.replace('/', '-') for t in BGM_Tracks] # For saving files
|
||||
|
||||
|
||||
npc_layer_count = 0x200
|
||||
npc_layer_structure = [
|
||||
("Dialogue/trigger ID", 1, None),
|
||||
("0x01", 1, None),
|
||||
("Sprite ID", 1, None),
|
||||
("X", 1, None),
|
||||
("Y", 1, None),
|
||||
("Move Pattern", 1, None),
|
||||
("Palette", 1, None)
|
||||
]
|
||||
npc_layer_headers = ["Ptr Address", "Layer", "Data Address"] + [x[0] for x in npc_layer_structure]
|
||||
|
||||
|
||||
zone_count = 0x200
|
|
@ -0,0 +1,7 @@
|
|||
from ..helpers import load_raw
|
||||
|
||||
filename_RPGe = 'Final Fantasy V (Japan) [En by RPGe v1.1].sfc'
|
||||
filename_SNES = 'Final Fantasy V (Japan).sfc'
|
||||
|
||||
ROM_RPGe = load_raw(filename_RPGe)
|
||||
ROM_SNES = load_raw(filename_SNES)
|
|
@ -0,0 +1,12 @@
|
|||
name num_entries address snes_address bytes snes_bytes rpge_ptr_offset snes_ptr_offset dialog
|
||||
ability_names 33 0x116200 8
|
||||
battle_commands 96 0x201150 0x115800 7 5
|
||||
character_names 5 0x115500 6
|
||||
dialogue 0x900 0x2013F0 0x082220 3 2 0x000000 0x0A0000 True
|
||||
enemy_names 384 0x200050 0x105C00 10 8
|
||||
items 0x100 0x111380 9
|
||||
job_names 22 0x115600 8
|
||||
magics 87 0x111C80 6
|
||||
magics2 73 0x111E8A 9
|
||||
menu_strings 139 0x00F987 2 0x270000 0x000000
|
||||
zone_names 0x100 0x107000 2 0x270000 0x107200 True
|
|
|
@ -0,0 +1,212 @@
|
|||
from ..helpers import get_slices, get_contiguous_address_slices, load_table, load_tsv
|
||||
from . import files
|
||||
from typing import Callable, Iterable
|
||||
from pathlib import Path
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class StringBlock:
|
||||
raw: bytes
|
||||
glyphs: Iterable[int]
|
||||
decoded: str
|
||||
string_address: int
|
||||
pointer_address: int
|
||||
|
||||
def __len__(self):
|
||||
return len(self.raw)
|
||||
|
||||
|
||||
dir = Path(__file__).parent
|
||||
#print(dir)
|
||||
|
||||
|
||||
# Transcription of RPGe glyph tiles
|
||||
Glyphs_small_RPGe = load_table(dir/'Glyphs_small_RPGe.txt')
|
||||
Glyphs_dialog_RPGe = load_table(dir/'Glyphs_dialog_RPGe.txt')
|
||||
Glyphs_small_SNES = load_table(dir/'Glyphs_small_SNES.txt')
|
||||
Glyphs_small_dakuten_SNES = load_table(dir/'Glyphs_small_dakuten_SNES.txt')
|
||||
Glyphs_dialog_SNES = load_table(dir/'Glyphs_dialog_SNES.txt')
|
||||
Glyphs_dialog_kanji_SNES = load_table(dir/'Glyphs_dialog_kanji_SNES.txt')
|
||||
|
||||
|
||||
class DialogMacros:
|
||||
expansions_SNES = {
|
||||
# Is 0x00 a wait for input marker?
|
||||
# 0x01 is linebreak
|
||||
0x02: [0x20, 0xBC, 0x82], # 0x02 expands to Bartz's name バッツ. Used for his dialogue in EN, only used for other chars in JP.
|
||||
0x03: [0x6E, 0xA8, 0x78, 0x7E, 0xAA], # 0x03 is クリスタル
|
||||
0x04: [0x7E, 0x8C, 0x6E, 0xC5, 0xB8], # expands to タイクーン
|
||||
0x06: [0x37, 0xBF], # expands to じゃ
|
||||
0x07: [0x8D, 0xAB], # expands to いる
|
||||
0x08: [0xFF, 0xFF, 0xFF, 0xFF], # 4 spaces
|
||||
0x09: [0xFF, 0xFF, 0xFF], # 3 spaces
|
||||
0x0A: [0xFF, 0xFF], # 2 spaces
|
||||
0x0B: [0x1E12, 0x1E13], # expands to 魔物
|
||||
# 0x0C appears to be a pause in delivery - affects previous char
|
||||
0x0D: [0x1E24, 0x9B, 0x1E52, 0x1E57], # expands to 風の神殿
|
||||
0x0E: [0x1E04, 0x1E0A], # expands to 飛竜
|
||||
# 0x0F - unknown (invisible control char)
|
||||
# 0x10 is a gil substitution
|
||||
# 0x11 and 0x12 appear to be item (obtained) substitutions
|
||||
0x13: [0x1E07, 0x1E0D], # expands to 封印
|
||||
0x14: [0x76, 0x46, 0xD0], # Cid speaking - シド「
|
||||
0x15: [0x9E, 0x46, 0xD0], # Mid speaking - ミド「
|
||||
0x16: [0x1E05, 0x1E06], # expands to 世界
|
||||
# 0x17 uses the next byte for pause duration (seconds?)
|
||||
0x18: [0x8E, 0x6E, 0x78, 0x44, 0x78], # expands to エクスデス
|
||||
0x19: [0xAC, 0x92, 0xD0], # Lenna speaking - レナ「
|
||||
0x1A: [0x2A, 0xA6, 0x64, 0xD0], # Galuf speaking - ガラフ「
|
||||
0x1B: [0x64, 0xC4, 0xA8, 0x78, 0xD0], # Faris speaking - ファリス「
|
||||
0x1C: [0x6E, 0xAA, 0xAA, 0xD0], # Krile/Kara speaking - クルル「
|
||||
0x1D: [0x91, 0x37, 0x8D, 0x81, 0xBF, 0xB9], # expands to おじいちゃん
|
||||
# 0x1E-0x1F form kanji with the next byte
|
||||
# 0x20-0xCC are standard character set
|
||||
0xCD: [0xC9, 0xC9], # % (0xCD) to !!
|
||||
# 0xCE is /
|
||||
0xCF: [0xBD, 0x85], # : (0xCF) appears to expand to って
|
||||
# 0xD0-0xD4 are 「」。AB
|
||||
0xD5: [0x1E1B, 0x95, 0x1E08, 0xAD], # expands to 手に入れ
|
||||
# 0xD6, 0xD7, 0xD8 are YLR
|
||||
0xD9: [0x93, 0x8D], # expands to ない
|
||||
# 0xDA-0xDC are HMP
|
||||
0xDD: [0xC7, 0xC7], # S (0xDD) to ……
|
||||
0xDE: [0x3F, 0x8D, 0x37, 0xC3, 0x89, 0x25], # C (0xDE) to だいじょうぶ
|
||||
0xDF: [0x61, 0xE3], # T (0xDF) to は、
|
||||
0xE0: [0xB9, 0x3F], # expands to んだ
|
||||
0xE1: [0x85, 0x8D], # expands to てい
|
||||
0xE2: [0x77, 0x7F], # expands to した
|
||||
# 0xE3 is 、
|
||||
0xE4: [0x77, 0x85], # ◯ (0xE4) appears to expand to して
|
||||
# 0xE5 is used for Bartz speaking in JP. This only appears as 『
|
||||
0xE6: [0x91, 0x1E0F, 0x1E03], # F (0xE6) appears to expand to otousan (お父様)
|
||||
0xE7: [0xC9, 0xCB], # °C (0xE7) to !? - yes this is the wrong order interrobang
|
||||
0xE8: [0x45, 0x79], # ・ (0xE8) appears to expand to です
|
||||
# 0xE9, 0xEA are ()
|
||||
0xEB: [0x73, 0x9B], # expands to この
|
||||
0xEC: [0x9B, 0x1E02], # expands to の力
|
||||
0xED: [0x70, 0xAA, 0x2A, 0xC5], # expands to ケルガー
|
||||
0xEE: [0x1E86, 0x1ED7, 0x1E87, 0x1E62, 0x1EA7], # expands to 古代図書舘 (ancient library?)
|
||||
0xEF: [0x1E1C, 0xBD, 0x85], # expands to 言って
|
||||
0xF0: [0x1E2B, 0x1E0B, 0xD0], # soldier speaking - 兵士「
|
||||
0xF1: [0x6B, 0xA7], # expands to から
|
||||
0xF2: [0x1E2C, 0x6A, 0x1E0C], # expands to 火カ船
|
||||
0xF3: [0x1E0E, 0x3D, 0x6F], # expands to 海ぞく
|
||||
0xF4: [0x8D, 0x37, 0xC3, 0x89], # expands to いじょう
|
||||
0xF5: [0x2B, 0xE3], # expands to が、
|
||||
0xF6: [0x7F, 0x81], # expands to たち
|
||||
0xF7: [0x7F, 0x9B], # expands to たの
|
||||
0xF8: [0x9D, 0x79], # expands to ます
|
||||
0xF9: [0x6F, 0x3F, 0x75, 0x8D], # expands to ください
|
||||
0xFA: [0x6B, 0xBD, 0x7F], # expands to かった
|
||||
0xFB: [0x7F, 0xC9], # expands to た!
|
||||
0xFC: [0x95, 0xE3], # expands to に、
|
||||
0xFD: [0x8D, 0x93, 0x8D, 0x6B, 0xA7, 0x93, 0xB9, 0x3F], # expands to いないからなんだ
|
||||
0xFE: [0x1F20, 0x1F38, 0x9B, 0x61, 0x35, 0x9D], # expands to 次元のはざま
|
||||
# 0xFF is space
|
||||
}
|
||||
|
||||
# XX: Maybe convert this to subclasses with the same method signature. Don't care enough yet.
|
||||
def expand_RPGe_dialog(raw: bytes) -> list[int]:
|
||||
output = []
|
||||
it = iter(raw)
|
||||
while (n := next(it, None)) is not None:
|
||||
if n == 0x02: # expands to Bartz (or whatever his name is)
|
||||
output += [0x61, 0x7A, 0x8B, 0x8D, 0x93]
|
||||
elif n == 0x17: # Pause marker
|
||||
pause_duration = next(it, None) # Consume the duration
|
||||
# For now, we just strip these
|
||||
output.append(n)
|
||||
return output
|
||||
|
||||
@classmethod
|
||||
def expand_SNES_dialog(cls, raw: bytes) -> list[int]:
|
||||
output = []
|
||||
it = iter(raw)
|
||||
while (n := next(it, None)) is not None:
|
||||
if n == 0x02: # expands to Bartz (or whatever his name is)
|
||||
output += [0x61, 0x7A, 0x8B, 0x8D, 0x93]
|
||||
elif n == 0x17: # Pause marker
|
||||
pause_duration = next(it, None) # Consume the duration
|
||||
# For now, we just strip these
|
||||
elif n in (0x1E, 0x1F): # Kanji prefix
|
||||
output.append((n<<8) + next(it, 0)) # Convert e.g. 0x1E, 0x2C to 0x1E2C
|
||||
else:
|
||||
output += cls.expansions_SNES.get(n, [n])
|
||||
return output
|
||||
|
||||
|
||||
RPGe_Dialogue_Width = [4 for i in range(256)]
|
||||
RPGe_Dialogue_Width[0x50:0xB1] = [a+1 for a in [
|
||||
5, 2, 6, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6, 8, 8, 8, # 0x50
|
||||
6, 6, 5, 6, 5, 5, 6, 6, 2, 6, 7, 5, 10, 7, 6, 6, # 0x60
|
||||
6, 6, 6, 6, 6, 6,10, 6, 6, 6, 6, 6, 5, 6, 6, 5, # 0x70
|
||||
6, 6, 2, 5, 6, 2,10, 6, 6, 6, 6, 5, 5, 4, 6, 6, # 0x80
|
||||
10, 6, 6, 6, 5, 7, 6, 5, 5, 2, 5, 2, 2, 2, 3, 3, # 0x90
|
||||
5, 2, 6, 2, 7, 8, 0, 0, 6, 9, 2, 5, 8, 7, 7, 8, # 0xA0
|
||||
9
|
||||
]]
|
||||
|
||||
|
||||
def decode_RPGe_small(glyph: int) -> str:
|
||||
return Glyphs_small_RPGe[glyph]
|
||||
|
||||
|
||||
def decode_RPGe_dialog(glyph: int) -> str:
|
||||
return Glyphs_dialog_RPGe[glyph]
|
||||
|
||||
|
||||
def decode_SNES_small(glyph: int) -> str:
|
||||
return Glyphs_small_SNES[glyph]
|
||||
# return Glyphs_small_dakuten_SNES[glyph]
|
||||
|
||||
|
||||
def decode_SNES_dialog(glyph: int) -> str:
|
||||
if glyph > 0xFF:
|
||||
kanji_idx = glyph - 0x1E00
|
||||
if kanji_idx < 0x1AA: # Hardcoded len(Glyphs_dialog_kanji_SNES)
|
||||
return Glyphs_dialog_kanji_SNES[kanji_idx]
|
||||
return f'[0x{glyph:04x}]'
|
||||
return Glyphs_dialog_SNES[glyph]
|
||||
|
||||
|
||||
def decode_glyphs(glyphs: list[str], glyph_decoder: Callable[[int], str]) -> str:
|
||||
return ''.join(glyph_decoder(c) for c in glyphs)
|
||||
|
||||
|
||||
def make_snes_jp_en_strings(data: dict[str, object]) -> tuple[StringBlock, StringBlock]:
|
||||
print(data)
|
||||
indirect_offset_jp = data.get('snes_ptr_offset')
|
||||
indirect_offset_en = data.get('rpge_ptr_offset')
|
||||
pointer_slices_jp = get_slices(data.get('snes_address', data['address']), data.get('snes_bytes', data['bytes']), data['num_entries'])
|
||||
pointer_slices_en = get_slices(data['address'], data['bytes'], data['num_entries'])
|
||||
|
||||
if indirect_offset_jp is not None:
|
||||
address_slices_jp = get_contiguous_address_slices(files.ROM_SNES, pointer_slices_jp, indirect_offset_jp)
|
||||
else:
|
||||
address_slices_jp = pointer_slices_jp
|
||||
if indirect_offset_en is not None:
|
||||
address_slices_en = get_contiguous_address_slices(files.ROM_RPGe, pointer_slices_en, indirect_offset_en)
|
||||
else:
|
||||
address_slices_en = pointer_slices_en
|
||||
raws_jp = [files.ROM_SNES[s] for s in address_slices_jp]
|
||||
raws_en = [files.ROM_RPGe[s] for s in address_slices_en]
|
||||
|
||||
if data.get('dialog'):
|
||||
glyphs_jp = [DialogMacros.expand_SNES_dialog(raw) for raw in raws_jp]
|
||||
glyphs_en = [DialogMacros.expand_RPGe_dialog(raw) for raw in raws_en]
|
||||
strings_jp = [decode_glyphs(glyphs, decode_SNES_dialog) for glyphs in glyphs_jp]
|
||||
strings_en = [decode_glyphs(glyphs, decode_RPGe_dialog) for glyphs in glyphs_en]
|
||||
else:
|
||||
glyphs_jp = raws_jp
|
||||
glyphs_en = raws_en
|
||||
strings_jp = [decode_glyphs(glyphs, decode_SNES_small) for glyphs in glyphs_jp]
|
||||
strings_en = [decode_glyphs(glyphs, decode_RPGe_small) for glyphs in glyphs_en]
|
||||
return StringBlock(raws_jp, glyphs_jp, strings_jp, address_slices_jp, pointer_slices_jp), StringBlock(raws_en, glyphs_en, strings_en, address_slices_en, pointer_slices_en)
|
||||
|
||||
|
||||
class Strings:
|
||||
config = load_tsv(dir/'string_blocks.tsv')
|
||||
blocks_SNES_RPGe = {k: make_snes_jp_en_strings(v) for k,v in config.items()}
|
||||
blocks_SNES = {k:v[0] for k,v in blocks_SNES_RPGe.items()}
|
||||
blocks_RPGe = {k:v[1] for k,v in blocks_SNES_RPGe.items()}
|
|
@ -14,25 +14,26 @@
|
|||
You should have received a copy of the GNU General Public License
|
||||
along with ff5reader. If not, see <http://www.gnu.org/licenses/>.
|
||||
'''
|
||||
from ast import literal_eval
|
||||
|
||||
HEX_PREFIX = '#' # '#' '$' or '0x' are also nice
|
||||
|
||||
|
||||
def divceil(numerator, denominator):
|
||||
def divceil(numerator: int, denominator: int) -> int:
|
||||
'''
|
||||
Reverse floor division for fast ceil
|
||||
'''
|
||||
return -(-numerator // denominator)
|
||||
|
||||
|
||||
def hex_length(i):
|
||||
def hex_length(i: int) -> int:
|
||||
'''
|
||||
String length of hexadecimal representation of integer
|
||||
'''
|
||||
return divceil(i.bit_length(), 4)
|
||||
|
||||
|
||||
def hex(num, digits=2):
|
||||
def hex(num: int, digits: int = 2) -> str:
|
||||
'''
|
||||
Consolidate hex formatting for consistency
|
||||
'''
|
||||
|
@ -40,14 +41,102 @@ def hex(num, digits=2):
|
|||
return HEX_PREFIX + '{:0{}X}'.format(num, digits)
|
||||
|
||||
|
||||
def indirect(rom, start, length=2, endian='little'):
|
||||
def indirect(rom: bytes, start: int, length: int = 2, endian: str = 'little') -> int:
|
||||
'''
|
||||
Read little-endian value at start address in rom
|
||||
'''
|
||||
return int.from_bytes(rom[start:start+length], endian)
|
||||
|
||||
def indirect2(rom: bytes, slice, endian: str = 'little') -> int:
|
||||
return int.from_bytes(rom[slice], endian)
|
||||
|
||||
def parse_struct(rom, offset, structure):
|
||||
def memory_address_to_rom_address(address: int) -> int:
|
||||
# SNES memory space in HiROM Mode:
|
||||
# 0xC00000 to 0xFDFFFF are a view of the ROM, i.e. banks $C0 to $FD.
|
||||
# Banks $80-$BF mirror banks $00-3F, which have a partial view to the ROM.
|
||||
# We don't care about potential RAM addresses at low offsets,
|
||||
# so let's just pretend $00-$3F, $40-$7F, $80-$BF and $C0-$FF all map to $00-$3F of the ROM
|
||||
return address & 0x3FFFFF
|
||||
|
||||
|
||||
def get_slices(start_address: int, each_length: int, num_strings: int) -> list:
|
||||
return [slice(start_address+(each_length*id), start_address+(each_length*(id+1))) for id in range(num_strings)]
|
||||
|
||||
|
||||
def get_contiguous_address_slices(rom: bytes, slices, indirect_offset: int = 0) -> list:
|
||||
pointers = [indirect2(rom, s) + indirect_offset for s in slices]
|
||||
output = []
|
||||
for ptr, ptr_next in zip(pointers[:-1], pointers[1:]):
|
||||
if ptr_next < ptr:
|
||||
break
|
||||
start = memory_address_to_rom_address(ptr)
|
||||
end = memory_address_to_rom_address(ptr_next)
|
||||
output.append(slice(start, end))
|
||||
return output
|
||||
|
||||
|
||||
def get_bytestring_slices(rom: bytes, start_address: int, each_length: int, num_strings: int, indirect_offset=None, indirect_null_terminated=False) -> list:
|
||||
if indirect_offset is not None:
|
||||
if not indirect_null_terminated:
|
||||
pointers = [indirect(rom, address, each_length) + indirect_offset for address in range(start_address, start_address + (each_length * num_strings), each_length)]
|
||||
output = []
|
||||
for ptr, ptr_next in zip(pointers[:-1], pointers[1:]):
|
||||
if ptr_next < ptr:
|
||||
break
|
||||
start = memory_address_to_rom_address(ptr)
|
||||
end = memory_address_to_rom_address(ptr_next)
|
||||
output.append(slice(start, end))
|
||||
return output
|
||||
else:
|
||||
def get_indirect(address: int) -> bytes:
|
||||
ptr = memory_address_to_rom_address(indirect(rom, address, each_length) + indirect_offset)
|
||||
# While previously we used the start address of the next string, this is not necessarily correct.
|
||||
# Scan for a zero-byte end-of-string marker and pay the extra cycles.
|
||||
end_ptr = ptr
|
||||
# print(ptr)
|
||||
while rom[end_ptr] > 0:
|
||||
end_ptr += 1
|
||||
return slice(ptr, end_ptr)
|
||||
return [get_indirect(start_address+(each_length*id)) for id in range(num_strings)]
|
||||
else:
|
||||
return [slice(start_address+(each_length*id), start_address+(each_length*(id+1))) for id in range(num_strings)]
|
||||
|
||||
|
||||
def get_bytestrings(rom: bytes, start_address: int, each_length: int, num_strings: int, indirect_offset=None, indirect_null_terminated=False) -> list[bytes]:
|
||||
return [rom[s] for s in get_bytestring_slices(rom, start_address, each_length, num_strings, indirect_offset, indirect_null_terminated)]
|
||||
|
||||
|
||||
def load_table(filename: str) -> tuple[str]:
|
||||
with open(filename, 'r') as f:
|
||||
return tuple(literal_eval(f'"{line}"') if line.startswith('\\') else line for line in f.read().rstrip('\n').split('\n'))
|
||||
|
||||
def __cast_string_to_object(input: str) -> object:
|
||||
if len(input) == 0:
|
||||
return None
|
||||
try:
|
||||
return literal_eval(input)
|
||||
except:
|
||||
return input # Unescaped string
|
||||
|
||||
def load_tsv(filename: str) -> dict[dict[str, str]]:
|
||||
with open(filename, 'r') as f:
|
||||
header, *lines = f.read().rstrip('\n').split('\n')
|
||||
first_column_name, *headers = header.split('\t')
|
||||
# Cheeky Py3.8 one-liner
|
||||
# return {(s := line.split('\t'))[0]:dict(zip(headers, s[1:])) for line in lines}
|
||||
output = {}
|
||||
for line in lines:
|
||||
name, *values = line.split('\t')
|
||||
output[name] = dict((h,__cast_string_to_object(v)) for h,v in zip(headers, values) if len(v) > 0)
|
||||
return output
|
||||
|
||||
|
||||
def load_raw(filename: str) -> bytes:
|
||||
with open(filename, 'rb') as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
def parse_struct(rom: int, offset: int, structure: list[tuple[str, int, object]]):
|
||||
'''
|
||||
Read in a section of rom with a given structure, output a list
|
||||
'''
|
||||
|
@ -65,7 +154,7 @@ def parse_struct(rom, offset, structure):
|
|||
return out
|
||||
|
||||
|
||||
def decompress_lzss(rom, start, header=False, length=None):
|
||||
def decompress_lzss(rom: bytes, start: int, header: bool = False, length=None) -> bytes:
|
||||
'''
|
||||
Algorithm from http://slickproductions.org/slickwiki/index.php/Noisecross:Final_Fantasy_V_Compression
|
||||
'''
|
||||
|
@ -104,7 +193,7 @@ def decompress_lzss(rom, start, header=False, length=None):
|
|||
return bytes(output[:uncompressed_length])
|
||||
|
||||
|
||||
def decompress_lzss_FFVa(rom, start, header=False, length=None):
|
||||
def decompress_lzss_FFVa(rom: bytes, start: int, header: bool = False, length=None) -> bytes:
|
||||
'''
|
||||
Oops, it's just GBA BIOS decompression functions
|
||||
see https://web.archive.org/web/20130323133944/http://nocash.emubase.de/gbatek.htm#biosdecompressionfunctions
|
||||
|
@ -141,7 +230,7 @@ def decompress_lzss_FFVa(rom, start, header=False, length=None):
|
|||
return bytes(output[:uncompressed_length])
|
||||
|
||||
|
||||
def findall(rom, string):
|
||||
def findall(rom: bytes, string: str) -> list[int]:
|
||||
results = []
|
||||
start = 0
|
||||
while True:
|
||||
|
@ -152,7 +241,7 @@ def findall(rom, string):
|
|||
start = val + 1
|
||||
|
||||
|
||||
def parse_ips(data):
|
||||
def parse_ips(data: bytes):
|
||||
assert data[:5] == b'PATCH' and data[-3:] == b'EOF', 'File header and footer missing!'
|
||||
patches = {}
|
||||
ptr = 5
|
||||
|
@ -168,15 +257,3 @@ def parse_ips(data):
|
|||
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
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
import os
|
||||
from array import array
|
||||
from struct import unpack
|
||||
import includes.const as const
|
||||
import includes.ff5.const as ff5const
|
||||
|
||||
pyqt_version = 0
|
||||
skip_pyqt5 = "PYQT4" in os.environ
|
||||
|
@ -155,7 +155,7 @@ def create_tritile(data):
|
|||
img = QImage(16, 12, QImage.Format_Indexed8)
|
||||
imgbits = img.bits()
|
||||
imgbits.setsize(img.byteCount())
|
||||
img.setColorTable(const.dialogue_palette)
|
||||
img.setColorTable(ff5const.dialogue_palette)
|
||||
tile = array('B', range(192))
|
||||
for p, row, b in [(p,j,b) for p in range(2) for j in range(12) for b in reversed(range(8))]:
|
||||
tile[(7-b) + (row*16) + (p*8)] = (data[row + (p*12)] >> b & 1)
|
||||
|
@ -177,7 +177,7 @@ def create_quadtile(data, ltr=False):
|
|||
del painter
|
||||
return QPixmap.fromImage(img)
|
||||
|
||||
def generate_glyphs(rom, offset, num=0x100, palette=const.small_palette):
|
||||
def generate_glyphs(rom, offset, num=0x100, palette=ff5const.small_palette):
|
||||
spritelist = []
|
||||
for i in range(num):
|
||||
j = offset + (i*16)
|
||||
|
|
|
@ -25,7 +25,7 @@ Trailing 8 bytes are 16 4bit nibbles that make up the compressed samples.
|
|||
import sys
|
||||
from midiutil import MIDIFile
|
||||
from includes.helpers import indirect, hex
|
||||
from includes.const import BGM_Tracks_Safe
|
||||
from includes.ff5.const import BGM_Tracks_Safe
|
||||
import struct
|
||||
import wave
|
||||
|
||||
|
|
Loading…
Reference in New Issue