Initial zone/fieldmap drawing
This commit is contained in:
parent
7fe8fb7351
commit
5d701a02fc
119
ff5reader.py
119
ff5reader.py
|
@ -152,30 +152,32 @@ class FF5Reader(QMainWindow):
|
||||||
(data & 0x03F000) >> 12,
|
(data & 0x03F000) >> 12,
|
||||||
(data & 0xFC0000) >> 18]
|
(data & 0xFC0000) >> 18]
|
||||||
return ' '.join([hex(i,2) for i in tilesets])
|
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),
|
zone_structure = [('NPC Layer', 2, None), # 00 01
|
||||||
('Name', 1, [z[2] for z in zone_names]),
|
('Name', 1, [z[2] for z in zone_names]), # 02
|
||||||
('ShadowFlags', 1, None),
|
('ShadowFlags', 1, None), # 03
|
||||||
(hex(4, 2), 1, None),
|
('Graphic maths', 1, None), # 04
|
||||||
(hex(5, 2), 1, None),
|
('Tile properties', 1, None), # 05
|
||||||
('Flags '+hex(6,2),1, None),
|
('Flags '+hex(6), 1, None), # 06
|
||||||
(hex(7, 2), 1, None),
|
(hex(7), 1, None), # 07
|
||||||
('Tilesets', 3, split_tilesets),
|
('Blockset', 1, None), # 08
|
||||||
(hex(11, 2), 1, None),
|
('Tilesets', 3, split_tilesets), # 09 0A 0B
|
||||||
('Collision Layer',1, None),
|
('Blockmaps', 4, split_blockmaps), # 0C 0D 0E 0F
|
||||||
(hex(13, 2), 1, None),
|
(hex(16), 1, None), # 10
|
||||||
(hex(14, 2), 1, None),
|
(hex(17), 1, None), # 11
|
||||||
(hex(15, 2), 1, None),
|
(hex(18), 1, None), # 12
|
||||||
(hex(16, 2), 1, None),
|
(hex(19), 1, None), # 13
|
||||||
(hex(17, 2), 1, None),
|
(hex(20), 1, None), # 14
|
||||||
(hex(18, 2), 1, None),
|
(hex(21), 1, None), # 15
|
||||||
(hex(19, 2), 1, None),
|
('Palette', 1, None), # 16
|
||||||
(hex(20, 2), 1, None),
|
(hex(23), 1, None), # 17
|
||||||
(hex(21, 2), 1, None),
|
(hex(24), 1, None), # 18
|
||||||
('Palette', 1, None),
|
('Music', 1, const.BGM_Tracks)] # 19
|
||||||
(hex(23, 2), 1, None),
|
|
||||||
(hex(24, 2), 1, None),
|
|
||||||
('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 = [parse_struct(ROM_en, 0x0E9C00 + (i*0x1A), zone_structure) for i in range(const.zone_count)]
|
zone_data = [parse_struct(ROM_en, 0x0E9C00 + (i*0x1A), zone_structure) for i in range(const.zone_count)]
|
||||||
|
|
||||||
|
@ -223,20 +225,21 @@ class FF5Reader(QMainWindow):
|
||||||
perfcount()
|
perfcount()
|
||||||
print('Generating map tiles')
|
print('Generating map tiles')
|
||||||
worldmap_palettes = [generate_palette(ROM_jp, 0x0FFCC0+(i*0x100), length=0x160, transparent=True) for i in range(3)]
|
worldmap_palettes = [generate_palette(ROM_jp, 0x0FFCC0+(i*0x100), length=0x160, transparent=True) for i in range(3)]
|
||||||
world_tiles = [make_worldmap_tiles(ROM_jp, 0x0FF0C0+(i*0x300), 0x1B8000+(i*0x2000), 0x0FF9C0+(i*0x100)) for i in range(3)]
|
world_tiles = [make_worldmap_blocks(ROM_jp, 0x0FF0C0+(i*0x300), 0x1B8000+(i*0x2000), 0x0FF9C0+(i*0x100)) for i in range(3)]
|
||||||
worldpixmaps = [make_worldmap_pixmap(ROM_jp, i, 0x0FFCC0+(t*0x100), world_tiles[t]) for i, t in enumerate([0, 1, 0, 2, 2])]
|
worldpixmaps = [make_worldmap_pixmap(ROM_jp, i, 0x0FFCC0+(t*0x100), world_tiles[t]) for i, t in enumerate([0, 1, 0, 2, 2])]
|
||||||
world_tiles_pixmaps = []
|
world_blocks_pixmaps = []
|
||||||
for i, tiles in enumerate(world_tiles):
|
for i, tiles in enumerate(world_tiles):
|
||||||
a = []
|
a = []
|
||||||
for t in tiles:
|
for t in tiles:
|
||||||
t.setColorTable(worldmap_palettes[i])
|
t.setColorTable(worldmap_palettes[i])
|
||||||
a.append(QPixmap.fromImage(t))
|
a.append(QPixmap.fromImage(t))
|
||||||
world_tiles_pixmaps.append(a)
|
world_blocks_pixmaps.append(a)
|
||||||
perfcount()
|
perfcount()
|
||||||
worldmap_subtiles = make_worldmap_subtiles_pixmap(ROM_jp, 0x1B8000, 0x0FF9C0, 0x0FFCC0)
|
worldmap_tiles = make_worldmap_tiles_pixmap(ROM_jp, 0x1B8000, 0x0FF9C0, 0x0FFCC0)
|
||||||
worldmap_subtiles += make_worldmap_subtiles_pixmap(ROM_jp, 0x1BA000, 0x0FFAC0, 0x0FFDC0)
|
worldmap_tiles += make_worldmap_tiles_pixmap(ROM_jp, 0x1BA000, 0x0FFAC0, 0x0FFDC0)
|
||||||
worldmap_subtiles += make_worldmap_subtiles_pixmap(ROM_jp, 0x1BC000, 0x0FFBC0, 0x0FFEC0, length=128)
|
worldmap_tiles += make_worldmap_tiles_pixmap(ROM_jp, 0x1BC000, 0x0FFBC0, 0x0FFEC0, length=128)
|
||||||
perfcount()
|
perfcount()
|
||||||
|
|
||||||
field_tiles = make_all_field_tiles(ROM_jp)
|
field_tiles = make_all_field_tiles(ROM_jp)
|
||||||
field_minitiles = make_all_field_minitiles(ROM_jp)
|
field_minitiles = make_all_field_minitiles(ROM_jp)
|
||||||
perfcount()
|
perfcount()
|
||||||
|
@ -245,6 +248,19 @@ class FF5Reader(QMainWindow):
|
||||||
perfcount()
|
perfcount()
|
||||||
fieldmap_tiles = [make_field_map_tile_pixmap(ROM_jp, i, st_field_tiles, st_field_minitiles) for i in range(const.zone_count)]
|
fieldmap_tiles = [make_field_map_tile_pixmap(ROM_jp, i, st_field_tiles, st_field_minitiles) for i in range(const.zone_count)]
|
||||||
perfcount()
|
perfcount()
|
||||||
|
print('Generating field map blocks')
|
||||||
|
zones = [parse_zone(ROM_jp, i) for i in range(const.zone_count)]
|
||||||
|
field_blocksets = [get_field_map_block_layouts(ROM_jp, i) for i in range(28)]
|
||||||
|
perfcount()
|
||||||
|
blockmaps = get_blockmaps(ROM_jp)
|
||||||
|
field_blocks = []
|
||||||
|
zone_pxs = []
|
||||||
|
for z in zones:
|
||||||
|
blocks = make_field_map_blocks_px(ROM_jp, z.blockset, field_tiles, field_minitiles, field_blocksets)
|
||||||
|
field_blocks.append(stitch_tileset_px(blocks))
|
||||||
|
zone_pxs += make_zone_pxs(blocks, [blockmaps[b] for b in z.blockmaps if b!=-1])
|
||||||
|
perfcount()
|
||||||
|
|
||||||
print('Generating Battle backgrounds')
|
print('Generating Battle backgrounds')
|
||||||
battle_bgs = make_battle_backgrounds(ROM_jp)
|
battle_bgs = make_battle_backgrounds(ROM_jp)
|
||||||
perfcount()
|
perfcount()
|
||||||
|
@ -275,31 +291,36 @@ class FF5Reader(QMainWindow):
|
||||||
strings_tab = QTabWidget()
|
strings_tab = QTabWidget()
|
||||||
structs_tab = QTabWidget()
|
structs_tab = QTabWidget()
|
||||||
sprites_tab = QTabWidget()
|
sprites_tab = QTabWidget()
|
||||||
|
backgrounds_tab = QTabWidget()
|
||||||
self.ff5widget.addTab(strings_tab, 'Strings')
|
self.ff5widget.addTab(strings_tab, 'Strings')
|
||||||
self.ff5widget.addTab(structs_tab, 'Structs')
|
self.ff5widget.addTab(structs_tab, 'Structs')
|
||||||
self.ff5widget.addTab(sprites_tab, 'Images')
|
self.ff5widget.addTab(sprites_tab, 'Images')
|
||||||
|
self.ff5widget.addTab(backgrounds_tab, 'Backgrounds')
|
||||||
|
|
||||||
sprites_tab.addTab(make_pixmap_table(self.glyph_sprites['glyphs_en_s'], scale=4), 'Glyphs (EN)')
|
sprites_tab.addTab(make_px_table(self.glyph_sprites['glyphs_en_s'], scale=4), 'Glyphs (EN)')
|
||||||
sprites_tab.addTab(make_pixmap_table(self.glyph_sprites['glyphs_en_l'], scale=2), 'Glyphs (Dialogue EN)')
|
sprites_tab.addTab(make_px_table(self.glyph_sprites['glyphs_en_l'], scale=2), 'Glyphs (Dialogue EN)')
|
||||||
sprites_tab.addTab(make_pixmap_table(self.glyph_sprites['glyphs_jp_s'], scale=4), 'Glyphs (JP)')
|
sprites_tab.addTab(make_px_table(self.glyph_sprites['glyphs_jp_s'], scale=4), 'Glyphs (JP)')
|
||||||
sprites_tab.addTab(make_pixmap_table(self.glyph_sprites['glyphs_jp_l'], scale=2), 'Glyphs (Large JP)')
|
sprites_tab.addTab(make_px_table(self.glyph_sprites['glyphs_jp_l'], scale=2), 'Glyphs (Large JP)')
|
||||||
sprites_tab.addTab(make_pixmap_table(self.glyph_sprites['glyphs_kanji'], scale=2),'Glyphs (Kanji)')
|
sprites_tab.addTab(make_px_table(self.glyph_sprites['glyphs_kanji'], scale=2),'Glyphs (Kanji)')
|
||||||
sprites_tab.addTab(make_pixmap_table(worldmap_subtiles, cols=16, scale=4), 'Worldmap Subtiles')
|
sprites_tab.addTab(make_px_table(self.battle_strips, cols=22, scale=2), 'Character Battle Sprites')
|
||||||
sprites_tab.addTab(make_pixmap_table(world_tiles_pixmaps[0], cols=16, scale=4), 'World 1 Tiles')
|
sprites_tab.addTab(make_px_table(status_strips, cols=22, scale=2), 'Status Sprites')
|
||||||
sprites_tab.addTab(make_pixmap_table(world_tiles_pixmaps[1], cols=16, scale=4), 'World 2 Tiles')
|
sprites_tab.addTab(make_px_table(enemy_sprites_named, cols=32, scale=1), 'Enemy Sprites')
|
||||||
sprites_tab.addTab(make_pixmap_table(world_tiles_pixmaps[2], cols=16, scale=4), 'Underwater Tiles')
|
|
||||||
sprites_tab.addTab(make_pixmap_table(worldpixmaps, cols=1, scale=1, large=True), 'Worldmaps')
|
|
||||||
sprites_tab.addTab(make_pixmap_table(fieldmap_tiles, cols=8, scale=2), 'Fieldmap Tiles')
|
|
||||||
sprites_tab.addTab(make_pixmap_table(battle_bgs, cols=8, scale=1), 'Battle BGs')
|
|
||||||
sprites_tab.addTab(make_pixmap_table(self.battle_strips, cols=22, scale=2), 'Character Battle Sprites')
|
|
||||||
sprites_tab.addTab(make_pixmap_table(status_strips, cols=22, scale=2), 'Status Sprites')
|
|
||||||
sprites_tab.addTab(make_pixmap_table(enemy_sprites_named, cols=32, scale=1), 'Enemy Sprites')
|
|
||||||
|
|
||||||
self.ff4widget.addTab(make_pixmap_table(self.battle_strips_ff4, cols=16, scale=2), 'Character Battle Sprites')
|
backgrounds_tab.addTab(make_px_table(worldmap_tiles, cols=16, scale=4), 'Worldmap Tiles')
|
||||||
self.ff4widget.addTab(make_pixmap_table(self.portraits_ff4, cols=14, scale=2), 'Character Portraits')
|
backgrounds_tab.addTab(make_px_table(world_blocks_pixmaps[0], cols=16, scale=4), 'World 1 Blocks')
|
||||||
self.ff4widget.addTab(make_pixmap_table(self.field_strips_ff4, cols=17, scale=2), 'Character Field Sprites')
|
backgrounds_tab.addTab(make_px_table(world_blocks_pixmaps[1], cols=16, scale=4), 'World 2 Blocks')
|
||||||
self.ff6widget.addTab(make_pixmap_table(self.battle_strips_ff6, cols=32, scale=2), 'Character Sprites')
|
backgrounds_tab.addTab(make_px_table(world_blocks_pixmaps[2], cols=16, scale=4), 'Underwater Blocks')
|
||||||
self.ff6widget.addTab(make_pixmap_table(self.portraits_ff6, cols=19, scale=2), 'Character Portraits')
|
backgrounds_tab.addTab(make_px_table(worldpixmaps, cols=1, scale=1, large=True), 'Worldmaps')
|
||||||
|
backgrounds_tab.addTab(make_px_table(fieldmap_tiles, cols=8, scale=1), 'Fieldmap Tiles')
|
||||||
|
backgrounds_tab.addTab(make_px_table(field_blocks, cols=16, scale=1), 'Field Blocks')
|
||||||
|
backgrounds_tab.addTab(make_px_table(zone_pxs, cols=4, scale=1, large=1), 'Zone')
|
||||||
|
backgrounds_tab.addTab(make_px_table(battle_bgs, cols=8, scale=1), 'Battle BGs')
|
||||||
|
|
||||||
|
self.ff4widget.addTab(make_px_table(self.battle_strips_ff4, cols=16, scale=2), 'Character Battle Sprites')
|
||||||
|
self.ff4widget.addTab(make_px_table(self.portraits_ff4, cols=14, scale=2), 'Character Portraits')
|
||||||
|
self.ff4widget.addTab(make_px_table(self.field_strips_ff4, cols=17, scale=2), 'Character Field Sprites')
|
||||||
|
self.ff6widget.addTab(make_px_table(self.battle_strips_ff6, cols=32, scale=2), 'Character Sprites')
|
||||||
|
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(zone_headers, zone_data, True), 'Zones')
|
||||||
|
|
|
@ -29,7 +29,7 @@ def hex_length(i):
|
||||||
'''
|
'''
|
||||||
return divceil(i.bit_length(), 4)
|
return divceil(i.bit_length(), 4)
|
||||||
|
|
||||||
def hex(num, digits):
|
def hex(num, digits=2):
|
||||||
'''
|
'''
|
||||||
Consolidate hex formatting for consistency
|
Consolidate hex formatting for consistency
|
||||||
'''
|
'''
|
||||||
|
@ -60,12 +60,16 @@ def parse_struct(rom, offset, structure):
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
def decompress_lzss(rom, start, header=False):
|
def decompress_lzss(rom, start, header=False, length=None):
|
||||||
'''
|
'''
|
||||||
Algorithm from http://slickproductions.org/slickwiki/index.php/Noisecross:Final_Fantasy_V_Compression
|
Algorithm from http://slickproductions.org/slickwiki/index.php/Noisecross:Final_Fantasy_V_Compression
|
||||||
'''
|
'''
|
||||||
|
ptr = start
|
||||||
|
if length:
|
||||||
|
uncompressed_length = length
|
||||||
|
else:
|
||||||
uncompressed_length = indirect(rom, start)
|
uncompressed_length = indirect(rom, start)
|
||||||
ptr = start+2
|
ptr += 2
|
||||||
output = []
|
output = []
|
||||||
buffer = [0 for i in range(0x800)]
|
buffer = [0 for i in range(0x800)]
|
||||||
buffer_p = 0x07DE
|
buffer_p = 0x07DE
|
||||||
|
|
|
@ -156,7 +156,7 @@ def make_table(headers, items, sortable=False, row_labels=True, scale=2):
|
||||||
return table
|
return table
|
||||||
|
|
||||||
|
|
||||||
def make_pixmap_table(items, cols=16, scale=4, large=False):
|
def make_px_table(items, cols=16, scale=4, large=False):
|
||||||
rows = divceil(len(items), cols)
|
rows = divceil(len(items), cols)
|
||||||
rd = hex_length(rows-1)+1
|
rd = hex_length(rows-1)+1
|
||||||
cd = hex_length(cols-1)
|
cd = hex_length(cols-1)
|
||||||
|
|
143
includes/snes.py
143
includes/snes.py
|
@ -20,6 +20,29 @@ Functions common to SNES FFs
|
||||||
|
|
||||||
from includes.helpers import *
|
from includes.helpers import *
|
||||||
from includes.snestile import *
|
from includes.snestile import *
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
zone = namedtuple('zone', 'npcs name shadowflags blockset tilesets blockmaps palette music')
|
||||||
|
|
||||||
|
def parse_zone(rom, id, start_address=0x0E9C00):
|
||||||
|
ptr = start_address+(id*0x1A)
|
||||||
|
npcs = indirect(rom, ptr)
|
||||||
|
name = rom[ptr+2]
|
||||||
|
shadowflags = rom[ptr+3]
|
||||||
|
blockset = rom[ptr+8]
|
||||||
|
tilesets_b = indirect(rom, ptr+9, length=3)
|
||||||
|
tilesets = [(tilesets_b & 0x00003F),
|
||||||
|
(tilesets_b & 0x000FC0) >> 6,
|
||||||
|
(tilesets_b & 0x03F000) >> 12,
|
||||||
|
(tilesets_b & 0xFC0000) >> 18]
|
||||||
|
pal_address = 0x03BB00 + (indirect(rom, ptr+0x16)<<8)
|
||||||
|
blockmaps_b = indirect(rom, ptr+0xC, length=4)
|
||||||
|
blockmaps = [(blockmaps_b & 0x000003FF) - 1,
|
||||||
|
((blockmaps_b & 0x000FFC00) >> 10) - 1,
|
||||||
|
((blockmaps_b & 0x3FF00000) >> 20) - 1]
|
||||||
|
palettes = [generate_palette(rom, pal_address+i*32, transparent=True) for i in range(8)]
|
||||||
|
music = rom[ptr+0x19]
|
||||||
|
return zone(npcs, name, shadowflags, blockset, tilesets, blockmaps, palettes, music)
|
||||||
|
|
||||||
|
|
||||||
def make_battle_strip(rom, palette_address, tile_address, num_tiles, bpp=4):
|
def make_battle_strip(rom, palette_address, tile_address, num_tiles, bpp=4):
|
||||||
|
@ -96,27 +119,27 @@ def make_character_status_sprites(rom):
|
||||||
return pixmaps
|
return pixmaps
|
||||||
|
|
||||||
|
|
||||||
def make_worldmap_subtiles(rom, tiles_address, lut_address, length=0x100):
|
def make_worldmap_tiles(rom, tiles_address, lut_address, length=0x100):
|
||||||
subtiles = []
|
subtiles = []
|
||||||
for i in range(length):
|
for i in range(length):
|
||||||
pal_index = rom[lut_address+i]//16
|
pal_index = rom[lut_address+i]//16
|
||||||
subtiles.append(create_tile_mode7_compressed_indexed(rom[tiles_address+i*32:tiles_address+i*32+32], pal_index))
|
subtiles.append(create_tile_mode7_compressed_indexed(rom[tiles_address+i*32:tiles_address+i*32+32], pal_index))
|
||||||
return subtiles
|
return subtiles
|
||||||
|
|
||||||
def stitch_worldmap_tiles(rom, subtiles, offset=0x0FF0C0):
|
def stitch_worldmap_tiles(rom, tiles, offset=0x0FF0C0):
|
||||||
tiles = []
|
blocks = []
|
||||||
for i in range(0xC0):
|
for i in range(0xC0):
|
||||||
canvas = Canvas_Indexed(2, 2)
|
canvas = Canvas_Indexed(2, 2)
|
||||||
for j in range(4):
|
for j in range(4):
|
||||||
k = indirect(rom, offset+(j*0xC0)+i, length=1)
|
k = indirect(rom, offset+(j*0xC0)+i, length=1)
|
||||||
canvas.draw_tile(j%2, j//2, subtiles[k])
|
canvas.draw_tile(j%2, j//2, tiles[k])
|
||||||
tiles.append(canvas.image)
|
blocks.append(canvas.image)
|
||||||
return tiles
|
return blocks
|
||||||
|
|
||||||
def make_worldmap_tiles(rom, tiles_address, subtiles_address, lut_address, length=0x100):
|
def make_worldmap_blocks(rom, blocks_address, tiles_address, lut_address, length=0x100):
|
||||||
return stitch_worldmap_tiles(rom, make_worldmap_subtiles(rom, subtiles_address, lut_address, length=length), tiles_address)
|
return stitch_worldmap_tiles(rom, make_worldmap_tiles(rom, tiles_address, lut_address, length=length), blocks_address)
|
||||||
|
|
||||||
def make_worldmap_subtiles_pixmap(rom, tiles_address, lut_address, palette_address, length=0x100):
|
def make_worldmap_tiles_pixmap(rom, tiles_address, lut_address, palette_address, length=0x100):
|
||||||
tiles = []
|
tiles = []
|
||||||
palettes = [generate_palette(rom, palette_address+i*32, transparent=True) for i in range(16)]
|
palettes = [generate_palette(rom, palette_address+i*32, transparent=True) for i in range(16)]
|
||||||
for i in range(length):
|
for i in range(length):
|
||||||
|
@ -182,6 +205,12 @@ def stitch_tileset(tiles):
|
||||||
canvas.draw_tile(i%16, i//16, tile)
|
canvas.draw_tile(i%16, i//16, tile)
|
||||||
return canvas
|
return canvas
|
||||||
|
|
||||||
|
def stitch_tileset_px(tiles_px):
|
||||||
|
canvas = Canvas(16, len(tiles_px)//16)
|
||||||
|
for i, tile in enumerate(tiles_px):
|
||||||
|
canvas.draw_pixmap(i%16, i//16, tile)
|
||||||
|
return canvas.pixmap()
|
||||||
|
|
||||||
def get_field_map_tiles(rom, id):
|
def get_field_map_tiles(rom, id):
|
||||||
'''
|
'''
|
||||||
This is a bit of a mess of pointer chains for now, so generalising it will have to wait.
|
This is a bit of a mess of pointer chains for now, so generalising it will have to wait.
|
||||||
|
@ -214,6 +243,46 @@ def make_field_map_tile_pixmap(rom, id, st_tiles, st_minitiles):
|
||||||
canvas.draw_pixmap(0, 48, st_minitiles[minitile].pixmap(p))
|
canvas.draw_pixmap(0, 48, st_minitiles[minitile].pixmap(p))
|
||||||
return canvas.pixmap()
|
return canvas.pixmap()
|
||||||
|
|
||||||
|
def get_field_map_block_layouts(rom, id, start_address=0x0F0000):
|
||||||
|
ptr = indirect(rom, start_address+(id*2)) + start_address
|
||||||
|
data = decompress_lzss(rom, ptr)
|
||||||
|
output = []
|
||||||
|
for i in range(0, 0x200, 2):
|
||||||
|
output.append([data[j+i+k] for j in range(0, 0x800, 0x200) for k in range(2)])
|
||||||
|
#for i in range(0, 0x800, 8):
|
||||||
|
#output.append([data[i+k] for k in range(8)])
|
||||||
|
return output
|
||||||
|
|
||||||
|
def make_field_map_blocks_px(rom, id, tilesets, minitilesets, blockmaps):
|
||||||
|
*i_tiles, i_minitiles, palettes = get_field_map_tiles(rom, id)
|
||||||
|
tiles = tilesets[i_tiles[0]] + tilesets[i_tiles[1]] + tilesets[i_tiles[2]]
|
||||||
|
tiles += minitilesets[i_minitiles]
|
||||||
|
i_blockmap = rom[0x0E9C08 + (id * 0x1A)]
|
||||||
|
blockmap = blockmaps[i_blockmap]
|
||||||
|
|
||||||
|
blocks = [make_tilemap_canvas(tm, tiles, cols=2, rows=2, pal_adjust=0, tile_modulo=0x1000) for tm in blockmap]
|
||||||
|
return [b.pixmap(palettes) for b in blocks]
|
||||||
|
|
||||||
|
def get_blockmaps(rom, start_address=0x0B0000, num=0x148):
|
||||||
|
bank = 0x0B0000
|
||||||
|
ptrs = [indirect(rom, start_address)+bank]
|
||||||
|
for i in range(1, num):
|
||||||
|
ptr = indirect(rom, start_address+(i*2))
|
||||||
|
if (ptr+bank) < ptrs[-1]:
|
||||||
|
bank += 0x010000
|
||||||
|
ptrs.append(ptr+bank)
|
||||||
|
blockmaps = [decompress_lzss(rom, ptr) for ptr in ptrs]
|
||||||
|
return blockmaps
|
||||||
|
|
||||||
|
def make_zone_pxs(blocks, blockmaps):
|
||||||
|
output = []
|
||||||
|
for bm in blockmaps:
|
||||||
|
canvas = Canvas(64, 64, tilesize=16)
|
||||||
|
for i, b in enumerate(bm):
|
||||||
|
canvas.draw_pixmap(i%64, i//64, blocks[b])
|
||||||
|
output.append(canvas.pixmap())
|
||||||
|
return output
|
||||||
|
|
||||||
def decompress_battle_tilemap(rom, address):
|
def decompress_battle_tilemap(rom, address):
|
||||||
'''
|
'''
|
||||||
Decompresses the tilemap for a battle background.
|
Decompresses the tilemap for a battle background.
|
||||||
|
@ -248,14 +317,13 @@ def decompress_battle_tilemap(rom, address):
|
||||||
output[1::2] = [i&0xDF for i in o2[:length//2]]
|
output[1::2] = [i&0xDF for i in o2[:length//2]]
|
||||||
return bytes(output)
|
return bytes(output)
|
||||||
|
|
||||||
def apply_battle_tilemap_flips(rom, id, battle_terrain):
|
def apply_battle_tilemap_flips(rom, id, tilemap):
|
||||||
if id==0xFF:
|
if id==0xFF:
|
||||||
return battle_terrain
|
return tilemap
|
||||||
ptr = indirect(rom, 0x14C736+(id*2))+0x140000
|
ptr = indirect(rom, 0x14C736+(id*2))+0x140000
|
||||||
length = len(battle_terrain)//2
|
length = len(tilemap)//2
|
||||||
output = list(battle_terrain)
|
output = list(tilemap)
|
||||||
buffer = []
|
buffer = []
|
||||||
|
|
||||||
while len(buffer) < length:
|
while len(buffer) < length:
|
||||||
a = rom[ptr]
|
a = rom[ptr]
|
||||||
ptr += 1
|
ptr += 1
|
||||||
|
@ -267,49 +335,54 @@ def apply_battle_tilemap_flips(rom, id, battle_terrain):
|
||||||
for b in reversed(range(0, 8, 1)):
|
for b in reversed(range(0, 8, 1)):
|
||||||
buffer.append((a>>b)&0x01)
|
buffer.append((a>>b)&0x01)
|
||||||
|
|
||||||
for i in range(len(battle_terrain)//2):
|
for i in range(len(tilemap)//2):
|
||||||
output[i*2+1] |= (buffer[i] << 6)
|
output[i*2+1] |= (buffer[i] << 6)
|
||||||
return bytes(output)
|
return bytes(output)
|
||||||
|
|
||||||
def make_tilemap_canvas(tilemap, tiles, tile_adjust=0, pal_adjust=-1):
|
def parse_tileset_word(data):
|
||||||
|
a, b = data[:2]
|
||||||
|
tile_index = a|((b & 0x03) << 8)
|
||||||
|
palette = (b & 0x1C) >> 2
|
||||||
|
priority = (b & 0x20) >> 5
|
||||||
|
h_flip = (b & 0x40) >> 6
|
||||||
|
v_flip = (b & 0x80) >> 7
|
||||||
|
return tile_index, palette, h_flip, v_flip, priority
|
||||||
|
|
||||||
|
def make_tilemap_canvas(tilemap, tiles, cols=64, rows=64, tile_adjust=0, pal_adjust=-1, tile_modulo=0x80):
|
||||||
'''
|
'''
|
||||||
Battle bg is 64x64 map size, 8x8 tile size
|
Battle bg is 64x64 map size, 8x8 tile size
|
||||||
4bpp tiles
|
4bpp tiles
|
||||||
'''
|
'''
|
||||||
canvas = Canvas_Indexed(64, 64)
|
canvas = Canvas_Indexed(cols, rows)
|
||||||
for i in range(len(tilemap)//2):
|
for i in range(len(tilemap)//2):
|
||||||
a, b = tilemap[i*2:(i+1)*2]
|
tile_index, p, h_flip, v_flip, priority = parse_tileset_word(tilemap[i*2:(i+1)*2])
|
||||||
tile_index = a|((b & 0x02) << 8)
|
if cols > 32:
|
||||||
p = (b & 0x1C) >> 2
|
|
||||||
priority = (b & 0x20) >> 5
|
|
||||||
h_flip = (b & 0x40) >> 6
|
|
||||||
v_flip = (b & 0x80) >> 7
|
|
||||||
|
|
||||||
x = (i % 32) + 32*((i//1024) % 2)
|
x = (i % 32) + 32*((i//1024) % 2)
|
||||||
y = (i //32) - 32*((i//1024) % 2)
|
y = (i //32) - 32*((i//1024) % 2)
|
||||||
|
else:
|
||||||
|
x = i % cols
|
||||||
|
y = i //cols
|
||||||
try:
|
try:
|
||||||
tile = tiles[(tile_index+tile_adjust)%0x80]
|
tile = tiles[(tile_index+tile_adjust)%tile_modulo]
|
||||||
canvas.draw_tile(x, y, tile, h_flip, v_flip, p+pal_adjust)
|
canvas.draw_tile(x, y, tile, h_flip, v_flip, p+pal_adjust)
|
||||||
except BaseException as e:
|
except BaseException as e:
|
||||||
print(e, p, hex(tile_index,2), hex(tile_adjust,2), hex(tile_index+tile_adjust,2))
|
print(e, p, hex(tile_index,2), hex(tile_adjust,2), hex(tile_index+tile_adjust,2))
|
||||||
return canvas
|
return canvas
|
||||||
|
|
||||||
def make_tilemap_pixmap(tilemap, tiles, palettes, tile_adjust=0, pal_adjust=-1):
|
def make_tilemap_pixmap(tilemap, tiles, palettes, cols=64, rows=64, tile_adjust=0, pal_adjust=-1):
|
||||||
'''
|
'''
|
||||||
Battle bg is 64x64 map size, 8x8 tile size
|
Battle bg is 64x64 map size, 8x8 tile size
|
||||||
4bpp tiles
|
4bpp tiles
|
||||||
'''
|
'''
|
||||||
canvas = Canvas(64, 64)
|
canvas = Canvas(cols, rows)
|
||||||
for i in range(len(tilemap)//2):
|
for i in range(len(tilemap)//2):
|
||||||
a, b = tilemap[i*2:(i+1)*2]
|
tile_index, p, h_flip, v_flip, priority = parse_tileset_word(tilemap[i*2:(i+1)*2])
|
||||||
tile_index = a|((b & 0x02) << 8)
|
if cols > 32:
|
||||||
p = (b & 0x1C) >> 2
|
|
||||||
priority = (b & 0x20) >> 5
|
|
||||||
h_flip = (b & 0x40) >> 6
|
|
||||||
v_flip = (b & 0x80) >> 7
|
|
||||||
|
|
||||||
x = (i % 32) + 32*((i//1024) % 2)
|
x = (i % 32) + 32*((i//1024) % 2)
|
||||||
y = (i //32) - 32*((i//1024) % 2)
|
y = (i //32) - 32*((i//1024) % 2)
|
||||||
|
else:
|
||||||
|
x = i % cols
|
||||||
|
y = i //cols
|
||||||
try:
|
try:
|
||||||
palette = palettes[p+pal_adjust]
|
palette = palettes[p+pal_adjust]
|
||||||
tile = tiles[(tile_index+tile_adjust)%0x80]
|
tile = tiles[(tile_index+tile_adjust)%0x80]
|
||||||
|
@ -366,7 +439,7 @@ def make_battle_backgrounds(rom):
|
||||||
a.append(b)
|
a.append(b)
|
||||||
a = [(i, j) for i, j in zip(a[0::2], a[1::2])]
|
a = [(i, j) for i, j in zip(a[0::2], a[1::2])]
|
||||||
animations.append(a)
|
animations.append(a)
|
||||||
animation_time = 15 # Frames before changing
|
animation_time = 8 # Frames before changing
|
||||||
|
|
||||||
pal_cycle_ptr_start = 0x14C6CD
|
pal_cycle_ptr_start = 0x14C6CD
|
||||||
pal_cycle_ptrs = [indirect(rom, pal_cycle_ptr_start+(i*2))+0x140000 for i in range(3)]
|
pal_cycle_ptrs = [indirect(rom, pal_cycle_ptr_start+(i*2))+0x140000 for i in range(3)]
|
||||||
|
|
|
@ -213,12 +213,13 @@ def generate_palette(rom, offset, length=32, transparent=False):
|
||||||
|
|
||||||
|
|
||||||
class Canvas:
|
class Canvas:
|
||||||
def __init__(self, cols, rows, color=bg_trans):
|
def __init__(self, cols, rows, color=bg_trans, tilesize=8):
|
||||||
self.image = QImage(8*cols, 8*rows, QImage.Format_ARGB32_Premultiplied)
|
self.image = QImage(tilesize*cols, tilesize*rows, QImage.Format_ARGB32_Premultiplied)
|
||||||
|
self.tilesize = tilesize
|
||||||
self.image.fill(color)
|
self.image.fill(color)
|
||||||
self.painter = QtGui.QPainter(self.image)
|
self.painter = QtGui.QPainter(self.image)
|
||||||
self.max_x = 1
|
self.max_col = 1
|
||||||
self.max_y = 1
|
self.max_row = 1
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
del self.painter
|
del self.painter
|
||||||
|
@ -226,19 +227,19 @@ class Canvas:
|
||||||
def draw_pixmap(self, col, row, pixmap, h_flip=False, v_flip=False):
|
def draw_pixmap(self, col, row, pixmap, h_flip=False, v_flip=False):
|
||||||
h_s = -1 if h_flip else 1
|
h_s = -1 if h_flip else 1
|
||||||
v_s = -1 if v_flip else 1
|
v_s = -1 if v_flip else 1
|
||||||
x = (col+h_flip)*8*h_s
|
x = (col+h_flip)*self.tilesize*h_s
|
||||||
y = (row+v_flip)*8*v_s
|
y = (row+v_flip)*self.tilesize*v_s
|
||||||
self.painter.scale(h_s, v_s)
|
self.painter.scale(h_s, v_s)
|
||||||
self.painter.drawPixmap(x, y, pixmap)
|
self.painter.drawPixmap(x, y, pixmap)
|
||||||
self.painter.scale(h_s, v_s) # Invert it again to restore it to normal
|
self.painter.scale(h_s, v_s) # Invert it again to restore it to normal
|
||||||
if col > self.max_x:
|
if col > self.max_col:
|
||||||
self.max_x = col
|
self.max_col = col
|
||||||
if row > self.max_y:
|
if row > self.max_row:
|
||||||
self.max_y = row
|
self.max_row = row
|
||||||
|
|
||||||
def pixmap(self, trim=False):
|
def pixmap(self, trim=False):
|
||||||
if trim:
|
if trim:
|
||||||
return QPixmap.fromImage(self.image.copy(0, 0, self.max_x*8+8, self.max_y*8+8))
|
return QPixmap.fromImage(self.image.copy(0, 0, (self.max_col+1)*self.tilesize, (self.max_row+1)*self.tilesize))
|
||||||
return QPixmap.fromImage(self.image)
|
return QPixmap.fromImage(self.image)
|
||||||
|
|
||||||
|
|
||||||
|
@ -270,6 +271,8 @@ class Canvas_Indexed:
|
||||||
self.max_row = max(row, self.max_row)
|
self.max_row = max(row, self.max_row)
|
||||||
|
|
||||||
def pixmap(self, palette, trim=False):
|
def pixmap(self, palette, trim=False):
|
||||||
|
if isinstance(palette[0], list):
|
||||||
|
palette = [i for p in palette for i in p] # Flatten
|
||||||
if trim:
|
if trim:
|
||||||
img = self.image.copy(0, 0, (self.max_col+1)*self.tilesize, (self.max_row+1)*self.tilesize)
|
img = self.image.copy(0, 0, (self.max_col+1)*self.tilesize, (self.max_row+1)*self.tilesize)
|
||||||
img.setColorTable(palette)
|
img.setColorTable(palette)
|
||||||
|
|
Loading…
Reference in New Issue