Special case added for single-block blockmaps.

This commit is contained in:
Luke Hubmayer-Werner 2018-04-04 22:41:07 +09:30
parent 2a00be9ade
commit 755c4221ca
4 changed files with 130 additions and 18 deletions

View File

@ -261,11 +261,16 @@ class FF5Reader(QMainWindow):
zone_pxs = []
block_cache = {'misses': 0, 'p_hits': 0, 'i_hits': 0}
zone_px_cache = {'misses': 0, 'hits': 0}
for z in zones:
blocks, miniblocks = make_field_map_blocks_px(ROM_jp, z, field_tiles, field_minitiles, field_blocksets, block_cache)
fm_blocks = [make_field_map_blocks_px(ROM_jp, z, field_tiles, field_minitiles, field_blocksets, block_cache) for z in zones]
#fm_blocks = [make_field_map_blocks_px2(ROM_jp, z, field_tiles, field_minitiles, field_blocksets, block_cache) for z in zones]
print('Block cache results: {misses} misses, {p_hits} full hits, {i_hits} palette misses'.format(**block_cache))
perfcount()
for i, z in enumerate(zones):
blocks, miniblocks = fm_blocks[i]
field_blocks.append(stitch_tileset_px([b.all for b in blocks+miniblocks]))
zone_pxs += make_zone_pxs(blocks, miniblocks, blockmaps, z, zone_px_cache)
print('Block cache results: {misses} misses, {p_hits} full hits, {i_hits} palette misses'.format(**block_cache))
#zone_pxs += make_zone_pxs2(blocks, miniblocks, blockmaps, z, zone_px_cache)
print('Zone pixmap cache results: {misses} misses, {hits} hits'.format(**zone_px_cache))
perfcount()
@ -321,7 +326,7 @@ class FF5Reader(QMainWindow):
backgrounds_tab.addTab(make_px_table(worldpixmaps, cols=1, scale=1, large=True), 'Worldmaps')
backgrounds_tab.addTab(make_px_table(fieldmap_tiles, cols=16, 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(zone_pxs, cols=4, scale=1, large=1, basicrows=True), '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')

View File

@ -156,15 +156,16 @@ def make_table(headers, items, sortable=False, row_labels=True, scale=2):
return table
def make_px_table(items, cols=16, scale=4, large=False):
def make_px_table(items, cols=16, scale=4, large=False, basicrows=False):
rows = divceil(len(items), cols)
rd = hex_length(rows-1)+1
rd = hex_length(rows-1)+1 if basicrows else hex_length((rows*cols)-1)+1
cd = hex_length(cols-1)
table = QTableWidget(rows, cols)
if large:
table.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
table.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
table.setVerticalHeaderLabels([hex(v*cols, rd) for v in range(rows)])
rl = [hex(v, rd) for v in range(rows)] if basicrows else [hex(v*cols, rd) for v in range(rows)]
table.setVerticalHeaderLabels(rl)
table.setHorizontalHeaderLabels([hex(v, cd) for v in range(cols)])
for i, item in enumerate(items):
if isinstance(item, QWidget):

View File

@ -202,17 +202,17 @@ def make_worldmap_pixmap2(rom, map_id, tiles):
for j in range(256):
chunk = make_worldmap_chunk(rom, j+id_offset)
for i, c in enumerate(chunk):
fragments.append(make_pixmapfragment(c, i*16, j*16))
fragments.append(make_pixmapfragment(c, i, j))
canvas.drawPixmapFragments(fragments, tiles)
return canvas.pixmap()
def make_field_tiles(rom, id):
tiles_address = indirect(rom, 0x1C2D84 + id*4, length=4) + 0x1C2E24
return [create_tile_indexed(rom[tiles_address+i*32:tiles_address+i*32+32]) for i in range(256)]
return [create_tile_indexed(rom[tiles_address+i*32:tiles_address+(i+1)*32]) for i in range(256)]
def make_field_minitiles(rom, id):
tiles_address = indirect(rom, 0x1C0000 + id*2) + 0x1C0024
return [create_tile_indexed(rom[tiles_address+i*16:tiles_address+i*16+16]) for i in range(256)]
return [create_tile_indexed(rom[tiles_address+i*16:tiles_address+(i+1)*16]) for i in range(256)]
def make_all_field_tiles(rom):
return [make_field_tiles(rom, i) for i in range(40)]
@ -221,11 +221,20 @@ def make_all_field_minitiles(rom):
return [make_field_minitiles(rom, i) for i in range(18)]
def stitch_tileset(tiles):
canvas = Canvas_Indexed(16, len(tiles)//16)
canvas = Canvas_Indexed(16, len(tiles)//16, tilesize=tiles[0].width())
for i, tile in enumerate(tiles):
canvas.draw_tile(i%16, i//16, tile)
return canvas
def stitch_set(data):
'''
For stitching slabs of VRAM
'''
canvas = Canvas_Indexed(1, len(data), tilesize=data[0].width())
for i, d in enumerate(data):
canvas.draw_tile(0, i, d)
return canvas
def stitch_tileset_px(tiles_px):
canvas = Canvas(16, len(tiles_px)//16, tilesize=tiles_px[0].width())
for i, tile in enumerate(tiles_px):
@ -288,6 +297,47 @@ def make_block(tilemap, tiles, cols=2, rows=2, tile_adjust=0, pal_adjust=0, tile
print(e, tm.palette, hex(tm.tile,2), hex(tile_adjust,2), hex(tm.tile+tile_adjust,2))
return canvases
def make_field_map_blocks_px2(rom, zone, tilesets, minitilesets, blocksets, cache):
'''
Batched version.
'''
cache_key_i = '{} {}'.format(' '.join([str(i) for i in zone.tilesets]), zone.blockset)
cache_key_p = '{} {}'.format(cache_key_i, zone.pal)
if cache_key_p in cache:
cache['p_hits'] += 1
return cache[cache_key_p]
elif cache_key_i in cache:
cache['i_hits'] += 1
blocks, miniblocks = cache[cache_key_i]
else:
cache['misses'] += 1
*i_tiles, i_minitiles = zone.tilesets
tiles = tilesets[i_tiles[0]] + tilesets[i_tiles[1]] + tilesets[i_tiles[2]]
minitiles = minitilesets[i_minitiles]
blockset = blocksets[zone.blockset]
blockset_parsed = [[parse_tileset_word(tm[i*2:(i+1)*2]) for i in range(len(tm)//2)] for tm in blockset]
l = len(blockset_parsed)
blocks_l = [None]*l*3
for i, bs in enumerate(blockset_parsed):
blocks_l[i], blocks_l[i+l], blocks_l[i+(2*l)] = [b.image for b in make_block(bs, tiles)]
miniblocks_l = [None]*l*3
for i, bs in enumerate(blockset_parsed):
miniblocks_l[i], miniblocks_l[i+l], miniblocks_l[i+(2*l)] = [b.image for b in make_block(bs, minitiles, tile_modulo=len(minitiles))]
blocks = stitch_tileset(blocks_l)
miniblocks = stitch_tileset(miniblocks_l)
cache[cache_key_i] = (blocks, miniblocks)
blocks_px = blocks.pixmap(zone.palette)
miniblocks_px = miniblocks.pixmap(zone.palette)
cache[cache_key_p] = (blocks_px, miniblocks_px)
return blocks_px, miniblocks_px
def get_blockmap(rom, address):
if rom[address] == 0:
return decompress_lzss(rom, address)
else: # Special case - the entire map is one block type (e.g. water layer)
return [rom[address]]*0x1000
def get_blockmaps(rom, start_address=0x0B0000, num=0x148):
bank = 0x0B0000
ptrs = [indirect(rom, start_address)+bank]
@ -296,7 +346,7 @@ def get_blockmaps(rom, start_address=0x0B0000, num=0x148):
if (ptr+bank) < ptrs[-1]:
bank += 0x010000
ptrs.append(ptr+bank)
blockmaps = [decompress_lzss(rom, ptr) for ptr in ptrs]
blockmaps = [get_blockmap(rom, ptr) for ptr in ptrs]
return blockmaps
def make_zone_pxs(blocks, miniblocks, blockmaps, zone, cache):
@ -319,7 +369,8 @@ def make_zone_pxs(blocks, miniblocks, blockmaps, zone, cache):
block = _blocks[b]
canvases[0].draw_pixmap(j%64, j//64, block.priority0)
canvases[1].draw_pixmap(j%64, j//64, block.priority1)
canvases[2].draw_pixmap(j%64, j//64, block.all)
canvases[2].draw_pixmap(0, 0, canvases[0].pixmap())
canvases[2].draw_pixmap(0, 0, canvases[1].pixmap())
output.append(canvases[2].pixmap())
layers[i*2:(i+1)*2] = canvases[0:2]
canvas = Canvas(64, 64, tilesize=16)
@ -330,6 +381,50 @@ def make_zone_pxs(blocks, miniblocks, blockmaps, zone, cache):
cache[cache_key] = output
return output
def make_zone_pxs2(blocks, miniblocks, blockmaps, zone, cache):
'''
Batched version
blocks and miniblocks are a single pixmap each
'''
cache_key = '{} {} {}'.format(' '.join([str(i) for i in zone.blockmaps+zone.tilesets]), zone.blockset, zone.pal)
if cache_key in cache:
cache['hits'] += 1
return cache[cache_key]
else:
cache['misses'] += 1
output = []
layers = [] # bg1.0 bg1.1 bg2.0 bg2.1 bg3.0 bg3.1
order = [4, 2, 0, 3, 1, 5] # Draw order from http://problemkaputt.de/fullsnes.htm#snespictureprocessingunitppu
for i, i_b in enumerate(zone.blockmaps):
if i_b == -1:
layers += [None, None]
output.append(None)
else:
canvases = (Canvas(64, 128, tilesize=16), Canvas(64, 64, tilesize=16))
_blocks = blocks if i < 2 else miniblocks
pxlist = []
append = pxlist.append
for j, b in enumerate(blockmaps[i_b]):
x = j%64
y = j//64
#append(make_pixmapfragment(b, x, y))
#append(make_pixmapfragment(b+0x100, x, y+64))
append(QtGui.QPainter.PixmapFragment.create(QtCore.QPoint(x*16, y*16), QtCore.QRectF((b%16)*16, (b//16)*16, 16, 16)))
append(QtGui.QPainter.PixmapFragment.create(QtCore.QPoint(x*16, y*16+1024), QtCore.QRectF((b%16)*16, (b//16)*16+0x100, 16, 16)))
canvases[0].drawPixmapFragments(pxlist, _blocks)
_layers = [canvases[0].pixmap(rect=(0, 0, 1024, 1024)), canvases[0].pixmap(rect=(0, 1024, 1024, 1024))]
canvases[1].draw_pixmap(0, 0, _layers[0])
canvases[1].draw_pixmap(0, 0, _layers[1])
output.append(canvases[1].pixmap())
layers += _layers
canvas = Canvas(64, 64, tilesize=16)
for i in order:
if layers[i]:
canvas.draw_pixmap(0, 0, layers[i])
output.append(canvas.pixmap())
cache[cache_key] = output
return output
def decompress_battle_tilemap(rom, address):
'''
Decompresses the tilemap for a battle background.

View File

@ -211,10 +211,11 @@ def generate_palette(rom, offset, length=32, transparent=False):
palette[0] = 0
return palette
def make_pixmapfragment(id, source_x, source_y):
pos = QtCore.QPoint(source_x, source_y)
source = QtCore.QRectF((id%16)*16, (id//16)*16, 16, 16)
return QtGui.QPainter.PixmapFragment.create(pos, source, 1, 1, 0, 1)
def make_pixmapfragment(id, target_col, target_row, tilesize=16):
return QtGui.QPainter.PixmapFragment.create(QtCore.QPoint(target_col*tilesize, target_row*tilesize), QtCore.QRectF((id%16)*tilesize, (id//16)*tilesize, tilesize, tilesize))
def make_pixmapfragment2(source_x, source_y, target_x, target_y, tilesize=16):
return QtGui.QPainter.PixmapFragment.create(QtCore.QPoint(target_x, target_y), QtCore.QRectF(source_x, source_y, tilesize, tilesize))
class Canvas:
def __init__(self, cols, rows, color=bg_trans, tilesize=8):
@ -224,6 +225,7 @@ class Canvas:
self.painter = QtGui.QPainter(self.image)
self.max_col = 1
self.max_row = 1
self.batch = []
def __del__(self):
del self.painter
@ -244,9 +246,18 @@ class Canvas:
def drawPixmapFragments(self, *args, **kwargs):
self.painter.drawPixmapFragments(*args, **kwargs)
def pixmap(self, trim=False):
def add_pixmapfragment(self, fragment):
self.batch.append(fragment)
def do_batch(self, pixmap):
self.painter.drawPixmapFragments(self.batch, pixmap)
self.batch = []
def pixmap(self, trim=False, rect=None):
if trim:
return QPixmap.fromImage(self.image.copy(0, 0, (self.max_col+1)*self.tilesize, (self.max_row+1)*self.tilesize))
if rect:
return QPixmap.fromImage(self.image.copy(*rect))
return QPixmap.fromImage(self.image)