Skip to content

Instantly share code, notes, and snippets.

@realmonster
Created July 24, 2018 16:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save realmonster/744ec17aab9ad711ee78a384601a2b60 to your computer and use it in GitHub Desktop.
Save realmonster/744ec17aab9ad711ee78a384601a2b60 to your computer and use it in GitHub Desktop.
Aero the Acro-Bat 2 (GEN) map converter into png
# Aero the Acro-Bat 2 (GEN) map converter into png
# for python 2.x
# by r57shell@uralweb.ru
# 24.07.2018
from PIL import Image, ImageDraw, ImageFont
from struct import *
import sys
import math
level_id = int(sys.argv[1], 16)
fname = "aero_map" + sys.argv[1]
with open("Aero the Acro-Bat 2 (U) [!].bin", 'rb') as f:
ROM = f.read()
class unpacker:
def __init__(self):
self.pos = 0
self.bit = 0
self.byte = 0
def get(self):
if self.bit == 0:
self.bit = 8
self.byte = unpack(">B",self.data[self.pos])[0]
self.pos -= 1
self.bit -= 1;
r = (self.byte>>7)&1
self.byte = (self.byte<<1)&0xFF;
return r
#return ((self.byte>>self.bit)&1)
def getn(self, n):
r = 0
for i in range(n):
r = r*2 + self.get()
return r
def unpack(self,data,offset):
self.data = data
self.pos = offset
v = unpack("<H",self.data[self.pos:self.pos+2])[0]
#print(hex(v))
self.pos += v
total = unpack("<H",self.data[self.pos:self.pos+2])[0]
#print(hex(total))
self.pos -= 3
self.byte = unpack(">B",self.data[self.pos+1])[0]
self.bit = unpack(">B",self.data[self.pos+2])[0]
l = 0
r = []
left = total
while left > 0:
v = self.get()
if v == 1: # literals
v = self.getn(2)
if v == 3:
v = self.get()
if v == 1:
v = 1 + self.get()
v = 3 + self.getn([4,8,16][v])
#print("literals "+str(v+1))
for i in range(v + 1):
r.append(ROM[self.pos])
self.pos -= 1
left -= 1
if left == 0:
break
# copy
v = self.get()
if v == 1:
v = self.getn(8)
l = 2
else:
v = self.get()
if v == 1:
v = self.get()
v = 1 + self.getn([4,8][v])
l = v + 3
v = self.get()
v = self.getn([8,16][v])
#print("copy "+str(v)+" "+str(l))
for i in range(l):
k = r[len(r)-v-l+1]
r.append(k)
left -= 1
return ''.join(r[::-1])
def rb(offs, data = ROM):
return unpack(">B",data[offs])[0]
def rw(offs, data = ROM):
return unpack(">H",data[offs:offs+2])[0]
def rl(offs, data = ROM):
return unpack(">L",data[offs:offs+4])[0]
unp = unpacker()
offset = rl(0x1CD2C + level_id*4)
pal = []
def loadpal():
pals = [
rl(offset + 8),
rl(offset + 4),
0xA0000,
rl(offset + 12)
]
s = ''.join([ROM[i:i+0x20] for i in pals])
for i in range(len(s)/2):
c = unpack(">H", s[i*2:i*2+2])[0]
r = int((c&15)*255./14.)
g = int(((c>>4)&15)*255./14.)
b = int(((c>>8)&15)*255./14.)
pal.append((r,g,b,255))
for i in range(0,len(pal),16):
pal[i] = (0,0,0,0)
loadpal()
map_blocks = rl(0xA1C42+rw(offset+2)*4)
map_chunks = rl(0xA1D82+rb(map_blocks)*4) # tiles "banks"
map_cells = rl(0xA2102+rb(map_blocks+1)*4) # cells 32x32
map_width = rb(map_blocks+2)
map_height = rb(map_blocks+3)
map_objs = rl(0xA1CE2+rw(offset+2)*4)
print(hex(map_blocks))
print(hex(map_chunks))
print(hex(map_cells))
print(hex(map_width)+' '+hex(map_height))
map_blocks = unp.unpack(ROM,map_blocks+4)
blocks = []
for x in range(map_width*(320//32)):
tmp = []
mw = map_width*(320//32)*2
for y in range(map_height*(256//32)):
tmp.append(rw(mw*y+x*2, map_blocks))
blocks.append(tmp)
collision_types = [0]
cell_types = [0]
cell_tiles = [0]*4
tiles = None
def loadgfx():
global tiles
tiles = 1
tiles_data = "\x00"*0x20
for i in range(100):
v = rb(map_chunks+2+i)
if v == 0xFF:
break
print(v)
chunk = rl(0xA1F1A+v*4)
v = rw(chunk)
offs = rl(chunk+4)
cells = rl(chunk+8)
types1 = rl(chunk+12)
types = rl(chunk+16)
print(hex(cells))
print(hex(types1))
print(hex(types))
if (v&0x8000) != 0:
v = v&0x1FF
gfx = ROM[offs:offs+(v<<7)]
tiles_data += gfx
for j in range(v):
cell_tiles.extend(range(tiles+j*4,tiles+j*4+4))
tiles += v*4
else:
gfx = unp.unpack(ROM,offs)
tiles_data += gfx
for j in range(v):
cell_tiles.extend([tiles+rw(cells+j*8+k) for k in (0,4,2,6)])
tiles += len(gfx)//0x20
for j in range(v):
collision_types.append(rb(types1+j))
cell_types.append(rb(types+j))
print(hex(tiles))
tiles = []
for i in range(len(tiles_data)):
v = unpack(">B", tiles_data[i:i+1])[0]
tiles.append((v>>4)&15)
tiles.append(v&15)
loadgfx()
cells = unp.unpack(ROM,map_cells)
objs = []
objs_count = rw(map_objs)
for i in range(objs_count):
objs.append([rw(map_objs+6+i*14+j*2) for j in range(7)])
if objs[i][1] >= 0x8000:
objs[i][1] = -0x10000 + objs[i][1]
if objs[i][2] >= 0x8000:
objs[i][2] = -0x10000 + objs[i][2]
#print(pal)
img = None
pixels = None
def drawtile(x, y, tileid):
color = (tileid >> 9) & 0x30
tile = (tileid & 0x7FF) * 64
if tile+64 > len(tiles):
return
flips = tileid & 0x1800
if flips == 0:
for j in range(8):
for i in range(8):
if tiles[tile+i+j*8] != 0:
pixels[x+i, y+j] = pal[color + tiles[tile+i+j*8]]
elif flips == 0x800:
for j in range(8):
for i in range(8):
if tiles[tile+7-i+j*8] != 0:
pixels[x+i, y+j] = pal[color + tiles[tile+7-i+j*8]]
elif flips == 0x1000:
for j in range(8):
for i in range(8):
if tiles[tile+i+56-j*8] != 0:
pixels[x+i, y+j] = pal[color + tiles[tile+i+56-j*8]]
else:
for j in range(8):
for i in range(8):
if tiles[tile+63-i-j*8] != 0:
pixels[x+i, y+j] = pal[color + tiles[tile+63-i-j*8]]
def color(s):
return (int(s[1:3],16),int(s[3:5],16),int(s[5:7],16),int(s[7:9],16))
def lerpa(a,b,w):
return int(a*(1-w/255.)+b*w/255.)
def setpixel(x,y,c):
c1 = pixels[x,y]
pixels[x,y] = (lerpa(c1[0],c[0],c[3]),lerpa(c1[1],c[1],c[3]),lerpa(c1[2],c[2],c[3]),lerpa(c1[3],255,c[3]))
def drawline(x1,y1,x2,y2,c):
dx = x2-x1
dy = y2-y1
l = dx*dx+dy*dy
if l != 0:
ls = math.sqrt(l)
dx /= ls*2
dy /= ls*2
ddx = 0
ddy = 0
px = None
py = None
while ddx*ddx+ddy*ddy <= l:
x = int(x1+ddx)
y = int(y1+ddy)
if (x != px) or (y != py):
setpixel(x, y, c)
px = x
py = y
ddx += dx
ddy += dy
def drawbox(x1, y1, x2, y2, c):
for j in range(max(0,-y1),min(y2-y1, img.height-y1)):
for i in range(max(0,-x1),min(x2-x1, img.width-x1)):
setpixel(x1+i,y1+j,c)
def drawobj(idx,p):
tt, x, y, l1, r1, l2, r2 = objs[idx]
#if l1 != 0xFFFF:
# drawline(x, y, objs[l1/14][1], objs[l1/14][2], color("#ff0000ff"))
#if r1 != 0xFFFF:
# drawline(x, y, objs[r1/14][1], objs[r1/14][2], color("#ff0000ff"))
#if l2 != 0xFFFF:
# drawline(x, y, objs[l2/14][1], objs[l2/14][2], color("#00ff00ff"))
#if r2 != 0xFFFF:
# drawline(x, y, objs[r2/14][1], objs[r2/14][2], color("#00ff00ff"))
if p == 1:
if x < 0:
x = 0
if y < 0:
y = 0
drawline(x,y,min(x+15,img.width-2),y,(0,255,0,255))
drawline(x,y,x,y+15,(0,255,0,255))
drawline(min(x+15,img.width-2),y,min(x+15,img.width-2),min(y+15,img.height-2),(0,255,0,255))
drawline(x,min(y+15,img.height-2),min(x+15,img.width-2),min(y+15,img.height-2),(0,255,0,255))
imgd.text((x, y), ("%X" % (tt&0x3FFF)), font=fnt, fill=(0,255,0,255))
def draw_cell(px, py, val, p):
pal = (val & 0xE000)
idx = (val & 0x1FF)
addr = idx*4
ids = [pal|cell_tiles[addr+i] for i in range(4)]
vis = [(ids[i]&0x8000) == p*0x8000 for i in range(4)]
if vis[0]:
drawtile(px , py , ids[0])
if vis[1]:
drawtile(px + 8, py , ids[1])
if vis[2]:
drawtile(px , py + 8, ids[2])
if vis[3]:
drawtile(px + 8, py + 8, ids[3])
img = Image.new("RGBA",(map_width*320,map_height*256))
pixels = img.load()
imgd = ImageDraw.Draw(img)
fnt = ImageFont.truetype('c:/Windows/Fonts/arial.ttf', 12)
for p in range(2):
for x in range(map_width*(320//32)):
for y in range(map_height*(256//32)):
px = x*32
py = y*32
idx = blocks[x][y]
addr = (idx & 0x7FF)*8
draw_cell(px, py, rw(addr, cells), p)
draw_cell(px + 16, py, rw(addr + 2, cells), p)
draw_cell(px, py + 16, rw(addr + 4, cells), p)
draw_cell(px + 16, py + 16, rw(addr + 6, cells), p)
for i in range(objs_count):
drawobj(i, p)
img.save(fname+".png")
def draw_collision(px, py, val, p):
if (val & 0x1000) == 0:
return
color = None
v2 = val & 0x1FF
tt = cell_types[v2]
v3 = collision_types[v2]
offs = 0xA21A2 if level_id < 0x50 else 0xA41A2
cc = (0,0xCC,0,0xAA)
if rl(0x97F6 + tt*4) == 0xB238:
cc = (0,0xFF,0xFF,0xAA)
for k in range(16):
mask = rw(offs + 32*v3 + k*2)
if mask != 0:
j = 1
for i in range(16):
if (mask & j) != 0:
setpixel(px+k, py+15-i, cc)
j *= 2
color = None
if (tt == 0x49) or (tt == 0x1D):
color = (0xFF,0,0,0xAA)
if color:
drawbox(px, py, px+16, py+16, color)
for x in range(map_width*(320//32)):
for y in range(map_height*(256//32)):
px = x*32
py = y*32
idx = blocks[x][y]
addr = (idx & 0x7FF)*8
draw_collision(px, py, rw(addr, cells), 1)
draw_collision(px + 16, py, rw(addr + 2, cells), 1)
draw_collision(px, py + 16, rw(addr + 4, cells), 1)
draw_collision(px + 16, py + 16, rw(addr + 6, cells), 1)
img.save(fname+"_c.png")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment