BOMBERMAN '94 notes last edited Jul 14 2025 RIPPING LEVEL TILES it seems level maps are visually hard-coded, so replacing tiles and actually seeing the replacements is tricky (use 0FB6, that'll replace a tile on the very top-right, use BizHawk background viewer to see it) (make macro to replace it with values 00 to B0-ish, screenshot BG viewer, crop, etc) RIPPING SPRITES Enemies and bosses have tiles easily visible in tile viewer. that's good! Bomberman and Louie, however, only load one sprite at a time. That's bad! There are RAM addresses to fiddle with animation frames and the like, but they're kind of spotty and unreliable; see RAM addresses below. The player graphics are stored at ROM addresses 3E200 and 48200, but are compressed. (the Mega Bomberman ROM is uncompressed, so we can get an idea of what's stored there -- but we want to rip from '94 because it's got more colour depth!) Taking tiles from VRAM and comparing to their order in ROM suggests they're compressed via run-length encoding -- groups of consecutive "00" bytes are compressed into binary. 18 "00"s becomes "FFFFC", because in binary that is 18 "1"s; just remove the 0s! (this 'compression' is used only for player sprites/Louies as far as I'm aware) (this is NOT the 'univeral' PC Engine compression used in this game, Bomberman '90 or '93, Neutopia 1, etc) (i assume this unique method is because it reads 1 sprite at a time, instead of loading a bunch of stuff at once) Big thanks to @vervalkon/Raccoon Sam for helping decrypt the compression used for the player sprites; see bottom. ROM ADDRESSES 3E200 COMPRESSED GFX - bomberman, bomberlady, yankii, old man, sumo 48200 COMPRESSED GFX - mini bomber, prof, bombsaver, miner, louie 60200 graphics for normal/battle menu, freezes when entering game 65000~6F000 battle menu graphics (icons buttons, trophy/victory screen) 76600~7B600 freezes entering normal game stage A6200 sprite graphics for normal menus (space, stage select) B6200 ? bizhawk ram addresses 37 3d 44 something to do with graphics loading process? (4-byte) 48 4c 00B2 normal: world (#-1) 00B4 normal: level (1-#) 00B6 normal: screen (1-3a, etc) 0220 bg palettes 0412 spr palettes (~ 612) 0662 gameplay state? 12 for game, 5 for world map...???? 075a 147dec in stage select places tablet piece...? 09BA p1 bomb timer 0C9F battle: p1 chara 0CDD related to P1's sprites/animation frame? 0E9E+ sprite decompression of some kind...? fucks up bg gfx (runs through values crazy first during load transitions) 0F00 related to louie egg sprite (battle) 0FB6 level tile data, BETTER RESULTS THAN 1168 1168+ level tile data? doesn't display graphics on-screen, but seems to have effect - 10dec will kill all objects on it 144e nornal: Bomberman OBJ id? just makes him play victory animation if changed 1454 normal: enemy ID? 52 w1 blue robo 112 w1 red bush 221 w1 rabbit 14b2 battle: p1 palette 14b3 battle: p2 PALETTE 14b7 battle: p1 Louie PALETTE 1Cb5 battle: p1 Louie ability (also applies to bosses) 1557 p1 x-pos 1558 p2 x-pos 1559 p3 x-pos 1593 battle: p1 y-pos [louie?] 1594 normal: boss 2 y-pos 158F battle: p2 y-pos 15B6 battle: p1 chara ( 60 bomberman 94 128 162 196 230 8 42 76 15CA battle: p1 chara (second byte, necessary for prof, bomsaver and miner) 15DE battle: p1 animation? 15F2 (second byte) (plays a new animation when you blow yourself up) ---bomberman 135-16 ride down 25 ride up 34 ride left 43 ride right 60 walk up 69 walk right 78 walk down 87 walk left 96 jump up 103 jump right 114 jump down 123 jump left 124 die 137 dizzy / lose 154 level victory 161 battle victory 172 UNUSED sleep start 177 UNUSED sleep loop 185 UNUSED lift up? 194 UNUSED? lift up on louie? 203 victory on louie ? 210 UNUSED swim down 217 UNUSED swim up 224 surprise (boss intro) ---bomberlady 233 WALK UP 242 walk right 251 walk down 136-4 walk left 13 jump up 20 jump right 27 jump down 34 jump left 41 die 54 dizzy / lose 71 level win 82 battle win 95 UNUSED idle 102 victory on louie ---yankii bomber 109 walk up 118 walk right 127 walk down 136 walk left 145 jump up 152 jump right 159 jump down 166 jump left 173 die 186 dizzy 203 level win 214 battle win 223 UNUSED idle 228 victory on louie? ---old man 235 walk up 137-64 dizzy 94 battle win ---sumo 137-222 battle win 239 UNUSED idle ---bomsaver 139-214 walk up 241 walk left 140-61 battle win 140-80 victory on louie ---louie 138-16 walk down 25 walk up 34 walk left 43 walk right 52 jump down 59 jump up 66 jump left 73 jump right 80 dance 149 die 160 UNUSED? idle 165 UNUSED? dizzy 174 squashed 1606 battle: p1 animation timer freeze? 160b battle: p1 animation timer freeze? 161A battle: p1 anim frame 161B battle: p2 anim frame 161F battle: P1 louie anim frame 1620 normal: boss 1 anim frame? 1621 normal: boss 2 anim frame 1704 normal: lives 1709 p1 invincibility 1b8A battle timer 1B8F battle: p1 chara (only changes on battle start?) 1B99 p1 fire up 1BA3 p1 bomb up 1BDF normal: p1 direction 1C95 normal: p1 x-block position TAIKENBAN ISO ADDRESES 009D stage select...? 9D and 9F seem important, but only work with specific values 1 0 5 0 lava 2 0 1 0 basic 4 0 2 0 conveyor 6 0 4 0 arrows 9 0 7 0 water 0B00~ palette 1595 p1 x-pos (+3E compared to rom) 15D1 p1 y-pos 165D p1 anim frame? 1BFB stage select menu DEDEN NO DEN ISO ADDRESSES 41000 player 45000 louie 77000 player (repeats from here, no chnage) 7B000 louie AD000 player B2600 louie Python script from @VERVALKON / Raccoon Sam used to decrypt the player graphics -- thank you so much! import struct def getB(number=1): return int.from_bytes(rom.read(number), byteorder='little') def i2b(num): binary_str = format(num, f'08b') return [int(bit) for bit in binary_str] #START = 0x3E000 #LIMIT = 0x4692B START = 0x48000 LIMIT = 0x4F74D ROMNAME = "bm.pce" OUTNAME = "bmout.bin" with open(ROMNAME, "rb") as rom: with open(OUTNAME, "wb") as out: rom.seek(START) while True: if rom.tell() >= LIMIT: break surrogate = 0 #evaluate what sort of a bitmask it is bitmask = [] while True: sniff = getB() if sniff != 0xFF: bitmask.extend(i2b(sniff)) break else: bitmask.extend(i2b(sniff)) #churn through the bitmask while bitmask: #get the first bit act = bitmask.pop(0) if act == 0: surrogate = getB() #if it's 1... out.write(bytes([surrogate]))