Initial zone/fieldmap drawing

This commit is contained in:
Luke Hubmayer-Werner 2018-03-27 23:19:20 +10:30
parent 7fe8fb7351
commit 5d701a02fc
5 changed files with 205 additions and 104 deletions

View File

@ -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')

View File

@ -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

View File

@ -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)

View File

@ -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)]

View File

@ -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)