Basic dialogue support

This commit is contained in:
Luke Hubmayer-Werner 2017-03-13 21:08:49 +10:30
parent 5161c5427d
commit 8f33b58648
1 changed files with 161 additions and 99 deletions

View File

@ -210,12 +210,12 @@ Glyphs_JP_large = list(Glyphs_JP2) # Large glyphs are subtly different again
Glyphs_JP_large[0xE0:0xEB] = ['','','+','', '', '', '', '°C', '', '', ''] Glyphs_JP_large[0xE0:0xEB] = ['','','+','', '', '', '', '°C', '', '', '']
Glyphs_Kanji = ( Glyphs_Kanji = (
'','','',' ', ' ',' ',' ',' ', '','',' ','', ' ',' ',' ','', # 0x000 '','','','', ' ',' ',' ',' ', '','',' ','', ' ',' ',' ','', # 0x000
'','',' ',' ', '',' ',' ','', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x010 '','',' ',' ', '',' ',' ','', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x010
' ','','',' ', '',' ',' ',' ', ' ',' ',' ',' ', '',' ',' ',' ', # 0x020 ' ','','',' ', '',' ',' ',' ', ' ',' ',' ',' ', '',' ',' ',' ', # 0x020
' ',' ',' ',' ', ' ',' ',' ',' ', '',' ',' ',' ', ' ',' ',' ',' ', # 0x030 ' ',' ',' ',' ', ' ',' ',' ',' ', '',' ',' ',' ', ' ',' ',' ',' ', # 0x030
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ','',' ',' ', # 0x040 ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ','',' ',' ', # 0x040
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x050 ' ',' ','',' ', ' ',' ','殿',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x050
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x060 ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x060
' ',' ','',' ', ' ',' ',' ',' ', ' ',' ','',' ', ' ',' ',' ',' ', # 0x070 ' ',' ','',' ', ' ',' ',' ',' ', ' ',' ','',' ', ' ',' ',' ',' ', # 0x070
@ -289,7 +289,7 @@ class FF5Reader(QMainWindow):
generate_glyphs_large(ROM2, glyph_sprites_large, 0x03E800) generate_glyphs_large(ROM2, glyph_sprites_large, 0x03E800)
generate_glyphs_large(ROM2, glyph_sprites_kanji, 0x1BD000, 0x1AA) generate_glyphs_large(ROM2, glyph_sprites_kanji, 0x1BD000, 0x1AA)
global zone_names global zone_names
zone_names = make_string_img_list_indirect(0x107000, 2, 0x100, start_str=0x270000, start_jp_str=0x107200, large=True) zone_names = make_string_img_list(0x107000, 2, 0x100, start_str=0x270000, start_jp_str=0x107200, indirect=True, large=True)
items = make_string_img_list(0x111380, 9, 256) items = make_string_img_list(0x111380, 9, 256)
magics = make_string_img_list(0x111C80, 6, 87) magics = make_string_img_list(0x111C80, 6, 87)
more_magics = make_string_img_list(0x111E8A, 9, 73) more_magics = make_string_img_list(0x111E8A, 9, 73)
@ -355,52 +355,14 @@ class FF5Reader(QMainWindow):
npc_layers[-1].append("0x{0:0{1}X}".format(val, z[1]*2)) npc_layers[-1].append("0x{0:0{1}X}".format(val, z[1]*2))
j += z[1] j += z[1]
glyph_layout = QGridLayout() dialogue = make_string_img_list(0x2013F0, 3, 0x500, start_jp=0x082220, len_jp=2, start_str=0x0, start_jp_str=0x0A0000, indirect=True, large=True) # start_str=0x210000
for i in range(len(glyph_sprites)):
item = glyph_sprites[i]
pixmap_scaled = item.scaled(item.size() * 4)
lab = QLabel()
lab.setPixmap(pixmap_scaled)
glyph_layout.addWidget(lab, i // 16, i % 16)
glyph_frame = QWidget()
glyph_frame.setLayout(glyph_layout)
glyph_layout2 = QGridLayout()
for i in range(len(glyph_sprites2)):
item = glyph_sprites2[i]
pixmap_scaled = item.scaled(item.size() * 4)
lab = QLabel()
lab.setPixmap(pixmap_scaled)
glyph_layout2.addWidget(lab, i // 16, i % 16)
glyph_frame2 = QWidget()
glyph_frame2.setLayout(glyph_layout2)
glyph_layout3 = QGridLayout()
for i in range(len(glyph_sprites_large)):
item = glyph_sprites_large[i]
pixmap_scaled = item.scaled(item.size() * 2)
lab = QLabel()
lab.setPixmap(pixmap_scaled)
glyph_layout3.addWidget(lab, i // 16, i % 16)
glyph_frame3 = QWidget()
glyph_frame3.setLayout(glyph_layout3)
glyph_layout4 = QGridLayout()
for i in range(len(glyph_sprites_kanji)):
item = glyph_sprites_kanji[i]
pixmap_scaled = item.scaled(item.size() * 2)
lab = QLabel()
lab.setPixmap(pixmap_scaled)
glyph_layout4.addWidget(lab, i // 16, i % 16)
glyph_frame4 = QWidget()
glyph_frame4.setLayout(glyph_layout4)
self.tabwidget = QTabWidget() self.tabwidget = QTabWidget()
self.enemy_sprites = QFrame() self.enemy_sprites = QFrame()
self.tabwidget.addTab(glyph_frame, "Glyphs (EN)") self.tabwidget.addTab(make_pixmap_table(glyph_sprites, 4), "Glyphs (EN)")
self.tabwidget.addTab(glyph_frame2, "Glyphs (JP)") self.tabwidget.addTab(make_pixmap_table(glyph_sprites2, 4), "Glyphs (JP)")
self.tabwidget.addTab(glyph_frame3, "Glyphs (Large)") self.tabwidget.addTab(make_pixmap_table(glyph_sprites_large, 2), "Glyphs (Large)")
self.tabwidget.addTab(glyph_frame4, "Glyphs (Kanji)") self.tabwidget.addTab(make_pixmap_table(glyph_sprites_kanji, 2), "Glyphs (Kanji)")
self.tabwidget.addTab(self.enemy_sprites, "Enemy Sprites") self.tabwidget.addTab(self.enemy_sprites, "Enemy Sprites")
self.tabwidget.addTab(make_table(zone_headers, zone_data, True), "Zones") self.tabwidget.addTab(make_table(zone_headers, zone_data, True), "Zones")
self.tabwidget.addTab(make_table(imglist_headers, zone_names, True), "Zone Names") self.tabwidget.addTab(make_table(imglist_headers, zone_names, True), "Zone Names")
@ -409,6 +371,7 @@ class FF5Reader(QMainWindow):
self.tabwidget.addTab(make_table(imglist_headers, magics, row_labels=False), "Magics") self.tabwidget.addTab(make_table(imglist_headers, magics, row_labels=False), "Magics")
self.tabwidget.addTab(make_table(imglist_headers, more_magics, row_labels=False), "More Magics") self.tabwidget.addTab(make_table(imglist_headers, more_magics, row_labels=False), "More Magics")
self.tabwidget.addTab(make_table(imglist_headers, enemy_names, row_labels=False), "Enemy Names") self.tabwidget.addTab(make_table(imglist_headers, enemy_names, row_labels=False), "Enemy Names")
self.tabwidget.addTab(make_table(imglist_headers, dialogue), "Dialogue")
layout = QHBoxLayout() layout = QHBoxLayout()
layout.addWidget(self.tabwidget) layout.addWidget(self.tabwidget)
@ -458,36 +421,136 @@ def make_string_img_large(bytestring):
if len(bytestring) < 1: if len(bytestring) < 1:
raise ValueError('Empty bytestring was passed') raise ValueError('Empty bytestring was passed')
string = "" string = ""
img = QImage(len(bytestring)*16, 14, QImage.Format_RGB16) rows = 20 # This is just for testing
img = QImage(len(bytestring)*16, 14*rows, QImage.Format_RGB16)
img.fill(bg_color) img.fill(bg_color)
painter = QtGui.QPainter(img) painter = QtGui.QPainter(img)
it = iter(range(len(bytestring))) it = iter(range(len(bytestring)))
x = 0 x = 0
xmax = x
y = 0
tabstop = 0
try:
for i in it: for i in it:
j = bytestring[i] j = bytestring[i]
if j < 0x1E: '''
string = string + '[0x{:04X}]'.format(int.from_bytes(bytestring[i:i+2],'little')) There's a lot of dialogue substitutions, this is going to be annoying and/or messy.
next(it) Is 0x00 a wait for input marker?
0x03 is クリスタル
0x0A is a speaker tab, perhaps linebreak as well?
0x0D appears to expand to 風の神殿
: (0xCF) appears to expand to って
S (0xDD) to
C (0xDE) to だいじょうぶ
T (0xDF) to
(0xE4) appears to expand to して
F (0xE6) appears to expand to otousan (お父様)
(0xE8) appears to expand to です
% (0xCD) to !!
°C (0xE7) to !?
'''
if j == 0x01:
string = string + '\n[0x01]'
y += 1
xmax = x if x > xmax else xmax
x = 0
continue
elif j == 0x09:
string = string + '\t[0x09]'
x = tabstop
continue
elif j == 0x0A:
if x != 0:
y += 1
xmax = x if x > xmax else xmax
string = string + '\n'
string = string + '\t[0x0A]'
x = tabstop
continue
elif j == 0x19:
# Lenna is speaking
string = string + "[レナ「]"
painter.drawPixmap(0*16, (y*14)+1, glyph_sprites_large[0xAC])
painter.drawPixmap(1*16, (y*14)+1, glyph_sprites_large[0x92])
painter.drawPixmap(2*16, (y*14)+1, glyph_sprites_large[0xD0])
x += 3
tabstop = x
continue
elif j == 0x1B:
# Faris is speaking
string = string + "[ファリス「]"
painter.drawPixmap(0*16, (y*14)+1, glyph_sprites_large[0x64])
painter.drawPixmap(1*16, (y*14)+1, glyph_sprites_large[0xC4])
painter.drawPixmap(2*16, (y*14)+1, glyph_sprites_large[0xA8])
painter.drawPixmap(3*16, (y*14)+1, glyph_sprites_large[0x78])
painter.drawPixmap(4*16, (y*14)+1, glyph_sprites_large[0xD0])
x += 5
tabstop = x
continue
elif j < 0x1E:
string = string + '[0x{:02X}]'.format(j)
continue
elif j == 0x1E: elif j == 0x1E:
string = string + Glyphs_Kanji[bytestring[i+1]] string = string + Glyphs_Kanji[bytestring[i+1]]
painter.drawPixmap(x*16, 1, glyph_sprites_kanji[bytestring[i+1]]) painter.drawPixmap(x*16, (y*14)+1, glyph_sprites_kanji[bytestring[i+1]])
next(it) next(it)
elif j == 0x1F: elif j == 0x1F:
string = string + Glyphs_Kanji[0x100 + bytestring[i+1]] string = string + Glyphs_Kanji[0x100 + bytestring[i+1]]
painter.drawPixmap(x*16, 1, glyph_sprites_kanji[0x100 + bytestring[i+1]]) painter.drawPixmap(x*16, (y*14)+1, glyph_sprites_kanji[0x100 + bytestring[i+1]])
next(it) next(it)
else: else:
string = string + Glyphs_JP_large[j] string = string + Glyphs_JP_large[j]
painter.drawPixmap(x*16, 1, glyph_sprites_large[j]) painter.drawPixmap(x*16, (y*14)+1, glyph_sprites_large[j])
x += 1 x += 1
if (j == 0xD0) or (j == 0xE5):
tabstop = x
except (StopIteration, IndexError):
pass
del painter del painter
return string, QPixmap.fromImage(img.copy(0, 0, x*16, 14)) xmax = x if x > xmax else xmax
return string, QPixmap.fromImage(img.copy(0, 0, xmax*16, (y+1)*14))
def make_string_img_list(start, length, num, start_jp=None, len_jp=None, indirect=False, large=False): def make_string_img_list(start, length, num, start_jp=None, len_jp=None, start_str=None, start_jp_str=None, indirect=False, large=False):
start_jp = start if start_jp is None else start_jp start_jp = start if start_jp is None else start_jp
len_jp = length if len_jp is None else len_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
stringlist = [] stringlist = []
id_digits = hex_length(num-1) 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[en:en+length],'little') + start_str
en_end = int.from_bytes(ROM[en+length:en+(length*2)],'little') + start_str
if en_start >= 0xC00000:
en_start -= 0xC00000
en_end -= 0xC00000
jp_start = int.from_bytes(ROM2[jp:jp+len_jp],'little') + start_jp_str
jp_end = int.from_bytes(ROM2[jp+len_jp:jp+(len_jp*2)],'little') + start_jp_str
if (en_end == start_str) or (jp_end == start_jp_str) or (en_end > len(ROM)) or (jp_end > len(ROM2)):
break
try:
if en_end > en_start:
string, img = make_string_img(ROM[en_start:en_end])
else:
string = ''
img = None
if jp_end > jp_start:
if large:
string_JP, img_JP = make_string_img_large(ROM2[jp_start:jp_end])
else:
string_JP, img_JP = make_string_img(ROM2[jp_start:jp_end], jp=True)
else:
string_JP = ''
img_JP = None
except ValueError:
print("ID: {} \tRef.0x{:06X} 0x{:06X} \tRange EN: 0x{:06X}-0x{:06X} \tRange JP: 0x{:06X}-0x{:06X}".format(id, en, jp, en_start, en_end, jp_start, jp_end))
raise
stringlist.append(["0x{:06X}".format(en), "0x{0:0{1}X}".format(id, id_digits), string, img, string_JP, img_JP, "0x{:06X}".format(jp_start)])
else:
for id in range(num): for id in range(num):
j1 = start + (id*length) j1 = start + (id*length)
j2 = start_jp + (id*len_jp) j2 = start_jp + (id*len_jp)
@ -499,34 +562,6 @@ def make_string_img_list(start, length, num, start_jp=None, len_jp=None, indirec
stringlist.append(["0x{:06X}".format(j1), "0x{0:0{1}X}".format(id, id_digits), string, img, string_JP, img_JP]) stringlist.append(["0x{:06X}".format(j1), "0x{0:0{1}X}".format(id, id_digits), string, img, string_JP, img_JP])
return stringlist return stringlist
def make_string_img_list_indirect(start, length, num, start_jp=None, len_jp=None, start_str=None, start_jp_str=None, large=False):
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
stringlist = []
id_digits = hex_length(num-1)
for id in range(num):
en = start + (id*length)
jp = start_jp + (id*len_jp)
en_start = int.from_bytes(ROM[en:en+length],'little') + start_str
en_end = int.from_bytes(ROM[en+length:en+(length*2)],'little') + start_str
jp_start = int.from_bytes(ROM2[jp:jp+len_jp],'little') + start_jp_str
jp_end = int.from_bytes(ROM2[jp+len_jp:jp+(len_jp*2)],'little') + start_jp_str
if (en_end == start_str) or (jp_end == start_jp_str) or (en_end > len(ROM)) or (jp_end > len(ROM2)):
break
try:
string, img = make_string_img(ROM[en_start:en_end])
if large:
string_JP, img_JP = make_string_img_large(ROM2[jp_start:jp_end])
else:
string_JP, img_JP = make_string_img(ROM2[jp_start:jp_end], jp=True)
except ValueError:
print("ID: {} \tRef.0x{:06X} 0x{:06X} \tRange EN: 0x{:06X}-0x{:06X} \tRange JP: 0x{:06X}-0x{:06X}".format(id, en, jp, en_start, en_end, jp_start, jp_end))
raise
stringlist.append(["0x{:06X}".format(en), "0x{0:0{1}X}".format(id, id_digits), string, img, string_JP, img_JP, "0x{:06X}".format(jp_start)])
return stringlist
def make_table(headers, items, sortable=False, row_labels=True): def make_table(headers, items, sortable=False, row_labels=True):
""" """
Helper function to tabulate 2d lists Helper function to tabulate 2d lists
@ -553,12 +588,39 @@ def make_table(headers, items, sortable=False, row_labels=True):
if item[:2] == "0x": if item[:2] == "0x":
q_item.setFont(monofont) q_item.setFont(monofont)
table.setItem(row, col, q_item) table.setItem(row, col, q_item)
# Stupid hack to get table to size correctly
table.hide()
geometry = table.viewport().geometry()
table.viewport().setGeometry(QtCore.QRect(0, 0, 0x7FFFFFFF, 0x7FFFFFFF))
table.resizeColumnsToContents() table.resizeColumnsToContents()
table.resizeRowsToContents()
table.viewport().setGeometry(geometry)
table.show()
if sortable: if sortable:
table.setSortingEnabled(True) table.setSortingEnabled(True)
table.sortItems(0) table.sortItems(0)
return table return table
def make_pixmap_table(items, scale=4):
table = QTableWidget()
rows = divceil(len(items), 16)
rd = hex_length(rows-1)+1
cols = 16
table.setRowCount(rows)
table.setVerticalHeaderLabels(['0x{0:0{1}X}'.format(v*16, rd) for v in range(rows)])
table.setColumnCount(cols)
table.setHorizontalHeaderLabels(['0x{:X}'.format(v) for v in range(cols)])
for i in range(len(items)):
item = items[i]
pixmap_scaled = item.scaled(item.size() * scale)
lab = QLabel()
lab.setPixmap(pixmap_scaled)
table.setCellWidget(i // cols, i % cols, lab)
table.resizeColumnsToContents()
return table
def main(): def main():
app = QApplication(sys.argv) app = QApplication(sys.argv)