Created
July 24, 2018 16:15
-
-
Save realmonster/744ec17aab9ad711ee78a384601a2b60 to your computer and use it in GitHub Desktop.
Aero the Acro-Bat 2 (GEN) map converter into png
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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