Refactoring, split into files

This commit is contained in:
Luke Hubmayer-Werner 2017-03-13 23:20:19 +10:30
parent 8f33b58648
commit 7c3d2e6ce9
3 changed files with 254 additions and 210 deletions

92
const.py Normal file
View File

@ -0,0 +1,92 @@
'''
No license for now
'''
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
'', '', '0', '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[0xE3] = '[洋剣]'
Glyphs_JP[0xE7:0xF0] = ['[刂]', '[槍]', '[鉞]', '[刀]', '[棒]', '[杖]', '[弓]', '', '[鞭]']
Glyphs_JP2 = list(Glyphs_JP) # Japanese glyphs using the dakuten encoding
Glyphs_JP2[0x20:0x52] = [
'','','','', '','','','', '','','','', '','','','', # 0x20
'','','','', '','','','', '','','','', '','','','', # 0x30
'','','','', '','','','', # 0x40-0x48
'','','','', '','','','', '',''] # 0x48-0x52
Glyphs_JP_large = list(Glyphs_JP2) # Large glyphs are subtly different again
Glyphs_JP_large[0xE0:0xEB] = ['','','+','', '', '', '', '°C', '', '', '']
Glyphs_Kanji = ( # TODO: finish this
'','','','', ' ',' ',' ',' ', '','',' ','', ' ',' ',' ','', # 0x000
'','',' ',' ', '',' ',' ','', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x010
' ','','',' ', '',' ',' ',' ', ' ',' ',' ',' ', '',' ',' ',' ', # 0x020
' ',' ',' ',' ', ' ',' ',' ',' ', '',' ',' ',' ', ' ',' ',' ',' ', # 0x030
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ','',' ',' ', # 0x040
' ',' ','',' ', ' ',' ','殿',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x050
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x060
' ',' ','',' ', ' ',' ',' ',' ', ' ',' ','',' ', ' ',' ',' ',' ', # 0x070
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x080
' ',' ',' ',' ', ' ','',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x090
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x0A0
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x0B0
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ','',' ',' ', # 0x0C0
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x0D0
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x0E0
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x0F0
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x100
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x110
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x120
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x130
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x140
'',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x150
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x160
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x170
' ',' ',' ',' ', ' ',' ',' ',' ', ' ','',' ',' ', ' ',' ',' ',' ', # 0x180
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x190
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ') # 0x1A0
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")

View File

@ -7,10 +7,12 @@ import sys
import os import os
import struct import struct
from array import array from array import array
#from heximg import HexImg from snestile import create_tile, create_tritile
import const
import time
pyqt_version = 0 pyqt_version = 0
skip_pyqt5 = True # "PYQT4" in os.environ skip_pyqt5 = "PYQT4" in os.environ
if not skip_pyqt5: if not skip_pyqt5:
try: try:
@ -35,7 +37,7 @@ if not skip_pyqt5:
except ImportError: except ImportError:
print("Couldn't import Qt5 dependencies. " print("Couldn't import Qt5 dependencies. "
"Make sure you installed the PyQt5 package.") "Make sure you installed the PyQt5 package.")
if pyqt_version is 0: if pyqt_version == 0:
try: try:
import sip import sip
sip.setapi('QVariant', 2) sip.setapi('QVariant', 2)
@ -70,11 +72,6 @@ if not monofont.fixedPitch():
monofont.setStyleHint(QFont.TypeWriter) monofont.setStyleHint(QFont.TypeWriter)
if not monofont.fixedPitch(): if not monofont.fixedPitch():
monofont.setFamily("Monospace") monofont.setFamily("Monospace")
#"Ubuntu Mono",
#"DejaVu Sans Mono",
#"Liberation Mono",
#"courier"])
#monofont.setPixelSize(8)
def divceil(numerator, denominator): def divceil(numerator, denominator):
# Reverse floor division for ceil # Reverse floor division for ceil
@ -86,186 +83,24 @@ def hex_length(i):
filename = "Final Fantasy V (Japan) [En by RPGe v1.1].sfc" filename = "Final Fantasy V (Japan) [En by RPGe v1.1].sfc"
with open(filename, 'rb') as file1: with open(filename, 'rb') as file1:
ROM = file1.read() ROM = file1.read()
print(len(ROM)) print(len(ROM), filename)
filename2 = "Final Fantasy V (Japan).sfc" filename2 = "Final Fantasy V (Japan).sfc"
with open(filename2, 'rb') as file2: with open(filename2, 'rb') as file2:
ROM2 = file2.read() ROM2 = file2.read()
print(len(ROM2)) print(len(ROM2), filename2)
col_palette = [QColor( 0, 0, 0), col_palette = [QColor(0, 0, 0), QColor(0, 0, 128, 0),
QColor( 0, 0,128,0), QColor(128, 128, 128), QColor(255, 255, 255)]
QColor(128,128,128),
QColor(255,255,255)]
bg_color = QColor(0, 0, 128) bg_color = QColor(0, 0, 128)
#for i in range(4, 256):
#col_palette.append(QColor(i, i, i))
def create_tile(bytes):
'''
Creates a QPixmap of a SNES tile. DO NOT USE OUTSIDE OF QApplication CONTEXT
'''
planes = len(bytes)//8
tile = array('B', range(64))
img = QImage(8, 8, QImage.Format_Indexed8)
imgbits = img.bits()
imgbits.setsize(img.byteCount())
if planes == 0:
raise ValueError("Empty bytes passed")
if planes == 1:
img.setColorTable([0x00000080, 0xFFFFFFFF])
t_ptr = 0
for j, x in [(j,x) for j in range(8) for x in reversed(range(8))]:
tile[t_ptr] = (bytes[j] >> x & 1)
t_ptr += 1
else:
img.setColorTable([c.rgba() for c in col_palette])
t_ptr = 0
for j, x in [(j,x) for j in range(0, 16, 2) for x in reversed(range(8))]:
tile[t_ptr] = (bytes[j] >> x & 1) | ((bytes[j+1] >> x & 1) << 1)
t_ptr += 1
t_ptr = 0
if planes == 3:
for j, x in [(j,x) for j in range(16, 24, 1) for x in reversed(range(8))]:
tile[t_ptr] |= ((bytes[j] >> x & 1) << 2)
t_ptr += 1
elif planes >= 4:
for j, x in [(j,x) for j in range(16, 32, 2) for x in reversed(range(8))]:
tile[t_ptr] |= ((bytes[j] >> x & 1) << 2) | ((bytes[j+1] >> x & 1) << 3)
t_ptr += 1
if planes == 8:
t_ptr = 0
for j, x in [(j,x) for j in range(32, 48, 2) for x in reversed(range(8))]:
tile[t_ptr] |= ((bytes[j] >> x & 1) << 4) | ((bytes[j+1] >> x & 1) << 5) \
| ((bytes[j+16] >> x & 1) << 6) | ((bytes[j+17] >> x & 1) << 7)
t_ptr += 1
imgbits[:64] = tile
return QPixmap.fromImage(img)
def create_tritile(bytes):
img = QImage(16, 12, QImage.Format_Indexed8)
imgbits = img.bits()
imgbits.setsize(img.byteCount())
img.setColorTable([0xFF000080, 0xFFFFFFFF])
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)] = (bytes[row + (p*12)] >> b & 1)
imgbits[:192] = tile
return QPixmap.fromImage(img)
def create_quadtile(bytes, ltr=False):
img = QImage(16, 16, QImage.Format_ARGB32_Premultiplied)
img.fill(QColor(0,0,0,0))
painter = QtGui.QPainter(img)
painter.drawPixmap(0, 0, create_tile(bytes[0:8]))
painter.drawPixmap(8, 8, create_tile(bytes[24:32]))
if ltr:
painter.drawPixmap(8, 0, create_tile(bytes[8:16]))
painter.drawPixmap(0, 8, create_tile(bytes[16:24]))
else:
painter.drawPixmap(0, 8, create_tile(bytes[8:16]))
painter.drawPixmap(8, 0, create_tile(bytes[16:24]))
del painter
return QPixmap.fromImage(img)
glyph_sprites = [] glyph_sprites = []
glyph_sprites2 = [] glyph_sprites_jp = []
glyph_sprites_large = [] glyph_sprites_large = []
glyph_sprites_kanji = [] glyph_sprites_kanji = []
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
'', '', '0', '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[0xE3] = '[洋剣]'
Glyphs_JP[0xE7:0xF0] = ['[刂]', '[槍]', '[鉞]', '[刀]', '[棒]', '[杖]', '[弓]', '', '[鞭]']
Glyphs_JP2 = list(Glyphs_JP) # Japanese glyphs using the dakuten encoding
Glyphs_JP2[0x20:0x52] = [
'','','','', '','','','', '','','','', '','','','', # 0x20
'','','','', '','','','', '','','','', '','','','', # 0x30
'','','','', '','','','', # 0x40-0x48
'','','','', '','','','', '',''] # 0x48-0x52
Glyphs_JP_large = list(Glyphs_JP2) # Large glyphs are subtly different again
Glyphs_JP_large[0xE0:0xEB] = ['','','+','', '', '', '', '°C', '', '', '']
Glyphs_Kanji = (
'','','','', ' ',' ',' ',' ', '','',' ','', ' ',' ',' ','', # 0x000
'','',' ',' ', '',' ',' ','', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x010
' ','','',' ', '',' ',' ',' ', ' ',' ',' ',' ', '',' ',' ',' ', # 0x020
' ',' ',' ',' ', ' ',' ',' ',' ', '',' ',' ',' ', ' ',' ',' ',' ', # 0x030
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ','',' ',' ', # 0x040
' ',' ','',' ', ' ',' ','殿',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x050
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x060
' ',' ','',' ', ' ',' ',' ',' ', ' ',' ','',' ', ' ',' ',' ',' ', # 0x070
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x080
' ',' ',' ',' ', ' ','',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x090
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x0A0
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x0B0
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ','',' ',' ', # 0x0C0
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x0D0
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x0E0
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x0F0
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x100
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x110
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x120
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x130
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x140
'',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x150
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x160
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x170
' ',' ',' ',' ', ' ',' ',' ',' ', ' ','',' ',' ', ' ',' ',' ',' ', # 0x180
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', # 0x190
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ') # 0x1A0
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")
stringlist_headers = ["Address", "ID", "Name"] stringlist_headers = ["Address", "ID", "Name"]
imglist_headers = stringlist_headers + ["Img", "Name JP", "Img JP"] imglist_headers = stringlist_headers + ["Img", "Name JP", "Img JP"]
npc_layer_count = 0x200 npc_layer_count = 0x200
npc_layer_structure = [("Dialogue/trigger ID", 1, None), npc_layer_structure = [("Dialogue/trigger ID", 1, None),
("0x01", 1, None), ("0x01", 1, None),
@ -277,7 +112,6 @@ npc_layer_structure = [("Dialogue/trigger ID", 1, None),
npc_layer_headers = ["Address", "Layer"] + [x[0] for x in npc_layer_structure] npc_layer_headers = ["Address", "Layer"] + [x[0] for x in npc_layer_structure]
class FF5Reader(QMainWindow): class FF5Reader(QMainWindow):
""" """
Main GUI class Main GUI class
@ -285,7 +119,7 @@ class FF5Reader(QMainWindow):
def __init__(self): def __init__(self):
QMainWindow.__init__(self, None) QMainWindow.__init__(self, None)
generate_glyphs(ROM, glyph_sprites, 0x11F000) generate_glyphs(ROM, glyph_sprites, 0x11F000)
generate_glyphs(ROM2, glyph_sprites2, 0x11F000) generate_glyphs(ROM2, glyph_sprites_jp, 0x11F000)
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
@ -319,7 +153,7 @@ class FF5Reader(QMainWindow):
("Palette", 1, None), ("Palette", 1, None),
("0x17", 1, None), ("0x17", 1, None),
("0x18", 1, None), ("0x18", 1, None),
("Music", 1, BGM_Tracks)] ("Music", 1, const.BGM_Tracks)]
zone_headers = ["Address"] + [z[0] for z in zone_structure] zone_headers = ["Address"] + [z[0] for z in zone_structure]
zone_data = [] zone_data = []
@ -333,7 +167,7 @@ class FF5Reader(QMainWindow):
if z[2] and val < len(z[2]): if z[2] and val < len(z[2]):
zone_data[-1].append(z[2][val][2]) zone_data[-1].append(z[2][val][2])
else: else:
zone_data[-1].append("0x{0:0{1}X}".format(val, z[1]*2)) zone_data[-1].append("0x{:0{}X}".format(val, z[1]*2))
j += z[1] j += z[1]
npc_layers = [] npc_layers = []
@ -352,7 +186,7 @@ class FF5Reader(QMainWindow):
if z[2] and val < len(z[2]): if z[2] and val < len(z[2]):
npc_layers[-1].append(z[2][val]) npc_layers[-1].append(z[2][val])
else: else:
npc_layers[-1].append("0x{0:0{1}X}".format(val, z[1]*2)) npc_layers[-1].append("0x{:0{}X}".format(val, z[1]*2))
j += z[1] j += z[1]
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 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
@ -360,7 +194,7 @@ class FF5Reader(QMainWindow):
self.tabwidget = QTabWidget() self.tabwidget = QTabWidget()
self.enemy_sprites = QFrame() self.enemy_sprites = QFrame()
self.tabwidget.addTab(make_pixmap_table(glyph_sprites, 4), "Glyphs (EN)") self.tabwidget.addTab(make_pixmap_table(glyph_sprites, 4), "Glyphs (EN)")
self.tabwidget.addTab(make_pixmap_table(glyph_sprites2, 4), "Glyphs (JP)") self.tabwidget.addTab(make_pixmap_table(glyph_sprites_jp, 4), "Glyphs (JP)")
self.tabwidget.addTab(make_pixmap_table(glyph_sprites_large, 2), "Glyphs (Large)") self.tabwidget.addTab(make_pixmap_table(glyph_sprites_large, 2), "Glyphs (Large)")
self.tabwidget.addTab(make_pixmap_table(glyph_sprites_kanji, 2), "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")
@ -384,7 +218,7 @@ class FF5Reader(QMainWindow):
def generate_glyphs(rom, spritelist, offset, num=0x100): def generate_glyphs(rom, spritelist, offset, num=0x100):
for i in range(num): for i in range(num):
j = offset + (i*16) j = offset + (i*16)
spritelist.append(create_tile(rom[j:j+16])) spritelist.append(create_tile(rom[j:j+16], col_palette))
def generate_glyphs_large(rom, spritelist, offset, num=0x100): def generate_glyphs_large(rom, spritelist, offset, num=0x100):
for i in range(num): for i in range(num):
@ -400,19 +234,19 @@ def make_string_img(bytestring, jp=False):
painter = QtGui.QPainter(img) painter = QtGui.QPainter(img)
if jp: if jp:
for x, j in enumerate(bytestring): for x, j in enumerate(bytestring):
string = string + Glyphs_JP2[j] string = string + const.Glyphs_JP2[j]
if 0x20 <= j < 0x52: if 0x20 <= j < 0x52:
if j > 0x48: if j > 0x48:
painter.drawPixmap(x*8, 2, glyph_sprites2[j+0x17]) painter.drawPixmap(x*8, 2, glyph_sprites_jp[j+0x17])
painter.drawPixmap(x*8+1,-5, glyph_sprites2[0x52]) painter.drawPixmap(x*8+1,-5, glyph_sprites_jp[0x52])
else: else:
painter.drawPixmap(x*8, 2, glyph_sprites2[j+0x40]) painter.drawPixmap(x*8, 2, glyph_sprites_jp[j+0x40])
painter.drawPixmap(x*8+1,-6, glyph_sprites2[0x51]) painter.drawPixmap(x*8+1,-6, glyph_sprites_jp[0x51])
else: else:
painter.drawPixmap(x*8, 2, glyph_sprites2[j]) painter.drawPixmap(x*8, 2, glyph_sprites_jp[j])
else: else:
for x, j in enumerate(bytestring): for x, j in enumerate(bytestring):
string = string + Glyphs[j] string = string + const.Glyphs[j]
painter.drawPixmap(x*8, 1, glyph_sprites[j]) painter.drawPixmap(x*8, 1, glyph_sprites[j])
del painter del painter
return string, QPixmap.fromImage(img) return string, QPixmap.fromImage(img)
@ -436,6 +270,7 @@ def make_string_img_large(bytestring):
j = bytestring[i] j = bytestring[i]
''' '''
There's a lot of dialogue substitutions, this is going to be annoying and/or messy. There's a lot of dialogue substitutions, this is going to be annoying and/or messy.
TODO: The best way of going about this would be preprocessing the string, I think.
Is 0x00 a wait for input marker? Is 0x00 a wait for input marker?
0x03 is クリスタル 0x03 is クリスタル
0x0A is a speaker tab, perhaps linebreak as well? 0x0A is a speaker tab, perhaps linebreak as well?
@ -492,15 +327,15 @@ def make_string_img_large(bytestring):
string = string + '[0x{:02X}]'.format(j) string = string + '[0x{:02X}]'.format(j)
continue continue
elif j == 0x1E: elif j == 0x1E:
string = string + Glyphs_Kanji[bytestring[i+1]] string = string + const.Glyphs_Kanji[bytestring[i+1]]
painter.drawPixmap(x*16, (y*14)+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 + const.Glyphs_Kanji[0x100 + bytestring[i+1]]
painter.drawPixmap(x*16, (y*14)+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 + const.Glyphs_JP_large[j]
painter.drawPixmap(x*16, (y*14)+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): if (j == 0xD0) or (j == 0xE5):
@ -549,7 +384,7 @@ def make_string_img_list(start, length, num, start_jp=None, len_jp=None, start_s
except ValueError: 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)) 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 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)]) stringlist.append(["0x{:06X}".format(en), "0x{:0{}X}".format(id, id_digits), string, img, string_JP, img_JP, "0x{:06X}".format(jp_start)])
else: else:
for id in range(num): for id in range(num):
j1 = start + (id*length) j1 = start + (id*length)
@ -559,29 +394,26 @@ def make_string_img_list(start, length, num, start_jp=None, len_jp=None, start_s
string_JP, img_JP = make_string_img_large(ROM2[j2:j2+len_jp]) string_JP, img_JP = make_string_img_large(ROM2[j2:j2+len_jp])
else: else:
string_JP, img_JP = make_string_img(ROM2[j2:j2+len_jp], jp=True) string_JP, img_JP = make_string_img(ROM2[j2:j2+len_jp], jp=True)
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{}X}".format(id, id_digits), string, img, string_JP, img_JP])
return stringlist 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
""" """
table = QTableWidget() cols = len(headers)
rows = len(items) rows = len(items)
rd = hex_length(rows-1) rd = hex_length(rows-1)
cols = len(headers) table = QTableWidget(rows, cols)
table.setRowCount(rows)
if row_labels: if row_labels:
table.setVerticalHeaderLabels(['0x{0:0{1}X}'.format(v, rd) for v in range(rows)]) table.setVerticalHeaderLabels(['0x{:0{}X}'.format(v, rd) for v in range(rows)])
else: else:
table.verticalHeader().setVisible(False) table.verticalHeader().setVisible(False)
table.setColumnCount(cols)
table.setHorizontalHeaderLabels(headers) table.setHorizontalHeaderLabels(headers)
for row, col, item in [(x,y,items[x][y]) for x in range(rows) for y in range(cols)]: for row, col, item in [(x,y,items[x][y]) for x in range(rows) for y in range(cols)]:
if type(item) == type(QPixmap()): if type(item) == type(QPixmap()):
pixmap_scaled = item.scaled(item.size() * 2)
lab = QLabel() lab = QLabel()
lab.setPixmap(pixmap_scaled) lab.setPixmap(item.scaled(item.size() * 2))
table.setCellWidget(row, col, lab) table.setCellWidget(row, col, lab)
elif item is not None: elif item is not None:
q_item = QTableWidgetItem(item) q_item = QTableWidgetItem(item)
@ -604,19 +436,16 @@ def make_table(headers, items, sortable=False, row_labels=True):
return table return table
def make_pixmap_table(items, scale=4): def make_pixmap_table(items, scale=4):
table = QTableWidget()
rows = divceil(len(items), 16)
rd = hex_length(rows-1)+1
cols = 16 cols = 16
table.setRowCount(rows) rows = divceil(len(items), cols)
table.setVerticalHeaderLabels(['0x{0:0{1}X}'.format(v*16, rd) for v in range(rows)]) rd = hex_length(rows-1)+1
table.setColumnCount(cols) table = QTableWidget(rows, cols)
table.setVerticalHeaderLabels(['0x{:0{}X}'.format(v*cols, rd) for v in range(rows)])
table.setHorizontalHeaderLabels(['0x{:X}'.format(v) for v in range(cols)]) table.setHorizontalHeaderLabels(['0x{:X}'.format(v) for v in range(cols)])
for i in range(len(items)): for i in range(len(items)):
item = items[i] item = items[i]
pixmap_scaled = item.scaled(item.size() * scale)
lab = QLabel() lab = QLabel()
lab.setPixmap(pixmap_scaled) lab.setPixmap(item.scaled(item.size() * scale))
table.setCellWidget(i // cols, i % cols, lab) table.setCellWidget(i // cols, i % cols, lab)
table.resizeColumnsToContents() table.resizeColumnsToContents()
return table return table
@ -624,7 +453,6 @@ def make_pixmap_table(items, scale=4):
def main(): def main():
app = QApplication(sys.argv) app = QApplication(sys.argv)
mainwindow = FF5Reader() mainwindow = FF5Reader()
sys.exit(app.exec_()) sys.exit(app.exec_())

124
snestile.py Normal file
View File

@ -0,0 +1,124 @@
'''
No license for now
'''
import os
from array import array
pyqt_version = 0
skip_pyqt5 = "PYQT4" in os.environ
if not skip_pyqt5:
try:
from PyQt5 import QtGui
from PyQt5.QtGui import QImage, QPixmap, QColor, QPainter
pyqt_version = 5
except ImportError:
print("Missing PyQt5, trying PyQt4...")
if pyqt_version == 0:
try:
from PyQt4 import QtGui
from PyQt4.QtGui import QImage, QPixmap, QColor, QPainter
pyqt_version = 4
except ImportError:
print("Missing PyQt4 dependencies")
raise
def create_tile(bytes, palette):
'''
Creates a QPixmap of a SNES tile. DO NOT USE OUTSIDE OF QApplication CONTEXT
'''
planes = len(bytes)//8
tile = array('B', range(64))
img = QImage(8, 8, QImage.Format_Indexed8)
imgbits = img.bits()
imgbits.setsize(img.byteCount())
if planes == 0:
raise ValueError("Empty bytes passed")
if planes == 1:
img.setColorTable([0x00000080, 0xFFFFFFFF])
for i, (j, x) in enumerate([(j,x) for j in range(8) for x in reversed(range(8))]):
tile[i] = (bytes[j] >> x & 1)
else:
img.setColorTable([c.rgba() for c in palette])
for i, (j, x) in enumerate([(j,x) for j in range(0, 16, 2) for x in reversed(range(8))]):
tile[i] = (bytes[j] >> x & 1) | ((bytes[j+1] >> x & 1) << 1)
if planes == 3:
for i, (j, x) in enumerate([(j,x) for j in range(16, 24, 1) for x in reversed(range(8))]):
tile[i] |= ((bytes[j] >> x & 1) << 2)
elif planes >= 4:
for i, (j, x) in enumerate([(j,x) for j in range(16, 32, 2) for x in reversed(range(8))]):
tile[i] |= ((bytes[j] >> x & 1) << 2) | ((bytes[j+1] >> x & 1) << 3)
if planes == 8:
for i, (j, x) in enumerate([(j,x) for j in range(32, 48, 2) for x in reversed(range(8))]):
tile[i] |= ((bytes[j] >> x & 1) << 4) | ((bytes[j+1] >> x & 1) << 5) \
| ((bytes[j+16] >> x & 1) << 6) | ((bytes[j+17] >> x & 1) << 7)
imgbits[:64] = tile
return QPixmap.fromImage(img)
def create_tile_old(bytes, palette):
'''
Creates a QPixmap of a SNES tile. DO NOT USE OUTSIDE OF QApplication CONTEXT
'''
planes = len(bytes)//8
tile = array('B', range(64))
img = QImage(8, 8, QImage.Format_Indexed8)
imgbits = img.bits()
imgbits.setsize(img.byteCount())
if planes == 0:
raise ValueError("Empty bytes passed")
if planes == 1:
img.setColorTable([0x00000080, 0xFFFFFFFF])
t_ptr = 0
for j, x in [(j,x) for j in range(8) for x in reversed(range(8))]:
tile[t_ptr] = (bytes[j] >> x & 1)
t_ptr += 1
else:
img.setColorTable([c.rgba() for c in palette])
t_ptr = 0
for j, x in [(j,x) for j in range(0, 16, 2) for x in reversed(range(8))]:
tile[t_ptr] = (bytes[j] >> x & 1) | ((bytes[j+1] >> x & 1) << 1)
t_ptr += 1
t_ptr = 0
if planes == 3:
for j, x in [(j,x) for j in range(16, 24, 1) for x in reversed(range(8))]:
tile[t_ptr] |= ((bytes[j] >> x & 1) << 2)
t_ptr += 1
elif planes >= 4:
for j, x in [(j,x) for j in range(16, 32, 2) for x in reversed(range(8))]:
tile[t_ptr] |= ((bytes[j] >> x & 1) << 2) | ((bytes[j+1] >> x & 1) << 3)
t_ptr += 1
if planes == 8:
t_ptr = 0
for j, x in [(j,x) for j in range(32, 48, 2) for x in reversed(range(8))]:
tile[t_ptr] |= ((bytes[j] >> x & 1) << 4) | ((bytes[j+1] >> x & 1) << 5) \
| ((bytes[j+16] >> x & 1) << 6) | ((bytes[j+17] >> x & 1) << 7)
t_ptr += 1
imgbits[:64] = tile
return QPixmap.fromImage(img)
def create_tritile(bytes):
img = QImage(16, 12, QImage.Format_Indexed8)
imgbits = img.bits()
imgbits.setsize(img.byteCount())
img.setColorTable([0xFF000080, 0xFFFFFFFF])
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)] = (bytes[row + (p*12)] >> b & 1)
imgbits[:192] = tile
return QPixmap.fromImage(img)
def create_quadtile(bytes, ltr=False):
img = QImage(16, 16, QImage.Format_ARGB32_Premultiplied)
img.fill(QColor(0,0,0,0))
painter = QtGui.QPainter(img)
painter.drawPixmap(0, 0, create_tile(bytes[0:8]))
painter.drawPixmap(8, 8, create_tile(bytes[24:32]))
if ltr:
painter.drawPixmap(8, 0, create_tile(bytes[8:16]))
painter.drawPixmap(0, 8, create_tile(bytes[16:24]))
else:
painter.drawPixmap(0, 8, create_tile(bytes[8:16]))
painter.drawPixmap(8, 0, create_tile(bytes[16:24]))
del painter
return QPixmap.fromImage(img)