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 hidden or 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