Special case added for single-block blockmaps.
This commit is contained in:
parent
2a00be9ade
commit
755c4221ca
13
ff5reader.py
13
ff5reader.py
|
@ -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')
|
||||
|
|
|
@ -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):
|
||||
|
|
107
includes/snes.py
107
includes/snes.py
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue