Merge remote-tracking branch 'kanzure/master' into mergetastic

Conflicts:
	main.asm
This commit is contained in:
Sanky 2013-10-21 20:24:46 +02:00
commit b5268ab920
115 changed files with 51333 additions and 22734 deletions

2
.gitignore vendored
View file

@ -17,7 +17,7 @@
.*.swp
# no data from extras/
extras/*.json
*.json
# for any of the poor souls with save game files in their working directory
baserom.sgm

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "extras"]
path = extras
url = git://github.com/kanzure/pokemon-reverse-engineering-tools.git

View file

@ -1,17 +1,16 @@
.SUFFIXES: .asm .tx .o .gbc
TEXTFILES = text/oakspeech.tx text/pokedex.tx text/mapRedsHouse1F.tx \
text/mapBluesHouse.tx text/mapPalletTown.tx
TEXTFILES := $(shell find ./ -type f -name '*.asm')
all: pokered.gbc
pokered.o: pokered.asm main.tx constants.asm ${TEXTFILES}
rgbasm -o pokered.o pokered.asm
pokered.o: pokered.tx main.tx constants.tx music.tx wram.tx ${TEXTFILES:.asm=.tx}
rgbasm -o pokered.o pokered.tx
pokeblue.o: pokeblue.asm main.tx constants.asm ${TEXTFILES}
rgbasm -o pokeblue.o pokeblue.asm
pokeblue.o: pokeblue.tx main.tx constants.tx music.tx wram.tx ${TEXTFILES:.asm=.tx}
rgbasm -o pokeblue.o pokeblue.tx
redrle: extras/redrle.c
redrle: extras/redtools/redrle.c
${CC} -o $@ $>
.asm.tx:
@ -25,8 +24,9 @@ pokered.gbc: pokered.o
pokeblue.gbc: pokeblue.o
rgblink -o $@ $*.o
rgbfix -jsv -k 01 -l 0x33 -m 0x13 -p 0 -r 03 -t "POKEMON BLUE" $@
cmp blue.gbc $@
clean:
rm -f main.tx pokered.o pokered.gbc pokeblue.o pokeblue.gbc redrle ${TEXTFILES}
rm -f pokered.o pokered.gbc pokeblue.o pokeblue.gbc redrle $(TEXTFILES:.asm=.tx)
more: pokered.gbc pokeblue.gbc

View file

@ -105,6 +105,57 @@ PREDEF_JUMP: MACRO
; wram locations
; data for all sprites on the current map
; holds info for 16 sprites with $10 bytes each
; player sprite is always sprite 0
; C1x0: picture ID (fixed, loaded at map init)
; C1x1: movement status (0: uninitialized, 1: ready, 2: delayed, 3: moving)
; C1x2: sprite image index (changed on update, $ff if off screen, includes facing direction, progress in walking animation and a sprite-specific offset)
; C1x3: Y screen position delta (-1,0 or 1; added to c1x4 on each walking animation update)
; C1x4: Y screen position (in pixels, always 4 pixels above grid which makes sprites appear to be in the center of a tile)
; C1x5: X screen position delta (-1,0 or 1; added to c1x6 on each walking animation update)
; C1x6: X screen position (in pixels, snaps to grid if not currently walking)
; C1x7: intra-animation-frame counter (counting upwards to 4 until c1x8 is incremented)
; C1x8: animation frame counter (increased every 4 updates, hold four states (totalling to 16 walking frames)
; C1x9: facing direction (0: down, 4: up, 8: left, $c: right)
; C1xA
; C1xB
; C1xC
; C1xD
; C1xE
; C1xF
W_SPRITESTATEDATA1 EQU $C100 ; to $C200
; more data for all sprites on the current map
; holds info for 16 sprites with $10 bytes each
; player sprite is always sprite 0
; C2x0: walk animation counter (counting from $10 backwards when moving)
; C2x1:
; C2x2: Y displacement (initialized at 8, supposed to keep moving sprites from moving too far, but bugged)
; C2x3: X displacement (initialized at 8, supposed to keep moving sprites from moving too far, but bugged)
; C2x4: Y position (in 2x2 tile grid steps, topmost 2x2 tile has value 4)
; C2x5: X position (in 2x2 tile grid steps, leftmost 2x2 tile has value 4)
; C2x6: movement byte 1 (determines whether a sprite can move, $ff:not moving, $fe:random movements, others unknown)
; C2x7: (?) (set to $80 when in grass, else $0; may be used to draw grass above the sprite)
; C2x8: delay until next movement (counted downwards, status (c1x1) is set to ready if reached 0)
; C2x9
; C2xA
; C2xB
; C2xC
; C2xD
; C2xE: sprite image base offset (in video ram, player always has value 1, used to compute c1x2)
; C2xF
W_SPRITESTATEDATA2 EQU $C200 ; to $C300
; buffer for OAM data. Is copied to OAM RAM by OAM DMA
W_OAMBUFFER EQU $C300 ; size $a0, to $C3A0
; buffer for tiles that are visible on screen (20 columns by 18 rows = $168 bytes)
W_SCREENTILESBUFFER EQU $C3A0 ; size $168, to $C508
; buffer for temporarily saving and restoring current screen's tiles (e.g. if menus are drawn on top)
W_SCREENTILESBACKBUFFER EQU $C508 ; size $168, to $C670
; the tiles of the row or column to be redrawn by RedrawExposedScreenEdge
W_SCREENEDGETILES EQU $CBFC
@ -144,6 +195,10 @@ W_LISTSCROLLOFFSET EQU $CC36
; set to 0 if you can't go past the top or bottom of the menu
W_MENUWRAPPINGENABLED EQU $CC4A
W_TRAINERHEADERFLAGBIT EQU $CC55
W_RLEBYTECOUNTER EQU $CCD2
; current HP of player and enemy substitutes
W_PLAYERSUBSITUTEHP EQU $CCD7
W_ENEMYSUBSITUTEHP EQU $CCD8
@ -167,6 +222,10 @@ W_PLAYERMONSPECIALMOD EQU $CD1D
W_PLAYERMONACCURACYMOD EQU $CD1E
W_PLAYERMONEVASIONMOD EQU $CD1F
W_ENGAGEDTRAINERCLASS EQU $CD2D
W_ENGAGEDTRAINERSETNUM EQU $CD2E
; stat modifiers for the enemy's current pokemon
; value can range from 1 - 13 ($1 to $D)
; 7 is normal
@ -178,11 +237,38 @@ W_ENEMYMONACCURACYMOD EQU $CD32
W_ENEMYMONEVASIONMOD EQU $CD33
W_WHICHTRADE EQU $CD3D ; which entry from TradeMons to select
W_TRAINERSPRITEOFFSET EQU $CD3D
W_TRAINERENGAGEDISTANCE EQU $CD3E
W_TRAINERFACINGDIR EQU $CD3F
W_TRAINERSCREENYPOS EQU $CD40
W_TRAINERSCREENXPOS EQU $CD41
; bit 0: is player engaged by trainer (to avoid being engaged by multiple trainers simultaniously)
W_FLAGS_CD60 EQU $CD60
; bit 1 means button presses will be ignored for that futton
W_JOYPADFORBIDDENBUTTONSMASK EQU $CD6B
; second buffer for temporarily saving and restoring current screen's tiles (e.g. if menus are drawn on top)
W_SCREENTILESBACKBUFFER2 EQU $CD81 ; size $168, to $CEE9
W_HPBARMAXHP EQU $CEE9
W_HPBAROLDHP EQU $CEEB
W_HPBARNEWHP EQU $CEED
W_HPBARDELTA EQU $CEEF
W_HPBARHPDIFFERENCE EQU $CEFD
W_BUFFER EQU $CEE9 ; used for temporary things
W_ANIMSOUNDID EQU $CF07 ; sound ID during battle animations
; movement byte 2 of current sprite
W_CURSPRITEMOVEMENT2 EQU $CF14
W_GYMCITYNAME EQU $CF5F
W_GYMLEADERNAME EQU $CF70
W_WHICHPOKEMON EQU $CF92 ; which pokemon you selected
W_LISTMENUID EQU $CF94 ; ID used by DisplayListMenuID
@ -203,6 +289,10 @@ W_PLAYERMOVETYPE EQU $CFD5
W_PLAYERMOVEACCURACY EQU $CFD6
W_PLAYERMOVEMAXPP EQU $CFD7
W_ENEMYMONID EQU $CFD8
W_ENEMYMONNAME EQU $CFDA
W_ENEMYMONCURHP EQU $CFE6 ; active opponent's hp (16 bits)
W_ENEMYMONNUMBER EQU $CFE8 ; active opponent's position in team (0 to 5)
W_ENEMYMONSTATUS EQU $CFE9 ; active opponent's status condition
@ -214,16 +304,23 @@ W_ENEMYMONSTATUS EQU $CFE9 ; active opponent's status condition
; bit 5 frz
; bit 6 par
; unused? (XXX confirm)
W_ENEMYMONTYPES EQU $CFEA
W_ENEMYMONTYPE1 EQU $CFEA
W_ENEMYMONTYPE2 EQU $CFEB
W_ENEMYMONLEVEL EQU $CFF3
W_ENEMYMONMAXHP EQU $CFF4 ; (16 bits)
W_ENEMYMONDEFENSE EQU $CFF8
W_ENEMYMONSPECIAL EQU $CFFC
W_ENEMYMONPP EQU $CFFE
W_ENEMYMONTYPES EQU $CFEA
W_ENEMYMONTYPE1 EQU $CFEA
W_ENEMYMONTYPE2 EQU $CFEB
W_ENEMYMONMOVES EQU $CFED
W_ENEMYMONATKDEFIV EQU $CFF1
W_ENEMYMONSPDSPCIV EQU $CFF2
W_ENEMYMONLEVEL EQU $CFF3
W_ENEMYMONMAXHP EQU $CFF4 ; (16 bits)
W_ENEMYMONATTACK EQU $CFF6
W_ENEMYMONDEFENSE EQU $CFF8
W_ENEMYMONSPEED EQU $CFFA
W_ENEMYMONSPECIAL EQU $CFFC
W_ENEMYMONPP EQU $CFFE
W_PLAYERMONCURHP EQU $D015 ; active opponent's hp (16 bits)
W_PLAYERMONNAME EQU $D009
W_PLAYERMONID EQU $D014
W_PLAYERMONCURHP EQU $D015 ; active opponent's hp (16 bits)
W_PLAYERMONSTATUS EQU $D018 ; the status of the players current monster
; bit 0 slp
; bit 1 slp
@ -233,12 +330,18 @@ W_PLAYERMONSTATUS EQU $D018 ; the status of the players current monster
; bit 5 frz
; bit 6 par
; unused? (XXX confirm)
W_PLAYERMONTYPES EQU $D019
W_PLAYERMONTYPE1 EQU $D019
W_PLAYERMONTYPE2 EQU $D01A
W_PLAYERMONLEVEL EQU $D022
W_PLAYERMONMAXHP EQU $D023 ; (16 bits)
W_PLAYERMONPP EQU $D02D
W_PLAYERMONTYPES EQU $D019
W_PLAYERMONTYPE1 EQU $D019
W_PLAYERMONTYPE2 EQU $D01A
W_PLAYERMONMOVES EQU $D01C
W_PLAYERMONIVS EQU $D020 ; 4x 4 bit: atk, def, spd, spc
W_PLAYERMONLEVEL EQU $D022
W_PLAYERMONMAXHP EQU $D023 ; (16 bits)
W_PLAYERMONATK EQU $D025
W_PLAYERMONDEF EQU $D027
W_PLAYERMONSPEED EQU $D029
W_PLAYERMONSPECIAL EQU $D02B
W_PLAYERMONPP EQU $D02D
W_TRAINERCLASS EQU $D031
@ -246,6 +349,8 @@ W_ISINBATTLE EQU $D057 ; no battle, this is 0
; wild battle, this is 1
; trainer battle, this is 2
W_PLAYERMONSALIVEFLAGS EQU $D058 ; 6 bit array, 1 if player mon is alive
W_CUROPPONENT EQU $D059 ; in a wild battle, this is the species of pokemon
; in a trainer battle, this is the trainer class + $C8
@ -310,7 +415,6 @@ W_BASECOORDY EQU $D082
W_FBTILECOUNTER EQU $D084 ; counts how many tiles of the current frame block have been drawn
W_SUBANIMFRAMEDELAY EQU $D086 ; duration of each frame of the current subanimation in terms of screen refreshes
W_SUBANIMCOUNTER EQU $D087 ; counts the number of subentries left in the current subanimation
W_NUMFBTILES EQU $D089 ; number of tiles in current battle animation frame block
@ -321,6 +425,12 @@ W_SUBANIMTRANSFORM EQU $D08B ; controls what transformations are applied to the
; 03: translate base coordinates of frame blocks, but don't change their internal coordinates or flip their tiles
; 04: reverse the subanimation
W_PBSTOREDREGISTERH EQU $D08C
W_PBSTOREDREGISTERL EQU $D08D
W_PBSTOREDREGISTERD EQU $D08E
W_PBSTOREDREGISTERE EQU $D08F
W_PBSTOREDROMBANK EQU $D092
W_SUBANIMADDRPTR EQU $D094 ; the address _of the address_ of the current subanimation entry (2 bytes)
W_SUBANIMSUBENTRYADDR EQU $D096 ; the address of the current subentry of the current subanimation (2 bytes)
@ -334,10 +444,53 @@ W_FBMODE EQU $D09E ; controls how the frame blocks are put together to form fram
; 03: delay, but don't clean OAM buffer
; 04: delay, without cleaning OAM buffer, and do not advance [W_FBDESTADDR], so that the next frame block will overwrite this one
W_DAMAGE EQU $D0D7
; sprite data is written column by column, each byte contains 8 columns (one for ech bit)
; for 2bpp sprites, pairs of two consecutive bytes (i.e. pairs of consecutive rows of sprite data)
; contain the upper and lower bit of each of the 8 pixels, respectively
SPRITEBUFFERSIZE EQU $188 ; 7 * 7 (tiles) * 8 (bytes per tile)
S_SPRITEBUFFER0 EQU $A000 + 0 * SPRITEBUFFERSIZE
S_SPRITEBUFFER1 EQU $A000 + 1 * SPRITEBUFFERSIZE
S_SPRITEBUFFER2 EQU $A000 + 2 * SPRITEBUFFERSIZE
W_SPRITECURPOSX EQU $D0A1
W_SPRITECURPOSY EQU $D0A2
W_SPRITEWITDH EQU $D0A3
W_SPRITEHEIGHT EQU $D0A4
W_SPRITEINPUTCURBYTE EQU $D0A5 ; current input byte
W_SPRITEINPUTBITCOUNTER EQU $D0A6 ; bit offset of last read input bit
; determines where in the output byte the two bits are placed. Each byte contains four columns (2bpp data)
; 3 -> XX000000 1st column
; 2 -> 00XX0000 2nd column
; 1 -> 0000XX00 3rd column
; 0 -> 000000XX 4th column
W_SPRITEOUTPUTBITOFFSET EQU $D0A7
; bit 0 determines used buffer (0 -> $a188, 1 -> $a310)
; bit 1 loading last sprite chunk? (there are at most 2 chunks per load operation)
W_SPRITELOADFLAGS EQU $D0A8
W_SPRITEUNPACKMODE EQU $D0A9
W_SPRITEFLIPPED EQU $D0AA
W_SPRITEINPUTPTR EQU $D0AB ; pointer to next input byte
W_SPRITEOUTPUTPTR EQU $D0AD ; pointer to current output byte
W_SPRITEOUTPUTPTRCACHED EQU $D0AF ; used to revert pointer for different bit offsets
W_SPRITEDECODETABLE0PTR EQU $D0B1 ; pointer to differential decoding table (assuming initial value 0)
W_SPRITEDECODETABLE1PTR EQU $D0B3 ; pointer to differential decoding table (assuming initial value 1)
H_SPRITEWIDTH EQU $FF8B ; in bytes
H_SPRITEINTERLACECOUNTER EQU $FF8B
H_SPRITEHEIGHT EQU $FF8C ; in bytes
H_SPRITEOFFSET EQU $FF8D
; OAM flags used by this game
OAMFLAG_ENDOFDATA EQU %00000001 ; pseudo OAM flag, only used by game logic
OAMFLAG_CANBEMASKED EQU %00000010 ; pseudo OAM flag, only used by game logic
OAMFLAG_VFLIPPED EQU %00100000 ; OAM flag flips the sprite vertically. Used for making left facing sprites face right and to alternate between left and right foot animation when walking up or down
; List type
; used in $D0B6
W_LISTTYPE EQU $D0B6
MONSTER_NAME EQU 1
MOVE_NAME EQU 2
; ???_NAME EQU 3
@ -346,6 +499,36 @@ PLAYEROT_NAME EQU 5
ENEMYOT_NAME EQU 6
TRAINER_NAME EQU 7
W_MONHEADER EQU $d0b8
W_MONHDEXNUM EQU $d0b8
W_MONHBASESTATS EQU $d0b9
;W_MONHBASEHP EQU $d0b9
;W_MONHBASEATTACK EQU $d0ba
;W_MONHBASEDEFENSE EQU $d0bb
W_MONHBASESPEED EQU $d0bc
;W_MONHBASESPECIAL EQU $d0bd
W_MONHTYPES EQU $d0be
W_MONHTYPE1 EQU $d0be
W_MONHTYPE2 EQU $d0bf
W_MONHCATCHRATE EQU $d0c0
;W_MONHBASEXP EQU $d0c1
W_MONHSPRITEDIM EQU $d0c2
W_MONHFRONTSPRITE EQU $d0c3
W_MONHBACKSPRITE EQU $d0c5
W_MONHMOVES EQU $d0c7
;W_MONHMOVE1 EQU $d0c7
;W_MONHMOVE2 EQU $d0c8
;W_MONHMOVE3 EQU $d0c9
;W_MONHMOVE4 EQU $d0ca
W_MONHGROWTHRATE EQU $d0cb
W_MONHLEARNSET EQU $d0cc ; bit field, 7 bytes
;W_MONHPADDING EQU $d0d7
W_DAMAGE EQU $D0D7
W_CURENEMYLVL EQU $D127
W_ISLINKBATTLE EQU $D12B
@ -358,60 +541,6 @@ W_PLAYERNAME EQU $D158 ; 11 characters, including null
W_NUMINPARTY EQU $D163
W_OWNEDPOKEMON EQU $D2F7 ; bit field, 19 bytes
W_SEENPOKEMON EQU $D30A ; bit field, 19 bytes
;number of items in bag
W_NUMBAGITEMS EQU $D31D
; BAGITEM01 is an item id
; BAGCOUNT01 is how many of this item
W_BAGITEM01 EQU $D31E
W_BAGITEM01QTY EQU $D31F
W_BAGITEM02 EQU $D320
W_BAGITEM02QTY EQU $D321
W_BAGITEM03 EQU $D322
W_BAGITEM03QTY EQU $D323
W_BAGITEM04 EQU $D324
W_BAGITEM04QTY EQU $D325
W_BAGITEM05 EQU $D326
W_BAGITEM05QTY EQU $D327
W_BAGITEM06 EQU $D328
W_BAGITEM06QTY EQU $D329
W_BAGITEM07 EQU $D32A
W_BAGITEM07QTY EQU $D32B
W_BAGITEM08 EQU $D32C
W_BAGITEM08QTY EQU $D32D
W_BAGITEM09 EQU $D32E
W_BAGITEM09QTY EQU $D32F
W_BAGITEM10 EQU $D330
W_BAGITEM10QTY EQU $D331
W_BAGITEM11 EQU $D332
W_BAGITEM11QTY EQU $D333
W_BAGITEM12 EQU $D334
W_BAGITEM12QTY EQU $D335
W_BAGITEM13 EQU $D336
W_BAGITEM13QTY EQU $D337
W_BAGITEM14 EQU $D338
W_BAGITEM14QTY EQU $D339
W_BAGITEM15 EQU $D33A
W_BAGITEM15QTY EQU $D33B
W_BAGITEM16 EQU $D33C
W_BAGITEM16QTY EQU $D33D
W_BAGITEM17 EQU $D33E
W_BAGITEM17QTY EQU $D33F
W_BAGITEM18 EQU $D340
W_BAGITEM18QTY EQU $D341
W_BAGITEM19 EQU $D342
W_BAGITEM19QTY EQU $D343
W_BAGITEM20 EQU $D344
W_BAGITEM20QTY EQU $D345
; money is in decimal
W_PLAYERMONEY3 EQU $D347
W_PLAYERMONEY2 EQU $D348
W_PLAYERMONEY1 EQU $D349
W_PARTYMON1 EQU $D164
W_PARTYMON2 EQU $D165
W_PARTYMON3 EQU $D166
@ -620,6 +749,60 @@ W_PARTYMON4NAME EQU $D2D6
W_PARTYMON5NAME EQU $D2E1
W_PARTYMON6NAME EQU $D2EC
W_OWNEDPOKEMON EQU $D2F7 ; bit field, 19 bytes
W_SEENPOKEMON EQU $D30A ; bit field, 19 bytes
;number of items in bag
W_NUMBAGITEMS EQU $D31D
; BAGITEM01 is an item id
; BAGCOUNT01 is how many of this item
W_BAGITEM01 EQU $D31E
W_BAGITEM01QTY EQU $D31F
W_BAGITEM02 EQU $D320
W_BAGITEM02QTY EQU $D321
W_BAGITEM03 EQU $D322
W_BAGITEM03QTY EQU $D323
W_BAGITEM04 EQU $D324
W_BAGITEM04QTY EQU $D325
W_BAGITEM05 EQU $D326
W_BAGITEM05QTY EQU $D327
W_BAGITEM06 EQU $D328
W_BAGITEM06QTY EQU $D329
W_BAGITEM07 EQU $D32A
W_BAGITEM07QTY EQU $D32B
W_BAGITEM08 EQU $D32C
W_BAGITEM08QTY EQU $D32D
W_BAGITEM09 EQU $D32E
W_BAGITEM09QTY EQU $D32F
W_BAGITEM10 EQU $D330
W_BAGITEM10QTY EQU $D331
W_BAGITEM11 EQU $D332
W_BAGITEM11QTY EQU $D333
W_BAGITEM12 EQU $D334
W_BAGITEM12QTY EQU $D335
W_BAGITEM13 EQU $D336
W_BAGITEM13QTY EQU $D337
W_BAGITEM14 EQU $D338
W_BAGITEM14QTY EQU $D339
W_BAGITEM15 EQU $D33A
W_BAGITEM15QTY EQU $D33B
W_BAGITEM16 EQU $D33C
W_BAGITEM16QTY EQU $D33D
W_BAGITEM17 EQU $D33E
W_BAGITEM17QTY EQU $D33F
W_BAGITEM18 EQU $D340
W_BAGITEM18QTY EQU $D341
W_BAGITEM19 EQU $D342
W_BAGITEM19QTY EQU $D343
W_BAGITEM20 EQU $D344
W_BAGITEM20QTY EQU $D345
; money is in decimal
W_PLAYERMONEY3 EQU $D347
W_PLAYERMONEY2 EQU $D348
W_PLAYERMONEY1 EQU $D349
W_RIVALNAME EQU $D34A ; 11 characters, including null
W_OPTIONS EQU $D355
@ -663,48 +846,19 @@ W_SPRITESET EQU $D39D ; sprite set for the current map (11 sprite picture ID's)
W_SPRITESETID EQU $D3A8 ; sprite set ID for the current map
W_NUMSPRITES EQU $D4E1 ; number of sprites on the current map
W_PEOPLEMOVEPERMISSIONS EQU $D4E4
; coins are in decimal
W_PLAYERCOINS1 EQU $D5A4
W_PLAYERCOINS2 EQU $D5A5
; two bytes per sprite (movement byte 2 , text ID)
W_MAPSPRITEDATA EQU $D4e4
W_OAKSLABCURSCRIPT EQU $D5F0
; two bytes per sprite (trainer class/item ID , trainer set ID)
W_MAPSPRITEEXTRADATA EQU $D504
W_RIVALSTARTER EQU $D715
W_PLAYERSTARTER EQU $D717
W_GRASSRATE EQU $D887
W_GRASSMONS EQU $D888
W_WATERRATE EQU $D8A4 ; OVERLOADED
W_WATERMONS EQU $D8A5 ; OVERLOADED
W_ENEMYMONCOUNT EQU $D89C
W_ENEMYMON1HP EQU $D8A5 ; 16 bits
W_ENEMYMON1MOVE3 EQU $D8AE
W_ENEMYMON2MOVE3 EQU $D8DA
W_ENEMYMON3MOVE3 EQU $D906
W_ENEMYMON4MOVE3 EQU $D932
W_ENEMYMON5MOVE3 EQU $D95E
W_ENEMYMON6MOVE3 EQU $D98A
W_PLAYTIMEHOURS EQU $DA40 ; two bytes
W_PLAYTIMEMINUTES EQU $DA42 ; two bytes
W_PLAYTIMESECONDS EQU $DA44 ; one byte
W_PLAYTIMEFRAMES EQU $DA45 ; one byte
W_NUMSAFARIBALLS EQU $DA47
; number of mons in current box
W_NUMINBOX EQU $DA80
W_TILESETBANK EQU $D52B
W_TILESETBLOCKSPTR EQU $D52C ; maps blocks (4x4 tiles) to it's tiles
W_TILESETGFXPTR EQU $D52E
W_TILESETCOLLISIONPTR EQU $D530 ; list of all walkable tiles
W_TILESETTALKINGOVERTILES EQU $D532 ; 3 bytes
W_GRASSTILE EQU $D535
;number of items in box
W_NUMBOXITEMS EQU $D53A
@ -812,8 +966,203 @@ W_BOXITEM50 EQU $D59D
W_BOXITEM50QTY EQU $D59E
;box end of list $D59F
; coins are in decimal
W_PLAYERCOINS1 EQU $D5A4
W_PLAYERCOINS2 EQU $D5A5
W_MISSABLEOBJECTFLAGS EQU $D5A6 ; $20 bytes, bit array of missable objects. bit 1 = removed
; each entry consists of 2 bytes
; * the sprite ID (depending on the current map)
; * the missable object index (global, used for W_MISSABLEOBJECTFLAGS)
; terminated with $FF
W_MISSABLEOBJECTLIST EQU $D5CE
W_GAMEPROGRESSFLAGS EQU $D5F0 ; $c8 bytes
W_OAKSLABCURSCRIPT EQU $D5F0
W_PALLETTOWNCURSCRIPT EQU $D5F1
W_BLUESHOUSECURSCRIPT EQU $D5F3
W_VIRIDIANCITYCURSCRIPT EQU $D5F4
W_PEWTERCITYCURSCRIPT EQU $D5F7
W_ROUTE3CURSCRIPT EQU $D5F8
W_ROUTE4CURSCRIPT EQU $D5F9
W_VIRIDIANGYMCURSCRIPT EQU $D5FB
W_PEWTERGYMCURSCRIPT EQU $D5FC
W_CERULEANGYMCURSCRIPT EQU $D5FD
W_VERMILIONGYMCURSCRIPT EQU $D5FE
W_CELADONGYMCURSCRIPT EQU $D5FF
W_ROUTE6CURSCRIPT EQU $D600
W_ROUTE8CURSCRIPT EQU $D601
W_ROUTE24CURSCRIPT EQU $D602
W_ROUTE25CURSCRIPT EQU $D603
W_ROUTE9CURSCRIPT EQU $D604
W_ROUTE10CURSCRIPT EQU $D605
W_MTMOON1CURSCRIPT EQU $D606
W_MTMOON3CURSCRIPT EQU $D607
W_SSANNE8CURSCRIPT EQU $D608
W_SSANNE9CURSCRIPT EQU $D609
W_ROUTE22CURSCRIPT EQU $D60A
W_REDSHOUSE2CURSCRIPT EQU $D60C
W_VIRIDIANMARKETCURSCRIPT EQU $D60D
W_ROUTE22GATECURSCRIPT EQU $D60E
W_CERULEANCITYCURSCRIPT EQU $D60F
W_SSANNE5CURSCRIPT EQU $D617
W_VIRIDIANFORESTCURSCRIPT EQU $D618
W_MUSEUMF1CURSCRIPT EQU $D619
W_ROUTE13CURSCRIPT EQU $D61A
W_ROUTE14CURSCRIPT EQU $D61B
W_ROUTE17CURSCRIPT EQU $D61C
W_ROUTE19CURSCRIPT EQU $D61D
W_ROUTE21CURSCRIPT EQU $D61E
W_SAFARIZONEENTRANCECURSCRIPT EQU $D61F
W_ROCKTUNNEL2CURSCRIPT EQU $D620
W_ROCKTUNNEL1CURSCRIPT EQU $D621
W_ROUTE11CURSCRIPT EQU $D623
W_ROUTE12CURSCRIPT EQU $D624
W_ROUTE15CURSCRIPT EQU $D625
W_ROUTE16CURSCRIPT EQU $D626
W_ROUTE18CURSCRIPT EQU $D627
W_ROUTE20CURSCRIPT EQU $D628
W_SSANNE10CURSCRIPT EQU $D629
W_VERMILIONCITYCURSCRIPT EQU $D62A
W_POKEMONTOWER2CURSCRIPT EQU $D62B
W_POKEMONTOWER3CURSCRIPT EQU $D62C
W_POKEMONTOWER4CURSCRIPT EQU $D62D
W_POKEMONTOWER5CURSCRIPT EQU $D62E
W_POKEMONTOWER6CURSCRIPT EQU $D62F
W_POKEMONTOWER7CURSCRIPT EQU $D630
W_ROCKETHIDEOUT1CURSCRIPT EQU $D631
W_ROCKETHIDEOUT2CURSCRIPT EQU $D632
W_ROCKETHIDEOUT3CURSCRIPT EQU $D633
W_ROCKETHIDEOUT4CURSCRIPT EQU $D634
W_ROUTE6GATECURSCRIPT EQU $D636
W_ROUTE8GATECURSCRIPT EQU $D637
W_CINNABARISLANDCURSCRIPT EQU $D639
W_MANSION1CURSCRIPT EQU $D63A
W_MANSION2CURSCRIPT EQU $D63C
W_MANSION3CURSCRIPT EQU $D63D
W_MANSION4CURSCRIPT EQU $D63E
W_VICTORYROAD2CURSCRIPT EQU $D63F
W_VICTORYROAD3CURSCRIPT EQU $D640
W_FIGHTINGDOJOCURSCRIPT EQU $D642
W_SILPHCO2CURSCRIPT EQU $D643
W_SILPHCO3CURSCRIPT EQU $D644
W_SILPHCO4CURSCRIPT EQU $D645
W_SILPHCO5CURSCRIPT EQU $D646
W_SILPHCO6CURSCRIPT EQU $D647
W_SILPHCO7CURSCRIPT EQU $D648
W_SILPHCO8CURSCRIPT EQU $D649
W_SILPHCO9CURSCRIPT EQU $D64A
W_HALLOFFAMEROOMCURSCRIPT EQU $D64B
W_GARYCURSCRIPT EQU $D64C
W_LORELEICURSCRIPT EQU $D64D
W_BRUNOCURSCRIPT EQU $D64E
W_AGATHACURSCRIPT EQU $D64F
W_UNKNOWNDUNGEON3CURSCRIPT EQU $D650
W_VICTORYROAD1CURSCRIPT EQU $D651
W_LANCECURSCRIPT EQU $D653
W_SILPHCO10CURSCRIPT EQU $D658
W_SILPHCO11CURSCRIPT EQU $D659
W_FUCHSIAGYMCURSCRIPT EQU $D65B
W_SAFFRONGYMCURSCRIPT EQU $D65C
W_CINNABARGYMCURSCRIPT EQU $D65E
W_CELADONGAMECORNERCURSCRIPT EQU $D65F
W_ROUTE16GATECURSCRIPT EQU $D660
W_BILLSHOUSECURSCRIPT EQU $D661
W_ROUTE5GATECURSCRIPT EQU $D662
W_POWERPLANTCURSCRIPT EQU $D663 ; overload
W_ROUTE7GATECURSCRIPT EQU $D663 ; overload
W_SSANNE2CURSCRIPT EQU $D665
W_SEAFOAMISLANDS4CURSCRIPT EQU $D666
W_ROUTE23CURSCRIPT EQU $D667
W_SEAFOAMISLANDS5CURSCRIPT EQU $D668
W_ROUTE18GATECURSCRIPT EQU $D669
W_TOWNVISITEDFLAG EQU $D70B ; 2 bytes bit array, 1 means visited
W_SAFARITIMER1 EQU $D70D ; use 01 for maximum
W_SAFARITIMER2 EQU $D70E ; use F4 for maximum
W_FOSSILITEM EQU $D70F ; item given to cinnabar lab
W_FOSSILMON EQU $D710 ; mon that will result from the item
W_ENEMYMONORTRAINERCLASS EQU $D713 ; trainer classes start at $c8
W_RIVALSTARTER EQU $D715
W_PLAYERSTARTER EQU $D717
; bit 4: use variable [W_CURMAPSCRIPT] instead of the provided index for next frame's map script (used to start battle when talking to trainers)
W_FLAGS_D733 EQU $D733
W_GRASSRATE EQU $D887
W_GRASSMONS EQU $D888
W_WATERRATE EQU $D8A4 ; OVERLOADED
W_WATERMONS EQU $D8A5 ; OVERLOADED
W_ENEMYMONCOUNT EQU $D89C
W_ENEMYMON1HP EQU $D8A5 ; 16 bits
W_ENEMYMON1MOVE3 EQU $D8AE
W_ENEMYMON2MOVE3 EQU $D8DA
W_ENEMYMON3MOVE3 EQU $D906
W_ENEMYMON4MOVE3 EQU $D932
W_ENEMYMON5MOVE3 EQU $D95E
W_ENEMYMON6MOVE3 EQU $D98A
W_ENEMYMON1OT EQU $D9AC
W_ENEMYMON2OT EQU $D9B7
W_ENEMYMON3OT EQU $D9C2
W_ENEMYMON4OT EQU $D9CD
W_ENEMYMON5OT EQU $D9D8
W_ENEMYMON6OT EQU $D9E3
W_ENEMYMON1NAME EQU $D9EE
W_ENEMYMON2NAME EQU $D9F9
W_ENEMYMON3NAME EQU $DA04
W_ENEMYMON4NAME EQU $DA0F
W_ENEMYMON5NAME EQU $DA1A
W_ENEMYMON6NAME EQU $DA25 ; to $da2f
W_TRAINERHEADERPTR EQU $DA30
; index of current map script, mostly used as index for function pointer array
; mostly copied from map-specific map script pointer and wirtten back later
W_CURMAPSCRIPT EQU $DA39
W_PLAYTIMEHOURS EQU $DA40 ; two bytes
W_PLAYTIMEMINUTES EQU $DA42 ; two bytes
W_PLAYTIMESECONDS EQU $DA44 ; one byte
W_PLAYTIMEFRAMES EQU $DA45 ; one byte
W_NUMSAFARIBALLS EQU $DA47
; number of mons in current box
W_NUMINBOX EQU $DA80
W_BOXMON1DATA EQU $DA96
W_BOXMON2DATA EQU $DAB7
H_SOFTRESETCOUNTER EQU $FF8A ; initialized to 16, decremented each input iteration if the user presses the reset sequence (A+B+S+s). Soft reset when 0 is reached.
; counters for blinking down arrow
H_DOWNARROWBLINKCNT1 EQU $FF8B
@ -837,6 +1186,13 @@ H_NUMTOPRINT EQU $FF96 ; 3 bytes, big endian order
H_POWEROFTEN EQU $FF99 ; 3 bytes, big endian order
H_SAVEDNUMTOPRINT EQU $FF9C ; 3 bytes, big endian order (to back out of a subtraction)
H_OLDPRESSEDBUTTONS EQU $FFB1
H_NEWLYRELEASEDBUTTONS EQU $FFB2
H_NEWLYPRESSEDBUTTONS EQU $FFB3
H_CURRENTPRESSEDBUTTONS EQU $FFB4
H_LOADEDROMBANK EQU $FFB8
; is automatic background transfer during V-blank enabled?
; if nonzero, yes
; if zero, no
@ -910,8 +1266,12 @@ H_FRAMECOUNTER EQU $FFD5 ; decremented every V-blank (used for delays)
; you can detect that the V-blank handler has run since then.
H_VBLANKOCCURRED EQU $FFD6
H_CURRENTSPRITEOFFSET EQU $FFDA ; multiple of $10
H_WHOSETURN EQU $FFF3 ; 0 on players turn, 1 on enemys turn
H_JOYPADSTATE EQU $FFF8
; hardware registers, from the pandocs http://nocash.emubase.de/pandocs.htm
rJOYP EQU $FF00
rDIV EQU $FF04
@ -925,6 +1285,15 @@ rWY EQU $FF4A
rWX EQU $FF4B
rIE EQU $FFFF
BTN_A EQU %00000001
BTN_B EQU %00000010
BTN_SELECT EQU %00000100
BTN_START EQU %00001000
BTN_RIGHT EQU %00010000
BTN_LEFT EQU %00100000
BTN_UP EQU %01000000
BTN_DOWN EQU %10000000
; OAM attribute flags
OAM_HFLIP EQU %00100000 ; horizontal flip
OAM_VFLIP EQU %01000000 ; vertical flip
@ -1082,6 +1451,9 @@ SQUIRTLE EQU $B1
CHARMELEON EQU $B2
WARTORTLE EQU $B3
CHARIZARD EQU $B4
FOSSIL_KABUTOPS EQU $B6
FOSSIL_AERODACTYL EQU $B7
MON_GHOST EQU $B8
ODDISH EQU $B9
GLOOM EQU $BA
VILEPLUME EQU $BB
@ -1364,7 +1736,7 @@ X_DEFEND EQU $42
X_SPEED EQU $43
X_SPECIAL EQU $44
COIN_CASE EQU $45
OAKS_PARCEL EQU $46
OAKS_PARCEL EQU $46
ITEMFINDER EQU $47
SILPH_SCOPE EQU $48
POKE_FLUTE EQU $49
@ -1439,6 +1811,7 @@ TM_50 EQU $FA
; {stat}_(UP|DOWN)(1|2) means that the move raises the user's (or lowers the target's) corresponding stat modifier by 1 (or 2) stages
; {status condition}_side_effect means that the move has a side chance of causing that condition
; {status condition}_effect means that the move causes the status condition every time it hits the target
NO_ADDITIONAL_EFFECT EQU $00
POISON_SIDE_EFFECT1 EQU $02
DRAIN_HP_EFFECT EQU $03
BURN_SIDE_EFFECT1 EQU $04
@ -2356,7 +2729,7 @@ MT_MOON_3_WIDTH EQU $14
TRASHED_HOUSE_HEIGHT EQU $04
TRASHED_HOUSE_WIDTH EQU $04
; CeruleanHouse2_h map_id=63
; CeruleanHouse_h map_id=63
CERULEAN_HOUSE_HEIGHT EQU $04
CERULEAN_HOUSE_WIDTH EQU $04
@ -2553,12 +2926,12 @@ CELADON_MART_4_HEIGHT EQU $04
CELADON_MART_4_WIDTH EQU $0a
; CeladonMartRoof_h map_id=126
CELADON_MART_5_HEIGHT EQU $04
CELADON_MART_5_WIDTH EQU $0a
CELADON_MART_ROOF_HEIGHT EQU $04
CELADON_MART_ROOF_WIDTH EQU $0a
; CeladonMartElevator_h map_id=127
CELADON_MART_6_HEIGHT EQU $02
CELADON_MART_6_WIDTH EQU $02
CELADON_MART_ELEVATOR_HEIGHT EQU $02
CELADON_MART_ELEVATOR_WIDTH EQU $02
; CeladonMansion1_h map_id=128
CELADON_MANSION_1_HEIGHT EQU $06
@ -2593,8 +2966,8 @@ GAME_CORNER_HEIGHT EQU $09
GAME_CORNER_WIDTH EQU $0a
; CeladonMart5_h map_id=136
CELADON_HOUSE_HEIGHT EQU $04
CELADON_HOUSE_WIDTH EQU $0a
CELADON_MART_5_HEIGHT EQU $04
CELADON_MART_5_WIDTH EQU $0a
; CeladonPrizeRoom_h map_id=137
CELADONPRIZE_ROOM_HEIGHT EQU $04
@ -2605,8 +2978,8 @@ CELADON_DINER_HEIGHT EQU $04
CELADON_DINER_WIDTH EQU $05
; CeladonHouse_h map_id=139
CELADON_HOUSE_2_HEIGHT EQU $04
CELADON_HOUSE_2_WIDTH EQU $04
CELADON_HOUSE_HEIGHT EQU $04
CELADON_HOUSE_WIDTH EQU $04
; CeladonHotel_h map_id=140
CELADONHOTEL_HEIGHT EQU $04
@ -2952,9 +3325,9 @@ UNKNOWN_DUNGEON_1_WIDTH EQU $0f
NAME_RATERS_HOUSE_HEIGHT EQU $04
NAME_RATERS_HOUSE_WIDTH EQU $04
; CeruleanHouse3_h map_id=230
CERULEAN_HOUSE_3_HEIGHT EQU $04
CERULEAN_HOUSE_3_WIDTH EQU $04
; CeruleanHouse2_h map_id=230
CERULEAN_HOUSE_2_HEIGHT EQU $04
CERULEAN_HOUSE_2_WIDTH EQU $04
; RockTunnel2_h map_id=232
ROCK_TUNNEL_2_HEIGHT EQU $12

1
extras Submodule

@ -0,0 +1 @@
Subproject commit 2374a9ce905ccc4c701f81904b01019641e884b5

View file

@ -1,41 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2011-01-04
#purpose: insert labels into map headers
import sys
asm = None
asm_lines = []
def load_asm():
global asm, asm_lines
asm = open("../pokered.asm", "r").read()
asm_lines = asm.split("\n")
def find_with_start_of_line(name):
global asm_lines
for line in asm_lines:
if len(line) > len(name) and ": " in line:
if line[:len(name)] == name: return True
return False
def process_lines():
global asm, asm_lines
for line in asm_lines:
if not "_h:" in line: continue #skip
index = asm_lines.index(line)
name = line.split("_h:")[0]
if "Blocks" in asm_lines[index+3]: continue #skip, already done
#if not (str(name + "Blocks:") in asm): continue #skip, no block label found
if not find_with_start_of_line(name + "Blocks:"): continue #skip
orig_line = asm_lines[index+3]
fixed_line = orig_line.split(",")
fixed_line[0] = " dw " + name + "Blocks"
fixed_line = ",".join(fixed_line)
asm_lines[index+3] = fixed_line
if __name__ == "__main__":
load_asm()
process_lines()
sys.stdout.write("\n".join(asm_lines))

View file

@ -1,496 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-03
#purpose: map which addresses are left
#note: use python2.7 because of subprocess
import sys, os
from copy import copy, deepcopy
import subprocess
import json
from extract_maps import rom, assert_rom, load_rom, calculate_pointer, load_map_pointers, read_all_map_headers, map_headers
from pokered_dir import pokered_dir
try:
from pretty_map_headers import map_header_pretty_printer, map_name_cleaner
except Exception:
pass
#store each line of source code here
asm = None
#store each incbin line separately
incbin_lines = []
#storage for processed incbin lines
processed_incbins = {}
def offset_to_pointer(offset):
if type(offset) == str: offset = int(offset, base)
return int(offset) % 0x4000 + 0x4000
def load_asm(filename=os.path.join(pokered_dir, "main.asm")):
"""loads the asm source code into memory
this also detects if the revision of the repository
is using main.asm, common.asm or pokered.asm, which is
useful when generating images in romvisualizer.py"""
global asm
# chronological order is important
defaults = [os.path.join(pokered_dir, f) for f in ["main.asm", "common.asm", "pokered.asm"]]
if filename in defaults:
if not load_asm_if_one_exists_in(defaults):
raise Exception("This shouldn't happen")
elif os.path.exists(filename):
asm = get_all_lines_from_file(filename)
if asm is None:
raise Exception("file doesn't exists (did you mean one among: {0}?)".format(", ".join(defaults)))
return asm
def load_asm_if_one_exists_in(defaults):
global asm
for f in defaults:
if os.path.exists(f):
asm = get_all_lines_from_file(f)
return True
return False
def get_all_lines_from_file(filename):
try:
return open(filename, "r").read().split("\n")
except IOError as e:
raise(e)
def isolate_incbins():
"find each incbin line"
global incbin_lines
incbin_lines = []
for line in asm:
if line == "": continue
if line.count(" ") == len(line): continue
#clean up whitespace at beginning of line
while line[0] == " ":
line = line[1:]
if line[0:6] == "INCBIN" and "baserom.gbc" in line:
incbin_lines.append(line)
return incbin_lines
def process_incbins():
"parse incbin lines into memory"
global incbins
incbins = {} #reset
for incbin in incbin_lines:
processed_incbin = {}
line_number = asm.index(incbin)
partial_start = incbin[21:]
start = partial_start.split(",")[0].replace("$", "0x")
start = eval(start)
start_hex = hex(start).replace("0x", "$")
partial_interval = incbin[21:].split(",")[1]
partial_interval = partial_interval.replace(";", "#")
partial_interval = partial_interval.replace("$", "0x").replace("0xx", "0x")
interval = eval(partial_interval)
interval_hex = hex(interval).replace("0x", "$").replace("x", "")
end = start + interval
end_hex = hex(end).replace("0x", "$")
processed_incbin = {
"line_number": line_number,
"line": incbin,
"start": start,
"interval": interval,
"end": end,
}
#don't add this incbin if the interval is 0
if interval != 0:
processed_incbins[line_number] = processed_incbin
def find_incbin_to_replace_for(address):
"""returns a line number for which incbin to edit
if you were to insert bytes into main.asm"""
if type(address) == str: address = int(address, 16)
for incbin_key in processed_incbins.keys():
incbin = processed_incbins[incbin_key]
start = incbin["start"]
end = incbin["end"]
#print "start is: " + str(start)
#print "end is: " + str(end)
#print "address is: " + str(type(address))
#print "checking.... " + hex(start) + " <= " + hex(address) + " <= " + hex(end)
if start <= address <= end:
return incbin_key
return None
def split_incbin_line_into_three(line, start_address, byte_count):
"""
splits an incbin line into three pieces.
you can replace the middle one with the new content of length bytecount
start_address: where you want to start inserting bytes
byte_count: how many bytes you will be inserting
"""
if type(start_address) == str: start_address = int(start_address, 16)
original_incbin = processed_incbins[line]
start = original_incbin["start"]
end = original_incbin["end"]
#start, end1, end2 (to be printed as start, end1 - end2)
if start_address - start > 0:
first = (start, start_address, start)
else:
first = (None) #skip this one because we're not including anything
#this is the one you will replace with whatever content
second = (start_address, byte_count)
third = (start_address + byte_count, end - (start_address + byte_count))
output = ""
if first:
output += "INCBIN \"baserom.gbc\",$" + hex(first[0])[2:] + ",$" + hex(first[1])[2:] + " - $" + hex(first[2])[2:] + "\n"
output += "INCBIN \"baserom.gbc\",$" + hex(second[0])[2:] + "," + str(byte_count) + "\n"
output += "INCBIN \"baserom.gbc\",$" + hex(third[0])[2:] + ",$" + hex(third[1])[2:] #no newline
return output
def generate_diff_insert(line_number, newline):
original = "\n".join(line for line in asm)
newfile = deepcopy(asm)
newfile[line_number] = newline #possibly inserting multiple lines
newfile = "\n".join(line for line in newfile)
original_filename = "ejroqjfoad.temp"
newfile_filename = "fjiqefo.temp"
original_fh = open(original_filename, "w")
original_fh.write(original)
original_fh.close()
newfile_fh = open(newfile_filename, "w")
newfile_fh.write(newfile)
newfile_fh.close()
try:
diffcontent = subprocess.check_output(
"diff -u {0} {1}".format(os.path.join(pokered_dir, "main.asm"), newfile_filename),
shell=True)
except AttributeError, exc:
raise exc
except Exception, exc:
diffcontent = exc.output
os.system("rm " + original_filename)
os.system("rm " + newfile_filename)
return diffcontent
def insert_map_header_asm(map_id):
map = map_headers[map_id]
line_number = find_incbin_to_replace_for(map["address"])
if line_number == None: # or map_name_cleaner(map["name"], 0) in "\n".join(line for line in asm):
print "i think map id=" + str(map_id) + " has previously been added."
return #this map has already been added i bet
newlines = split_incbin_line_into_three(line_number, map["address"], 12 + (11 * len(map["connections"])))
map_header_asm = map_header_pretty_printer(map_headers[map_id])
newlines = newlines.split("\n")
if len(newlines) == 2: index = 0
elif len(newlines) == 3:
index = 1
newlines[0] += "\n" #spacing is a nice thing to have
newlines[index] = map_header_asm
newlines = "\n".join(line for line in newlines)
diff = generate_diff_insert(line_number, newlines)
print diff
print "... Applying diff."
#write the diff to a file
fh = open("temp.patch", "w")
fh.write(diff)
fh.close()
#apply the patch
os.system("patch {0} temp.patch".format(os.path.join(pokered_dir, "main.asm")))
#remove the patch
os.system("rm temp.patch")
def wrapper_insert_map_header_asm(map_id):
"reload the asm because it has changed (probably)"
load_asm()
isolate_incbins()
process_incbins()
insert_map_header_asm(map_id)
def dump_all_remaining_maps():
for map_id in map_headers:
print "Inserting map id=" + str(map_id)
wrapper_insert_map_header_asm(map_id)
def reset_incbins():
"reset asm before inserting another diff"
asm = None
incbin_lines = []
processed_incbins = {}
load_asm()
isolate_incbins()
process_incbins()
def apply_diff(diff, try_fixing=True, do_compile=True):
print "... Applying diff."
#write the diff to a file
fh = open("temp.patch", "w")
fh.write(diff)
fh.close()
#apply the patch
os.system("cp {0} {1}".format(
os.path.join(pokered_dir, "main.asm"),
os.path.join(pokered_dir, "main1.asm")))
os.system("patch {0} {1}".format(
os.path.join(pokered_dir, "main.asm"),
"temp.patch"))
#remove the patch
os.system("rm temp.patch")
#confirm it's working
if do_compile:
try:
subprocess.check_call("cd {0}; make clean; LC_CTYPE=C make".format(pokered_dir), shell=True)
return True
except Exception, exc:
if try_fixing:
os.system("mv {0} {1}".format(
os.path.join(pokered_dir, "main1.asm"),
os.path.join(pokered_dir, "main.asm")))
return False
def index(seq, f):
"""return the index of the first item in seq
where f(item) == True."""
return next((i for i in xrange(len(seq)) if f(seq[i])), None)
def is_probably_pointer(input):
try:
blah = int(input, 16)
return True
except:
return False
label_errors = ""
def get_labels_between(start_line_id, end_line_id, bank_id):
labels = []
#label = {
# "line_number": 15,
# "bank_id": 32,
# "label": "PalletTownText1",
# "local_pointer": "$5315",
# "address": 0x75315,
#}
global label_errors
errors = ""
current_line_offset = 0
sublines = asm[start_line_id : end_line_id + 1]
for line in sublines:
label = {}
line_id = start_line_id + current_line_offset
address = None
local_pointer = None
if ": ; 0x" in line:
temp = line.split(": ; 0x")[1]
if not " " in temp:
address = int("0x" + temp, 16)
else:
temp2 = temp.split(" ")[0]
address = int("0x" + temp2, 16)
elif ": ; " in line:
partial = line.split(": ; ")[1]
if ": ; $" in line:
temp = line.split(": ; $")[1]
if " " in temp:
temp = temp.split(" ")[0]
local_pointer = "$" + temp
elif " " in partial:
if " to " in partial:
temp = partial.split(" to ")[0]
if "0x" in temp:
address = int(temp, 16)
elif len(temp) == 4:
local_pointer = "$" + temp
else:
errors += "found \" to \" in partial on line " + str(line_id) + ", but don't know what to do (debug14)" + "\n"
errors += "line is: " + line + "\n"
continue
elif len(partial[4]) == 4 or partial[4] == " ": #then it's probably a local pointer
temp = partial[0:4]
local_pointer = "$" + temp
else:
errors += "found \": ; \" and another \" \" in line " + str(line_id) + ", but don't know what to do (debug15)" + "\n"
errors += "line is: " + line + "\n"
continue
else:
if len(partial) > 3 and partial[2] == ":": #14:3BAC
temp = partial[2].split(":")[1]
if len(temp) == 3 or len(temp) == 4:
local_pointer = "$" + temp
else:
temp = temp.split(" ")[0]
local_pointer = "$" + temp
elif len(partial) == 4 or (len(partial) == 3 and is_probably_pointer(partial)):
local_pointer = "$" + partial
else:
errors += "found \": ; \" in line " + str(line_id) + ", but don't know what to do (debug16)" + "\n"
errors += "line is: " + line + "\n"
continue
else:
#this line doesn't have a label
continue
if local_pointer != None and not is_probably_pointer(local_pointer.replace("0x", "").replace("$", "")):
continue
line_label = line.split(": ;")[0]
if address == None and local_pointer != None:
temp = int(local_pointer.replace("$", "0x"), 16)
if temp < 0x4000 or bank_id == 0:
address = temp
else:
address = calculate_pointer(int(local_pointer.replace("$", "0x"), 16), bank_id)
elif local_pointer == None and address != None:
if address < 0x4000:
local_pointer = hex(address).replace("0x", "$")
else:
local_pointer = hex((address % 0x4000) + 0x4000).replace("0x", "$")
print line_label + " is at " + hex(address)
label = {
"line_number": line_id,
"bank_id": bank_id,
"label": line_label,
"local_pointer": local_pointer,
"address": address
}
labels.append(label)
current_line_offset += 1
label_errors += errors
return labels
def scan_for_predefined_labels():
"""looks through the asm file for labels at specific addresses,
this relies on the label having its address after. ex:
ViridianCity_h: ; 0x18357 to 0x18384 (45 bytes) (bank=6) (id=1)
PalletTownText1: ; 4F96 0x18f96
ViridianCityText1: ; 0x19102
It would be more productive to use rgbasm to spit out all label
addresses, but faster to write this script. rgbasm would be able
to grab all label addresses better than this script..
"""
bank_intervals = {}
all_labels = []
if asm is None:
load_asm()
#figure out line numbers for each bank
for bank_id in range(0x2d):
abbreviation = ("%.x" % (bank_id)).upper()
abbreviation_next = ("%.x" % (bank_id+1)).upper()
if bank_id == 0:
abbreviation = "0"
abbreviation_next = "1"
start_line_id = index(asm, lambda line: "\"bank" + abbreviation + "\"" in line)
if bank_id != 0x2c:
end_line_id = index(asm, lambda line: "\"bank" + abbreviation_next + "\"" in line)
else:
end_line_id = len(asm) - 1
print "bank" + abbreviation + " starts at " + str(start_line_id) + " to " + str(end_line_id)
bank_intervals[bank_id] = {
"start": start_line_id,
"end": end_line_id,
}
for bank_id in bank_intervals.keys():
bank_data = bank_intervals[bank_id]
start_line_id = bank_data["start"]
end_line_id = bank_data["end"]
labels = get_labels_between(start_line_id, end_line_id, bank_id)
#bank_intervals[bank_id]["labels"] = labels
all_labels.extend(labels)
write_all_labels(all_labels)
return all_labels
def write_all_labels(all_labels):
fh = open("labels.json", "w")
fh.write(json.dumps(all_labels))
fh.close()
def analyze_intervals():
"""find the largest baserom.gbc intervals"""
global asm
global processed_incbins
if asm == None:
load_asm()
if processed_incbins == {}:
isolate_incbins()
process_incbins()
results = []
ordered_keys = sorted(processed_incbins, key=lambda entry: processed_incbins[entry]["interval"])
ordered_keys.reverse()
for key in ordered_keys:
results.append(processed_incbins[key])
return results
if __name__ == "__main__":
#load map headers
load_rom()
load_map_pointers()
read_all_map_headers()
#load incbins (mandatory)
load_asm()
#isolate_incbins()
#process_incbins()
#print processed_incbins
#line_number = find_incbin_to_replace_for(0x492c3)
#newlines = split_incbin_line_into_three(line_number, 0x492c3, 12)
#diff = generate_diff_insert(line_number, newlines)
#print diff
#insert_map_header_asm(86)
#dump_all_remaining_maps()
scan_for_predefined_labels()
print "Errors:"
print label_errors

View file

@ -1,716 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-06
#analyze texts, how many commands are unknown?
import extract_maps
import analyze_incbins #for asm
try:
from pretty_map_headers import map_name_cleaner, txt_bytes, spacing, constant_abbreviation_bytes
except Exception, exc: pass
from operator import itemgetter
import sys
debug = False #set to True to increase logging output
#how many times is each command byte called?
totals = {}
total_text_commands = 0
should_be_total = 0
def get_text_pointer(texts_pointer, text_id):
if type(texts_pointer) == str: texts_pointer = int(texts_pointer, 16)
if type(text_id) == str: text_id = int(text_id)
byte1 = ord(extract_maps.rom[texts_pointer + ((text_id - 1) * 2)])
byte2 = ord(extract_maps.rom[texts_pointer + ((text_id - 1) * 2) + 1])
pointer = (byte1 + (byte2 << 8))
return pointer
def how_many_until(byte, starting):
index = extract_maps.rom.find(byte, starting)
return index-starting
def print_command_debug_info(command_byte, text_id, text_pointer, map_id):
if debug:
print "byte is " + str(command_byte) + " on text #" + str(text_id) + " at " + hex(text_pointer) + " on map " + str(map_id) + " (" + extract_maps.map_headers[map_id]["name"] + ")"
def add_command_byte_to_totals(byte):
global totals
if not byte in totals.keys(): totals[byte] = 1
else: totals[byte] += 1
def process_00_subcommands(start_address, end_address):
"""split this text up into multiple lines
based on subcommands ending each line"""
lines = {}
subsection = extract_maps.rom[start_address:end_address+1]
line_count = 0
current_line = []
for pbyte in subsection:
byte = ord(pbyte)
current_line.append(byte)
if byte == 0x4f or byte == 0x51 or byte == 0x55:
lines[line_count] = current_line
current_line = []
line_count += 1
#don't forget the last line
lines[line_count] = current_line
line_count += 1
return lines
def parse_text_script(text_pointer, text_id, map_id, txfar=False):
global total_text_commands
offset = text_pointer
commands = {}
command_counter = 0
if extract_maps.rom == None:
extract_maps.load_rom()
end = False
while not end:
command = {}
command_byte = ord(extract_maps.rom[offset])
print_command_debug_info(command_byte, text_id, text_pointer, map_id)
if command_byte == 0:
#read until $57, $50 or $58
jump57 = how_many_until(chr(0x57), offset)
jump50 = how_many_until(chr(0x50), offset)
jump58 = how_many_until(chr(0x58), offset)
#whichever command comes first
jump = min([jump57, jump50, jump58])
end_address = offset + jump - 1 #we want the address before $57
command = {"type": command_byte,
"start_address": offset,
"end_address": end_address,
"size": jump,
"lines": process_00_subcommands(offset+1, end_address),
}
offset += jump
elif command_byte == 0x17:
#TX_FAR [pointer][bank]
pointer_byte1 = ord(extract_maps.rom[offset+1])
pointer_byte2 = ord(extract_maps.rom[offset+2])
pointer_bank = ord(extract_maps.rom[offset+3])
pointer = (pointer_byte1 + (pointer_byte2 << 8))
pointer = extract_maps.calculate_pointer(pointer, pointer_bank)
command = {"type": command_byte,
"start_address": offset,
"end_address": offset + 3, #last byte belonging to this command
"pointer": pointer, #parameter
}
offset += 3 + 1
elif command_byte == 0x50 or command_byte == 0x57 or command_byte == 0x58: #end text
command = {"type": command_byte,
"start_address": offset,
"end_address": offset,
}
#this byte simply indicates to end the script
end = True
#this byte simply indicates to end the script
if command_byte == 0x50 and ord(extract_maps.rom[offset+1]) == 0x50: #$50$50 means end completely
end = True
commands[command_counter+1] = command
#also save the next byte, before we quit
commands[command_counter+1]["start_address"] += 1
commands[command_counter+1]["end_address"] += 1
add_command_byte_to_totals(command_byte)
elif command_byte == 0x50: #only end if we started with $0
if len(commands.keys()) > 0:
if commands[0]["type"] == 0x0: end = True
elif command_byte == 0x57 or command_byte == 0x58: #end completely
end = True
offset += 1 #go past this 0x50
elif command_byte == 0x1:
#01 = text from RAM. [01][2-byte pointer]
size = 3 #total size, including the command byte
pointer_byte1 = ord(extract_maps.rom[offset+1])
pointer_byte2 = ord(extract_maps.rom[offset+2])
command = {"type": command_byte,
"start_address": offset+1,
"end_address": offset+2, #last byte belonging to this command
"pointer": [pointer_byte1, pointer_byte2], #RAM pointer
}
#view near these bytes
#subsection = extract_maps.rom[offset:offset+size+1] #peak ahead
#for x in subsection:
# print hex(ord(x))
#print "--"
offset += 2 + 1 #go to the next byte
#use this to look at the surrounding bytes
if debug:
print "next command is: " + hex(ord(extract_maps.rom[offset])) + " ... we are at command number: " + str(command_counter) + " near " + hex(offset) + " on map_id=" + str(map_id) + " for text_id=" + str(text_id) + " and txfar(recursion)=" + str(txfar)
elif command_byte == 0x7:
#07 = shift texts 1 row above (2nd line becomes 1st line); address for next text = 2nd line. [07]
size = 1
command = {"type": command_byte,
"start_address": offset,
"end_address": offset,
}
offset += 1
elif command_byte == 0x3:
#03 = set new address in RAM for text. [03][2-byte RAM address]
size = 3
command = {"type": command_byte, "start_address": offset, "end_address": offset+2}
offset += size
elif command_byte == 0x4: #draw box
#04 = draw box. [04][2-Byte pointer][height Y][width X]
size = 5 #including the command
command = {
"type": command_byte,
"start_address": offset,
"end_address": offset + size,
"pointer_bytes": [ord(extract_maps.rom[offset+1]), ord(extract_maps.rom[offset+2])],
"y": ord(extract_maps.rom[offset+3]),
"x": ord(extract_maps.rom[offset+4]),
}
offset += size + 1
elif command_byte == 0x5:
#05 = write text starting at 2nd line of text-box. [05][text][ending command]
#read until $57, $50 or $58
jump57 = how_many_until(chr(0x57), offset)
jump50 = how_many_until(chr(0x50), offset)
jump58 = how_many_until(chr(0x58), offset)
#whichever command comes first
jump = min([jump57, jump50, jump58])
end_address = offset + jump - 1 #we want the address before $57
command = {"type": command_byte,
"start_address": offset,
"end_address": end_address,
"size": jump,
"lines": process_00_subcommands(offset+1, end_address),
}
offset = end_address + 1
elif command_byte == 0x6:
#06 = wait for keypress A or B (put blinking arrow in textbox). [06]
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x7:
#07 = shift texts 1 row above (2nd line becomes 1st line); address for next text = 2nd line. [07]
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x8:
#08 = asm until whenever
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
end = True
elif command_byte == 0x9:
#09 = write hex-to-dec number from RAM to textbox [09][2-byte RAM address][byte bbbbcccc]
# bbbb = how many bytes to read (read number is big-endian)
# cccc = how many digits display (decimal)
#(note: max of decimal digits is 7,i.e. max number correctly displayable is 9999999)
ram_address_byte1 = ord(extract_maps.rom[offset+1])
ram_address_byte2 = ord(extract_maps.rom[offset+2])
read_byte = ord(extract_maps.rom[offset+3])
command = {
"type": command_byte,
"address": [ram_address_byte1, ram_address_byte2],
"read_byte": read_byte, #split this up when we make a macro for this
}
offset += 4
elif command_byte == 0xB:
#0B = sound_86 (Hiro obtains ITEM)[same as 0F]
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0xE:
#0E = sound_91 (learnt something)
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0xF:
#0F = sound_86 (given rare candy)[same as 0B]
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x10:
#10 = sound_89 (PKMN successfully caught)
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x11:
#11 = sound_94 (Hiro gives OAK the PARCEL)
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x12:
#12 = sound_9A (successfully caught)
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x13:
#13 = sound_98 (song heard when "new data will be added for..")
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x14:
#14 = MonCry (Nidorina)
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x15:
#14 = MonCry (Pidgeot)
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x16:
#14 = MonCry (Dewgong)
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x19:
#19 = play a 'bump' noise
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x1F:
#1F = play some pokemon's roar, don't know which..
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x20:
#20 = oddish roar?
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x3F:
#3F = some other roar
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x9D:
#9D = a roar or some other sound, four times in quick succession
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x76:
#76 = another roar
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0xCA:
#CA = stop music, start this other song that i can't name
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0xF6:
#F6 = play a 'blurp blurp' noise.. like something is increasing
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0xFA:
#FA = change music to champion song?
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0xFE:
#FE = another roar, kinda glitchy?
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0xFF:
#FF = change music to a specific song that i don't know the name of
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
else:
#if len(commands) > 0:
# print "Unknown text command " + hex(command_byte) + " at " + hex(offset) + ", script began with " + hex(commands[0]["type"])
if debug:
print "Unknown text command at " + hex(offset) + " - command: " + hex(ord(extract_maps.rom[offset])) + " on map_id=" + str(map_id) + " text_id=" + str(text_id)
#end at the first unknown command
end = True
add_command_byte_to_totals(command_byte)
commands[command_counter] = command
command_counter += 1
total_text_commands += len(commands)
return commands
def analyze_texts():
global should_be_total
texts = {}
for map_id in extract_maps.map_headers:
if map_id in extract_maps.bad_maps: continue #skip
map2 = extract_maps.map_headers[map_id]
map2["texts"] = {}
referenced_texts = map2["referenced_texts"]
should_be_total += len(referenced_texts)
texts_pointer = int(map2["texts_pointer"], 16)
#print "Checking texts on... map_id=" + str(map_id) + " and len(referenced_texts)=" + str(len(referenced_texts))
for text_id in referenced_texts:
text_pointer = get_text_pointer(texts_pointer, text_id)
if 0x4000 <= text_pointer <= 0x7fff: #only care about bank when it's between 4000-7fff
text_pointer = extract_maps.calculate_pointer(text_pointer, int(map2["bank"], 16))
#print "Working on map id=" + str(map2["id"]) + " and text id=" + str(text_id)
#print "for map_id=" + str(map_id) + " texts_pointer=" + hex(texts_pointer) + " text_id=" + str(text_id) + " the pointer=" + hex(text_pointer)
commands = parse_text_script(text_pointer, text_id, map_id)
#process TX_FARs
for command_id in commands:
#skip commands starting with an unknown command byte
if len(commands[command_id]) == 0: continue
if commands[command_id]["type"] == 0x17:
TX_FAR = parse_text_script(commands[command_id]["pointer"], text_id, map_id, txfar=True)
if debug:
if len(TX_FAR.keys()) > 0:
#print "TX_FAR object: " + str(TX_FAR)
print "processing a TX_FAR at " + hex(commands[command_id]["pointer"]) + "... first byte is: " + str(ord(extract_maps.rom[commands[command_id]["pointer"]])) + " .. offset: " + hex(commands[command_id]["pointer"])
##sys.exit(0)
commands[command_id]["TX_FAR"] = TX_FAR
#map2["texts"][text_id][command_id]["TX_FAR"] = parse_text_script(command["pointer"], text_id, map_id)
map2["texts"][text_id] = commands
texts[map_id] = map2["texts"]
extract_maps.map_headers[map_id]["texts"] = map2["texts"]
return texts
def find_missing_08s(all_texts):
"""determines which $08s have yet to be inserted
based on their start addresses"""
missing_08s = 0
for map_id in all_texts.keys():
for text_id in all_texts[map_id].keys():
for line_id in all_texts[map_id][text_id].keys():
if not line_id == 0:
current_line = all_texts[map_id][text_id][line_id]
if "type" in current_line.keys():
if current_line["type"] == 0x8:
missing_08s += 1
print "missing $08 on map_id=" + str(map_id) + " text_id=" + str(text_id) + " line_id=" + str(line_id) + " at " + hex(current_line["start_address"])
return missing_08s
def text_pretty_printer_at(start_address, label="SomeLabel"):
commands = parse_text_script(start_address, None, None)
needs_to_begin_with_0 = True #how should this be determined?
#wanted_command = None
#if needs_to_begin_with_0:
# wanted_command = None
# for command_id in commands:
# command = commands[command_id]
# if command["type"] == 0:
# wanted_command = command_id
#
# if wanted_command == None:
# raise "error: address did not start with a $0 text"
#start with zero please
byte_count = 0
output = ""
had_text_end_byte = False
had_text_end_byte_57_58 = False
had_db_last = False
first_line = True
for this_command in commands.keys():
if not "lines" in commands[this_command].keys():
command = commands[this_command]
if not "type" in command.keys():
print "ERROR in command: " + str(command)
continue #dunno what to do here?
if command["type"] == 0x1: #TX_RAM
if first_line:
output = "\n"
output += label + ": ; " + hex(start_address)
first_line = False
p1 = command["pointer"][0]
p2 = command["pointer"][1]
#remember to account for big endian -> little endian
output += "\n" + spacing + "TX_RAM $%.2x%.2x" %(p2, p1)
byte_count += 3
had_db_last = False
elif command["type"] == 0x17: #TX_FAR
if first_line:
output = "\n"
output += label + ": ; " + hex(start_address)
first_line = False
#p1 = command["pointer"][0]
#p2 = command["pointer"][1]
output += "\n" + spacing + "TX_FAR _" + label + " ; " + hex(command["pointer"])
byte_count += 4 #$17, bank, address word
had_db_last = False
elif command["type"] == 0x9: #TX_RAM_HEX2DEC
if first_line:
output = "\n" + label + ": ; " + hex(start_address)
first_line = False
#address, read_byte
output += "\n" + spacing + "TX_NUM $%.2x%.2x, $%.2x" % (command["address"][1], command["address"][0], command["read_byte"])
had_db_last = False
byte_count += 4
elif command["type"] == 0x50 and not had_text_end_byte:
#had_text_end_byte helps us avoid repeating $50s
if first_line:
output = "\n" + label + ": ; " + hex(start_address)
first_line = False
if had_db_last:
output += ", $50"
else:
output += "\n" + spacing + "db $50"
byte_count += 1
had_db_last = True
elif command["type"] in [0x57, 0x58] and not had_text_end_byte_57_58:
if first_line: #shouldn't happen, really
output = "\n" + label + ": ; " + hex(start_address)
first_line = False
if had_db_last:
output += ", $%.2x" % (command["type"])
else:
output += "\n" + spacing + "db $%.2x" % (command["type"])
byte_count += 1
had_db_last = True
elif command["type"] in [0x57, 0x58] and had_text_end_byte_57_58:
pass #this is ok
elif command["type"] == 0x50 and had_text_end_byte:
pass #this is also ok
elif command["type"] == 0x0b:
if first_line:
output = "\n" + label + ": ; " + hex(start_address)
first_line = False
if had_db_last:
output += ", $0b"
else:
output += "\n" + spacing + "db $0B"
byte_count += 1
had_db_last = True
elif command["type"] == 0x11:
if first_line:
output = "\n" + label + ": ; " + hex(start_address)
first_line = False
if had_db_last:
output += ", $11"
else:
output += "\n" + spacing + "db $11"
byte_count += 1
had_db_last = True
elif command["type"] == 0x6: #wait for keypress
if first_line:
output = "\n" + label + ": ; " + hex(start_address)
first_line = False
if had_db_last:
output += ", $6"
else:
output += "\n" + spacing + "db $6"
byte_count += 1
had_db_last = True
else:
print "ERROR in command: " + hex(command["type"])
had_db_last = False
#everything else is for $0s, really
continue
lines = commands[this_command]["lines"]
#reset this in case we have non-$0s later
had_db_last = False
#add the ending byte to the last line- always seems $57
#this should already be in there, but it's not because of a bug in the text parser
lines[len(lines.keys())-1].append(commands[len(commands.keys())-1]["type"])
if first_line:
output = "\n"
output += label + ": ; " + hex(start_address) + "\n"
first_line = False
else:
output += "\n"
first = True #first byte
for line_id in lines:
line = lines[line_id]
output += spacing + "db "
if first and needs_to_begin_with_0:
output += "$0, "
first = False
byte_count += 1
quotes_open = False
first_byte = True
was_byte = False
for byte in line:
if byte == 0x50:
had_text_end_byte = True #don't repeat it
if byte in [0x58, 0x57]:
had_text_end_byte_57_58 = True
if byte in txt_bytes:
if not quotes_open and not first_byte: #start text
output += ", \""
quotes_open = True
first_byte = False
if not quotes_open and first_byte: #start text
output += "\""
quotes_open = True
output += txt_bytes[byte]
elif byte in constant_abbreviation_bytes:
if quotes_open:
output += "\""
quotes_open = False
if not first_byte:
output += ", "
output += constant_abbreviation_bytes[byte]
else:
if quotes_open:
output += "\""
quotes_open = False
#if you want the ending byte on the last line
#if not (byte == 0x57 or byte == 0x50 or byte == 0x58):
if not first_byte:
output += ", "
output += "$" + hex(byte)[2:]
was_byte = True
#add a comma unless it's the end of the line
#if byte_count+1 != len(line):
# output += ", "
first_byte = False
byte_count += 1
#close final quotes
if quotes_open:
output += "\""
quotes_open = False
output += "\n"
include_newline = "\n"
if len(output)!=0 and output[-1] == "\n":
include_newline = ""
output += include_newline + "; " + hex(start_address) + " + " + str(byte_count) + " bytes = " + hex(start_address + byte_count)
print output
return (output, byte_count)
def is_label_in_asm(label):
for line in analyze_incbins.asm:
if label in line:
if line[0:len(label)] == label:
return True
return False
def find_undone_texts():
usable_table = {}
if analyze_incbins.asm == None:
analyze_incbins.load_asm()
for map_id in extract_maps.map_headers:
#skip bad maps
if map_id in extract_maps.bad_maps: continue
map2 = extract_maps.map_headers[map_id]
name = map_name_cleaner(map2["name"], None)[:-2] + "Text"
for text_id in map2["referenced_texts"]:
label = name + str(text_id)
if len(extract_maps.map_headers[map_id]["texts"][text_id].keys()) == 0: continue
if len(extract_maps.map_headers[map_id]["texts"][text_id][0].keys()) == 0: continue
try:
address = extract_maps.map_headers[map_id]["texts"][text_id][0]["start_address"]
except:
address = extract_maps.map_headers[map_id]["texts"][text_id][1]["start_address"]
if not is_label_in_asm(label):
print label + " map_id=" + str(map_id) + " text_id=" + str(text_id) + " at " + hex(address) + " byte is: " + hex(ord(extract_maps.rom[address]))
if not address in usable_table.keys():
usable_table[address] = 1
else:
usable_table[address] += 1
print "\n\n which ones are priority?"
sorted_results = sorted(usable_table.iteritems(), key=itemgetter(1), reverse=True)
for result in sorted_results:
print str(result[1]) + " times: " + hex(result[0])
def scan_rom_for_tx_fars(printer=True):
"""find TX_FARs
search only addresses that are INCBINed
keep only TX_FARs that are valid
returns a list of [TX_FAR target address, TX_FAR address]"""
rom = extract_maps.rom
analyze_incbins.load_asm()
analyze_incbins.isolate_incbins()
analyze_incbins.process_incbins()
possible_tx_fars = []
possible_tx_far_targets = []
for incbin_line_number in analyze_incbins.processed_incbins.keys():
incbin = analyze_incbins.processed_incbins[incbin_line_number]
start_address = incbin["start"]
end_address = incbin["end"]
if incbin["interval"] == 0: continue #skip this one
subrom = rom[start_address:end_address]
for address in range(start_address, end_address):
current_byte = ord(rom[address])
if current_byte == 0x17:
if ord(rom[address+4]) == 0x50:
byte1 = ord(rom[address+1])
byte2 = ord(rom[address+2])
address2 = byte1 + (byte2 << 8)
if address2 > 0x3fff:
address2 = extract_maps.calculate_pointer(address2, ord(rom[address+3]))
#print "possible TX_FAR at " + hex(address) + " to " + hex(address2)
possible_tx_fars.append(address)
possible_tx_far_targets.append([address2, address])
if printer:
pre_handled = []
#address_bundle is [TX_FAR target address, TX_FAR address]
for address_bundle in possible_tx_far_targets:
if address_bundle[0] in [0xa82f8, 0xa8315]:
continue #bad
if address_bundle[0] in pre_handled:
continue #already did this
print "-------"
print "TX_FAR is at: " + hex(address_bundle[1])
#let's try printing out the TX_FAR?
text_pretty_printer_at(address_bundle[1], "blah")
text_pretty_printer_at(address_bundle[0], "_blah")
print "-------"
pre_handled.append(address_bundle[0])
return possible_tx_far_targets
if __name__ == "__main__":
extract_maps.load_rom()
extract_maps.load_map_pointers()
extract_maps.read_all_map_headers()
#text_output = analyze_texts()
#print text_output
#these aren't really "missing", just a certain type that was
#known to be missed on a first pass.
#missing_08s = find_missing_08s(text_output)
#print "\n\n---- stats ----\n\n"
#popular_text_commands = sorted(totals.iteritems(), key=itemgetter(1), reverse=True)
#convert the first values (command byte) to hex
#for popular_item in popular_text_commands:
# print hex(popular_item[0]) + " was used " + str(popular_item[1]) + " times."
#print "popular text commands: " + str(popular_text_commands)
#print "total text commands: " + str(total_text_commands)
#print "total text scripts: " + str(should_be_total)
#print "missing 08s: " + str(missing_08s)
#print "\n\n"
#text_pretty_printer_at(0x800b1)
#find_undone_texts()
scan_rom_for_tx_fars()

View file

@ -1,104 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-15
#help with connection math
import extract_maps
from pretty_map_headers import map_constants, map_name_cleaner, offset_to_pointer
def print_connections(map_id, in_connection_id=None, do_output=False):
map1 = extract_maps.map_headers[map_id]
map1_name = map1["name"]
connections = map1["connections"]
output = ""
if in_connection_id != None:
connections2 = {}
connections2[in_connection_id] = connections[in_connection_id]
connections = connections2
for connection_id in connections:
connection = connections[connection_id]
direction = connection["direction"]
connected_pointer = int(connection["connected_map_tile_pointer"], 16)
current_pointer = int(connection["current_map_tile_pointer"], 16)
map2_id = connection["map_id"]
map2 = extract_maps.map_headers[map2_id]
map2_name = map2["name"]
map2_cname = map_name_cleaner(map2["name"], None)[:-2]
map2_bank = int(map2["bank"], 16)
map2_blocks_pointer = offset_to_pointer(int(map2["map_pointer"], 16))
map2_height = int(map2["y"], 16)
map2_width = int(map2["x"], 16)
output += map1_name + " (id=" + str(map_id) + ") " + direction + " to " + map2_name + "\n"
output += "map2 blocks pointer: " + hex(map2_blocks_pointer) + "\n"
output += "map2 height: " + str(map2_height) + "\n"
output += "map2 width: " + str(map2_width) + "\n"
output += "map1 connection pointer: " + hex(connected_pointer) + "\n"
shift = 0
#not sure about the calculated shift for NORTH or SOUTH
if direction == "NORTH":
calculated = map2_blocks_pointer + (map2_height - 3) * map2_width
result = connected_pointer - calculated
if result != 0:
shift = result #seems to always be 2?
calculated = map2_blocks_pointer + (map2_height - 3) * map2_width + shift
output += "shift: " + str(shift) + "\n"
formula = map2_cname + "Blocks + (" + map2_cname + "Height - 3) * " + map2_cname + "Width + " + str(shift)
else:
formula = map2_cname + "Blocks + (" + map2_cname + "Height - 3) * " + map2_cname + "Width"
elif direction == "SOUTH":
calculated = map2_blocks_pointer
result = connected_pointer - calculated
formula = map2_cname + "Blocks"
if result != 0:
shift = result
calculated = map2_blocks_pointer + shift
output += "shift: " + str(shift) + "\n"
formula += " + " + str(shift)
elif direction == "WEST":
calculated = map2_blocks_pointer - 3 + (map2_width)
result = connected_pointer - calculated
formula = map2_cname + "Blocks - 3 + (" + map2_cname + "Width)"
if result != 0:
shift = result / map2_width
shift += 1
calculated = map2_blocks_pointer - 3 + (map2_width * shift)
output += "shift: " + str(shift) + "\n"
formula = map2_cname + "Blocks - 3 + (" + map2_cname + "Width * " + str(shift) + ")"
elif direction == "EAST":
calculated = map2_blocks_pointer + (map2_width)
result = connected_pointer - calculated
output += ".. result is: " + str(result) + "\n"
formula = map2_cname + "Blocks + (" + map2_cname + "Width)"
if result != 0:
shift = result / map2_width
shift += 1
calculated = map2_blocks_pointer + (map2_width * shift)
output += "shift: " + str(shift) + "\n"
formula = map2_cname + "Blocks" + " + (" + map2_cname + "Width * " + str(shift) + ")"
output += "formula: " + formula + "\n"
result = connected_pointer - calculated
output += "result: " + str(result) + "\n"
output += "\n\n"
if in_connection_id != None:
return formula
if do_output == True:
return output
if __name__ == "__main__":
extract_maps.load_rom()
extract_maps.load_map_pointers()
extract_maps.read_all_map_headers()
#trouble:
#print_connections(13)
#print_connections(15)
for map_id in extract_maps.map_headers.keys():
if map_id not in extract_maps.bad_maps:
print print_connections(map_id, do_output=True)

View file

@ -1,702 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-02
#url: http://hax.iimarck.us/files/rbheaders.txt
import json
#parse hex values as base 16 (see calculate_pointer)
base = 16
#where to load the rom from
rom_filename = "../baserom.gbc"
rom = None #load the rom later
#map header pointers start at 0x1AE
start_map_header_pointers = 0x1AE
#bank bytes for each map header start at 0xC23D
start_map_header_pointer_banks = 0xC23D
#number of maps in this list
map_count = 0xF8 #including the 0th the total is is 248 or 0xF8
bad_maps = [0x0b, 0x45, 0x4b, 0x4e, 0x69, 0x6a, 0x6b, 0x6d, 0x6e, 0x6f, 0x70, 0x72, 0x73, 0x74, 0x75, 0xad, 0xcc, 0xcd, 0xce, 0xe7, 0xed, 0xee, 0xf1, 0xf2, 0xf3, 0xf4]
maps = {
0x00: "Pallet Town",
0x01: "Viridian City",
0x02: "Pewter City",
0x03: "Cerulean City",
0x04: "Lavender Town", #??
0x05: "Vermilion City", #correct
0x06: "Celadon City",
0x07: "Fuchsia City",
0x08: "Cinnabar Island",
0x09: "Indigo Plateau",
0x0A: "Saffron City",
0x0B: "FREEZE",
0x0C: "Route 1",
0x0D: "Route 2",
0x0E: "Route 3",
0x0F: "Route 4",
0x10: "Route 5",
0x11: "Route 6",
0x12: "Route 7",
0x13: "Route 8",
0x14: "Route 9",
0x15: "Route 10",
0x16: "Route 11",
0x17: "Route 12",
0x18: "Route 13",
0x19: "Route 14",
0x1A: "Route 15",
0x1B: "Route 16",
0x1C: "Route 17",
0x1D: "Route 18",
0x1E: "Route 19",
0x1F: "Route 20",
0x20: "Route 21",
0x21: "Route 22",
0x22: "Route 23",
0x23: "Route 24",
0x24: "Route 25",
0x25: "Red's House 1F",
0x26: "Red's House 2F",
0x27: "Blue's House",
0x28: "Oak's Lab",
0x29: "Viridian Poke Center",
0x2A: "Viridian Mart",
0x2B: "School",
0x2C: "Viridian House",
0x2D: "Viridian Gym",
0x2E: "Digletts Cave (Route 2)",
0x2F: "Viridian Forest (exit)",
0x30: "Route 2 House",
0x31: "Route 2 Gate",
0x32: "Viridian Forest (Entrance)",
0x33: "Viridian Forest",
0x34: "Museum F1",
0x35: "Museum F2",
0x36: "Pewter Gym",
0x37: "Pewter House (1)",
0x38: "Pewter Mart",
0x39: "Pewter House (2)",
0x3A: "Pewter Pokecenter",
0x3B: "Mt. Moon (1)",
0x3C: "Mt. Moon (2)",
0x3D: "Mt. Moon (3)",
0x3E: "Cerulean House (Trashed)",
0x3F: "Cerulean House (2)",
0x40: "Cerulean Pokecenter",
0x41: "Cerulean Gym",
0x42: "Bike Shop",
0x43: "Cerulean Mart",
0x44: "Mt. Moon Pokecenter",
0x45: "COPY OF: Trashed House",
0x46: "Route 5 Gate",
0x47: "Underground Tunnel Entrance (Route 5)",
0x48: "Day Care M",
0x49: "Route 6 Gate",
0x4A: "Underground Tunnel Entrance (Route 6)",
0x4B: "COPY OF: Underground Tunnel Entrance (Route 6)",
0x4C: "Route 7 Gate",
0x4D: "Underground Path Entrance (Route 7)",
0x4E: "COPY OF: Underground Path Entrance (Route 7)",
0x4F: "Route 8 Gate",
0x50: "Underground Path Entrance (Route 8)",
0x51: "Rock Tunnel Pokecenter",
0x52: "Rock Tunnel (1)",
0x53: "Power Plant",
0x54: "Route 11 Gate",
0x55: "Digletts Cave Entrance (Route 11)",
0x56: "Route 11 Gate (Upstairs)",
0x57: "Route 12 Gate",
0x58: "Bill's House",
0x59: "Vermilion Pokecenter",
0x5A: "Fan Club",
0x5B: "Vermilion Mart",
0x5C: "Vermilion Gym",
0x5D: "Vermilion House (1)",
0x5E: "Vermilion Dock",
0x5F: "S.S. Anne (1)",
0x60: "S.S. Anne (2)",
0x61: "S.S. Anne (3)",
0x62: "S.S. Anne (4)",
0x63: "S.S. Anne (5)",
0x64: "S.S. Anne (6)",
0x65: "S.S. Anne (7)",
0x66: "S.S. Anne (8)",
0x67: "S.S. Anne (9)",
0x68: "S.S. Anne (10)",
0x69: "FREEZE",
0x6A: "FREEZE",
0x6B: "FREEZE",
0x6C: "Victory Road (1)",
0x6D: "FREEZE",
0x6E: "FREEZE",
0x6F: "FREEZE",
0x70: "FREEZE",
0x71: "Lance",
0x72: "FREEZE",
0x73: "FREEZE",
0x74: "FREEZE",
0x75: "FREEZE",
0x76: "Hall of Fame Room",
0x77: "Underground Path (N/S)",
0x78: "Gary",
0x79: "Underground Path (W/E)",
0x7A: "Celadon Mart (1)",
0x7B: "Celadon Mart (2)",
0x7C: "Celadon Mart (3)",
0x7D: "Celadon Mart (4)",
0x7E: "Celadon Mart Roof",
0x7F: "Celadon Mart Elevator",
0x80: "Celadon Mansion (1)",
0x81: "Celadon Mansion (2)",
0x82: "Celadon Mansion (3)",
0x83: "Celadon Mansion (4)",
0x84: "Celadon Mansion (5)",
0x85: "Celadon Pokecenter",
0x86: "Celadon Gym",
0x87: "Celadon Game Corner",
0x88: "Celadon Mart 5",
0x89: "Celadon Prize Room",
0x8A: "Celadon Diner",
0x8B: "Celadon House",
0x8C: "Celadon Hotel",
0x8D: "Lavender Pokecenter",
0x8E: "Pokemon Tower (1)",
0x8F: "Pokemon Tower (2)",
0x90: "Pokemon Tower (3)",
0x91: "Pokemon Tower (4)",
0x92: "Pokemon Tower (5)",
0x93: "Pokemon Tower (6) ",
0x94: "Pokemon Tower (7)",
0x95: "Lavender House (1)",
0x96: "Lavender Mart",
0x97: "Lavender House (2)",
0x98: "Fuchsia Mart",
0x99: "Fuchsia House (1)",
0x9A: "Fuchsia Pokecenter",
0x9B: "Fuchsia House (2)",
0x9C: "Safari Zone Entrance",
0x9D: "Fuchsia Gym",
0x9E: "Fuchsia Meeting Room",
0x9F: "Seafoam Islands (2)",
0xA0: "Seafoam Islands (3)",
0xA1: "Seafoam Islands (4)",
0xA2: "Seafoam Islands (5)",
0xA3: "Vermilion House (2)",
0xA4: "Fuchsia House (3)",
0xA5: "Mansion (1)",
0xA6: "Cinnabar Gym",
0xA7: "Lab (1)",
0xA8: "Lab (2)",
0xA9: "Lab (3)",
0xAA: "Lab (4)",
0xAB: "Cinnabar Pokecenter",
0xAC: "Cinnabar Mart",
0xAD: "COPY: Cinnabar Mart",
0xAE: "Indigo Plateau Lobby",
0xAF: "Copycat's House F1",
0xB0: "Copycat's House F2",
0xB1: "Fighting Dojo",
0xB2: "Saffron Gym",
0xB3: "Saffron House (1)",
0xB4: "Saffron Mart",
0xB5: "Silph Co (1)",
0xB6: "Saffron Pokecenter",
0xB7: "Saffron House (2)",
0xB8: "Route 15 Gate",
0xBA: "Route 16 Gate Map",
0xBB: "Route 16 Gate Upstairs",
0xBC: "Route 16 House",
0xBD: "Route 12 House",
0xBE: "Route 18 Gate",
0xBF: "Route 18 Gate Header",
0xC0: "Seafoam Islands (1)",
0xC1: "Route 22 Gate",
0xC2: "Victory Road (2)",
0xC3: "Route 12 Gate Upstairs",
0xC4: "Vermilion House (3)",
0xC5: "Diglett's Cave",
0xC6: "Victory Road (3)",
0xC7: "Rocket Hideout (1)",
0xC8: "Rocket Hideout (2)",
0xC9: "Rocket Hideout (3)",
0xCA: "Rocket Hideout (4) ",
0xCB: "Rocket Hideout (Elevator)",
0xCC: "FREEZE",
0xCD: "FREEZE",
0xCE: "FREEZE",
0xCF: "Silph Co (2)",
0xD0: "Silph Co (3)",
0xD1: "Silph Co (4)",
0xD2: "Silph Co (5)",
0xD3: "Silph Co (6)",
0xD4: "Silph Co (7)",
0xD5: "Silph Co (8)",
0xD6: "Mansion (2)",
0xD7: "Mansion (3)",
0xD8: "Mansion (4)",
0xD9: "Safari Zone East",
0xDA: "Safari Zone North",
0xDB: "Safari Zone West",
0xDC: "Safari Zone Center",
0xDD: "Safari Zone Rest House (1)",
0xDE: "Safari Zone Secret House",
0xDF: "Safari Zone Rest House (2)",
0xE0: "Safari Zone Rest House (3)",
0xE1: "Safari Zone Rest House (4)",
0xE2: "Unknown Dungeon (2)",
0xE3: "Unknown Dungeon (3)",
0xE4: "Unknown Dungeon (1)",
0xE5: "Name Rater",
0xE6: "Cerulean House (3)",
0xE7: "FREEZE",
0xE8: "Rock Tunnel (2)",
0xE9: "Silph Co (9)",
0xEA: "Silph Co (10)",
0xEB: "Silph Co (11)",
0xEC: "Silph Co (Elevator)",
0xED: "FREEZE",
0xEE: "FREEZE",
0xEF: "Battle Center M",
0xF0: "Trade Center M",
0xF1: "FREEZE",
0xF2: "FREEZE",
0xF3: "FREEZE",
0xF4: "FREEZE",
0xF5: "Lorelei",
0xF6: "Bruno",
0xF7: "Agatha"
}
map_pointers = {
#0x00: {
# "name": "Pallet Town",
# "address": 0x182a1
# },
}
map_headers = {
#0x00: {
# "name": "Pallet Town",
# "address": 0x182a1,
# "tileset"
# "y"
# "x"
# "map_pointer"
# "texts_pointer"
# "script_pointer"
# "connection_byte"
# "num_connections"
# "connections":
# { "0":
# { map_id, connected_map_tile_pointer, current_map_tile_pointer, bigness, width, y, x, window_pointer }
# },
# "object_data_pointer"
# },
}
#haters gonna hate
def load_rom(filename=None):
"load the rom into a global (returns True/False)"
global rom
if not filename:
filename = rom_filename
try:
rom = open(filename, "rb").read()
return True
except Exception, exception:
print "error loading rom"
return False
def assert_rom():
global rom
assert rom, "rom must be loaded, see load_rom()"
def calculate_pointer(short_pointer, bank):
short_pointer = int(short_pointer)
bank = int(bank)
pointer = short_pointer - 0x4000 + (bank * 0x4000)
#result will be an integer
return pointer
def get_nth_map_header_pointer_bank_byte_address(map_id):
"returns the address to the bank byte associated with this map pointer"
address = start_map_header_pointer_banks + map_id
return address
def get_nth_map_header_pointer_bank_byte(map_id):
"returns the bank number for this map header"
assert_rom()
address = get_nth_map_header_pointer_bank_byte_address(map_id)
bank_byte = ord(rom[address])
return bank_byte
def get_nth_map_header_pointer(map_id):
"returns the full pointer to the map header struct for this map"
assert_rom()
#figure out where the bytes for this pointer are located
byte1_address = start_map_header_pointers + (map_id * 2)
byte2_address = start_map_header_pointers + (map_id * 2) + 1
#grab the two bytes making up the partial pointer
byte1 = ord(rom[byte1_address])
byte2 = ord(rom[byte2_address])
#swap the bytes (16-bit pointers for z80 are little endian)
temp = byte1
byte1 = byte2
byte2 = temp
del temp
#combine these into a single pointer (0x byte1 byte2)
partial_pointer = (byte2 + (byte1 << 8))
#print hex(partial_pointer)
#get the bank id
bank = get_nth_map_header_pointer_bank_byte(map_id)
#calculate the full pointer
pointer = calculate_pointer(partial_pointer, bank)
#return it as an integer
return pointer
def load_map_pointers():
global maps
global map_pointers
for map in maps.keys():
pointer = get_nth_map_header_pointer(map)
#print maps[map] + "\t\t\t" + hex(pointer)
entry = {
"name": maps[map],
"address": hex(pointer),
"bank": hex(get_nth_map_header_pointer_bank_byte(map))
}
map_pointers[map] = entry
#print json.dumps(map_pointers)
def read_connection_bytes(connection_bytes, bank):
map_id = ord(connection_bytes[0])
#connection strip
connected_map_tile_pointer_byte1 = ord(connection_bytes[1])
connected_map_tile_pointer_byte2 = ord(connection_bytes[2])
connected_map_tile_pointer = (connected_map_tile_pointer_byte1 + (connected_map_tile_pointer_byte2 << 8))
#connection strip
current_map_tile_pointer_byte1 = ord(connection_bytes[3])
current_map_tile_pointer_byte2 = ord(connection_bytes[4])
current_map_tile_pointer = (current_map_tile_pointer_byte1 + (current_map_tile_pointer_byte2 << 8))
bigness_byte = ord(connection_bytes[5])
width_byte = ord(connection_bytes[6])
y = ord(connection_bytes[7])
x = ord(connection_bytes[8])
#window
window_pointer_byte1 = ord(connection_bytes[9])
window_pointer_byte2 = ord(connection_bytes[10])
window_pointer = (window_pointer_byte1 + (window_pointer_byte2 << 8))
connection_data = {
"map_id": map_id,
"connected_map_tile_pointer": hex(connected_map_tile_pointer),
"current_map_tile_pointer": hex(current_map_tile_pointer),
"bigness": hex(bigness_byte),
"width": hex(width_byte),
"y": y,
"x": x,
"window_pointer": hex(window_pointer),
}
return connection_data
def read_warp_data(address, warp_count):
warps = {}
for warp_id in range(0, warp_count):
offset = address + (warp_id*4) #4 bytes per warp
warp = {}
warp["y"] = ord(rom[offset])
warp["x"] = ord(rom[offset+1])
warp["warp_to_point"] = ord(rom[offset+2])
warp["warp_to_map_id"] = ord(rom[offset+3])
warps[warp_id] = warp
return warps
def read_sign_data(address, sign_count):
signs = {}
for sign_id in range(0, sign_count):
offset = address + (sign_id * 3)
sign = {}
sign["y"] = ord(rom[offset])
sign["x"] = ord(rom[offset+1])
sign["text_id"] = ord(rom[offset+2])
signs[sign_id] = sign
return signs
def read_warp_tos(address, warp_count):
warp_tos = {}
for warp_to_id in range(0, warp_count):
offset = address + (warp_to_id * 4)
warp_to = {}
warp_to["event_displacement"] = [ord(rom[offset]),ord(rom[offset+1])]
warp_to["y"] = ord(rom[offset+2])
warp_to["x"] = ord(rom[offset+3])
warp_tos[warp_to_id] = warp_to
return warp_tos
def get_object_data(address):
if type(address) == str: address = int(address, base)
output = {}
maps_border_tile = ord(rom[address])
number_of_warps = ord(rom[address+1])
if number_of_warps == 0: warps = {}
else:
warps = read_warp_data(address+2, number_of_warps)
offset = number_of_warps * 4
address = address + 2 + offset
number_of_signs = ord(rom[address])
if number_of_signs == 0: signs = {}
else:
signs = read_sign_data(address+1, number_of_signs)
offset = number_of_signs * 3
address = address + 1 + offset
number_of_things = ord(rom[address])
address = address + 1
things = {}
for thing_id in range(0, number_of_things):
thing = {}
picture_number = ord(rom[address])
y = ord(rom[address+1])
x = ord(rom[address+2])
movement1 = ord(rom[address+3])
movement2 = ord(rom[address+4])
text_string_number = ord(rom[address+5])
address += 5 + 1
if text_string_number & (1 << 6) != 0: #trainer
thing["type"] = "trainer"
thing["trainer_type"] = ord(rom[address])
thing["pokemon_set"] = ord(rom[address+1])
address += 2
elif text_string_number & (1 << 7) != 0: #item
thing["type"] = "item"
thing["item_number"] = ord(rom[address])
address += 1
else: #normal person
thing["type"] = "person"
thing["picture_number"] = picture_number
thing["y"] = y
thing["x"] = x
thing["movement1"] = movement1
thing["movement2"] = movement2
thing["original_text_string_number"] = text_string_number
thing["text_string_number"] = text_string_number & 0xF
things[thing_id] = thing
warp_tos = read_warp_tos(address, number_of_warps)
output["maps_border_tile"] = maps_border_tile
output["number_of_warps"] = number_of_warps
output["warps"] = warps
output["number_of_signs"] = number_of_signs
output["signs"] = signs
output["number_of_things"] = number_of_things
output["things"] = things
output["warp_tos"] = warp_tos
return output
def compute_object_data_size(object):
size = 4
size += 6 * (int(object["number_of_things"]))
trainer_count = 0
item_count = 0
for thing in object["things"]:
thing = object["things"][thing]
if thing["type"] == "trainer": trainer_count += 1
elif thing["type"] == "item": item_count += 1
size += 2 * trainer_count
size += item_count
size += 8 * object["number_of_warps"]
size += 3 * object["number_of_signs"]
return size
def get_direction(connection_byte, connection_id):
"""given a connection byte and a connection id, which direction is this connection?
the 0th connection of $5 is SOUTH and the 1st connection is EAST"""
connection_options = [0b1000, 0b0100, 0b0010, 0b0001]
results = ["NORTH", "SOUTH", "WEST", "EAST"]
for option in connection_options:
if (option & connection_byte) == 0:
results[connection_options.index(option)] = ""
#prune results
while "" in results:
results.remove("")
return results[connection_id]
def read_map_header(address, bank):
address = int(address, base)
bank = int(bank, base)
tileset = ord(rom[address])
y = ord(rom[address+1])
x = ord(rom[address+2])
map_pointer_byte1 = ord(rom[address+3])
map_pointer_byte2 = ord(rom[address+4])
partial_map_pointer = (map_pointer_byte1 + (map_pointer_byte2 << 8))
map_pointer = calculate_pointer(partial_map_pointer, bank)
texts_pointer_byte1 = ord(rom[address+5])
texts_pointer_byte2 = ord(rom[address+6])
partial_texts_pointer = (texts_pointer_byte1 + (texts_pointer_byte2 << 8))
texts_pointer = calculate_pointer(partial_texts_pointer, bank)
script_pointer_byte1 = ord(rom[address+7])
script_pointer_byte2 = ord(rom[address+8])
partial_script_pointer = (script_pointer_byte1 + ( script_pointer_byte2 << 8))
script_pointer = calculate_pointer(partial_script_pointer, bank)
connection_byte = ord(rom[address+9]) #0xc = NORTH | SOUTH
# <&IIMarckus> the connection byte is a bitmask allowing 0-4 connections
# <&IIMarckus> each connection is 11 bytes
# <&IIMarckus> or'd
# <&IIMarckus> east = 1, west = 2, south = 4, north = 8
# <&IIMarckus> so a connection byte of 0xc means north/south
# <&IIMarckus> which means there are 22 more bytes, 11 for each connection
# < kanzure> 4 | 8 = c?
# <&IIMarckus> yes
# <&IIMarckus> easier to see if you convert to binary
# <&IIMarckus> 0100 | 1000 = 1100
num_connections = 0
connection_value = bin(connection_byte)[2:]
if connection_value[0] == "1": #NORTH
num_connections += 1
if len(connection_value) > 1 and connection_value[1] == "1": #SOUTH
num_connections += 1
if len(connection_value) > 2 and connection_value[2] == "1": #WEST
num_connections += 1
if len(connection_value) > 3 and connection_value[3] == "1": #EAST
num_connections += 1
#quick test for connection data
#connection0_stuff = rom[(address + 10):(address + 10 + 11)]
#print "Route: " + hex(ord(connection0_stuff[0]))
#setup
connections = {}
#go straight to object data if there are no connections
if num_connections > 0:
for connection in range(0, num_connections):
base_connection_address = address + 10 + (11 * connection)
connection_bytes = rom[base_connection_address : base_connection_address + 11]
connection_data = read_connection_bytes(connection_bytes, bank)
connection_data["direction"] = get_direction(connection_byte, connection)
connections[connection] = connection_data
#we might have to jump around a bit
offset = address + 10 + (11 * num_connections)
#object data
object_data_pointer_byte1 = ord(rom[offset])
object_data_pointer_byte2 = ord(rom[offset+1])
partial_object_data_pointer = (object_data_pointer_byte1 + (object_data_pointer_byte2 << 8))
object_data_pointer = calculate_pointer(partial_object_data_pointer, bank)
object_data = get_object_data(object_data_pointer)
texts = set()
for thing_id in object_data["things"].keys():
thing = object_data["things"][thing_id]
texts.add(thing["text_string_number"])
for sign_id in object_data["signs"].keys():
sign = object_data["signs"][sign_id]
texts.add(sign["text_id"])
texts = list(texts)
number_of_referenced_texts = len(texts)
map_header = {
"tileset": hex(tileset),
"y": hex(y),
"x": hex(x),
"map_pointer": hex(map_pointer),
"texts_pointer": hex(texts_pointer),
"number_of_referenced_texts": number_of_referenced_texts,
"referenced_texts": texts,
"script_pointer": hex(script_pointer),
"connection_byte": hex(connection_byte),
"num_connections": str(num_connections),
"connections": connections, #NORTH, SOUTH, WEST, EAST order matters
"object_data_pointer": hex(object_data_pointer),
"object_data": object_data,
}
return map_header
def read_all_map_headers():
if rom == None: load_rom()
assert_rom()
if len(map_pointers) == 0: load_map_pointers()
for map_id in map_pointers.keys():
if map_id in bad_maps: continue
map2 = map_pointers[map_id]
map_header = read_map_header(map2["address"], map2["bank"])
map_header["id"] = map_id
map_header["name"] = map2["name"]
map_header["address"] = map2["address"]
map_header["bank"] = map2["bank"]
map_headers[map_id] = map_header
return map_headers
if __name__ == "__main__":
#read binary data from file
load_rom()
#where are the map structs?
load_map_pointers()
#print json.dumps(map_pointers)
#experimental...
#print json.dumps(read_map_header(map_pointers[0]["address"], map_pointers[0]["bank"]))
read_all_map_headers()
#print json.dumps(map_headers)
#print map_headers[37]
for header in map_headers:
if header in bad_maps: continue
print "map " + str(header) + " has " + str(map_headers[header]["number_of_referenced_texts"]) + " referenced texts"

View file

@ -1,94 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-14
#split out blocksets into binary data files
# There's also code here to spit out asm for each blockset,
# but it's too many lines and will probably crash rgbasm.
import sys
import extract_maps
extract_maps.load_rom()
spacing = " "
tileblocks = {
"Tset00_Block": [0x645E0, 0x64DE0, ""],
"Tset01_Block": [0x65270, 0x653A0, ""],
"Tset02_Block": [0x693BF, 0x6960F, ""],
"Tset03_Block": [0x6A9FF, 0x6B1FF, ""],
"Tset05_Block": [0x6867F, 0x68DBF, ""],
"Tset08_Block": [0x65980, 0x65BB0, ""],
"Tset09_Block": [0x69BFF, 0x6A3FF, ""],
"Tset0B_Block": [0x6FEF0, 0x70000, ""],
"Tset0D_Block": [0x6E930, 0x6ED10, ""],
"Tset0E_Block": [0x66BF0, 0x66D60, ""],
"Tset0F_Block": [0x6C5C0, 0x6CCA0, ""],
"Tset10_Block": [0x67350, 0x676F0, ""],
"Tset13_Block": [0x66190, 0x66610, ""],
"Tset11_Block": [0x6D0C0, 0x6D8C0, ""],
"Tset12_Block": [0x6DEA0, 0x6E390, ""],
"Tset14_Block": [0x6F2D0, 0x6F670, ""],
"Tset15_Block": [0x6FB20, 0x6FD60, ""],
"Tset16_Block": [0x6B7FF, 0x6C000, ""],
"Tset17_Block": [0x67B50, 0x68000, ""],
}
#10:02 <+sawakita> each block is composed by 4x4 tiles
#10:03 <+sawakita> so you can see a blockset as a list of 16-bytes long arrays
#10:07 <+sawakita> 4 bytes for each row of the block, left-to-right, top-to-down
#10:08 <+sawakita> so first byte is top-left tile
#10:08 <+kanzure> top-left tile byte is a tile id?
#10:08 <+sawakita> exactly
#10:09 <+kanzure> what does a tile id reference
#10:10 <+sawakita> tile ID $00 is the first tile of the tileset's graphics (first 16-bytes, since gfx are 2bpp)
output = ""
for tileblock_id in tileblocks.keys():
tileblock = tileblocks[tileblock_id]
start_address = tileblock[0]
end_address = tileblock[1]
block_count = (end_address - start_address) / 16
main_data = extract_maps.rom[start_address:end_address]
blockset_id = int(tileblock_id[4:6], 16)
output = "; "
output += tileblock_id + " - " + str(block_count) + " blocks (" + hex(start_address) + " to " + hex(end_address) + ")\n"
#print tileblock_id + " has " + str(block_count) + " block tiles."
for block_id in range(0, block_count):
start_address2 = start_address + (16 * block_id)
end_address2 = start_address + (16 * block_id) + 16
data = extract_maps.rom[start_address2:end_address2]
output += spacing + "; block " + str(block_id + 1) + " on " + tileblock_id + "\n"
#output += spacing + spacing + "db $%.2x" % (ord(data[0]))
#for data_bit in range(1,15):
# output += ", $%.2x" % (ord(data[data_bit]))
#output += ", $%.2x" % (ord(data[15]))
#output += "\n"
for row_id in range(0,4):
subdata = data[row_id * 4:row_id * 4 + 4]
subdata2 = [ord(subdata[0]), ord(subdata[1]), ord(subdata[2]), ord(subdata[3])]
output += spacing + spacing + " ; row " + str(row_id + 1) + "\n"
output += spacing + spacing + spacing + "db $%.2x, $%.2x, $%.2x, $%.2x\n" % (subdata2[0], subdata2[1], subdata2[2], subdata2[3])
fh = open("../gfx/blocksets/%.2x.bst" % (blockset_id), "w")
fh.write(main_data)
fh.close()
print output
"""
Tset00_Block:
; block 1
; row 1
db 0, 0, 0, 0
; row 2
db 0, 0, 0, 0
; row 3
db 0, 0, 0, 0
; row 4
db 0, 0, 0, 0
"""

View file

@ -1,37 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-14
#throw tilesets into separate files
import extract_maps
extract_maps.load_rom()
locations = {
"Tset00_GFX": [0x64000, 0x645E0, "00"],
"Tset01_GFX": [0x64DE0, 0x65270, "01"],
"Tset08_GFX": [0x653A0, 0x65980, "08"],
"Tset13_GFX": [0x65BB0, 0x66190, "13"],
"Tset0E_GFX": [0x66610, 0x66BF0, "0e"],
"Tset10_GFX": [0x66D60, 0x67350, "10"],
"Tset17_GFX": [0x676F0, 0x67B50, "17"],
"Tset05_GFX": [0x6807F, 0x6867F, "05"],
"Tset02_GFX": [0x68DBF, 0x693BF, "02"],
"Tset09_GFX": [0x6960F, 0x69BFF, "09"],
"Tset03_GFX": [0x6A3FF, 0x6A9FF, "03"],
"Tset16_GFX": [0x6B1FF, 0x6B7FF, "16"],
"Tset0F_GFX": [0x6C000, 0x6C5C0, "0f"],
"Tset11_GFX": [0x6CCA0, 0x6D0C0, "11"],
"Tset12_GFX": [0x6D8C0, 0x6DEA0, "12"],
"Tset0D_GFX": [0x6E390, 0x6E930, "0d"],
"Tset14_GFX": [0x6ED10, 0x6F2D0, "14"],
"Tset15_GFX": [0x6F670, 0x6FB20, "15"],
"Tset0B_GFX": [0x6FD60, 0x6FEF0, "0b"],
}
for tileset_id in locations.keys():
tileset = locations[tileset_id]
print "writing ../gfx/tilesets/" + tileset[2] + ".2bpp"
fh = open("../gfx/tilesets/" + tileset[2] + ".2bpp", "w")
fh.write(extract_maps.rom[tileset[0]:tileset[1]])
fh.close()
print "Done."

View file

@ -1,82 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-27
#fix trainer header labels to not suck so much
import analyze_incbins
def replace_trainer_header_labels(debug=False):
"""trainer header labels could be better"""
asm = analyze_incbins.asm
if debug: print str(type(asm))
single_asm = "\n".join(asm)
current_map_name = "asdjkl;"
line_id = 0
trainer_header_counter = 1
for line in asm:
trainer_header_base = current_map_name + "TrainerHeader"
trainer_header_name = trainer_header_base + str(trainer_header_counter)
#we've found a TrainerHeaders thing
if "TrainerHeaders:" in line:
current_map_name = line.split("TrainerHeaders")[0]
if line[0:len(current_map_name)] == current_map_name:
trainer_header_counter = 1
#replace a trainer header label
elif "TrainerHeader_" in line and line[0:14] == "TrainerHeader_":
temp = line.split("TrainerHeader_")[1]
temp = temp.split(": ;")[0]
old_label = "TrainerHeader_" + temp
new_label = current_map_name + "TH" + str(trainer_header_counter) #trainer_header_name
single_asm = single_asm.replace(old_label + ":", new_label + ":")
single_asm = single_asm.replace(old_label + "\n", new_label + "\n")
if debug: print "old_label = " + old_label
if debug: print "new_label = " + new_label
trainer_header_counter += 1
elif trainer_header_base in line and line[0:len(trainer_header_base)] == trainer_header_base and (line[len(trainer_header_base)+1:len(trainer_header_base)+2] == ":" or line[len(trainer_header_base)+2:len(trainer_header_base)+3] == ":"):
if line[len(trainer_header_base)+1:len(trainer_header_base)+2] == ":":
trainer_header_counter = int(line[len(trainer_header_base):len(trainer_header_base)+1])
elif line[len(trainer_header_base)+2:len(trainer_header_base)+3] == ":":
trainer_header_counter = int(line[len(trainer_header_base):len(trainer_header_base)+2])
trainer_header_name = trainer_header_base + str(trainer_header_counter)
#replace a text label
elif " TextBeforeBattle" in line and not current_map_name in line:
old_label = line.split("dw ")[1].split(" ;")[0]
new_label = current_map_name + "B4BattleTxt" + str(trainer_header_counter) #trainer_header_name + "BeforeBattleText"
single_asm = single_asm.replace(old_label + ":", new_label + ":")
single_asm = single_asm.replace(old_label + "\n", new_label + "\n")
single_asm = single_asm.replace(old_label + " ;", new_label + " ;")
if debug: print "old_label = " + old_label
if debug: print "new_label = " + new_label
#replace a text label
elif " TextAfterBattle" in line and not current_map_name in line:
old_label = line.split("dw ")[1].split(" ;")[0]
new_label = current_map_name + "AfterBattleTxt" + str(trainer_header_counter) #trainer_header_name + "AfterBattleText"
single_asm = single_asm.replace(old_label + ":", new_label + ":")
single_asm = single_asm.replace(old_label + "\n", new_label + "\n")
single_asm = single_asm.replace(old_label + " ;", new_label + " ;")
if debug: print "old_label = " + old_label
if debug: print "new_label = " + new_label
#replace a text label
elif " TextEndBattle" in line and not current_map_name in line:
old_label = line.split("dw ")[1].split(" ;")[0]
new_label = current_map_name + "EndBattleTxt" + str(trainer_header_counter) #trainer_header_name + "EndBattleText"
single_asm = single_asm.replace(old_label + ":", new_label + ":")
single_asm = single_asm.replace(old_label + "\n", new_label + "\n")
single_asm = single_asm.replace(old_label + " ;", new_label + " ;")
if debug: print "old_label = " + old_label
if debug: print "new_label = " + new_label
line_id += 1
print single_asm
if __name__ == "__main__":
analyze_incbins.load_asm()
replace_trainer_header_labels()

View file

@ -1,853 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-09
import extract_maps
import os
import json
from copy import copy, deepcopy
from pretty_map_headers import random_hash, map_name_cleaner
from ctypes import c_int8
import sys
spacing = " "
temp_opt_table = [
[ "ADC A", 0x8f, 0 ],
[ "ADC B", 0x88, 0 ],
[ "ADC C", 0x89, 0 ],
[ "ADC D", 0x8a, 0 ],
[ "ADC E", 0x8b, 0 ],
[ "ADC H", 0x8c, 0 ],
[ "ADC [HL]", 0x8e, 0 ],
[ "ADC L", 0x8d, 0 ],
[ "ADC x", 0xce, 1 ],
[ "ADD A", 0x87, 0 ],
[ "ADD B", 0x80, 0 ],
[ "ADD C", 0x81, 0 ],
[ "ADD D", 0x82, 0 ],
[ "ADD E", 0x83, 0 ],
[ "ADD H", 0x84, 0 ],
[ "ADD [HL]", 0x86, 0 ],
[ "ADD HL, BC", 0x9, 0 ],
[ "ADD HL, DE", 0x19, 0 ],
[ "ADD HL, HL", 0x29, 0 ],
[ "ADD HL, SP", 0x39, 0 ],
[ "ADD L", 0x85, 0 ],
[ "ADD SP, x", 0xe8, 1 ],
[ "ADD x", 0xc6, 1 ],
[ "AND A", 0xa7, 0 ],
[ "AND B", 0xa0, 0 ],
[ "AND C", 0xa1, 0 ],
[ "AND D", 0xa2, 0 ],
[ "AND E", 0xa3, 0 ],
[ "AND H", 0xa4, 0 ],
[ "AND [HL]", 0xa6, 0 ],
[ "AND L", 0xa5, 0 ],
[ "AND x", 0xe6, 1 ],
[ "BIT 0, A", 0x47cb, 3 ],
[ "BIT 0, B", 0x40cb, 3 ],
[ "BIT 0, C", 0x41cb, 3 ],
[ "BIT 0, D", 0x42cb, 3 ],
[ "BIT 0, E", 0x43cb, 3 ],
[ "BIT 0, H", 0x44cb, 3 ],
[ "BIT 0, [HL]", 0x46cb, 3 ],
[ "BIT 0, L", 0x45cb, 3 ],
[ "BIT 1, A", 0x4fcb, 3 ],
[ "BIT 1, B", 0x48cb, 3 ],
[ "BIT 1, C", 0x49cb, 3 ],
[ "BIT 1, D", 0x4acb, 3 ],
[ "BIT 1, E", 0x4bcb, 3 ],
[ "BIT 1, H", 0x4ccb, 3 ],
[ "BIT 1, [HL]", 0x4ecb, 3 ],
[ "BIT 1, L", 0x4dcb, 3 ],
[ "BIT 2, A", 0x57cb, 3 ],
[ "BIT 2, B", 0x50cb, 3 ],
[ "BIT 2, C", 0x51cb, 3 ],
[ "BIT 2, D", 0x52cb, 3 ],
[ "BIT 2, E", 0x53cb, 3 ],
[ "BIT 2, H", 0x54cb, 3 ],
[ "BIT 2, [HL]", 0x56cb, 3 ],
[ "BIT 2, L", 0x55cb, 3 ],
[ "BIT 3, A", 0x5fcb, 3 ],
[ "BIT 3, B", 0x58cb, 3 ],
[ "BIT 3, C", 0x59cb, 3 ],
[ "BIT 3, D", 0x5acb, 3 ],
[ "BIT 3, E", 0x5bcb, 3 ],
[ "BIT 3, H", 0x5ccb, 3 ],
[ "BIT 3, [HL]", 0x5ecb, 3 ],
[ "BIT 3, L", 0x5dcb, 3 ],
[ "BIT 4, A", 0x67cb, 3 ],
[ "BIT 4, B", 0x60cb, 3 ],
[ "BIT 4, C", 0x61cb, 3 ],
[ "BIT 4, D", 0x62cb, 3 ],
[ "BIT 4, E", 0x63cb, 3 ],
[ "BIT 4, H", 0x64cb, 3 ],
[ "BIT 4, [HL]", 0x66cb, 3 ],
[ "BIT 4, L", 0x65cb, 3 ],
[ "BIT 5, A", 0x6fcb, 3 ],
[ "BIT 5, B", 0x68cb, 3 ],
[ "BIT 5, C", 0x69cb, 3 ],
[ "BIT 5, D", 0x6acb, 3 ],
[ "BIT 5, E", 0x6bcb, 3 ],
[ "BIT 5, H", 0x6ccb, 3 ],
[ "BIT 5, [HL]", 0x6ecb, 3 ],
[ "BIT 5, L", 0x6dcb, 3 ],
[ "BIT 6, A", 0x77cb, 3 ],
[ "BIT 6, B", 0x70cb, 3 ],
[ "BIT 6, C", 0x71cb, 3 ],
[ "BIT 6, D", 0x72cb, 3 ],
[ "BIT 6, E", 0x73cb, 3 ],
[ "BIT 6, H", 0x74cb, 3 ],
[ "BIT 6, [HL]", 0x76cb, 3 ],
[ "BIT 6, L", 0x75cb, 3 ],
[ "BIT 7, A", 0x7fcb, 3 ],
[ "BIT 7, B", 0x78cb, 3 ],
[ "BIT 7, C", 0x79cb, 3 ],
[ "BIT 7, D", 0x7acb, 3 ],
[ "BIT 7, E", 0x7bcb, 3 ],
[ "BIT 7, H", 0x7ccb, 3 ],
[ "BIT 7, [HL]", 0x7ecb, 3 ],
[ "BIT 7, L", 0x7dcb, 3 ],
[ "CALL C, ?", 0xdc, 2 ],
[ "CALL NC, ?", 0xd4, 2 ],
[ "CALL NZ, ?", 0xc4, 2 ],
[ "CALL Z, ?", 0xcc, 2 ],
[ "CALL ?", 0xcd, 2 ],
[ "CCF", 0x3f, 0 ],
[ "CP A", 0xbf, 0 ],
[ "CP B", 0xb8, 0 ],
[ "CP C", 0xb9, 0 ],
[ "CP D", 0xba, 0 ],
[ "CP E", 0xbb, 0 ],
[ "CP H", 0xbc, 0 ],
[ "CP [HL]", 0xbe, 0 ],
[ "CPL", 0x2f, 0 ],
[ "CP L", 0xbd, 0 ],
[ "CP x", 0xfe, 1 ],
[ "DAA", 0x27, 0 ],
[ "DEBUG", 0xed, 0 ],
[ "DEC A", 0x3d, 0 ],
[ "DEC B", 0x5, 0 ],
[ "DEC BC", 0xb, 0 ],
[ "DEC C", 0xd, 0 ],
[ "DEC D", 0x15, 0 ],
[ "DEC DE", 0x1b, 0 ],
[ "DEC E", 0x1d, 0 ],
[ "DEC H", 0x25, 0 ],
[ "DEC HL", 0x2b, 0 ],
[ "DEC [HL]", 0x35, 0 ],
[ "DEC L", 0x2d, 0 ],
[ "DEC SP", 0x3b, 0 ],
[ "DI", 0xf3, 0 ],
[ "EI", 0xfb, 0 ],
[ "HALT", 0x76, 0 ],
[ "INC A", 0x3c, 0 ],
[ "INC B", 0x4, 0 ],
[ "INC BC", 0x3, 0 ],
[ "INC C", 0xc, 0 ],
[ "INC D", 0x14, 0 ],
[ "INC DE", 0x13, 0 ],
[ "INC E", 0x1c, 0 ],
[ "INC H", 0x24, 0 ],
[ "INC HL", 0x23, 0 ],
[ "INC [HL]", 0x34, 0 ],
[ "INC L", 0x2c, 0 ],
[ "INC SP", 0x33, 0 ],
[ "JP C, ?", 0xda, 2 ],
[ "JP HL", 0xe9, 0 ],
[ "JP NC, ?", 0xd2, 2 ],
[ "JP NZ, ?", 0xc2, 2 ],
[ "JP Z, ?", 0xca, 2 ],
[ "JP ?", 0xc3, 2 ],
[ "JR C, x", 0x38, 1 ],
[ "JR NC, x", 0x30, 1 ],
[ "JR NZ, x", 0x20, 1 ],
[ "JR Z, x", 0x28, 1 ],
[ "JR x", 0x18, 1 ],
[ "LD A, A", 0x7f, 0 ],
[ "LD A, B", 0x78, 0 ],
[ "LD A, C", 0x79, 0 ],
[ "LD A, D", 0x7a, 0 ],
[ "LD A, E", 0x7b, 0 ],
[ "LD A, H", 0x7c, 0 ],
[ "LD A, L", 0x7d, 0 ],
[ "LD A, [$FF00+C]", 0xf2, 0 ],
[ "LD A, [$FF00+x]", 0xf0, 1 ],
# [ "LDH A, [x]", 0xf0, 1 ], #rgbds has trouble with this one?
[ "LD A, [BC]", 0xa, 0 ],
[ "LD A, [DE]", 0x1a, 0 ],
# [ "LD A, [HL+]", 0x2a, 0 ],
# [ "LD A, [HL-]", 0x3a, 0 ],
[ "LD A, [HL]", 0x7e, 0 ],
[ "LD A, [HLD]", 0x3a, 0 ],
[ "LD A, [HLI]", 0x2a, 0 ],
[ "LD A, [?]", 0xfa, 2 ],
[ "LD A, x", 0x3e, 1 ],
[ "LD B, A", 0x47, 0 ],
[ "LD B, B", 0x40, 0 ],
[ "LD B, C", 0x41, 0 ],
[ "LD [BC], A", 0x2, 0 ],
[ "LD B, D", 0x42, 0 ],
[ "LD B, E", 0x43, 0 ],
[ "LD B, H", 0x44, 0 ],
[ "LD B, [HL]", 0x46, 0 ],
[ "LD B, L", 0x45, 0 ],
[ "LD B, x", 0x6, 1 ],
[ "LD C, A", 0x4f, 0 ],
[ "LD C, B", 0x48, 0 ],
[ "LD C, C", 0x49, 0 ],
[ "LD C, D", 0x4a, 0 ],
[ "LD C, E", 0x4b, 0 ],
[ "LD C, H", 0x4c, 0 ],
[ "LD C, [HL]", 0x4e, 0 ],
[ "LD C, L", 0x4d, 0 ],
[ "LD C, x", 0xe, 1 ],
[ "LD D, A", 0x57, 0 ],
# [ "LDD A, [HL]", 0x3a, 0 ],
[ "LD D, B", 0x50, 0 ],
[ "LD D, C", 0x51, 0 ],
[ "LD D, D", 0x52, 0 ],
[ "LD D, E", 0x53, 0 ],
[ "LD [DE], A", 0x12, 0 ],
[ "LD D, H", 0x54, 0 ],
[ "LD D, [HL]", 0x56, 0 ],
# [ "LDD [HL], A", 0x32, 0 ],
[ "LD D, L", 0x55, 0 ],
[ "LD D, x", 0x16, 1 ],
[ "LD E, A", 0x5f, 0 ],
[ "LD E, B", 0x58, 0 ],
[ "LD E, C", 0x59, 0 ],
[ "LD E, D", 0x5a, 0 ],
[ "LD E, E", 0x5b, 0 ],
[ "LD E, H", 0x5c, 0 ],
[ "LD E, [HL]", 0x5e, 0 ],
[ "LD E, L", 0x5d, 0 ],
[ "LD E, x", 0x1e, 1 ],
[ "LD [$FF00+C], A", 0xe2, 0 ],
[ "LD [$FF00+x], A", 0xe0, 1 ],
# [ "LDH [x], A", 0xe0, 1 ],
[ "LD H, A", 0x67, 0 ],
[ "LD H, B", 0x60, 0 ],
[ "LD H, C", 0x61, 0 ],
[ "LD H, D", 0x62, 0 ],
[ "LD H, E", 0x63, 0 ],
[ "LD H, H", 0x64, 0 ],
[ "LD H, [HL]", 0x66, 0 ],
[ "LD H, L", 0x65, 0 ],
# [ "LD [HL+], A", 0x22, 0 ],
# [ "LD [HL-], A", 0x32, 0 ],
[ "LD [HL], A", 0x77, 0 ],
[ "LD [HL], B", 0x70, 0 ],
[ "LD [HL], C", 0x71, 0 ],
[ "LD [HL], D", 0x72, 0 ],
[ "LD [HLD], A", 0x32, 0 ],
[ "LD [HL], E", 0x73, 0 ],
[ "LD [HL], H", 0x74, 0 ],
[ "LD [HLI], A", 0x22, 0 ],
[ "LD [HL], L", 0x75, 0 ],
[ "LD HL, SP+x", 0xf8, 1 ],
[ "LD [HL], x", 0x36, 1 ],
[ "LD H, x", 0x26, 1 ],
# [ "LDI A, [HL]", 0x2a, 0 ],
# [ "LDI [HL], A", 0x22, 0 ],
[ "LD L, A", 0x6f, 0 ],
[ "LD L, B", 0x68, 0 ],
[ "LD L, C", 0x69, 0 ],
[ "LD L, D", 0x6a, 0 ],
[ "LD L, E", 0x6b, 0 ],
[ "LD L, H", 0x6c, 0 ],
[ "LD L, [HL]", 0x6e, 0 ],
[ "LD L, L", 0x6d, 0 ],
[ "LD L, x", 0x2e, 1 ],
# [ "LD PC, HL", 0xe9, 0 ], #prefer jp [hl]
[ "LD SP, HL", 0xf9, 0 ],
[ "LD BC, ?", 0x1, 2 ],
[ "LD DE, ?", 0x11, 2 ],
[ "LD HL, ?", 0x21, 2 ],
[ "LD SP, ?", 0x31, 2 ],
# [ "LD [?], SP", 0x8, 2 ],
[ "LD [?], A", 0xea, 2 ],
[ "NOP", 0x0, 0 ],
[ "OR A", 0xb7, 0 ],
[ "OR B", 0xb0, 0 ],
[ "OR C", 0xb1, 0 ],
[ "OR D", 0xb2, 0 ],
[ "OR E", 0xb3, 0 ],
[ "OR H", 0xb4, 0 ],
[ "OR [HL]", 0xb6, 0 ],
[ "OR L", 0xb5, 0 ],
[ "OR x", 0xf6, 1 ],
[ "POP AF", 0xf1, 0 ],
[ "POP BC", 0xc1, 0 ],
[ "POP DE", 0xd1, 0 ],
[ "POP HL", 0xe1, 0 ],
[ "PUSH AF", 0xf5, 0 ],
[ "PUSH BC", 0xc5, 0 ],
[ "PUSH DE", 0xd5, 0 ],
[ "PUSH HL", 0xe5, 0 ],
[ "RES 0, A", 0x87cb, 3 ],
[ "RES 0, B", 0x80cb, 3 ],
[ "RES 0, C", 0x81cb, 3 ],
[ "RES 0, D", 0x82cb, 3 ],
[ "RES 0, E", 0x83cb, 3 ],
[ "RES 0, H", 0x84cb, 3 ],
[ "RES 0, [HL]", 0x86cb, 3 ],
[ "RES 0, L", 0x85cb, 3 ],
[ "RES 1, A", 0x8fcb, 3 ],
[ "RES 1, B", 0x88cb, 3 ],
[ "RES 1, C", 0x89cb, 3 ],
[ "RES 1, D", 0x8acb, 3 ],
[ "RES 1, E", 0x8bcb, 3 ],
[ "RES 1, H", 0x8ccb, 3 ],
[ "RES 1, [HL]", 0x8ecb, 3 ],
[ "RES 1, L", 0x8dcb, 3 ],
[ "RES 2, A", 0x97cb, 3 ],
[ "RES 2, B", 0x90cb, 3 ],
[ "RES 2, C", 0x91cb, 3 ],
[ "RES 2, D", 0x92cb, 3 ],
[ "RES 2, E", 0x93cb, 3 ],
[ "RES 2, H", 0x94cb, 3 ],
[ "RES 2, [HL]", 0x96cb, 3 ],
[ "RES 2, L", 0x95cb, 3 ],
[ "RES 3, A", 0x9fcb, 3 ],
[ "RES 3, B", 0x98cb, 3 ],
[ "RES 3, C", 0x99cb, 3 ],
[ "RES 3, D", 0x9acb, 3 ],
[ "RES 3, E", 0x9bcb, 3 ],
[ "RES 3, H", 0x9ccb, 3 ],
[ "RES 3, [HL]", 0x9ecb, 3 ],
[ "RES 3, L", 0x9dcb, 3 ],
[ "RES 4, A", 0xa7cb, 3 ],
[ "RES 4, B", 0xa0cb, 3 ],
[ "RES 4, C", 0xa1cb, 3 ],
[ "RES 4, D", 0xa2cb, 3 ],
[ "RES 4, E", 0xa3cb, 3 ],
[ "RES 4, H", 0xa4cb, 3 ],
[ "RES 4, [HL]", 0xa6cb, 3 ],
[ "RES 4, L", 0xa5cb, 3 ],
[ "RES 5, A", 0xafcb, 3 ],
[ "RES 5, B", 0xa8cb, 3 ],
[ "RES 5, C", 0xa9cb, 3 ],
[ "RES 5, D", 0xaacb, 3 ],
[ "RES 5, E", 0xabcb, 3 ],
[ "RES 5, H", 0xaccb, 3 ],
[ "RES 5, [HL]", 0xaecb, 3 ],
[ "RES 5, L", 0xadcb, 3 ],
[ "RES 6, A", 0xb7cb, 3 ],
[ "RES 6, B", 0xb0cb, 3 ],
[ "RES 6, C", 0xb1cb, 3 ],
[ "RES 6, D", 0xb2cb, 3 ],
[ "RES 6, E", 0xb3cb, 3 ],
[ "RES 6, H", 0xb4cb, 3 ],
[ "RES 6, [HL]", 0xb6cb, 3 ],
[ "RES 6, L", 0xb5cb, 3 ],
[ "RES 7, A", 0xbfcb, 3 ],
[ "RES 7, B", 0xb8cb, 3 ],
[ "RES 7, C", 0xb9cb, 3 ],
[ "RES 7, D", 0xbacb, 3 ],
[ "RES 7, E", 0xbbcb, 3 ],
[ "RES 7, H", 0xbccb, 3 ],
[ "RES 7, [HL]", 0xbecb, 3 ],
[ "RES 7, L", 0xbdcb, 3 ],
[ "RETI", 0xd9, 0 ],
[ "RET C", 0xd8, 0 ],
[ "RET NC", 0xd0, 0 ],
[ "RET NZ", 0xc0, 0 ],
[ "RET Z", 0xc8, 0 ],
[ "RET", 0xc9, 0 ],
[ "RLA", 0x17, 0 ],
[ "RL A", 0x17cb, 3 ],
[ "RL B", 0x10cb, 3 ],
[ "RL C", 0x11cb, 3 ],
[ "RLCA", 0x7, 0 ],
[ "RLC A", 0x7cb, 3 ],
[ "RLC B", 0xcb, 3 ],
[ "RLC C", 0x1cb, 3 ],
[ "RLC D", 0x2cb, 3 ],
[ "RLC E", 0x3cb, 3 ],
[ "RLC H", 0x4cb, 3 ],
[ "RLC [HL]", 0x6cb, 3 ],
[ "RLC L", 0x5cb, 3 ],
[ "RL D", 0x12cb, 3 ],
[ "RL E", 0x13cb, 3 ],
[ "RL H", 0x14cb, 3 ],
[ "RL [HL]", 0x16cb, 3 ],
[ "RL L", 0x15cb, 3 ],
[ "RRA", 0x1f, 0 ],
[ "RR A", 0x1fcb, 3 ],
[ "RR B", 0x18cb, 3 ],
[ "RR C", 0x19cb, 3 ],
[ "RRCA", 0xf, 0 ],
[ "RRC A", 0xfcb, 3 ],
[ "RRC B", 0x8cb, 3 ],
[ "RRC C", 0x9cb, 3 ],
[ "RRC D", 0xacb, 3 ],
[ "RRC E", 0xbcb, 3 ],
[ "RRC H", 0xccb, 3 ],
[ "RRC [HL]", 0xecb, 3 ],
[ "RRC L", 0xdcb, 3 ],
[ "RR D", 0x1acb, 3 ],
[ "RR E", 0x1bcb, 3 ],
[ "RR H", 0x1ccb, 3 ],
[ "RR [HL]", 0x1ecb, 3 ],
[ "RR L", 0x1dcb, 3 ],
[ "RST $0", 0xc7, 0 ],
[ "RST $10", 0xd7, 0 ],
[ "RST $18", 0xdf, 0 ],
[ "RST $20", 0xe7, 0 ],
[ "RST $28", 0xef, 0 ],
[ "RST $30", 0xf7, 0 ],
[ "RST $38", 0xff, 0 ],
[ "RST $8", 0xcf, 0 ],
[ "SBC A", 0x9f, 0 ],
[ "SBC B", 0x98, 0 ],
[ "SBC C", 0x99, 0 ],
[ "SBC D", 0x9a, 0 ],
[ "SBC E", 0x9b, 0 ],
[ "SBC H", 0x9c, 0 ],
[ "SBC [HL]", 0x9e, 0 ],
[ "SBC L", 0x9d, 0 ],
[ "SBC x", 0xde, 1 ],
[ "SCF", 0x37, 0 ],
[ "SET 0, A", 0xc7cb, 3 ],
[ "SET 0, B", 0xc0cb, 3 ],
[ "SET 0, C", 0xc1cb, 3 ],
[ "SET 0, D", 0xc2cb, 3 ],
[ "SET 0, E", 0xc3cb, 3 ],
[ "SET 0, H", 0xc4cb, 3 ],
[ "SET 0, [HL]", 0xc6cb, 3 ],
[ "SET 0, L", 0xc5cb, 3 ],
[ "SET 1, A", 0xcfcb, 3 ],
[ "SET 1, B", 0xc8cb, 3 ],
[ "SET 1, C", 0xc9cb, 3 ],
[ "SET 1, D", 0xcacb, 3 ],
[ "SET 1, E", 0xcbcb, 3 ],
[ "SET 1, H", 0xcccb, 3 ],
[ "SET 1, [HL]", 0xcecb, 3 ],
[ "SET 1, L", 0xcdcb, 3 ],
[ "SET 2, A", 0xd7cb, 3 ],
[ "SET 2, B", 0xd0cb, 3 ],
[ "SET 2, C", 0xd1cb, 3 ],
[ "SET 2, D", 0xd2cb, 3 ],
[ "SET 2, E", 0xd3cb, 3 ],
[ "SET 2, H", 0xd4cb, 3 ],
[ "SET 2, [HL]", 0xd6cb, 3 ],
[ "SET 2, L", 0xd5cb, 3 ],
[ "SET 3, A", 0xdfcb, 3 ],
[ "SET 3, B", 0xd8cb, 3 ],
[ "SET 3, C", 0xd9cb, 3 ],
[ "SET 3, D", 0xdacb, 3 ],
[ "SET 3, E", 0xdbcb, 3 ],
[ "SET 3, H", 0xdccb, 3 ],
[ "SET 3, [HL]", 0xdecb, 3 ],
[ "SET 3, L", 0xddcb, 3 ],
[ "SET 4, A", 0xe7cb, 3 ],
[ "SET 4, B", 0xe0cb, 3 ],
[ "SET 4, C", 0xe1cb, 3 ],
[ "SET 4, D", 0xe2cb, 3 ],
[ "SET 4, E", 0xe3cb, 3 ],
[ "SET 4, H", 0xe4cb, 3 ],
[ "SET 4, [HL]", 0xe6cb, 3 ],
[ "SET 4, L", 0xe5cb, 3 ],
[ "SET 5, A", 0xefcb, 3 ],
[ "SET 5, B", 0xe8cb, 3 ],
[ "SET 5, C", 0xe9cb, 3 ],
[ "SET 5, D", 0xeacb, 3 ],
[ "SET 5, E", 0xebcb, 3 ],
[ "SET 5, H", 0xeccb, 3 ],
[ "SET 5, [HL]", 0xeecb, 3 ],
[ "SET 5, L", 0xedcb, 3 ],
[ "SET 6, A", 0xf7cb, 3 ],
[ "SET 6, B", 0xf0cb, 3 ],
[ "SET 6, C", 0xf1cb, 3 ],
[ "SET 6, D", 0xf2cb, 3 ],
[ "SET 6, E", 0xf3cb, 3 ],
[ "SET 6, H", 0xf4cb, 3 ],
[ "SET 6, [HL]", 0xf6cb, 3 ],
[ "SET 6, L", 0xf5cb, 3 ],
[ "SET 7, A", 0xffcb, 3 ],
[ "SET 7, B", 0xf8cb, 3 ],
[ "SET 7, C", 0xf9cb, 3 ],
[ "SET 7, D", 0xfacb, 3 ],
[ "SET 7, E", 0xfbcb, 3 ],
[ "SET 7, H", 0xfccb, 3 ],
[ "SET 7, [HL]", 0xfecb, 3 ],
[ "SET 7, L", 0xfdcb, 3 ],
[ "SLA A", 0x27cb, 3 ],
[ "SLA B", 0x20cb, 3 ],
[ "SLA C", 0x21cb, 3 ],
[ "SLA D", 0x22cb, 3 ],
[ "SLA E", 0x23cb, 3 ],
[ "SLA H", 0x24cb, 3 ],
[ "SLA [HL]", 0x26cb, 3 ],
[ "SLA L", 0x25cb, 3 ],
[ "SRA A", 0x2fcb, 3 ],
[ "SRA B", 0x28cb, 3 ],
[ "SRA C", 0x29cb, 3 ],
[ "SRA D", 0x2acb, 3 ],
[ "SRA E", 0x2bcb, 3 ],
[ "SRA H", 0x2ccb, 3 ],
[ "SRA [HL]", 0x2ecb, 3 ],
[ "SRA L", 0x2dcb, 3 ],
[ "SRL A", 0x3fcb, 3 ],
[ "SRL B", 0x38cb, 3 ],
[ "SRL C", 0x39cb, 3 ],
[ "SRL D", 0x3acb, 3 ],
[ "SRL E", 0x3bcb, 3 ],
[ "SRL H", 0x3ccb, 3 ],
[ "SRL [HL]", 0x3ecb, 3 ],
[ "SRL L", 0x3dcb, 3 ],
[ "STOP", 0x10, 0 ],
[ "SUB A", 0x97, 0 ],
[ "SUB B", 0x90, 0 ],
[ "SUB C", 0x91, 0 ],
[ "SUB D", 0x92, 0 ],
[ "SUB E", 0x93, 0 ],
[ "SUB H", 0x94, 0 ],
[ "SUB [HL]", 0x96, 0 ],
[ "SUB L", 0x95, 0 ],
[ "SUB x", 0xd6, 1 ],
[ "SWAP A", 0x37cb, 3 ],
[ "SWAP B", 0x30cb, 3 ],
[ "SWAP C", 0x31cb, 3 ],
[ "SWAP D", 0x32cb, 3 ],
[ "SWAP E", 0x33cb, 3 ],
[ "SWAP H", 0x34cb, 3 ],
[ "SWAP [HL]", 0x36cb, 3 ],
[ "SWAP L", 0x35cb, 3 ],
[ "XOR A", 0xaf, 0 ],
[ "XOR B", 0xa8, 0 ],
[ "XOR C", 0xa9, 0 ],
[ "XOR D", 0xaa, 0 ],
[ "XOR E", 0xab, 0 ],
[ "XOR H", 0xac, 0 ],
[ "XOR [HL]", 0xae, 0 ],
[ "XOR L", 0xad, 0 ],
[ "XOR x", 0xee, 1 ],
[ "E", 0x100, -1 ],
]
#find conflicts
conflict_table = {}
for line in temp_opt_table:
if line[1] in conflict_table.keys():
print "CONFLICT: " + line[0] + " ($" + hex(line[1])[2:] + ") .... " + conflict_table[line[1]]
else:
conflict_table[line[1]] = line[0]
#construct real opt_table
opt_table = {}
for line in temp_opt_table:
opt_table[line[1]] = [line[0], line[2]]
del temp_opt_table
del line
end_08_scripts_with = [
0xe9, #jp hl
#0xc3, #jp
##0x18, #jr
0xc9, #ret
###0xda, 0xe9, 0xd2, 0xc2, 0xca, 0xc3, 0x38, 0x30, 0x20, 0x28, 0x18, 0xd8, 0xd0, 0xc0, 0xc8, 0xc9
]
relative_jumps = [0x38, 0x30, 0x20, 0x28, 0x18, 0xc3, 0xda, 0xc2]
relative_unconditional_jumps = [0xc3, 0x18]
#TODO: replace call and a pointer with call and a label
call_commands = [0xdc, 0xd4, 0xc4, 0xcc, 0xcd]
all_labels = {}
def load_labels(filename="labels.json"):
global all_labels
if os.path.exists(filename):
all_labels = json.loads(open(filename, "r").read())
else:
print "You must run analyze_incbins.scan_for_predefined_labels() to create \"labels.json\". Trying..."
import analyze_incbins
analyze_incbins.scan_for_predefined_labels()
load_labels()
def find_label(local_address, bank_id=0):
global all_labels
#keep an integer
if type(local_address) == str:
local_address1 = int(local_address.replace("$", "0x"), 16)
else: local_address1 = local_address
#turn local_address into a string
if type(local_address) == str:
if "0x" in local_address: local_address = local_address.replace("0x", "$")
elif not "$" in local_address: local_address = "$" + local_address
if type(local_address) == int:
local_address = "$%.x" % (local_address)
local_address = local_address.upper()
for label_entry in all_labels:
if label_entry["local_pointer"].upper() == local_address:
if label_entry["bank_id"] == bank_id or (local_address1 < 0x8000 and (label_entry["bank_id"] == 0 or label_entry["bank_id"] == 1)):
return label_entry["label"]
return None
def random_asm_label():
return ".ASM_" + random_hash()
def asm_label(address):
# why using a random value when you can use the eff. address?
return ".ASM_" + hex(address)[2:]
def output_bank_opcodes(original_offset, max_byte_count=0x4000):
#fs = current_address
#b = bank_byte
#in = input_data -- extract_maps.rom
#bank_size = byte_count
#i = offset
#ad = end_address
#a, oa = current_byte_number
bank_id = 0
if original_offset > 0x8000:
bank_id = original_offset / 0x4000
print "bank id is: " + str(bank_id)
last_hl_address = None #for when we're scanning the main map script
last_a_address = None
used_3d97 = False
rom = extract_maps.rom
offset = original_offset
current_byte_number = 0 #start from the beginning
#we don't actually have an end address, but we'll just say $4000
end_address = original_offset + max_byte_count
byte_labels = {}
output = ""
keep_reading = True
while offset <= end_address and keep_reading:
current_byte = ord(extract_maps.rom[offset])
is_data = False
maybe_byte = current_byte
#first check if this byte already has a label
#if it does, use the label
#if not, generate a new label
if offset in byte_labels.keys():
line_label = byte_labels[offset]["name"]
byte_labels[offset]["usage"] += 1
else:
line_label = asm_label(offset)
byte_labels[offset] = {}
byte_labels[offset]["name"] = line_label
byte_labels[offset]["usage"] = 0
byte_labels[offset]["definition"] = True
output += line_label.lower() + "\n" #" ; " + hex(offset) + "\n"
#find out if there's a two byte key like this
temp_maybe = maybe_byte
temp_maybe += ( ord(rom[offset+1]) << 8)
if temp_maybe in opt_table.keys() and ord(rom[offset+1])!=0:
opstr = opt_table[temp_maybe][0].lower()
if "x" in opstr:
for x in range(0, opstr.count("x")):
insertion = ord(rom[offset + 1])
insertion = "$" + hex(insertion)[2:]
opstr = opstr[:opstr.find("x")].lower() + insertion + opstr[opstr.find("x")+1:].lower()
current_byte += 1
offset += 1
if "?" in opstr:
for y in range(0, opstr.count("?")):
byte1 = ord(rom[offset + 1])
byte2 = ord(rom[offset + 2])
number = byte1
number += byte2 << 8;
insertion = "$%.4x" % (number)
opstr = opstr[:opstr.find("?")].lower() + insertion + opstr[opstr.find("?")+1:].lower()
current_byte_number += 2
offset += 2
output += spacing + opstr #+ " ; " + hex(offset)
output += "\n"
current_byte_number += 2
offset += 2
elif maybe_byte in opt_table.keys():
op_code = opt_table[maybe_byte]
op_code_type = op_code[1]
op_code_byte = maybe_byte
#type = -1 when it's the E op
#if op_code_type != -1:
if op_code_type == 0 and ord(rom[offset]) == op_code_byte:
op_str = op_code[0].lower()
output += spacing + op_code[0].lower() #+ " ; " + hex(offset)
output += "\n"
offset += 1
current_byte_number += 1
elif op_code_type == 1 and ord(rom[offset]) == op_code_byte:
oplen = len(op_code[0])
opstr = copy(op_code[0])
xes = op_code[0].count("x")
include_comment = False
for x in range(0, xes):
insertion = ord(rom[offset + 1])
insertion = "$" + hex(insertion)[2:]
if current_byte == 0x18 or current_byte==0x20 or current_byte in relative_jumps: #jr or jr nz
#generate a label for the byte we're jumping to
target_address = offset + 2 + c_int8(ord(rom[offset + 1])).value
if target_address in byte_labels.keys():
byte_labels[target_address]["usage"] = 1 + byte_labels[target_address]["usage"]
line_label2 = byte_labels[target_address]["name"]
else:
line_label2 = asm_label(target_address)
byte_labels[target_address] = {}
byte_labels[target_address]["name"] = line_label2
byte_labels[target_address]["usage"] = 1
byte_labels[target_address]["definition"] = False
insertion = line_label2.lower()
include_comment = True
elif current_byte == 0x3e:
last_a_address = ord(rom[offset + 1])
opstr = opstr[:opstr.find("x")].lower() + insertion + opstr[opstr.find("x")+1:].lower()
output += spacing + opstr
if include_comment:
output += " ; " + hex(offset)
if current_byte in relative_jumps:
output += " $" + hex(ord(rom[offset + 1]))[2:]
output += "\n"
current_byte_number += 1
offset += 1
insertion = ""
current_byte_number += 1
offset += 1
include_comment = False
elif op_code_type == 2 and ord(rom[offset]) == op_code_byte:
oplen = len(op_code[0])
opstr = copy(op_code[0])
qes = op_code[0].count("?")
for x in range(0, qes):
byte1 = ord(rom[offset + 1])
byte2 = ord(rom[offset + 2])
number = byte1
number += byte2 << 8;
insertion = "$%.4x" % (number)
if maybe_byte in call_commands or current_byte in relative_unconditional_jumps or current_byte in relative_jumps:
result = find_label(insertion, bank_id)
if result != None:
insertion = result
opstr = opstr[:opstr.find("?")].lower() + insertion + opstr[opstr.find("?")+1:].lower()
output += spacing + opstr #+ " ; " + hex(offset)
output += "\n"
current_byte_number += 2
offset += 2
current_byte_number += 1
offset += 1
if current_byte == 0x21:
last_hl_address = byte1 + (byte2 << 8)
if current_byte == 0xcd:
if number == 0x3d97: used_3d97 = True
#duck out if this is jp $24d7
if current_byte == 0xc3 or current_byte in relative_unconditional_jumps:
if current_byte == 0xc3:
if number == 0x3d97: used_3d97 = True
#if number == 0x24d7: #jp
if not has_outstanding_labels(byte_labels) or all_outstanding_labels_are_reverse(byte_labels, offset):
keep_reading = False
is_data = False
break
else:
is_data = True
#stop reading at a jump, relative jump or return
if current_byte in end_08_scripts_with:
if not has_outstanding_labels(byte_labels) and all_outstanding_labels_are_reverse(byte_labels, offset):
keep_reading = False
is_data = False #cleanup
break
else:
is_data = False
keep_reading = True
else:
is_data = False
keep_reading = True
else:
# if is_data and keep_reading:
output += spacing + "db $" + hex(ord(rom[offset]))[2:] #+ " ; " + hex(offset)
output += "\n"
offset += 1
current_byte_number += 1
#else the while loop would have spit out the opcode
#these two are done prior
#offset += 1
#current_byte_number += 1
#clean up unused labels
for label_line in byte_labels.keys():
address = label_line
label_line = byte_labels[label_line]
if label_line["usage"] == 0:
output = output.replace((label_line["name"] + "\n").lower(), "")
#add the offset of the final location
output += "; " + hex(offset)
return (output, offset, last_hl_address, last_a_address, used_3d97)
def has_outstanding_labels(byte_labels):
"""
if a label is used once, it means it has to be called or specified later
"""
for label_line in byte_labels.keys():
real_line = byte_labels[label_line]
if real_line["definition"] == False: return True
return False
def all_outstanding_labels_are_reverse(byte_labels, offset):
for label_id in byte_labels.keys():
line = byte_labels[label_id] # label_id is also the address
if line["definition"] == False:
if not label_id < offset: return False
return True
def text_asm_pretty_printer(label, address_of_08, include_08=True):
"""returns (output, end_address)"""
output = label + ": ; " + hex(address_of_08) + "\n"
if include_08:
output += spacing + "db $08 ; asm\n"
results = output_bank_opcodes(address_of_08 + 1)
else:
results = output_bank_opcodes(address_of_08)
output += results[0]
end_address = results[1]
return (output, end_address)
if __name__ == "__main__":
extract_maps.load_rom()
extract_maps.load_map_pointers()
extract_maps.read_all_map_headers()
#0x18f96 is PalletTownText1
#0x19B5D is BluesHouseText1
print output_bank_opcodes(int(sys.argv[1], 16))[0]

View file

@ -1,8 +0,0 @@
import json
import analyze_incbins
analyze_incbins.scan_for_predefined_labels()
with open('../pokered.sym', 'w') as sym:
for label in json.load(open('labels.json')):
sym.write('{0:x}:{1} {2}\n'.format(label['bank_id'], label['local_pointer'][1:], label['label']))

View file

@ -1,82 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-05
#insert object data into pokered.asm
import extract_maps
from pretty_map_headers import map_name_cleaner, object_data_pretty_printer, make_object_label_name, make_text_label, map_constants
from analyze_incbins import asm, offset_to_pointer, find_incbin_to_replace_for, split_incbin_line_into_three, generate_diff_insert, load_asm, isolate_incbins, process_incbins
import analyze_incbins
import os, sys
import subprocess
spacing = " "
def insert_object(map_id):
map = extract_maps.map_headers[map_id]
object = map["object_data"]
size = extract_maps.compute_object_data_size(object)
address = int(map["object_data_pointer"], 16)
line_number = find_incbin_to_replace_for(address)
if line_number == None:
print "skipping object data for map " + str(map["id"]) + " at " + map["object_data_pointer"] + " for " + str(size) + " bytes."
return
newlines = split_incbin_line_into_three(line_number, address, size)
object_asm = object_data_pretty_printer(map_id)
newlines = newlines.split("\n")
if len(newlines) == 2: index = 0 #replace the 1st line with new content
elif len(newlines) == 3: index = 1 #replace the 2nd line with new content
newlines[index] = object_asm
if len(newlines) == 3 and newlines[2][-2:] == "$0":
#get rid of the last incbin line if it is only including 0 bytes
del newlines[2]
#note that this has to be done after adding in the new asm
newlines = "\n".join(line for line in newlines)
diff = generate_diff_insert(line_number, newlines)
print diff
print "... Applying diff."
#write the diff to a file
fh = open("temp.patch", "w")
fh.write(diff)
fh.close()
#apply the patch
os.system("patch ../pokered.asm temp.patch")
#remove the patch
os.system("rm temp.patch")
#confirm it's working
subprocess.check_call("cd ../; make clean; LC_CTYPE=UTF-8 make", shell=True)
def insert_all_objects():
for map_id in extract_maps.map_headers.keys():
if map_id not in extract_maps.bad_maps:
insert_object(map_id)
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
load_asm()
isolate_incbins()
process_incbins()
if __name__ == "__main__":
#load map headers and object data
extract_maps.load_rom()
extract_maps.load_map_pointers()
extract_maps.read_all_map_headers()
#load incbins
load_asm()
isolate_incbins()
process_incbins()
#insert_object(1)
insert_all_objects()

View file

@ -1,891 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-07, 2012-01-17, 2012-01-27
#insert TX_FAR targets into pokered.asm
#and other insertion tasks
import extract_maps
from analyze_texts import analyze_texts, text_pretty_printer_at, scan_rom_for_tx_fars
from pretty_map_headers import map_name_cleaner, make_text_label, map_constants, find_all_tx_fars, tx_far_pretty_printer, tx_far_label_maker
import pretty_map_headers
from analyze_incbins import asm, offset_to_pointer, find_incbin_to_replace_for, split_incbin_line_into_three, generate_diff_insert, load_asm, isolate_incbins, process_incbins, reset_incbins, apply_diff
import analyze_incbins
from gbz80disasm import text_asm_pretty_printer, output_bank_opcodes, load_labels, find_label
import os, sys
import subprocess
spacing = " "
tx_fars = None
failed_attempts = {}
pokemons = ["BULBASAUR", "IVYSAUR", "VENUSAUR", "CHARMANDER", "CHARMELEON", "CHARIZARD", "SQUIRTLE", "WARTORTLE", "BLASTOISE", "CATERPIE", "METAPOD", "BUTTERFREE", "WEEDLE", "KAKUNA", "BEEDRILL", "PIDGEY", "PIDGEOTTO", "PIDGEOT", "RATTATA", "RATICATE", "SPEAROW", "FEAROW", "EKANS", "ARBOK", "PIKACHU", "RAICHU", "SANDSHREW", "SANDSLASH", "NIDORANF", "NIDORINA", "NIDOQUEEN", "NIDORANM", "NIDORINO", "NIDOKING", "CLEFAIRY", "CLEFABLE", "VULPIX", "NINETALES", "JIGGLYPUFF", "WIGGLYTUFF", "ZUBAT", "GOLBAT", "ODDISH", "GLOOM", "VILEPLUME", "PARAS", "PARASECT", "VENONAT", "VENOMOTH", "DIGLETT", "DUGTRIO", "MEOWTH", "PERSIAN", "PSYDUCK", "GOLDUCK", "MANKEY", "PRIMEAPE", "GROWLITHE", "ARCANINE", "POLIWAG", "POLIWHIRL", "POLIWRATH", "ABRA", "KADABRA", "ALAKAZAM", "MACHOP", "MACHOKE", "MACHAMP", "BELLSPROUT", "WEEPINBELL", "VICTREEBEL", "TENTACOOL", "TENTACRUEL", "GEODUDE", "GRAVELER", "GOLEM", "PONYTA", "RAPIDASH", "SLOWPOKE", "SLOWBRO", "MAGNEMITE", "MAGNETON", "FARFETCH_D", "DODUO", "DODRIO", "SEEL", "DEWGONG", "GRIMER", "MUK", "SHELLDER", "CLOYSTER", "GASTLY", "HAUNTER", "GENGAR", "ONIX", "DROWZEE", "HYPNO", "KRABBY", "KINGLER", "VOLTORB", "ELECTRODE", "EXEGGCUTE", "EXEGGUTOR", "CUBONE", "MAROWAK", "HITMONLEE", "HITMONCHAN", "LICKITUNG", "KOFFING", "WEEZING", "RHYHORN", "RHYDON", "CHANSEY", "TANGELA", "KANGASKHAN", "HORSEA", "SEADRA", "GOLDEEN", "SEAKING", "STARYU", "STARMIE", "MR_MIME", "SCYTHER", "JYNX", "ELECTABUZZ", "MAGMAR", "PINSIR", "TAUROS", "MAGIKARP", "GYARADOS", "LAPRAS", "DITTO", "EEVEE", "VAPOREON", "JOLTEON", "FLAREON", "PORYGON", "OMANYTE", "OMASTAR", "KABUTO", "KABUTOPS", "AERODACTYL", "SNORLAX", "ARTICUNO", "ZAPDOS", "MOLTRES", "DRATINI", "DRAGONAIR", "DRAGONITE", "MEWTWO", "MEW"]
moves = [["POUND", 0x01], ["KARATE_CHOP", 0x02], ["DOUBLESLAP", 0x03], ["COMET_PUNCH", 0x04], ["MEGA_PUNCH", 0x05], ["PAY_DAY", 0x06], ["FIRE_PUNCH", 0x07], ["ICE_PUNCH", 0x08], ["THUNDERPUNCH", 0x09], ["SCRATCH", 0x0A], ["VICEGRIP", 0x0B], ["GUILLOTINE", 0x0C], ["RAZOR_WIND", 0x0D], ["SWORDS_DANCE", 0x0E], ["CUT", 0x0F], ["GUST", 0x10], ["WING_ATTACK", 0x11], ["WHIRLWIND", 0x12], ["FLY", 0x13], ["BIND", 0x14], ["SLAM", 0x15], ["VINE_WHIP", 0x16], ["STOMP", 0x17], ["DOUBLE_KICK", 0x18], ["MEGA_KICK", 0x19], ["JUMP_KICK", 0x1A], ["ROLLING_KICK", 0x1B], ["SAND_ATTACK", 0x1C], ["HEADBUTT", 0x1D], ["HORN_ATTACK", 0x1E], ["FURY_ATTACK", 0x1F], ["HORN_DRILL", 0x20], ["TACKLE", 0x21], ["BODY_SLAM", 0x22], ["WRAP", 0x23], ["TAKE_DOWN", 0x24], ["THRASH", 0x25], ["DOUBLE_EDGE", 0x26], ["TAIL_WHIP", 0x27], ["POISON_STING", 0x28], ["TWINEEDLE", 0x29], ["PIN_MISSILE", 0x2A], ["LEER", 0x2B], ["BITE", 0x2C], ["GROWL", 0x2D], ["ROAR", 0x2E], ["SING", 0x2F], ["SUPERSONIC", 0x30], ["SONICBOOM", 0x31], ["DISABLE", 0x32], ["ACID", 0x33], ["EMBER", 0x34], ["FLAMETHROWER", 0x35], ["MIST", 0x36], ["WATER_GUN", 0x37], ["HYDRO_PUMP", 0x38], ["SURF", 0x39], ["ICE_BEAM", 0x3A], ["BLIZZARD", 0x3B], ["PSYBEAM", 0x3C], ["BUBBLEBEAM", 0x3D], ["AURORA_BEAM", 0x3E], ["HYPER_BEAM", 0x3F], ["PECK", 0x40], ["DRILL_PECK", 0x41], ["SUBMISSION", 0x42], ["LOW_KICK", 0x43], ["COUNTER", 0x44], ["SEISMIC_TOSS", 0x45], ["STRENGTH", 0x46], ["ABSORB", 0x47], ["MEGA_DRAIN", 0x48], ["LEECH_SEED", 0x49], ["GROWTH", 0x4A], ["RAZOR_LEAF", 0x4B], ["SOLARBEAM", 0x4C], ["POISONPOWDER", 0x4D], ["STUN_SPORE", 0x4E], ["SLEEP_POWDER", 0x4F], ["PETAL_DANCE", 0x50], ["STRING_SHOT", 0x51], ["DRAGON_RAGE", 0x52], ["FIRE_SPIN", 0x53], ["THUNDERSHOCK", 0x54], ["THUNDERBOLT", 0x55], ["THUNDER_WAVE", 0x56], ["THUNDER", 0x57], ["ROCK_THROW", 0x58], ["EARTHQUAKE", 0x59], ["FISSURE", 0x5A], ["DIG", 0x5B], ["TOXIC", 0x5C], ["CONFUSION", 0x5D], ["PSYCHIC_M", 0x5E], ["HYPNOSIS", 0x5F], ["MEDITATE", 0x60], ["AGILITY", 0x61], ["QUICK_ATTACK", 0x62], ["RAGE", 0x63], ["TELEPORT", 0x64], ["NIGHT_SHADE", 0x65], ["MIMIC", 0x66], ["SCREECH", 0x67], ["DOUBLE_TEAM", 0x68], ["RECOVER", 0x69], ["HARDEN", 0x6A], ["MINIMIZE", 0x6B], ["SMOKESCREEN", 0x6C], ["CONFUSE_RAY", 0x6D], ["WITHDRAW", 0x6E], ["DEFENSE_CURL", 0x6F], ["BARRIER", 0x70], ["LIGHT_SCREEN", 0x71], ["HAZE", 0x72], ["REFLECT", 0x73], ["FOCUS_ENERGY", 0x74], ["BIDE", 0x75], ["METRONOME", 0x76], ["MIRROR_MOVE", 0x77], ["SELFDESTRUCT", 0x78], ["EGG_BOMB", 0x79], ["LICK", 0x7A], ["SMOG", 0x7B], ["SLUDGE", 0x7C], ["BONE_CLUB", 0x7D], ["FIRE_BLAST", 0x7E], ["WATERFALL", 0x7F], ["CLAMP", 0x80], ["SWIFT", 0x81], ["SKULL_BASH", 0x82], ["SPIKE_CANNON", 0x83], ["CONSTRICT", 0x84], ["AMNESIA", 0x85], ["KINESIS", 0x86], ["SOFTBOILED", 0x87], ["HI_JUMP_KICK", 0x88], ["GLARE", 0x89], ["DREAM_EATER", 0x8A], ["POISON_GAS", 0x8B], ["BARRAGE", 0x8C], ["LEECH_LIFE", 0x8D], ["LOVELY_KISS", 0x8E], ["SKY_ATTACK", 0x8F], ["TRANSFORM", 0x90], ["BUBBLE", 0x91], ["DIZZY_PUNCH", 0x92], ["SPORE", 0x93], ["FLASH", 0x94], ["PSYWAVE", 0x95], ["SPLASH", 0x96], ["ACID_ARMOR", 0x97], ["CRABHAMMER", 0x98], ["EXPLOSION", 0x99], ["FURY_SWIPES", 0x9A], ["BONEMERANG", 0x9B], ["REST", 0x9C], ["ROCK_SLIDE", 0x9D], ["HYPER_FANG", 0x9E], ["SHARPEN", 0x9F], ["CONVERSION", 0xA0], ["TRI_ATTACK", 0xA1], ["SUPER_FANG", 0xA2], ["SLASH", 0xA3], ["SUBSTITUTE", 0xA4], ["STRUGGLE", 0xA5]]
elemental_types = [
["NORMAL", "EQU", 0x00],
["FIGHTING", "EQU", 0x01],
["FLYING", "EQU", 0x02],
["POISON", "EQU", 0x03],
["GROUND", "EQU", 0x04],
["ROCK", "EQU", 0x05],
["BUG", "EQU", 0x07],
["GHOST", "EQU", 0x08],
["FIRE", "EQU", 0x14],
["WATER", "EQU", 0x15],
["GRASS", "EQU", 0x16],
["ELECTRIC", "EQU", 0x17],
["PSYCHIC", "EQU", 0x18],
["ICE", "EQU", 0x19],
["DRAGON", "EQU", 0x1A]]
def local_reset_incbins():
asm = None
incbin_lines = []
processed_incbins = {}
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
#reload
load_asm()
isolate_incbins()
process_incbins()
def find_tx_far_entry(map_id, text_id):
for tx_far_line in tx_fars:
if tx_far_line[0] == map_id and tx_far_line[1] == text_id:
return tx_far_line
def insert_tx_far(map_id, text_id, tx_far_line=None):
"inserts a tx_far"
global tx_fars
if tx_far_line == None:
tx_far_line = find_tx_far_entry(map_id, text_id)
text_pointer = tx_far_line[2]
start_address = tx_far_line[3]
tx_far_object = tx_far_line[4]
end_address = tx_far_object[1]["end_address"] + 1 #the end byte; +1 because of a bug somewhere :(
line_number = find_incbin_to_replace_for(start_address)
if line_number == None:
print "skipping tx_far for map_id=" + str(map_id) + " text_id=" + str(text_id) + " text_pointer=" + hex(text_pointer) + " tx_far_start_address=" + hex(start_address)
return
#also do a name check
label = tx_far_label_maker(extract_maps.map_headers[map_id]["name"], text_id)
if (label + ":") in "\n".join(analyze_incbins.asm):
print "skipping tx_far for map_id=" + str(map_id) + " text_id=" + str(text_id) + " text_pointer=" + hex(text_pointer) + " tx_far_start_address=" + hex(start_address)
return
newlines = split_incbin_line_into_three(line_number, start_address, end_address - start_address)
tx_far_asm = tx_far_pretty_printer(tx_far_line)
newlines = newlines.split("\n")
if len(newlines) == 2: index = 0 #replace the 1st line with new content
elif len(newlines) == 3: index = 1 #replace the 2nd line with new content
newlines[index] = tx_far_asm
if len(newlines) == 3 and newlines[2][-2:] == "$0":
#get rid of the last incbin line if it is only including 0 bytes
del newlines[2]
#note that this has to be done after adding in the new asm
newlines = "\n".join(line for line in newlines)
newlines = newlines.replace("$x", "$") #where does this keep coming from??
#signs are dumb; cluster the labels please
if "\"needs fulfilled!\", $55" in newlines:
newlines = "\n" + label + ": "
line_number += 1
if ("STRENGTH to move!" in newlines) or ("it the way it is." in newlines):
newlines = "\n" + label + ": "
line_number += 1
if "@\"" in newlines and not "@@\"" in newlines:
newlines = newlines.replace("@", "@@")
#Char52 doesn't work yet? oh well
newlines = newlines.replace("Char52", "$52")
diff = generate_diff_insert(line_number, newlines)
print "working on map_id=" + str(map_id) + " text_id=" + str(text_id)
print diff
apply_diff(diff)
def insert_all_tx_far_targets():
for tx_far in tx_fars:
map_id = tx_far[0]
text_id = tx_far[1]
#if map_id <= 185: continue #i'm just trying to get it going faster
insert_tx_far(map_id, text_id, tx_far_line=tx_far)
reset_incbins()
analyze_incbins.reset_incbins()
asm = None
incbin_lines = []
processed_incbins = {}
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
load_asm()
isolate_incbins()
process_incbins()
def all_texts_are_tx_fars(map_id):
map2 = extract_maps.map_headers[map_id]
for text_id in map2["texts"]:
txt = map2["texts"][text_id]
if not "TX_FAR" in txt[0].keys(): return False
return True
def texts_label_pretty_printer(map_id):
"output a texts label for map if all texts are TX_FARs and in the asm already"
#extract_maps.map_headers[map_id]["texts"][text_id][0]["TX_FAR"]
#if not all_texts_are_tx_fars(map_id): return None
map2 = extract_maps.map_headers[map_id]
#pointer to the list of texts
texts_list_pointer = int(map2["texts_pointer"], 16)
#get the label for this texts list
base_label = map_name_cleaner(map2["name"], None)[:-2]
label = base_label + "Texts"
#make up a label for each text
text_labels = []
text_id = 1
for text in map2["texts"].keys():
text_label = base_label + "Text" + str(text_id)
text_labels.append(text_label)
text_id += 1
output = label + ": ; " + hex(texts_list_pointer)
output += "\n"
output += spacing + "dw "
first = True
for labela in text_labels:
if not first:
output += ", " + labela
else:
output += labela
first = False
return output
def insert_texts_label(map_id):
#if not all_texts_are_tx_fars(map_id): return None
map2 = extract_maps.map_headers[map_id]
base_label = map_name_cleaner(map2["name"], None)[:-2]
label = base_label + "Texts"
texts_pointer = int(map2["texts_pointer"], 16)
insert_asm = texts_label_pretty_printer(map_id)
line_number = find_incbin_to_replace_for(texts_pointer)
if line_number == None:
print "skipping texts label for map_id=" + str(map_id) + " texts_pointer=" + hex(texts_pointer) + " because the address is taken"
return
#also do a name check
if (label + ":") in "\n".join(analyze_incbins.asm):
print "skipping texts label for map_id=" + str(map_id) + " texts_pointer=" + hex(texts_pointer) + " because the label is already used"
return
newlines = split_incbin_line_into_three(line_number, texts_pointer, len(map2["referenced_texts"])*2 )
newlines = newlines.split("\n")
if len(newlines) == 2: index = 0 #replace the 1st line with new content
elif len(newlines) == 3: index = 1 #replace the 2nd line with new content
newlines[index] = insert_asm
if len(newlines) == 3 and newlines[2][-2:] == "$0":
#get rid of the last incbin line if it is only including 0 bytes
del newlines[2]
#note that this has to be done after adding in the new asm
newlines = "\n".join(line for line in newlines)
newlines = newlines.replace("$x", "$")
diff = generate_diff_insert(line_number, newlines)
print "working on map_id=" + str(map_id) + " texts_pointer=" + hex(texts_pointer)
print diff
apply_diff(diff)
#untested as of 2012-01-07
def insert_all_texts_labels():
for map_id in extract_maps.map_headers.keys():
if map_id not in extract_maps.bad_maps:
if len(extract_maps.map_headers[map_id]["referenced_texts"]) > 0:
insert_texts_label(map_id)
reset_incbins()
analyze_incbins.reset_incbins()
asm = None
incbin_lines = []
processed_incbins = {}
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
load_asm()
isolate_incbins()
process_incbins()
def txt_to_tx_far_pretty_printer(address, label, target_label, include_byte=False):
output = "\n" + label + ": ; " + hex(address) + "\n"
output += spacing + "TX_FAR " + target_label + "\n"
if include_byte:
output += spacing + "db $50\n"
return output
def insert_text_label_tx_far(map_id, text_id):
if map_id in extract_maps.bad_maps:
print "bad map id=" + str(map_id)
return
map2 = extract_maps.map_headers[map_id]
if map2["texts"][text_id] == {0: {}}: return None
base_label = map_name_cleaner(map2["name"], None)[:-2]
label = base_label + "Text" + str(text_id)
target_label = "_" + label
start_address = map2["texts"][text_id][0]["start_address"]
if 0x4000 <= start_address <= 0x7fff:
start_address = extract_maps.calculate_pointer(start_address, int(map2["bank"],16))
include_byte = False
print map2["texts"][text_id]
if "type" in map2["texts"][text_id][1].keys():
if map2["texts"][text_id][1]["type"] == 0x50:
include_byte = True
tx_far_asm = txt_to_tx_far_pretty_printer(start_address, label, target_label, include_byte=include_byte)
line_number = find_incbin_to_replace_for(start_address)
if line_number == None:
print "skipping text label that calls TX_FAR for map_id=" + str(map_id) + " text_id=" + str(text_id) + " because the address is taken " + hex(start_address)
return
#also do a name check
if 1 < ("\n".join(analyze_incbins.asm)).count("\n" + label + ":"):
print "skipping text label that calls TX_FAR for map_id=" + str(map_id) + " text_id" + str(text_id) + " because the label is already used (" + label + ":)"
return
extra = 0
if include_byte: extra += 1
newlines = split_incbin_line_into_three(line_number, start_address, 4 + extra )
newlines = newlines.split("\n")
if len(newlines) == 2: index = 0 #replace the 1st line with new content
elif len(newlines) == 3: index = 1 #replace the 2nd line with new content
newlines[index] = tx_far_asm
if len(newlines) == 3 and newlines[2][-2:] == "$0":
#get rid of the last incbin line if it is only including 0 bytes
del newlines[2]
#note that this has to be done after adding in the new asm
newlines = "\n".join(line for line in newlines)
newlines = newlines.replace("$x", "$")
diff = generate_diff_insert(line_number, newlines)
print "working on map_id=" + str(map_id) + " text_id=" + str(text_id)
print diff
apply_diff(diff)
def insert_all_text_labels():
for map_id in extract_maps.map_headers.keys():
if map_id <= 100: continue #skip
if map_id not in extract_maps.bad_maps:
for text_id in extract_maps.map_headers[map_id]["referenced_texts"]:
insert_text_label_tx_far(map_id, text_id)
reset_incbins()
analyze_incbins.reset_incbins()
asm = None
incbin_lines = []
processed_incbins = {}
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
load_asm()
isolate_incbins()
process_incbins()
#TODO: if line_id !=0 then don't include the label?
def insert_08_asm(map_id, text_id, line_id=0):
map2 = extract_maps.map_headers[map_id]
base_label = map_name_cleaner(map2["name"], None)[:-2]
label = base_label + "Text" + str(text_id)
start_address = all_texts[map_id][text_id][line_id]["start_address"]
(text_asm, end_address) = text_asm_pretty_printer(label, start_address)
print "end address is: " + hex(end_address)
#find where to insert the assembly
line_number = find_incbin_to_replace_for(start_address)
if line_number == None:
print "skipping text label for a $08 on map_id=" + str(map_id) + " text_id=" + str(text_id) + " because the address is taken"
return
#also do a name check
if 1 <= ("\n".join(analyze_incbins.asm)).count("\n" + label + ":"):
print "skipping text label for a $08 on map_id=" + str(map_id) + " text_id=" + str(text_id) + " because the label is already taken (" + label + ":)"
return
newlines = split_incbin_line_into_three(line_number, start_address, end_address - start_address )
newlines = newlines.split("\n")
if len(newlines) == 2: index = 0 #replace the 1st line with new content
elif len(newlines) == 3: index = 1 #replace the 2nd line with new content
newlines[index] = text_asm
if len(newlines) == 3 and newlines[2][-2:] == "$0":
#get rid of the last incbin line if it is only including 0 bytes
del newlines[2]
#note that this has to be done after adding in the new asm
newlines = "\n".join(line for line in newlines)
newlines = newlines.replace("$x", "$")
diff = generate_diff_insert(line_number, newlines)
print "working on map_id=" + str(map_id) + " text_id=" + str(text_id)
print diff
result = apply_diff(diff)
if result == False:
failed_attempts[len(failed_attempts.keys())] = {"map_id": map_id, "text_id": text_id}
def find_all_08s():
all_08s = []
for map_id in all_texts:
for text_id in all_texts[map_id].keys():
if 0 in all_texts[map_id][text_id].keys():
for line_id in all_texts[map_id][text_id].keys():
if all_texts[map_id][text_id][line_id]["type"] == 0x8:
all_08s.append([map_id, text_id, line_id])
return all_08s
def insert_all_08s():
all_08s = find_all_08s()
for the_08_line in all_08s:
map_id = the_08_line[0]
if map_id <= 86: continue #speed things up
text_id = the_08_line[1]
line_id = the_08_line[2]
print "processing map_id=" + str(map_id) + " text_id=" + str(text_id)
insert_08_asm(map_id, text_id, line_id)
#reset everything
analyze_incbins.reset_incbins()
asm = None
incbin_lines = []
processed_incbins = {}
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
#reload
load_asm()
isolate_incbins()
process_incbins()
def insert_asm(start_address, label, text_asm=None, end_address=None):
if text_asm == None and end_address == None:
(text_asm, end_address) = text_asm_pretty_printer(label, start_address, include_08=False)
print "end address is: " + hex(end_address)
#find where to insert the assembly
line_number = find_incbin_to_replace_for(start_address)
if line_number == None:
print "skipping asm because the address is taken"
return False
#name check
if (label + ":") in "\n".join(analyze_incbins.asm):
print "skipping asm because the label is taken"
return False
newlines = split_incbin_line_into_three(line_number, start_address, end_address - start_address )
newlines = newlines.split("\n")
if len(newlines) == 2: index = 0 #replace the 1st line with new content
elif len(newlines) == 3: index = 1 #replace the 2nd line with new content
newlines[index] = text_asm
if len(newlines) == 3 and newlines[2][-2:] == "$0":
#get rid of the last incbin line if it is only including 0 bytes
del newlines[2]
#note that this has to be done after adding in the new asm
newlines = "\n".join(line for line in newlines)
newlines = newlines.replace("$x", "$")
diff = generate_diff_insert(line_number, newlines)
print diff
result = apply_diff(diff, try_fixing=True)
return True
def insert_text(address, label, apply=False, try_fixing=True):
"inserts a text script (but not $8s)"
start_address = address
line_number = find_incbin_to_replace_for(start_address)
if line_number == None:
print "skipping text at " + hex(start_address) + " with address " + label
return "skip"
#another reason to skip is if the interval is 0
processed_incbin = analyze_incbins.processed_incbins[line_number]
if processed_incbin["interval"] == 0:
print "skipping text at " + hex(start_address) + " with address " + label + " because the interval is 0"
return "skip"
text_asm, byte_count = text_pretty_printer_at(start_address, label)
end_address = start_address + byte_count
newlines = split_incbin_line_into_three(line_number, start_address, byte_count)
newlines = newlines.split("\n")
if len(newlines) == 2: index = 0 #replace the 1st line with new content
elif len(newlines) == 3: index = 1 #replace the 2nd line with new content
newlines[index] = text_asm
if len(newlines) == 3 and newlines[2][-2:] == "$0":
#get rid of the last incbin line if it is only including 0 bytes
del newlines[2]
#note that this has to be done after adding in the new asm
newlines = "\n".join(line for line in newlines)
newlines = newlines.replace("$x", "$") #where does this keep coming from??
#Char52 doesn't work yet
newlines = newlines.replace("Char52", "$52")
diff = generate_diff_insert(line_number, newlines)
print diff
if apply:
return apply_diff(diff, try_fixing=try_fixing)
else: #simulate a successful insertion
return True
#move this into another file?
def scan_for_map_scripts_pointer():
for map_id in extract_maps.map_headers.keys(): #skip id=0 (Pallet Town) because the naming conventions are wonky
map2 = extract_maps.map_headers[map_id]
if map_id in extract_maps.bad_maps or map_id in [0, 39, 37, 38]: continue #skip
script_pointer = int(map2["script_pointer"], 16)
main_asm_output, offset, last_hl_address, last_a_address, used_3d97 = output_bank_opcodes(script_pointer)
hl_pointer = "None"
first_script_text = ""
if last_hl_address != None and last_hl_address != "None" and used_3d97==True:
if last_hl_address > 0x3fff:
hl_pointer = extract_maps.calculate_pointer(last_hl_address, int(map2["bank"], 16))
else:
hl_pointer = last_hl_address
byte1 = ord(extract_maps.rom[hl_pointer])
byte2 = ord(extract_maps.rom[hl_pointer+1])
address = byte1 + (byte2 << 8)
if address > 0x3fff:
first_script_pointer = extract_maps.calculate_pointer(address, int(map2["bank"], 16))
else:
first_script_pointer = address
#for later output
first_script_text = " first_script=" + hex(first_script_pointer)
#go ahead and insert this script pointer
insert_asm(first_script_pointer, map_name_cleaner(map2["name"], None)[:-2] + "Script0")
#reset everything
#analyze_incbins.reset_incbins()
asm = None
incbin_lines = []
processed_incbins = {}
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
#reload
load_asm()
isolate_incbins()
process_incbins()
a_numbers = [0]
last_a_id = 0
script_pointers = [hex(first_script_pointer)]
latest_script_pointer = first_script_pointer
while last_a_id == (max(a_numbers)) or last_a_id==0:
asm_output, offset, last_hl_address2, last_a_id, byte1, byte2, address = None, None, None, None, None, None, None
asm_output, offset, last_hl_address2, last_a_id, used_3d97_2 = output_bank_opcodes(latest_script_pointer)
if last_a_id == (max(a_numbers) + 1):
a_numbers.append(last_a_id)
else:
break
byte1 = ord(extract_maps.rom[hl_pointer + (2*last_a_id)])
byte2 = ord(extract_maps.rom[hl_pointer + (2*last_a_id) + 1])
address2 = byte1 + (byte2 << 8)
if address2 > 0x3fff:
latest_script_pointer = extract_maps.calculate_pointer(address2, int(map2["bank"], 16))
else:
latest_script_pointer = address2
script_pointers.append(hex(latest_script_pointer))
#print "latest script pointer (part 1): " + hex(address2)
#print "latest script pointer: " + hex(latest_script_pointer)
#go ahead and insert the asm for this script
result = insert_asm(latest_script_pointer, map_name_cleaner(map2["name"], None)[:-2] + "Script" + str(len(script_pointers) - 1))
if result:
#reset everything
#analyze_incbins.reset_incbins()
asm = None
incbin_lines = []
processed_incbins = {}
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
#reload
load_asm()
isolate_incbins()
process_incbins()
print "map_id=" + str(map_id) + " scripts are: " + str(script_pointers)
if last_hl_address == None: last_hl_address = "None"
else: last_hl_address = hex(last_hl_address)
if hl_pointer != None and hl_pointer != "None": hl_pointer = hex(hl_pointer)
print "map_id=" + str(map_id) + " " + map2["name"] + " script_pointer=" + hex(script_pointer) + " script_pointers=" + hl_pointer + first_script_text
print main_asm_output
print "\n\n"
#insert asm for the main script
result = insert_asm(script_pointer, map_name_cleaner(map2["name"], None)[:-2] + "Script")
if result:
#reset everything
#analyze_incbins.reset_incbins()
asm = None
incbin_lines = []
processed_incbins = {}
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
#reload
load_asm()
isolate_incbins()
process_incbins()
#insert script pointer list asm if there's anything of value
if hl_pointer != None and hl_pointer != "None" and used_3d97==True:
start_address = int(hl_pointer, 16) #where to insert this list
total_size = len(a_numbers) * 2
script_label = map_name_cleaner(map2["name"], None)[:-2] + "Script"
scripts_label = script_label + "s"
script_asm = scripts_label + ": ; " + hex(start_address) + "\n"
script_asm += spacing + "dw"
first = True
for id in a_numbers:
if first:
script_asm += " "
first = False
else:
script_asm += ", "
script_asm += script_label + str(id)
script_asm += "\n" #extra newline?
result = insert_asm(start_address, scripts_label, text_asm=script_asm, end_address=start_address + total_size)
if result:
#reset everything
#analyze_incbins.reset_incbins()
asm = None
incbin_lines = []
processed_incbins = {}
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
#reload
load_asm()
isolate_incbins()
process_incbins()
else:
print "trouble inserting map script pointer list"
print script_asm
sys.exit(0)
def scan_rom_for_tx_fars_and_insert():
"""calls analyze_texts.scan_rom_for_tx_fars()
looks through INCBIN'd addresses from main.asm,
finds TX_FARs that aren't included yet.
"""
x = 0
address_bundles = scan_rom_for_tx_fars(printer=True)
for address_bundle in address_bundles:
tx_far_address = address_bundle[1]
tx_far_target_address = address_bundle[0]
if tx_far_address in [0xeff2]: continue #skip
#if tx_far_address < 0x7627b: continue #because it stopped a few times for errors
tx_far_label = "UnnamedText_%.2x" % (tx_far_address)
tx_far_target_label = "_" + tx_far_label
#let's also do a quick check if it might be in the file already
if not (": ; " + hex(tx_far_address) in analyze_incbins.asm):
print "inserting text at " + hex(tx_far_address)
result = insert_text(tx_far_target_address, tx_far_target_label, apply=True)
else:
#we can't just pretend like it worked, because we don't know what label was used
#so, figure out the label
for line in analyze_incbins.asm_lines:
if ": ; " + hex(tx_far_address) in line:
tx_far_target_label = line.split(":")[0]
result = "skip"
if result == True or result == None:
local_reset_incbins()
result2 = insert_text(tx_far_address, tx_far_label, apply=True)
local_reset_incbins()
elif result == "skip":
print "skipping " + hex(tx_far_address)
# result2 = insert_text(tx_far_address, tx_far_label, apply=True)
# local_reset_incbins()
#just skip these for now
#if not result or not result2:
# sys.exit(0)
def get_mon_name(id):
return pokemons[id]
def get_type_label(id):
for line in elemental_types:
if line[2] == id: return line[0]
return None
def get_attack_label(id):
for move in moves:
if move[1] == id: return move[0]
return "0" #no move
def get_pointer_target_at(address):
rom = extract_maps.rom
byte1 = ord(rom[address])
byte2 = ord(rom[address+1])
pointer = (byte1 + (byte2 << 8))
return pointer
def get_frontsprite_label(id):
return get_mon_name(id).title() + "PicFront"
def get_backsprite_label(id):
return get_mon_name(id).title() + "PicBack"
def base_data_pretty_printer(id):
"""returns beautified asm for this pokemon
uses 28 bytes
pokedex number, base hp, base attack, base defense, base speed, base special
type 1 (label), type 2 (label), catch rate, base experience yield
dimensions of frontsprite (byte)
frontsprite label pointer
backsprite label pointer
attacks known at level 1 (4 bytes, 4 constants)
growth rate (byte)
incbin - tm/hm flags (7 bytes)
padding (0)
"""
output = ""
rom = extract_maps.rom
base_address = 0x383de + (28 * (id))
pokedex_number = id
mon_name = get_mon_name(id)
base_hp = ord(rom[base_address + 1])
base_attack = ord(rom[base_address + 2])
base_defense = ord(rom[base_address + 3])
base_speed = ord(rom[base_address + 4])
base_special = ord(rom[base_address + 5])
type1_id = ord(rom[base_address + 6])
type2_id = ord(rom[base_address + 7])
type1 = get_type_label(type1_id)
type2 = get_type_label(type2_id)
catch_rate = ord(rom[base_address + 8])
base_exp_yield = ord(rom[base_address + 9])
frontsprite_dimensions = ord(rom[base_address + 10])
frontsprite = get_frontsprite_label(id)
backsprite = get_backsprite_label(id)
#attacks known at level 0
attack1 = get_attack_label(ord(rom[base_address + 15]))
attack2 = get_attack_label(ord(rom[base_address + 16]))
attack3 = get_attack_label(ord(rom[base_address + 17]))
attack4 = get_attack_label(ord(rom[base_address + 18]))
growth_rate = ord(rom[base_address + 19])
incbin_start_address = base_address + 20
incbin_end_address = base_address + 27
output = mon_name.title() + ("BaseStats: ; 0x%.x" % (base_address)) + "\n"
output += spacing + "db DEX_" + mon_name.upper() + " ; pokedex id\n"
output += spacing + ("db " + str(base_hp)) + " ; base hp\n"
output += spacing + "db " + str(base_attack) + " ; base attack\n"
output += spacing + "db " + str(base_defense) + " ; base defense\n"
output += spacing + "db " + str(base_speed) + " ; base speed\n"
output += spacing + "db " + str(base_special) + " ; base special\n\n"
output += spacing + "db " + type1 + " ; species type 1\n"
output += spacing + "db " + type2 + " ; species type 2\n\n"
output += spacing + "db " + str(catch_rate) + " ; catch rate\n"
output += spacing + "db " + str(base_exp_yield) + " ; base exp yield\n"
output += spacing + ("db $%.2x" % (frontsprite_dimensions)) + " ; sprite dimensions\n\n"
output += spacing + "dw " + frontsprite + "\n"
output += spacing + "dw " + backsprite + "\n"
output += spacing + "\n" + spacing + "; attacks known at lvl 0\n"
output += spacing + "db " + attack1 + "\n"
output += spacing + "db " + attack2 + "\n"
output += spacing + "db " + attack3 + "\n"
output += spacing + "db " + attack4 + "\n\n"
output += spacing + "db " + str(growth_rate) + " ; growth rate\n"
output += spacing + "\n" + spacing + "; learnset\n"
#learnset crap
output += spacing + "db %" + bin(ord(rom[base_address + 20]))[2:].zfill(8) + "\n"
output += spacing + "db %" + bin(ord(rom[base_address + 21]))[2:].zfill(8) + "\n"
output += spacing + "db %" + bin(ord(rom[base_address + 22]))[2:].zfill(8) + "\n"
output += spacing + "db %" + bin(ord(rom[base_address + 23]))[2:].zfill(8) + "\n"
output += spacing + "db %" + bin(ord(rom[base_address + 24]))[2:].zfill(8) + "\n"
output += spacing + "db %" + bin(ord(rom[base_address + 25]))[2:].zfill(8) + "\n"
output += spacing + "db %" + bin(ord(rom[base_address + 26]))[2:].zfill(8) + "\n\n"
output += spacing + "db 0 ; padding\n"
return output
def insert_base_stats(id):
insert_asm = base_data_pretty_printer(id)
address = 0x383de + (28 * (id))
line_number = find_incbin_to_replace_for(address)
label = get_mon_name(id).title() + "BaseStats"
if line_number == None:
print "skipping, already inserted at " + hex(address)
return
#also do a name check
if (label + ":") in "\n".join(analyze_incbins.asm):
print "skipping " + label + " because it is already in use.."
return
newlines = split_incbin_line_into_three(line_number, address, 28 )
newlines = newlines.split("\n")
if len(newlines) == 2: index = 0 #replace the 1st line with new content
elif len(newlines) == 3: index = 1 #replace the 2nd line with new content
newlines[index] = insert_asm
if len(newlines) == 3 and newlines[2][-2:] == "$0":
#get rid of the last incbin line if it is only including 0 bytes
del newlines[2]
#note that this has to be done after adding in the new asm
newlines = "\n".join(line for line in newlines)
newlines = newlines.replace("$x", "$")
diff = generate_diff_insert(line_number, newlines)
print diff
apply_diff(diff, try_fixing=False, do_compile=False)
def insert_all_base_stats():
for id in range(0, 151):
#if id < 62: continue #skip
insert_base_stats(id)
#reset everything
reset_incbins()
analyze_incbins.reset_incbins()
asm = None
incbin_lines = []
processed_incbins = {}
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
#reload
load_asm()
isolate_incbins()
process_incbins()
if __name__ == "__main__":
#load map headers and object data
extract_maps.load_rom()
extract_maps.load_map_pointers()
extract_maps.read_all_map_headers()
load_labels()
#print base_data_pretty_printer(0)
load_asm()
isolate_incbins()
process_incbins()
#insert_base_stats(1)
insert_all_base_stats()
#load texts (these two have different formats)
#all_texts = pretty_map_headers.analyze_texts.analyze_texts()
#pretty_map_headers.all_texts = all_texts
#tx_fars = pretty_map_headers.find_all_tx_fars()
#load incbins
#reset_incbins()
#scan_for_map_scripts_pointer()
#scan_rom_for_tx_fars_and_insert()
#insert_text(0xa586b, "_VermilionCityText14")
#insert _ViridianCityText10
#insert_tx_far(1, 10)
#just me testing a pokemart sign duplicate
#insert_tx_far(3, 14)
#this is the big one
#insert_all_tx_far_targets()
#for map_id in extract_maps.map_headers.keys():
# if map_id not in extract_maps.bad_maps:
# if len(extract_maps.map_headers[map_id]["referenced_texts"]) > 0:
# texts_label_pretty_printer(map_id)
#insert_texts_label(240)
#insert_all_texts_labels()
#insert_text_label_tx_far(240, 1)
#insert_all_text_labels()
#insert_08_asm(83, 1)
#insert_all_08s()
#insert_asm(0x1da56, "NameRaterText1")
#insert_text_label_tx_far(91, 1)
#insert_text(0x44276, "ViridianPokeCenterText4")
#insert_texts_label(4)
#insert_all_texts_labels()

View file

@ -1,37 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-15
#dump map height/width constants
import extract_maps
from pretty_map_headers import map_name_cleaner, map_constants
def get_map_size_constants(do_sed=False):
output = ""
sed_lines = ""
for map_id in extract_maps.map_headers.keys():
if map_id in extract_maps.bad_maps: continue #skip
map2 = extract_maps.map_headers[map_id]
base_name = map_name_cleaner(map2["name"], None)[:-2]
constant_name = map_constants[map_id]
height = int(map2["y"], 16)
width = int(map2["x"], 16)
output += "; " + base_name + "_h map_id=" + str(map_id) + "\n"
output += constant_name + "_HEIGHT EQU $%.2x\n" % (height)
output += constant_name + "_WIDTH EQU $%.2x\n" % (width)
output += "\n"
sed_lines += "sed -i 's/" + base_name + "Height/" + constant_name + "_HEIGHT" + "/g' main.asm" + "\n"
sed_lines += "sed -i 's/" + base_name + "Width/" + constant_name + "_WIDTH" + "/g' main.asm" + "\n"
if do_sed:
return sed_lines
else:
return output
if __name__ == "__main__":
extract_maps.load_rom()
extract_maps.load_map_pointers()
extract_maps.read_all_map_headers()
print get_map_size_constants(do_sed=True)

View file

@ -1,171 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-03
#purpose: extract .blk files from baserom.gbc
#note: use python2.7 because of subprocess in analyze_incbins
import extract_maps #rom, assert_rom, load_rom, calculate_pointer, load_map_pointers, read_all_map_headers, map_headers
from pretty_map_headers import map_name_cleaner
from analyze_incbins import asm, offset_to_pointer, find_incbin_to_replace_for, split_incbin_line_into_three, generate_diff_insert, load_asm, isolate_incbins, process_incbins
import analyze_incbins
import os, sys
import subprocess
spacing = " "
used_map_pointers = []
def extract_map_block_data(map_id, savefile=False):
map = extract_maps.map_headers[map_id]
if map["name"] == "FREEZE": return #skip this one
blocksdata_pointer = int(map["map_pointer"], 16)
y = int(map["y"], 16)
x = int(map["x"], 16)
size = x*y
#fetch the data from the rom
blocksdata = extract_maps.rom[blocksdata_pointer:blocksdata_pointer+size]
#clean up the filename and label (for pokered.asm)
cleaned_name = map_name_cleaner(map["name"], None)
label_text = cleaned_name.replace("_h", "Blocks")
filename = cleaned_name.replace("_h", "").lower()
full_filepath = "maps/" + filename + ".blk"
if savefile:
print "Saving ../maps/" + filename + ".blk for map id=" + str(map_id)
fh = open("../maps/" + filename + ".blk", "w")
fh.write(blocksdata)
fh.close()
def make_labels(name):
cleaned_name = map_name_cleaner(name, None)
label_text = cleaned_name.replace("_h", "Blocks")
filename = cleaned_name.replace("_h", "").lower()
full_filepath = "maps/" + filename + ".blk"
return cleaned_name, label_text, filename, full_filepath
def generate_label_asm(name,size=None):
cleaned_name, label_text, filename, full_filepath = make_labels(name)
output = label_text + ":"
if size: output += " ; " + str(size) + "\n"
else: output += "\n"
output += spacing + "INCBIN \"" + full_filepath + "\""
return output
def insert_map_block_label(map_id):
map = extract_maps.map_headers[map_id]
address = int(map["map_pointer"], 16)
y = int(map["y"], 16)
x = int(map["x"], 16)
size = x*y
print "map name: " + map["name"]
print "map address: " + map["map_pointer"]
line_number = find_incbin_to_replace_for(address)
if line_number == None:
print "skipping map id=" + str(map_id) + " probably because it was already done."
used_map_pointers.append(map["map_pointer"])
return
newlines = split_incbin_line_into_three(line_number, address, size)
label_asm = generate_label_asm(map["name"], size)
newlines = newlines.split("\n")
if len(newlines) == 2: index = 0 #replace the 1st line with new content
elif len(newlines) == 3: index = 1 #replace the 2nd line with new content
newlines[index] = label_asm
if len(newlines) == 3 and newlines[2][-2:] == "$0":
#get rid of the last incbin line if it is only including 0 bytes
del newlines[2]
#note that this has to be done after adding in the new asm
newlines = "\n".join(line for line in newlines)
#fix a lame error from somewhere
newlines = newlines.replace("$x", "$")
diff = generate_diff_insert(line_number, newlines)
print diff
print "... Applying diff."
#write the diff to a file
fh = open("temp.patch", "w")
fh.write(diff)
fh.close()
#apply the patch
os.system("patch ../pokered.asm temp.patch")
#remove the patch
os.system("rm temp.patch")
#confirm it's working
subprocess.check_call("cd ../; make clean; LC_CTYPE=UTF-8 make", shell=True)
def get_all_map_blockdata():
for map in extract_maps.map_headers.keys():
extract_map_block_data(map)
def insert_all_labels():
"this is very buggy, don't use it"
#limit = 200 #0:150
for map in extract_maps.map_headers.keys():
mapmap = extract_maps.map_headers[map]
if mapmap["name"] == "FREEZE": continue #skip this one
if "Ash's" in mapmap["name"]: continue
if "Gary's" in mapmap["name"]: continue
if not ("cat" in mapmap["name"]) and "copy" in mapmap["name"].lower(): continue #skip this one
#bill's house breaks things?
#if mapmap["name"] == "Bill's House": continue
if mapmap["name"] == "Viridian Forest": continue
#if mapmap["name"] == "Cerulean Mart": continue
if mapmap["name"] == "Virdian Forest Exit": continue
#if "copy" in mapmap["name"].lower(): continue #skip this one too..
if mapmap["map_pointer"] in used_map_pointers: continue #skip for sure
#reset asm
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
#reload asm each time
load_asm()
#check if this label is already in there
cleaned_name, label_text, filename, full_filepath = make_labels(mapmap["name"])
if label_text in "\n".join(line for line in analyze_incbins.asm):
print "skipping (found label text in asm already)"
used_map_pointers.append(mapmap["map_pointer"])
continue #skip this one
isolate_incbins()
process_incbins()
print "XYZ|" + mapmap["name"]
insert_map_block_label(map)
used_map_pointers.append(mapmap["map_pointer"])
if __name__ == "__main__":
#load map headers
extract_maps.load_rom()
extract_maps.load_map_pointers()
extract_maps.read_all_map_headers()
#load incbins
load_asm()
isolate_incbins()
process_incbins()
#extract_map_block_data(2)
#get_all_map_blockdata()
#insert_map_block_label(49)
insert_all_labels()

View file

@ -1,4 +0,0 @@
import os
#main dir of repo (simply one level up than here)
pokered_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

View file

@ -1,755 +0,0 @@
# -*- coding: utf-8 -*-
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-02
#purpose: dump asm for each map header
import json
import extract_maps
import sprite_helper
import random
import string
import analyze_texts #hopefully not a dependency loop
base = 16
spacing = " "
all_texts = None
#map constants
map_constants = [
["PALLET_TOWN", 0x00],
["VIRIDIAN_CITY", 0x01],
["PEWTER_CITY", 0x02],
["CERULEAN_CITY", 0x03],
["LAVENDER_TOWN", 0x04],
["VERMILION_CITY", 0x05],
["CELADON_CITY", 0x06],
["FUCHSIA_CITY", 0x07],
["CINNABAR_ISLAND", 0x08],
["INDIGO_PLATEAU", 0x09],
["SAFFRON_CITY", 0x0A],
["ROUTE_1", 0x0C],
["ROUTE_2", 0x0D],
["ROUTE_3", 0x0E],
["ROUTE_4", 0x0F],
["ROUTE_5", 0x10],
["ROUTE_6", 0x11],
["ROUTE_7", 0x12],
["ROUTE_8", 0x13],
["ROUTE_9", 0x14],
["ROUTE_10", 0x15],
["ROUTE_11", 0x16],
["ROUTE_12", 0x17],
["ROUTE_13", 0x18],
["ROUTE_14", 0x19],
["ROUTE_15", 0x1A],
["ROUTE_16", 0x1B],
["ROUTE_17", 0x1C],
["ROUTE_18", 0x1D],
["ROUTE_19", 0x1E],
["ROUTE_20", 0x1F],
["ROUTE_21", 0x20],
["ROUTE_22", 0x21],
["ROUTE_23", 0x22],
["ROUTE_24", 0x23],
["ROUTE_25", 0x24],
["REDS_HOUSE_1F", 0x25],
["REDS_HOUSE_2F", 0x26],
["BLUES_HOUSE", 0x27],
["OAKS_LAB", 0x28],
["VIRIDIAN_POKECENTER", 0x29],
["VIRIDIAN_MART", 0x2A],
["VIRIDIAN_SCHOOL", 0x2B],
["VIRIDIAN_HOUSE", 0x2C],
["VIRIDIAN_GYM", 0x2D],
["DIGLETTS_CAVE_EXIT", 0x2E],
["VIRIDIAN_FOREST_EXIT", 0x2F],
["ROUTE_2_HOUSE", 0x30],
["ROUTE_2_GATE", 0x31],
["VIRIDIAN_FOREST_ENTRANCE", 0x32],
["VIRIDIAN_FOREST", 0x33],
["MUSEUM_1F", 0x34],
["MUSEUM_2F", 0x35],
["PEWTER_GYM", 0x36],
["PEWTER_HOUSE_1", 0x37],
["PEWTER_MART", 0x38],
["PEWTER_HOUSE_2", 0x39],
["PEWTER_POKECENTER", 0x3A],
["MT_MOON_1", 0x3B],
["MT_MOON_2", 0x3C],
["MT_MOON_3", 0x3D],
["TRASHED_HOUSE", 0x3E],
["CERULEAN_HOUSE", 0x3F],
["CERULEAN_POKECENTER", 0x40],
["CERULEAN_GYM", 0x41],
["BIKE_SHOP", 0x42],
["CERULEAN_MART", 0x43],
["MT_MOON_POKECENTER", 0x44],
["ROUTE_5_GATE", 0x46],
["PATH_ENTRANCE_ROUTE_5", 0x47],
["DAYCAREM", 0x48],
["ROUTE_6_GATE", 0x49],
["PATH_ENTRANCE_ROUTE_6", 0x4A],
["ROUTE_7_GATE", 0x4C],
["PATH_ENTRANCE_ROUTE_7", 0x4D],
["ROUTE_8_GATE", 0x4F],
["PATH_ENTRANCE_ROUTE_8", 0x50],
["ROCK_TUNNEL_POKECENTER", 0x51],
["ROCK_TUNNEL_1", 0x52],
["POWER_PLANT", 0x53],
["ROUTE_11_GATE_1F", 0x54],
["DIGLETTS_CAVE_ENTRANCE", 0x55],
["ROUTE_11_GATE_2F", 0x56],
["ROUTE_12_GATE", 0x57],
["BILLS_HOUSE", 0x58],
["VERMILION_POKECENTER", 0x59],
["POKEMON_FAN_CLUB", 0x5A],
["VERMILION_MART", 0x5B],
["VERMILION_GYM", 0x5C],
["VERMILION_HOUSE_1", 0x5D],
["VERMILION_DOCK", 0x5E],
["SS_ANNE_1", 0x5F],
["SS_ANNE_2", 0x60],
["SS_ANNE_3", 0x61],
["SS_ANNE_4", 0x62],
["SS_ANNE_5", 0x63],
["SS_ANNE_6", 0x64],
["SS_ANNE_7", 0x65],
["SS_ANNE_8", 0x66],
["SS_ANNE_9", 0x67],
["SS_ANNE_10", 0x68],
["VICTORY_ROAD_1", 0x6C],
["LANCES_ROOM", 0x71],
["HALL_OF_FAME", 0x76],
["UNDERGROUND_PATH_NS", 0x77],
["CHAMPIONS_ROOM", 0x78],
["UNDERGROUND_PATH_WE", 0x79],
["CELADON_MART_1", 0x7A],
["CELADON_MART_2", 0x7B],
["CELADON_MART_3", 0x7C],
["CELADON_MART_4", 0x7D],
["CELADON_MART_5", 0x7E],
["CELADON_MART_6", 0x7F],
["CELADON_MANSION_1", 0x80],
["CELADON_MANSION_2", 0x81],
["CELADON_MANSION_3", 0x82],
["CELADON_MANSION_4", 0x83],
["CELADON_MANSION_5", 0x84],
["CELADON_POKECENTER", 0x85],
["CELADON_GYM", 0x86],
["GAME_CORNER", 0x87],
["CELADON_HOUSE", 0x88],
["CELADONPRIZE_ROOM", 0x89],
["CELADON_DINER", 0x8A],
["CELADON_HOUSE_2", 0x8B],
["CELADONHOTEL", 0x8C],
["LAVENDER_POKECENTER", 0x8D],
["POKEMONTOWER_1", 0x8E],
["POKEMONTOWER_2", 0x8F],
["POKEMONTOWER_3", 0x90],
["POKEMONTOWER_4", 0x91],
["POKEMONTOWER_5", 0x92],
["POKEMONTOWER_6", 0x93],
["POKEMONTOWER_7", 0x94],
["LAVENDER_HOUSE_1", 0x95],
["LAVENDER_MART", 0x96],
["LAVENDER_HOUSE_2", 0x97],
["FUCHSIA_MART", 0x98],
["FUCHSIA_HOUSE_1", 0x99],
["FUCHSIA_POKECENTER", 0x9A],
["FUCHSIA_HOUSE_2", 0x9B],
["SAFARIZONEENTRANCE", 0x9C],
["FUCHSIA_GYM", 0x9D],
["FUCHSIAMEETINGROOM", 0x9E],
["SEAFOAM_ISLANDS_2", 0x9F],
["SEAFOAM_ISLANDS_3", 0xA0],
["SEAFOAM_ISLANDS_4", 0xA1],
["SEAFOAM_ISLANDS_5", 0xA2],
["VERMILION_HOUSE_2", 0xA3],
["FUCHSIA_HOUSE_3", 0xA4],
["MANSION_1", 0xA5],
["CINNABAR_GYM", 0xA6],
["CINNABAR_LAB_1", 0xA7],
["CINNABAR_LAB_2", 0xA8],
["CINNABAR_LAB_3", 0xA9],
["CINNABAR_LAB_4", 0xAA],
["CINNABAR_POKECENTER", 0xAB],
["CINNABAR_MART", 0xAC],
["INDIGO_PLATEAU_LOBBY", 0xAE],
["COPYCATS_HOUSE_1F", 0xAF],
["COPYCATS_HOUSE_2F", 0xB0],
["FIGHTINGDOJO", 0xB1],
["SAFFRON_GYM", 0xB2],
["SAFFRON_HOUSE_1", 0xB3],
["SAFFRON_MART", 0xB4],
["SILPH_CO_1F", 0xB5],
["SAFFRON_POKECENTER", 0xB6],
["SAFFRON_HOUSE_2", 0xB7],
["ROUTE_15_GATE", 0xB8],
["ROUTE_16_GATE_1F", 0xBA],
["ROUTE_16_GATE_2F", 0xBB],
["ROUTE_16_HOUSE", 0xBC],
["ROUTE_12_HOUSE", 0xBD],
["ROUTE_18_GATE_1F", 0xBE],
["ROUTE_18_GATE_2F", 0xBF],
["SEAFOAM_ISLANDS_1", 0xC0],
["ROUTE_22_GATE", 0xC1],
["VICTORY_ROAD_2", 0xC2],
["ROUTE_12_GATE_2F", 0xC3],
["VERMILION_HOUSE_3", 0xC4],
["DIGLETTS_CAVE", 0xC5],
["VICTORY_ROAD_3", 0xC6],
["ROCKET_HIDEOUT_1", 0xC7],
["ROCKET_HIDEOUT_2", 0xC8],
["ROCKET_HIDEOUT_3", 0xC9],
["ROCKET_HIDEOUT_4", 0xCA],
["ROCKET_HIDEOUT_ELEVATOR", 0xCB],
["SILPH_CO_2F", 0xCF],
["SILPH_CO_3F", 0xD0],
["SILPH_CO_4F", 0xD1],
["SILPH_CO_5F", 0xD2],
["SILPH_CO_6F", 0xD3],
["SILPH_CO_7F", 0xD4],
["SILPH_CO_8F", 0xD5],
["MANSION_2", 0xD6],
["MANSION_3", 0xD7],
["MANSION_4", 0xD8],
["SAFARI_ZONE_EAST", 0xD9],
["SAFARI_ZONE_NORTH", 0xDA],
["SAFARI_ZONE_WEST", 0xDB],
["SAFARI_ZONE_CENTER", 0xDC],
["SAFARI_ZONE_REST_HOUSE_1", 0xDD],
["SAFARI_ZONE_SECRET_HOUSE", 0xDE],
["SAFARI_ZONE_REST_HOUSE_2", 0xDF],
["SAFARI_ZONE_REST_HOUSE_3", 0xE0],
["SAFARI_ZONE_REST_HOUSE_4", 0xE1],
["UNKNOWN_DUNGEON_2", 0xE2],
["UNKNOWN_DUNGEON_3", 0xE3],
["UNKNOWN_DUNGEON_1", 0xE4],
["NAME_RATERS_HOUSE", 0xE5],
["CERULEAN_HOUSE_3", 0xE6],
["ROCK_TUNNEL_2", 0xE8],
["SILPH_CO_9F", 0xE9],
["SILPH_CO_10F", 0xEA],
["SILPH_CO_11F", 0xEB],
["SILPH_CO_ELEVATOR", 0xEC],
["BATTLE_CENTER", 0xEF],
["TRADE_CENTER", 0xF0],
["LORELEIS_ROOM", 0xF5],
["BRUNOS_ROOM", 0xF6],
["AGATHAS_ROOM", 0xF7],
["BEACH_HOUSE", 0xF8]]
#i prefer a different data structure
temp = {}
for constant in map_constants:
value = constant[1]
name = constant[0]
temp[value] = name
map_constants = temp
del temp
#these appear outside of quotes
constant_abbreviation_bytes = {
}
#these appear in quotes
char_conversion = [
(" ", 0x7F),
("A", 0x80),
("B", 0x81),
("C", 0x82),
("D", 0x83),
("E", 0x84),
("F", 0x85),
("G", 0x86),
("H", 0x87),
("I", 0x88),
("J", 0x89),
("K", 0x8A),
("L", 0x8B),
("M", 0x8C),
("N", 0x8D),
("O", 0x8E),
("P", 0x8F),
("Q", 0x90),
("R", 0x91),
("S", 0x92),
("T", 0x93),
("U", 0x94),
("V", 0x95),
("W", 0x96),
("X", 0x97),
("Y", 0x98),
("Z", 0x99),
("(", 0x9A),
(")", 0x9B),
(":", 0x9C),
(";", 0x9D),
("[", 0x9E),
("]", 0x9F),
("a", 0xA0),
("b", 0xA1),
("c", 0xA2),
("d", 0xA3),
("e", 0xA4),
("f", 0xA5),
("g", 0xA6),
("h", 0xA7),
("i", 0xA8),
("j", 0xA9),
("k", 0xAA),
("l", 0xAB),
("m", 0xAC),
("n", 0xAD),
("o", 0xAE),
("p", 0xAF),
("q", 0xB0),
("r", 0xB1),
("s", 0xB2),
("t", 0xB3),
("u", 0xB4),
("v", 0xB5),
("w", 0xB6),
("x", 0xB7),
("y", 0xB8),
("z", 0xB9),
("é", 0xBA),
("'d", 0xBB),
("'l", 0xBC),
("'s", 0xBD),
("'t", 0xBE),
("'v", 0xBF),
("'", 0xE0),
("-", 0xE3),
("'r", 0xE4),
("'m", 0xE5),
("?", 0xE6),
("!", 0xE7),
(".", 0xE8),
("", 0xEF),
#("¥", 0xF0),
("/", 0xF3),
(",", 0xF4),
("", 0xF5),
("0", 0xF6),
("1", 0xF7),
("2", 0xF8),
("3", 0xF9),
("4", 0xFA),
("5", 0xFB),
("6", 0xFC),
("7", 0xFD),
("8", 0xFE),
("9", 0xFF)]
#these appear in quotes
txt_bytes = {
0x50: "@",
0x54: "#",
0x75: "",
}
for item in char_conversion:
txt_bytes[item[1]] = item[0]
del char_conversion
#this was originally for renaming freeze maps for a unique name
def random_hash():
available_chars = string.hexdigits[:16]
return ''.join(
random.choice(available_chars)
for dummy in xrange(5))
def offset_to_pointer(offset):
if type(offset) == str: offset = int(offset, base)
return int(offset) % 0x4000 + 0x4000
def map_name_cleaner(name, id):
"names have to be acceptable asm labels"
#duck out early
if name == "FREEZE":
#name += "_" + random_hash() + "_h"
name += "_" + str(id) + "_h"
return name
#the long haul
name = name.replace(":", "")
name = name.replace("(", "")
name = name.replace(")", "")
name = name.replace("'", "")
name = name.replace("/", "") #N/S -> NS, W/E -> WE
name = name.replace(".", "") #S.S. -> SS, Mt. -> Mt
name = name.replace(" ", "") #or '_' ??
name = name + "_h"
return name
def write_connections(north, south, west, east):
#north 0, south 1, west 2, east 3
if north and south and west and east: return "NORTH | SOUTH | WEST | EAST"
if north and south and west and not east: return "NORTH | SOUTH | WEST"
if north and south and not west and east: return "NORTH | SOUTH | EAST"
if north and not south and west and east: return "NORTH | WEST | EAST"
if not north and south and west and east: return "SOUTH | WEST | EAST"
if north and south and not west and not east: return "NORTH | SOUTH"
if not north and not south and west and east: return "WEST | EAST"
if not north and south and west and not east: return "SOUTH | WEST"
if not north and south and not west and east: return "SOUTH | EAST"
if north and not south and west and not east: return "NORTH | WEST"
if north and not south and not west and east: return "NORTH | EAST"
raise Exception, "unpredicted outcome on write_connections"
#TODO: make this elegant
def connection_line(byte):
if type(byte) == str:
byte = int(byte, base)
connections = 0
north, south, west, east = False, False, False, False
temp = bin(byte)[2:]
if len(temp) == 1:
if temp[0] == "1": #EAST
east = True
elif len(temp) == 2:
if temp[0] == "1": #WEST
west = True
if temp[1] == "1": #EAST
east = True
elif len(temp) == 3:
if temp[0] == "1": #SOUTH
south = True
if temp[1] == "1": #WEST
west = True
if temp[2] == "1": #EAST
east = True
elif len(temp) == 4:
if temp[0] == "1": #NORTH
north = True
if temp[1] == "1": #SOUTH
south = True
if temp[2] == "1": #WEST
west = True
if temp[3] == "1": #EAST
east = True
if north: connections += 1
if south: connections += 1
if west: connections += 1
if east: connections += 1
#i don't have time to optimize this
if connections == 0:
return "$00"
if connections == 1:
if north: return "NORTH"
if south: return "SOUTH"
if west: return "WEST"
if east: return "EAST"
if connections >= 2:
return write_connections(north, south, west, east)
def connection_pretty_printer(connections):
#map_id, connected_map_tile_pointer, current_map_tile_pointer, bigness, width, y, x, window_pointer
output = ""
for connection in connections.keys():
connection = connections[connection]
map_id = hex(connection["map_id"])[2:].zfill(2)
connected_map_tile_pointer = connection["connected_map_tile_pointer"][2:]
current_map_tile_pointer = connection["current_map_tile_pointer"][2:]
bigness = hex(int(connection["bigness"], base))[2:].zfill(2)
width = hex(int(connection["width"], base))[2:].zfill(2)
y = hex(connection["y"])[2:].zfill(2)
x = hex(connection["x"])[2:].zfill(2)
window_pointer = connection["window_pointer"][2:]
output += spacing + "db $" + map_id + " ; some map\n"
output += spacing + "dw $" + connected_map_tile_pointer + ", $" + current_map_tile_pointer + " ; pointers (connected, current) (strip)\n"
output += spacing + "db $" + bigness + ", $" + width + " ; bigness, width\n"
output += spacing + "db $" + y + ", $" + x + " ; alignments (y, x)\n"
output += spacing + "dw $" + window_pointer + " ; window\n\n"
return output
def map_header_pretty_printer(map_header):
address = map_header["address"]
bank = map_header["bank"]
id = map_header["id"]
name = map_header["name"]
asm_name = map_name_cleaner(name, id)
if name == "FREEZE": return "" #skip freeze maps
tileset = map_header["tileset"][2:]
y = int(map_header["y"], base)
x = int(map_header["x"], base)
map_pointer = map_header["map_pointer"]
texts_pointer = map_header["texts_pointer"]
script_pointer = map_header["script_pointer"]
connection_byte = map_header["connection_byte"]
connections = map_header["connections"]
object_data_pointer = map_header["object_data_pointer"]
byte_size = 12 + (11 * len(connections.keys()))
map_pointer = hex(offset_to_pointer(map_pointer))[2:]
texts_pointer = hex(offset_to_pointer(texts_pointer))[2:]
script_pointer = hex(offset_to_pointer(script_pointer))[2:]
object_data_pointer = hex(offset_to_pointer(object_data_pointer))[2:]
#formatting: hex(y)[2:].zill(2) or "%02x" % (y,)
output = asm_name + ": ; " + address + " to " + hex(int(address, base) + byte_size) + " (" + str(byte_size) + " bytes) (id=" + str(id) + ")\n"
output += spacing + "db $" + str(tileset).zfill(2) + " ; tileset\n"
output += spacing + "db $" + hex(y)[2:].zfill(2) + ", $" + hex(x)[2:].zfill(2) + " ; dimensions (y, x)\n"
output += spacing + "dw $" + map_pointer + ", $" + texts_pointer + ", $" + script_pointer + " ; blocks, texts, scripts\n"
output += spacing + "db " + connection_line(connection_byte) + " ; connections\n\n"
if len(connections) > 0:
output += spacing + "; connections data\n\n"
output += connection_pretty_printer(connections)
output += spacing + "; end connection data\n\n"
#TODO: print out label for object_data_pointer if it's already in the file
output += spacing + "dw $" + object_data_pointer + " ; objects\n"
return output
def make_object_label_name(name):
"""make a label for the asm file
like: PalletTownObject"""
name = map_name_cleaner(name, None)
return name.replace("_h", "") + "Object"
def make_text_label(map_name, id):
"""using standard object labels
for instance, PalletTownText3"""
label = map_name_cleaner(map_name, None)[:-2] + "Text" + str(id)
return label
def object_data_pretty_printer(map_id):
map = extract_maps.map_headers[map_id]
output = ""
label_name = make_object_label_name(map["name"])
object_data_pointer = map["object_data_pointer"]
object = map["object_data"]
size = extract_maps.compute_object_data_size(object)
output += label_name + ": ; " + object_data_pointer + " (size=" + str(size) + ")\n"
output += spacing + "db $" + hex(object["maps_border_tile"])[2:] + " ; border tile\n"
output += "\n"
output += spacing + "db $" + hex(int(object["number_of_warps"]))[2:] + " ; warps\n"
#warps
for warp_id in object["warps"]:
warp = object["warps"][warp_id]
y = warp["y"]
x = warp["x"]
warp_to_point = warp["warp_to_point"]
warp_to_map_id = warp["warp_to_map_id"]
try:
warp_to_map_constant = map_constants[warp_to_map_id]
except Exception, exc:
warp_to_map_constant = "$" + hex(warp_to_map_id)[2:]
output += spacing + "db $" + hex(int(y))[2:] + ", $" + hex(int(x))[2:] + ", $" + hex(int(warp_to_point))[2:] + ", " + warp_to_map_constant + "\n"
output += "\n"
output += spacing + "db $" + hex(int(object["number_of_signs"]))[2:] + " ; signs\n"
#signs
for sign_id in object["signs"]:
sign = object["signs"][sign_id]
y = sign["y"]
x = sign["x"]
text_id = sign["text_id"]
output += spacing + "db $" + hex(int(y))[2:] + ", $" + hex(int(x))[2:] + ", $" + hex(int(text_id))[2:] + " ; " + make_text_label(map["name"], text_id) + "\n"
output += "\n"
output += spacing + "db $" + hex(int(object["number_of_things"]))[2:] + " ; people\n"
#people
for thing_id in object["things"]:
thing = object["things"][thing_id]
ending = ""
if thing["type"] == "item":
ending = ", $" + hex(int(thing["item_number"]))[2:] + " ; item\n"
elif thing["type"] == "trainer":
ending = ", $" + hex(int(thing["trainer_type"]))[2:] + ", $" + hex(int(thing["pokemon_set"]))[2:] + " ; trainer\n"
else:
ending = " ; person\n"
picture_number = hex(int(thing["picture_number"]))[2:]
y = hex(int(thing["y"]) - 4)[2:]
x = hex(int(thing["x"]) - 4)[2:]
movement1 = hex(int(thing["movement1"]))[2:]
movement2 = hex(int(thing["movement2"]))[2:]
text_id = hex(int(thing["original_text_string_number"]))[2:]
output += spacing + "db " + sprite_helper.sprites[thing["picture_number"]] + ", $" + y + " + 4, $" + x + " + 4, $" + movement1 + ", $" + movement2 + ", $" + text_id + ending
output += "\n"
if object["number_of_warps"] > 0:
output += spacing + "; warp-to\n"
for warp_to_id in object["warp_tos"]:
warp_to = object["warp_tos"][warp_to_id]
map_width = map["x"]
warp_to_y = hex(int(warp_to["y"]))[2:]
warp_to_x = hex(int(warp_to["x"]))[2:]
try:
previous_location = map_constants[object["warps"][warp_to_id]["warp_to_map_id"]]
comment = " ; " + previous_location
except Exception, exc:
comment = ""
output += spacing + "EVENT_DISP $" + map_width[2:] + ", $" + warp_to_y + ", $" + warp_to_x + comment + "\n"
#output += spacing + "dw $" + hex(int(warp_to["event_displacement"][1]))[2:] + hex(int(warp_to["event_displacement"][0]))[2:] + "\n"
#output += spacing + "db $" + hex(int(warp_to["y"]))[2:] + ", $" + hex(int(warp_to["x"]))[2:] + "\n"
#output += "\n"
output += "\n"
while output[-1] == "\n":
output = output[:-1]
output += "\n"
return output
def find_all_tx_fars():
global all_texts
tx_fars = [] #[map_id, text_id, text_pointer, tx_far_pointer, TX_FAR]
for map_id in all_texts:
map2 = all_texts[map_id]
for text_id in map2.keys():
text = map2[text_id]
for command_id in text.keys():
command = text[command_id]
if "TX_FAR" in command.keys():
TX_FAR = command["TX_FAR"]
if TX_FAR[0]["type"] == 0x0:
tx_fars.append([map_id, text_id, analyze_texts.get_text_pointer(int(extract_maps.map_headers[map_id]["texts_pointer"], 16), text_id), command["pointer"], TX_FAR])
return tx_fars
def tx_far_label_maker(map_name, text_id):
label = "_" + map_name_cleaner(map_name, None)[:-2] + "Text" + str(text_id)
return label
def tx_far_pretty_printer(tx_far):
"pretty output for a tx_far"
map_id = tx_far[0]
map2 = extract_maps.map_headers[map_id]
text_id = tx_far[1]
text_pointer = tx_far[2]
tx_far_start_address = tx_far[3]
text_far = tx_far[4]
lines = text_far[0]["lines"]
label = tx_far_label_maker(map2["name"], text_id)
#add the ending byte on the next line
#lines[len(lines.keys())+1] = [text_far[1]["type"]]
#add the ending byte to the last line- always seems $57
lines[len(lines.keys())-1].append(text_far[1]["type"])
output = "\n"
output += label + ": ; " + hex(tx_far_start_address) + "\n"
first = True
for line_id in lines:
line = lines[line_id]
output += spacing + "db "
if first:
output += "$0, "
first = False
quotes_open = False
first_byte = True
was_byte = False
byte_count = 0
for byte in line:
if byte in txt_bytes:
if not quotes_open and not first_byte: #start text
output += ", \""
quotes_open = True
first_byte = False
if not quotes_open and first_byte: #start text
output += "\""
quotes_open = True
output += txt_bytes[byte]
elif byte in constant_abbreviation_bytes:
if quotes_open:
output += "\""
quotes_open = False
if not first_byte:
output += ", "
output += constant_abbreviation_bytes[byte]
else:
if quotes_open:
output += "\""
quotes_open = False
#if you want the ending byte on the last line
#if not (byte == 0x57 or byte == 0x50 or byte == 0x58):
if not first_byte:
output += ", "
output += "$" + hex(byte)[2:]
was_byte = True
#add a comma unless it's the end of the line
#if byte_count+1 != len(line):
# output += ", "
first_byte = False
byte_count += 1
#close final quotes
if quotes_open:
output += "\""
quotes_open = False
output += "\n"
#output += "\n"
return output
def print_all_headers():
maps = []
for map in extract_maps.map_headers:
maps.append(extract_maps.map_headers[map])
maps = sorted(maps, key=lambda map: int(map["address"], base))
for map in maps:
output = map_header_pretty_printer(map)
if output != "": print output
if __name__ == "__main__":
#read binary data from file
extract_maps.load_rom()
#where are the map structs?
extract_maps.load_map_pointers()
#load map headers into memory
extract_maps.read_all_map_headers()
#load texts
all_texts = analyze_texts.analyze_texts()
#print them out
#print_all_headers()
#print out only the object data for pallet town (map 0)
#print object_data_pretty_printer(0)
#prepare to pretty print tx_fars
#first you must load all_texts
#tx_fars = find_all_tx_fars()
#for entry in tx_fars:
# print tx_far_pretty_printer(entry)

View file

@ -1,21 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-16
from optparse import OptionParser
from analyze_texts import text_pretty_printer_at
def main():
usage = "usage: %prog address label"
parser = OptionParser(usage)
(options, args) = parser.parse_args()
if len(args) == 1:
print "usage: python pretty_text.py address label"
args.append("UnnamedText_" + (args[0].replace("0x", "")))
elif len(args) != 2:
parser.error("we need both an address and a label")
address = int(args[0], 16)
label = args[1]
text_pretty_printer_at(address, label)
if __name__ == "__main__":
main()

View file

@ -1,126 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-24
from optparse import OptionParser
from gbz80disasm import load_labels, find_label
from extract_maps import calculate_pointer
import sys
spacing = "\t"
rom = None
def pretty_print_trainer_header(address, label=None):
"""make pretty text for a trainer header"""
global rom
output = ""
bank_id = 0
if address > 0x4000:
bank_id = address / 0x4000
#convert address to an integer if necessary
if type(address) == str:
if "$" in address: address = address.replace("$", "0x")
address = int(address, 16)
#label this section of asm
if label == None:
output += "TrainerHeader_" + hex(address)[2:] + ": ; 0x" + hex(address)[2:] + "\n"
else:
output += label + ": ; 0x" + hex(address)[2:] + "\n"
#flag's bit
output += spacing + "db $" + hex(ord(rom[address]))[2:] + " ; flag's bit\n"
#trainer's view range
view_range = ord(rom[address+1]) >> 4
output += spacing + "db ($" + hex(view_range)[2:] + " << 4) ; trainer's view range\n"
#flag's byte
pointer_byte1 = ord(rom[address+2])
pointer_byte2 = ord(rom[address+3])
partial_pointer = (pointer_byte1 + (pointer_byte2 << 8))
partial_pointer = "$%.2x" % partial_pointer
output += spacing + "dw " + partial_pointer + " ; flag's byte\n"
#TextBeforeBattle
pointer_byte1 = ord(rom[address+4])
pointer_byte2 = ord(rom[address+5])
partial_pointer = (pointer_byte1 + (pointer_byte2 << 8))
label = find_label(partial_pointer, bank_id)
if label == None:
print "label not found for (TextBeforeBattle) " + hex(calculate_pointer(partial_pointer, bank_id))
print ""
label = "$" + hex(partial_pointer)[2:]
#sys.exit(0)
output += spacing + "dw " + label + " ; " + hex(partial_pointer) + " TextBeforeBattle\n"
#TextAfterBattle
pointer_byte1 = ord(rom[address+6])
pointer_byte2 = ord(rom[address+7])
partial_pointer = (pointer_byte1 + (pointer_byte2 << 8))
label = find_label(partial_pointer, bank_id)
if label == None:
print "label not found for (TextAfterBattle) " + hex(calculate_pointer(partial_pointer, bank_id))
print ""
label = "$" + hex(partial_pointer)[2:]
#sys.exit(0)
output += spacing + "dw " + label + " ; " + hex(partial_pointer) + " TextAfterBattle\n"
#TextEndBattle
pointer_byte1 = ord(rom[address+8])
pointer_byte2 = ord(rom[address+9])
partial_pointer = (pointer_byte1 + (pointer_byte2 << 8))
label = find_label(partial_pointer, bank_id)
if label == None:
print "label not found for (TextEndBattle) " + hex(calculate_pointer(partial_pointer, bank_id))
print ""
label = "$" + hex(partial_pointer)[2:]
#sys.exit(0)
output += spacing + "dw " + label + " ; " + hex(partial_pointer) + " TextEndBattle\n"
#TextEndBattle
pointer_byte1 = ord(rom[address+10])
pointer_byte2 = ord(rom[address+11])
partial_pointer = (pointer_byte1 + (pointer_byte2 << 8))
label = find_label(partial_pointer, bank_id)
if label == None:
print "label not found for (TextEndBattle) " + hex(calculate_pointer(partial_pointer, bank_id))
print ""
label = "$" + hex(partial_pointer)[2:]
#sys.exit(0)
output += spacing + "dw " + label + " ; " + hex(partial_pointer) + " TextEndBattle\n"
output += "; " + hex(address+12) + "\n"
return output
def all_trainer_headers_at(address):
i = 0
while ord(rom[address + (i*12)]) != 0xff:
print pretty_print_trainer_header(address + (i*12))
i += 1
def main():
load_labels()
usage = "usage: %prog address"
parser = OptionParser(usage)
(options, args) = parser.parse_args()
if len(args) == 1:
print "usage: python pretty_trainer_headers.py address label\n"
args.append("TrainerHeader_" + (args[0].replace("0x", "")))
elif len(args) != 2:
parser.error("we need both an address and a label")
address = int(args[0], 16)
label = args[1]
global rom
rom = open("../baserom.gbc", "r").read()
#print pretty_print_trainer_header(address, label)
print all_trainer_headers_at(address)
if __name__ == "__main__":
main()

View file

@ -1,128 +0,0 @@
/*
* Copyright © 2011 IIMarckus <iimarckus@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This program compresses or decompresses the Town Map tilemap
* from Pokémon Red, Blue, and Yellow.
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void
usage()
{
fprintf(stderr, "Usage: redrle [-d] infile outfile\n");
exit(1);
}
int
main(int argc, char *argv[])
{
FILE *infile, *outfile;
bool d = false; /* compress or decompress flag */
if (argc < 3 || argc > 4)
usage();
if (strcmp(argv[1], "-d") == 0) {
if (argc != 4)
usage();
d = true;
}
infile = fopen(argv[argc - 2], "rb");
if (infile == NULL) {
fprintf(stderr, "Error opening file '%s': ", argv[argc - 2]);
perror(NULL);
exit(1);
}
outfile = fopen(argv[argc - 1], "wb");
if (outfile == NULL) {
fprintf(stderr, "Error opening file '%s': ", argv[argc - 1]);
perror(NULL);
exit(1);
}
if (d) { /* decompress */
for (;;) {
int i, count;
int byte = fgetc(infile);
if (byte == 0)
break;
count = byte & 0xF;
byte >>= 4;
if (feof(infile)) {
fprintf(stderr, "Decompress error: reached "
"end of file without finding terminating "
"null byte.\n");
exit(1);
}
for (i = 0; i < count;
++i)
fputc(byte, outfile);
}
} else { /* compress */
int byte, count = 0, lastbyte = 0;
for (;;) {
byte = fgetc(infile);
if (feof(infile)) {
while (count > 0xF) {
count -= 0xF;
fputc(lastbyte << 4 | 0xF, outfile);
}
if (count != 0) {
fputc(lastbyte << 4 | count, outfile);
}
break;
}
if (byte > 0xF) {
fprintf(stderr, "Compress error: read a byte "
"greater than 0xF.\n");
exit(1);
}
if (byte == lastbyte)
++count;
else {
while (count > 0xF) {
count -= 0xF;
fputc(lastbyte << 4 | 0xF, outfile);
}
if (count != 0) {
fputc(lastbyte << 4 | count, outfile);
count = 0;
}
lastbyte = byte;
count = 1;
}
}
fputc(0, outfile); /* Terminating 0x00 */
}
fclose(infile);
fclose(outfile);
return 0;
}

View file

@ -1,245 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-15
#replace dimensions with constants
import sys #for non-newline-terminated output :/
from add_map_labels_to_map_headers import find_with_start_of_line
from pretty_map_headers import map_name_cleaner, spacing, offset_to_pointer, map_constants
from connection_helper import print_connections
from ctypes import c_int8
# X/Y_Movement_Of_Connection
#~~~~~~~~~~~~~~~~~~~~~~~~~
#
# A X movement is how many map blocks there are to the left of one of your north/south connections.
# A Y movement is how many map blocks there are above your west/east connection.
#===============================================================================
# #4-#5 : Current Map Position
#===============================================================================
#
# This points to the part of the current map (further up in RAM)
# that the connection strips upperleft block is placed on the current map.
#
# ____________________
# Connection |
# Direction | Formula
# ___________|_______
#
# North: C6EB + X_Movement_of_Connection Strip
#
# South: C6EB + (Height of Map + 3) * (Width of Map + 6) +
# X_Movement_of_Connection Strip
#
# West: C6E8 + (Width of Map + 6) * (Y_Movement_of_"Connection Strip" + 3)
#
# East: C6E5 + (Width of Map + 6) * (Y_Movement_of_"Connection Strip" + 4)
asm = None
asm_lines = None
def load_asm():
global asm, asm_lines
asm = open("../main.asm", "r").read()
asm_lines = asm.split("\n")
def get_xy_movement_of_connection_strip(map_id, connection_id):
map1 = extract_maps.map_headers[map_id]
connections = map1["connections"]
connection = connections[connection_id]
direction = connection["direction"]
current_map_location = int(connection["current_map_tile_pointer"], 16)
map2 = extract_maps.map_headers[connection["map_id"]]
map2_height = int(map2["y"], 16)
map2_width = int(map2["x"], 16)
y_mov = None
#if direction == "WEST":
# y_mov = ((current_map_location - 0xC6E8) / (map2_width + 6)) - 3
#elif direction == "EAST":
# y_mov = ((current_map_location - 0xC6E5) / (map2_width + 6)) - 4
if direction in ["WEST", "EAST"]:
y_mov = c_int8(connection["y"]).value / -2
x_mov = None
#if direction == "NORTH":
# x_mov = current_map_location - 0xC6EB
#elif direction == "SOUTH":
# x_mov = current_map_location - 0xC6EB - ((map2_height + 3) * (map2_width + 6))
if direction in ["NORTH", "SOUTH"]:
x_mov = c_int8(connection["x"]).value / -2
return {"y_mov": y_mov, "x_mov": x_mov}
def find_line_starting_with(value):
global asm_lines
id = 0
for line in asm_lines:
if len(line) < len(value): continue
if line[:len(value)] == value:
return asm_lines.index(line)
id += 1
return False #not found
def current_map_position_formula(map_id, connection_id):
map1_id = map_id
map1 = extract_maps.map_headers[map_id]
connections = map1["connections"]
connection = connections[connection_id]
map1_height = int(map1["y"], 16)
map1_width = int(map1["x"], 16)
map1_name = map1["name"]
map1_name = map_name_cleaner(map1_name, None)[:-2]
direction = connection["direction"]
current_map_location = int(connection["current_map_tile_pointer"], 16)
map2_id = connection["map_id"]
map2 = extract_maps.map_headers[map2_id]
map2_name = map2["name"]
map2_name = map_name_cleaner(map2_name, None)[:-2]
map2_height = int(map2["y"], 16)
map2_width = int(map2["x"], 16)
y_mov = None
if direction == "WEST":
y_mov = ((current_map_location - 0xC6E8) / (map1_width + 6)) - 3
elif direction == "EAST":
y_mov = ((current_map_location - 0xC6E5) / (map1_width + 6)) - 4
x_mov = None
if direction == "NORTH":
x_mov = current_map_location - 0xC6EB
elif direction == "SOUTH":
x_mov = current_map_location - 0xC6EB - ((map1_height + 3) * (map1_width + 6))
formula = ""
if direction == "NORTH":
formula = "$C6EB + " + str(x_mov)
elif direction == "SOUTH":
formula = "$C6EB + (" + map1_name + "Height + 3) * (" + map1_name + "Width + 6) + " + str(x_mov)
elif direction == "WEST":
formula = "$C6E8 + (" + map1_name + "Width + 6) * (" + str(y_mov) + " + 3)"
elif direction == "EAST":
formula = "$C6E5 + (" + map1_name + "Width + 6) * (" + str(y_mov) + " + 4)"
return formula
def replace_values():
global asm_lines #0-15 ok
for map_id in [3]: #extract_maps.map_headers.keys():
if map_id in extract_maps.bad_maps: continue #skip
if map_id == 12: continue #skip Route 1
map1 = extract_maps.map_headers[map_id]
label_name = map_name_cleaner(map1["name"], None)
clean_name = label_name[:-2]
line_number = find_line_starting_with(label_name)
if line_number == False: continue #skip, not found
#replace dimensions if necessary
if "dimensions" in asm_lines[line_number + 2] and "$" in asm_lines[line_number + 2] and not "\t" in asm_lines[line_number+2]:
asm_lines[line_number + 2] = spacing + "db " + clean_name + "Height, " + clean_name + "Width ; dimensions (y, x)"
#skip the rest of this if there are no connections
if len(map1["connections"]) == 0: continue
if not "; connections data" in asm_lines[line_number + 6]: continue
connection_offset = line_number + 8
for connection_id in map1["connections"]:
connection = map1["connections"][connection_id]
direction = connection["direction"]
map2_id = connection["map_id"]
map2 = extract_maps.map_headers[map2_id]
map2_name = map_name_cleaner(map2["name"], None)[:-2]
map2_height = int(map2["y"], 16)
map2_width = int(map2["x"], 16)
movements = get_xy_movement_of_connection_strip(map_id, connection_id)
y_mov = movements["y_mov"]
x_mov = movements["x_mov"]
#replace the first two pointers
if " dw " in asm_lines[connection_offset + 1]:
formula = print_connections(map_id, in_connection_id=connection_id)
formula2 = current_map_position_formula(map_id, connection_id)
temp_line = asm_lines[connection_offset + 1]
temp_line = spacing + "dw " + formula + " ; connection strip location\n" #connection strip location
temp_line += spacing + "dw " + formula2 + " ; current map position" #current map position
asm_lines[connection_offset + 1] = temp_line
#bigness, width
if "bigness, width" in asm_lines[connection_offset + 2]:
temp_line = spacing + "db "
if int(connection["bigness"],16) == map2_width:
temp_line += map2_name + "Width"
elif int(connection["bigness"],16) == map2_height:
temp_line += map2_name + "Height"
else: #dunno wtf to do
temp_line += "$" + hex(int(connection["bigness"],16))[2:]
#if direction in ["NORTH", "SOUTH"]:
# temp_line += map2_name + "Width"
#elif direction in ["WEST", "EAST"]:
# temp_line += map2_name + "Height"
temp_line += ", " + map2_name + "Width"
temp_line += " ; bigness, width"
asm_lines[connection_offset + 2] = temp_line
#alignments (y, x)
if "alignments (y, x)" in asm_lines[connection_offset + 3]:
temp_line = spacing + "db "
if direction == "NORTH":
temp_line += "(" + map2_name + "Height * 2) - 1"
elif direction == "SOUTH":
temp_line += "0"
elif direction in ["WEST", "EAST"]:
#TODO: this might be y_mov/4 ??
temp_line += "(" + str(y_mov) + " * -2)"
temp_line += ", "
#Relative X-Position of player after entering connected map.
if direction in ["NORTH", "SOUTH"]:
temp_line += "(" + str(x_mov) + " * -2)"
elif direction == "WEST":
temp_line += "(" + map2_name + "Width * 2) - 1"
elif direction == "EAST":
temp_line += "0"
temp_line += " ; alignments (y, x)"
asm_lines[connection_offset + 3] = temp_line
#window
if "; window" in asm_lines[connection_offset + 4]:
temp_line = spacing + "dw "
if direction == "NORTH":
temp_line += "$C6E9 + " + map2_name + "Height * (" + map2_name + "Width + 6)"
elif direction in ["SOUTH", "EAST"]:
temp_line += "$C6EF + " + map2_name + "Width"
elif direction == "WEST":
temp_line += "$C6EE + 2 * " + map2_name + "Width"
temp_line += " ; window"
asm_lines[connection_offset + 4] = temp_line
#jump to the next connection
connection_offset += 6
if __name__ == "__main__":
import extract_maps
extract_maps.load_rom()
extract_maps.load_map_pointers()
extract_maps.read_all_map_headers()
load_asm()
replace_values()
sys.stdout.write("\n".join(asm_lines))

View file

@ -1,28 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-13
import os
changeset_numbers = range(1145, 1149)
def take_snapshot_image(changeset_number):
"turn main.asm into an image at a certain version"
print "reverting main.asm to r" + str(changeset_number)
#revert the file (it used to be common.asm)
os.system("rm ../main.asm; rm ../common.asm; rm ../pokered.asm")
os.system("hg revert ../main.asm -r" + str(changeset_number))
os.system("hg revert ../common.asm -r" + str(changeset_number))
os.system("hg revert ../pokered.asm -r" + str(changeset_number))
print "generating the image.."
#draw the image
os.system("python romviz.py")
#move the file
os.system("mv test.png versions/" + str(changeset_number) + ".png")
for changeset_number in changeset_numbers:
take_snapshot_image(changeset_number)

View file

@ -1,40 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-10
#show me an image
import Image
from math import floor
import extract_maps
import analyze_incbins
print "loading rom.."
extract_maps.load_rom()
#extract_maps.load_map_pointers()
#extract_maps.read_all_map_headers()
print "analyzing incbins.."
analyze_incbins.load_asm()
analyze_incbins.isolate_incbins()
analyze_incbins.process_incbins()
width = 1024
height = 1024
im = Image.new("P", (width, height), 0)
im.putpalette([
0, 0, 0,
126, 30, 156,
])
print "drawing incbins..."
for incbin_key in analyze_incbins.processed_incbins:
incbin = analyze_incbins.processed_incbins[incbin_key]
start = incbin["start"]
end = incbin["end"]
for pos in range(start, end+1):
widthx = int(pos % width)
heighty = int(floor(pos / height))
im.putpixel((widthx, heighty), 1)
im.save("test.png")

View file

@ -1,402 +0,0 @@
import extract_maps
spacing = "\t"
#provided by sawakita
#these were originally used for making the initial_icon_constants
#but the label names in constants.asm have since been edited
initial_icon_constants = {
0x01: ["Hiro", ""],
0x02: ["Rival", ""],
0x03: ["Oak", ""],
0x04: ["blonde boy", ""],
0x05: ["machoke/slowbro OW", "machoke slowbro"],
0x06: ["blonde(horse-tail-hair) girl", "blonde ponytail girl"],
0x07: ["black-hair boy 1", "black hair boy 1"],
0x08: ["little kid (F)", "little girl"],
0x09: ["bird", ""],
0x0A: ["fat bald man", "fat bald guy"],
0x0B: ["monk", ""],
0x0C: ["black-hair boy 2/Brock", "black hair boy 2"],
0x0D: ["girl", ""],
0x0E: ["hiker/angry man", "hiker"],
0x0F: ["foulard woman", "foulard woman"],
0x10: ["rich(black-hat) man", "gentleman"],
0x11: ["sister", ""],
0x12: ["motorbiker", "biker"],
0x13: ["sailor", ""],
0x14: ["cook", ""],
0x15: ["sun-glasses guy (bike seller)", "sunglasses guy"],
0x16: ["mr. fuji", ""],
0x17: ["giovanni", ""],
0x18: ["rocket guy", "rocket grunt"],
0x19: ["medium", ""],
0x1A: ["waiter", ""],
0x1B: ["erika", ""],
0x1C: ["mother (geisha)", "mom geisha"],
0x1D: ["brunette girl", ""],
0x1E: ["lance", ""],
0x1F: ["oak's aide/scientist", "oak scientist aide"],
0x20: ["oak's aide", "oak aide"],
0x21: ["punk", ""],
0x22: ["swimmer", ""],
0x23: ["white player", ""],
0x24: ["gym helper", ""],
0x25: ["old (wo)man", "old person"],
0x26: ["mart guy", ""],
0x27: ["fisher", ""],
0x28: ["old woman/medium?", "old medium woman"],
0x29: ["nurse", ""],
0x2A: ["cable-club woman", "cable club woman"],
0x2B: ["Mr. Masterball?", "mr masterball"],
0x2C: ["person that gives Lapras", "lapras giver"],
0x2D: ["semi-bald fat guy", "balding fat guy"],
0x2E: ["black hat white beard man ", ""],
0x2F: ["fat man", ""],
0x30: ["dojo guy", ""],
0x31: ["guard (cop?)", "guard cop"],
0x32: ["cop (guard)", "cop guard"],
0x33: ["mom", ""],
0x34: ["semi-bald man", "balding guy"],
0x35: ["young girl", ""],
0x36: ["gameboy kid", ""],
0x37: ["gameboy kid copy", ""],
0x38: ["clefairy-like", "clefairylike"],
0x39: ["Agatha", ""],
0x3A: ["Bruno", ""],
0x3B: ["Lorelei", ""],
0x3C: ["seel", ""],
0x3D: ["ball", ""],
0x3E: ["omanyte", ""],
0x3F: ["boulder", ""],
0x40: ["paper sheet", ""],
0x41: ["book/map/dex", ""],
0x42: ["clipboard", ""],
0x43: ["snorlax", ""],
0x44: ["old amber copy", ""],
0x45: ["old amber", ""],
0x46: ["lying old man unused 1", ""],
0x47: ["lying old man unused 2", ""],
0x48: ["lying old man", ""],
}
#somewhat more recent sprite labels
sprite_constants = {
0x01: "SPRITE_RED",
0x02: "SPRITE_BLUE",
0x03: "SPRITE_OAK",
0x04: "SPRITE_BUG_CATCHER",
0x05: "SPRITE_SLOWBRO",
0x06: "SPRITE_LASS",
0x07: "SPRITE_BLACK_HAIR_BOY_1",
0x08: "SPRITE_LITTLE_GIRL",
0x09: "SPRITE_BIRD",
0x0a: "SPRITE_FAT_BALD_GUY",
0x0b: "SPRITE_GAMBLER",
0x0c: "SPRITE_BLACK_HAIR_BOY_2",
0x0d: "SPRITE_GIRL",
0x0e: "SPRITE_HIKER",
0x0f: "SPRITE_FOULARD_WOMAN",
0x10: "SPRITE_GENTLEMAN",
0x11: "SPRITE_DAISY",
0x12: "SPRITE_BIKER",
0x13: "SPRITE_SAILOR",
0x14: "SPRITE_COOK",
0x15: "SPRITE_BIKE_SHOP_GUY",
0x16: "SPRITE_MR_FUJI",
0x17: "SPRITE_GIOVANNI",
0x18: "SPRITE_ROCKET",
0x19: "SPRITE_MEDIUM",
0x1a: "SPRITE_WAITER",
0x1b: "SPRITE_ERIKA",
0x1c: "SPRITE_MOM_GEISHA",
0x1d: "SPRITE_BRUNETTE_GIRL",
0x1e: "SPRITE_LANCE",
0x1f: "SPRITE_OAK_SCIENTIST_AIDE",
0x20: "SPRITE_OAK_AIDE",
0x21: "SPRITE_ROCKER",
0x22: "SPRITE_SWIMMER",
0x23: "SPRITE_WHITE_PLAYER",
0x24: "SPRITE_GYM_HELPER",
0x25: "SPRITE_OLD_PERSON",
0x26: "SPRITE_MART_GUY",
0x27: "SPRITE_FISHER",
0x28: "SPRITE_OLD_MEDIUM_WOMAN",
0x29: "SPRITE_NURSE",
0x2a: "SPRITE_CABLE_CLUB_WOMAN",
0x2b: "SPRITE_MR_MASTERBALL",
0x2c: "SPRITE_LAPRAS_GIVER",
0x2d: "SPRITE_WARDEN",
0x2e: "SPRITE_SS_CAPTAIN",
0x2f: "SPRITE_FISHER2",
0x30: "SPRITE_BLACKBELT",
0x31: "SPRITE_GUARD",
0x32: "SPRITE_____NOT____USED____",
0x33: "SPRITE_MOM",
0x34: "SPRITE_BALDING_GUY",
0x35: "SPRITE_YOUNG_BOY",
0x36: "SPRITE_GAMEBOY_KID",
0x37: "SPRITE_GAMEBOY_KID_COPY",
0x38: "SPRITE_CLEFAIRY",
0x39: "SPRITE_AGATHA",
0x3a: "SPRITE_BRUNO",
0x3b: "SPRITE_LORELEI",
0x3c: "SPRITE_SEEL",
0x3d: "SPRITE_BALL",
0x3e: "SPRITE_OMANYTE",
0x3f: "SPRITE_BOULDER",
0x40: "SPRITE_PAPER_SHEET",
0x41: "SPRITE_BOOK_MAP_DEX",
0x42: "SPRITE_CLIPBOARD",
0x43: "SPRITE_SNORLAX",
0x44: "SPRITE_OLD_AMBER_COPY",
0x45: "SPRITE_OLD_AMBER",
0x46: "SPRITE_LYING_OLD_MAN_UNUSED_1",
0x47: "SPRITE_LYING_OLD_MAN_UNUSED_2",
0x48: "SPRITE_LYING_OLD_MAN",
}
dont_use = [0x32, 0x44, 0x46, 0x47, 0x37]
#sprites after 0x23 have only one image
#SPRITE_BIKE_SHOP_GUY only has 1
icons = {}
unique_icons = set()
todo_sprites = {}
sprites = {}
def load_icons():
for map_id in map_headers:
if map_id in [0x0b, 0x45, 0x4b, 0x4e, 0x69, 0x6a, 0x6b, 0x6d, 0x6e, 0x6f, 0x70, 0x72, 0x73, 0x74, 0x75, 0xad, 0xcc, 0xcd, 0xce, 0xe7, 0xed, 0xee, 0xf1, 0xf2, 0xf3, 0xf4]: continue #skip
map = map_headers[map_id]
for thing_id in map["object_data"]["things"]:
thing = map["object_data"]["things"][thing_id]
pic = thing["picture_number"]
unique_icons.add(pic)
if not icons.has_key(pic): icons[pic] = []
alerter = None
if int(thing["y"])-4 > int(map["y"], 16)*2: alerter = True
if int(thing["x"])-4 > int(map["x"], 16)*2: alerter = True
icons[pic].append((map["name"] + " (id=" + str(map["id"]) + ")", thing["y"], thing["x"], alerter))
def print_appearances():
"""print appearances of each icon
see: http://diyhpl.us/~bryan/irc/pokered/sprite_appearances.txt
"""
output = ""
for icon_id in icons:
icon = icons[icon_id]
possible_name = ""
if icon_id in initial_icon_constants.keys():
possible_name = " (sawakita suggests: " + initial_icon_constants[icon_id][0] + ")"
output += "sprite " + hex(icon_id) + possible_name + ":\n"
for appearance in icon:
if appearance[3] != None: outside_alert = " !! OUTSIDE BOUNDS"
else: outside_alert = ""
output += spacing + ".. in " + appearance[0] + " at (" + str(appearance[1]) + ", " + str(appearance[2]) + ")" + outside_alert + "\n"
output += "\n"
print output
def insert_todo_sprites():
load_icons()
counter = 1
for icon in unique_icons:
if icon not in initial_icon_constants:
todo_sprites[icon] = counter
initial_icon_constants[icon] = None
counter += 1
def sprite_name_cleaner(badname):
output = "SPRITE_" + badname
output = output.replace(" ", "_")
output = output.replace("/", "_")
output = output.replace(".", "")
output = output.upper()
while output[-1] == "_":
output = output[:-1]
return output
def sprite_namer():
"makes up better constant names for each sprite"
insert_todo_sprites()
for sprite_id in initial_icon_constants:
suggestions = initial_icon_constants[sprite_id]
if suggestions == None:
sprites[sprite_id] = "SPRITE_TODO_" + str(todo_sprites[sprite_id])
continue #next please
original = suggestions[0]
if suggestions[1] != "": original = suggestions[1]
result = sprite_name_cleaner(original)
sprites[sprite_id] = result
def sprite_printer():
"""prints out a list of sprite constants to put into constants.asm
it's deprecated- use the names from the current file instead."""
for key in sprites:
line_length = len(sprites[key]) + len(" EQU $") + 2
if line_length < 40:
extra = (40 - line_length) * " "
else: extra = ""
value = hex(key)[2:]
if len(value) == 1: value = "0" + value
print sprites[key] + extra + " EQU $" + value
def parse_sprite_sheet_pointer_table():
"""parses the bytes making up the pointer table
first two bytes are the pointer
third byte is the number of bytes (1 * 4 tiles * 16 bytes each, or 3 * 4 tiles * 16 bytes per tile)
1 = 1 pose
3 = 3 poses, possibly 6 immediately after
$C0 or $40
fourth byte is the rom bank
so a quick estimation is that, if it has 3, and there's no other pointer that points to the one after the 3rd & next 3, then assume those next 3 are the 4th, 5th and 6th
"""
rom = extract_maps.rom
ptable_address = 0x17b27 #5:7b27
ptable_pointers = []
ptable_sheet_data = {}
#load up pointers please
for sprite_id in sprite_constants.keys():
pointer_offset = 0x17b27 + ((sprite_id -1) * 4)
pointer_byte1 = ord(rom[pointer_offset])
pointer_byte2 = ord(rom[pointer_offset+1])
partial_pointer = (pointer_byte1 + (pointer_byte2 << 8))
bank = ord(rom[pointer_offset+3])
pointer = extract_maps.calculate_pointer(partial_pointer, bank)
ptable_pointers.append(pointer)
#72 sprite pointers, we're not using id=$32
for sprite_id in sprite_constants.keys():
sprite_name = sprite_constants[sprite_id]
#some basic information about this sprite first
data_entry = {"sprite_id": sprite_id, "sprite_name": sprite_name}
#calculate where it is in the 0x17b27 pointer table
pointer_offset = 0x17b27 + ((sprite_id -1) * 4)
data_entry["sprite_ptr_table_entry_address"] = pointer_offset
#actual sprite pointer
pointer_byte1 = ord(rom[pointer_offset])
pointer_byte2 = ord(rom[pointer_offset+1])
partial_pointer = (pointer_byte1 + (pointer_byte2 << 8))
bank = ord(rom[pointer_offset+3])
pointer = extract_maps.calculate_pointer(partial_pointer, bank)
data_entry["pointer"] = pointer
data_entry["bank"] = bank
byte_count = ord(rom[pointer_offset+2])
data_entry["byte_count"] = byte_count
has_more_text = ""
data_entry["poses"] = 1
if byte_count == 0xc0: #has at least 3 poses
setter1, setter2, setter3 = False, False, False
data_entry["poses"] = 3
#let's check if there's possibly more
if not ((byte_count + pointer) in ptable_pointers): #yep, probably (#4)
data_entry["poses"] += 1
data_entry["byte_count"] += 64
setter1 = True
if setter1 and not ((byte_count + pointer + 64) in ptable_pointers): #has another (#5)
data_entry["poses"] += 1
data_entry["byte_count"] += 64
setter2 = True
if setter2 and not ((byte_count + pointer + 64 + 64) in ptable_pointers): #has a #6
data_entry["poses"] += 1
data_entry["byte_count"] += 64
setter3 = True
print ("$%.2x " % (sprite_id)) + sprite_name + " has $%.2x bytes" % (byte_count) + " pointing to 0x%.x" % (pointer) + " bank is $%.2x" % (bank) + " with pose_count=" + str(data_entry["poses"])
ptable_sheet_data[sprite_id] = data_entry
return ptable_sheet_data
def pretty_print_sheet_incbins(ptable_sheet_data):
"""make things look less awful"""
output = ""
used_addresses = []
for sheet_id in ptable_sheet_data:
sheet_data = ptable_sheet_data[sheet_id]
name = sheet_data["sprite_name"].split("SPRITE_")[1].lower().title()
clean_name = name.replace("_", "")
address = sheet_data["pointer"]
byte_count = sheet_data["byte_count"]
#if not (0x10000 <= address <= 0x12e7f): continue #skip
#if not (0x14180 <= address <= 0x17840): continue #skip
if address in used_addresses: continue #skip
used_addresses.append(address)
output += clean_name + "Sprite: ; 0x%.x" % (address) + "\n"
#output += spacing + "INCBIN \"baserom.gbc\",$%.x,$%.x - $%.x" % (address, address + byte_count, address) + "\n"
output += spacing + "INCBIN \"gfx/sprites/" + name.lower() + ".2bpp\" ; was $%.x" % (address) + "\n"
filename = "../gfx/sprites/" + name.lower() + ".2bpp"
#fh = open(filename, "w")
#fh.write(extract_maps.rom[address : address + byte_count])
#fh.close()
return output
def pretty_print_sheet_data(ptable_sheet_data):
"""make the pointer table not suck so much"""
output = "SpriteSheetPointerTable: ; 0x17b27\n"
used_addresses = []
for sheet_id in ptable_sheet_data:
sheet_data = ptable_sheet_data[sheet_id]
address = sheet_data["pointer"]
checker = False
for x in used_addresses:
if not checker and x[0] == address:
checker = True
clean_name = x[1]
if not checker:
name = sheet_data["sprite_name"].split("SPRITE_")[1].lower().title()
clean_name = name.replace("_", "")
clean_name += "Sprite"
byte_count = sheet_data["byte_count"]
if byte_count > 0x40:
byte_count = 0xc0
output += "\n\t; " + sprite_constants[sheet_data["sprite_id"]] + "\n"
output += spacing + "dw " + clean_name + "\n"
output += spacing + "db $%.2x ; byte count\n" % (byte_count)
output += spacing + "db BANK(" + clean_name + ")\n"
used_addresses.append((address, clean_name))
output += "; 0x17c47"
return output
if __name__ == "__main__":
extract_maps.load_rom()
#extract_maps.load_map_pointers()
#extract_maps.read_all_map_headers()
#sprite_namer()
#load_icons()
#print_appearances()
#sprite_printer()
ptable_sheet_data = parse_sprite_sheet_pointer_table()
print pretty_print_sheet_incbins(ptable_sheet_data)
print pretty_print_sheet_data(ptable_sheet_data)

View file

@ -1,55 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-03
#utilities for working with text pointers
import extract_maps #rom, assert_rom, load_rom, calculate_pointer, load_map_pointers, read_all_map_headers, map_headers
from pretty_map_headers import map_name_cleaner
#import analyze_incbins #asm, offset_to_pointer, find_incbin_to_replace_for, split_incbin_line_into_three, generate_diff_insert, load_asm, isolate_incbins, process_incbins
spacing = " "
def test_first_text_pointer_bytes(range=20): #30 for viridian city, 34 for cerulean city, 36 for celadon, 48 for fuchsia city, 50 for safron
"""
does the first text pointer byte always point to (end address of text pointer list) + 1?
range determines how far is acceptable.
r=15 means 30 text pointers
"""
for map_id in extract_maps.map_headers:
map = extract_maps.map_headers[map_id]
bank = int(map["bank"],16)
text_list_pointer = int(map["texts_pointer"], 16)
bad_names = ["FREEZE", "COPY: Cinnibar Mart", "COPY OF: Underground Tunnel Entrance (Route 6)", "COPY OF: Trashed House", "COPY OF: Underground Path Entrance (Route 7)"]
if map["name"] in bad_names: continue
#extract the bytes making up the first text pointer
pointer_byte1 = ord(extract_maps.rom[text_list_pointer])
pointer_byte2 = ord(extract_maps.rom[text_list_pointer+1])
#swap the bytes
temp = pointer_byte1
pointer_byte1 = pointer_byte2
pointer_byte2 = temp
del temp
#combine these into a single pointer
partial_pointer = (pointer_byte2 + (pointer_byte1 << 8))
#get the full pointer
first_text_pointer = extract_maps.calculate_pointer(partial_pointer, bank)
#if (first_text_pointer <= (text_list_pointer+range)):
print "map " + map["name"] + " (" + str(map["id"]) + ")"
print spacing + "text_pointer (list) = " + hex(text_list_pointer)
print spacing + "first_text_pointer (first text) = " + hex(first_text_pointer)
print spacing + "difference = " + str(first_text_pointer - text_list_pointer)
#return False
return True
if __name__ == "__main__":
extract_maps.load_rom()
extract_maps.load_map_pointers()
extract_maps.read_all_map_headers()
print test_first_text_pointer_bytes()

BIN
gfx/AB.2bpp Executable file

Binary file not shown.

BIN
gfx/attack_anim_1.2bpp Executable file

Binary file not shown.

BIN
gfx/attack_anim_2.2bpp Executable file

Binary file not shown.

BIN
gfx/badge_numbers.2bpp Executable file

Binary file not shown.

BIN
gfx/badges.2bpp Executable file

Binary file not shown.

BIN
gfx/battle_hud1.1bpp Executable file

Binary file not shown.

BIN
gfx/battle_hud2.1bpp Executable file

Binary file not shown.

BIN
gfx/battle_hud3.1bpp Executable file

Binary file not shown.

1
gfx/battle_transition.2bpp Executable file
View file

@ -0,0 +1 @@
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>

BIN
gfx/blank_leader_names.2bpp Executable file

Binary file not shown.

BIN
gfx/blocksets/04.bst Executable file

Binary file not shown.

BIN
gfx/blocksets/06.bst Executable file

Binary file not shown.

BIN
gfx/blocksets/07.bst Executable file

Binary file not shown.

BIN
gfx/blocksets/0a.bst Executable file

Binary file not shown.

BIN
gfx/blocksets/0c.bst Executable file

Binary file not shown.

BIN
gfx/blue/introfight.2bpp Normal file → Executable file

Binary file not shown.

BIN
gfx/circle_tile.2bpp Executable file

Binary file not shown.

BIN
gfx/copyright.2bpp Executable file

Binary file not shown.

BIN
gfx/diagonal_lines.2bpp Executable file

Binary file not shown.

BIN
gfx/emotion_bubbles.2bpp Executable file

Binary file not shown.

BIN
gfx/falling_star.2bpp Executable file

Binary file not shown.

BIN
gfx/gamefreak.2bpp Executable file

Binary file not shown.

BIN
gfx/gamefreak_intro.2bpp Executable file

Binary file not shown.

BIN
gfx/intro_fight.2bpp Executable file

Binary file not shown.

BIN
gfx/ledge_hopping_shadow.1bpp Executable file

Binary file not shown.

BIN
gfx/mon_ow_sprites.2bpp Executable file

Binary file not shown.

BIN
gfx/player_title.2bpp Executable file

Binary file not shown.

BIN
gfx/pokeball.2bpp Executable file

Binary file not shown.

BIN
gfx/pokecenter_ball.2bpp Executable file

Binary file not shown.

BIN
gfx/pokedex.2bpp Executable file

Binary file not shown.

BIN
gfx/red/introfight.2bpp Normal file → Executable file

Binary file not shown.

BIN
gfx/red_fishing.2bpp Executable file

Binary file not shown.

BIN
gfx/spinner_arrow.2bpp Executable file

Binary file not shown.

BIN
gfx/sprites/cycling.2bpp Executable file

Binary file not shown.

BIN
gfx/tilesets/00.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
gfx/tilesets/01.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 903 B

BIN
gfx/tilesets/02.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
gfx/tilesets/03.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
gfx/tilesets/04.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 903 B

BIN
gfx/tilesets/05.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
gfx/tilesets/06.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
gfx/tilesets/07.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
gfx/tilesets/08.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
gfx/tilesets/09.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
gfx/tilesets/0a.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
gfx/tilesets/0b.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 B

BIN
gfx/tilesets/0c.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
gfx/tilesets/0d.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 861 B

BIN
gfx/tilesets/0e.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
gfx/tilesets/0f.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 967 B

BIN
gfx/tilesets/10.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
gfx/tilesets/11.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,018 B

BIN
gfx/tilesets/12.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

BIN
gfx/tilesets/13.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,012 B

BIN
gfx/tilesets/14.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
gfx/tilesets/15.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

BIN
gfx/tilesets/16.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
gfx/tilesets/17.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,013 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
gfx/town_map.2bpp Executable file

Binary file not shown.

BIN
gfx/town_map.rle Executable file

Binary file not shown.

BIN
gfx/town_map_cursor.1bpp Executable file

Binary file not shown.

BIN
gfx/trade.2bpp Executable file

Binary file not shown.

BIN
gfx/trade2.2bpp Executable file

Binary file not shown.

BIN
gfx/trainer_info.2bpp Executable file

Binary file not shown.

BIN
gfx/up_arrow.1bpp Executable file

Binary file not shown.

62336
main.asm

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1 @@
%      
D@?D BC FG 6A 6

View file

@ -1 +0,0 @@
D@?D BC FG 6A 6

Some files were not shown because too many files have changed in this diff Show more