diff options
-rw-r--r-- | Makefile | 21 | ||||
-rw-r--r-- | battle/effect_commands.asm | 220 | ||||
m--------- | extras | 0 | ||||
-rw-r--r-- | main.asm | 128 | ||||
-rw-r--r-- | preprocessor.py | 631 | ||||
-rw-r--r-- | prequeue.py | 2 |
6 files changed, 244 insertions, 758 deletions
@@ -1,3 +1,4 @@ +PYTHON := python .SUFFIXES: .asm .tx .o .gbc .png .2bpp .lz TEXTFILES := $(shell find ./ -type f -name '*.asm' | grep -v pokecrystal.asm | grep -v constants.asm | grep -v gbhw.asm | grep -v hram.asm | grep -v constants | grep -v wram.asm) @@ -14,35 +15,35 @@ clean: @echo 'rm -f $(TEXTFILES:.asm=.tx)' @rm -f $(TEXTFILES:.asm=.tx) pokecrystal.o: $(TEXTFILES:.asm=.tx) wram.asm constants.asm $(shell find constants/ -type f -name '*.asm') hram.asm gbhw.asm $(LZ_GFX) $(TWOBPP_GFX) - python prequeue.py $(TEXTQUEUE) + $(PYTHON) prequeue.py $(TEXTQUEUE) rgbasm -o pokecrystal.o pokecrystal.asm .asm.tx: $(eval TEXTQUEUE := $(TEXTQUEUE) $<) @rm -f $@ baserom.gbc: - python -c "import os; assert 'baserom.gbc' in os.listdir('.'), 'Wait! Need baserom.gbc first. Check README and INSTALL for details.';" + $(PYTHON) -c "import os; assert 'baserom.gbc' in os.listdir('.'), 'Wait! Need baserom.gbc first. Check README and INSTALL for details.';" pokecrystal.gbc: pokecrystal.o rgblink -n pokecrystal.sym -m pokecrystal.map -o $@ $< rgbfix -Cjv -i BYTE -k 01 -l 0x33 -m 0x10 -p 0 -r 3 -t PM_CRYSTAL $@ pngs: - python extras/pokemontools/gfx.py mass-decompress - python extras/pokemontools/gfx.py dump-pngs + $(PYTHON) extras/pokemontools/gfx.py mass-decompress + $(PYTHON) extras/pokemontools/gfx.py dump-pngs lzs: $(LZ_GFX) $(TWOBPP_GFX) @: gfx/pics/%/front.lz: gfx/pics/%/tiles.2bpp gfx/pics/%/front.png - python extras/pokemontools/gfx.py png-to-lz --front $^ + $(PYTHON) extras/pokemontools/gfx.py png-to-lz --front $^ gfx/pics/%/tiles.2bpp: gfx/pics/%/tiles.png - python extras/pokemontools/gfx.py png-to-2bpp $< + $(PYTHON) extras/pokemontools/gfx.py png-to-2bpp $< gfx/pics/%/back.lz: gfx/pics/%/back.png - python extras/pokemontools/gfx.py png-to-lz --vert $< + $(PYTHON) extras/pokemontools/gfx.py png-to-lz --vert $< gfx/trainers/%.lz: gfx/trainers/%.png - python extras/pokemontools/gfx.py png-to-lz --vert $< + $(PYTHON) extras/pokemontools/gfx.py png-to-lz --vert $< .png.lz: - python extras/pokemontools/gfx.py png-to-lz $< + $(PYTHON) extras/pokemontools/gfx.py png-to-lz $< .png.2bpp: - python extras/pokemontools/gfx.py png-to-lz $< + $(PYTHON) extras/pokemontools/gfx.py png-to-lz $< diff --git a/battle/effect_commands.asm b/battle/effect_commands.asm index 94b54c0bd..5ac336ed9 100644 --- a/battle/effect_commands.asm +++ b/battle/effect_commands.asm @@ -9328,72 +9328,102 @@ BattleCommand50: ; 37492 ld a, [hBattleTurn] and a - jr nz, .asm_374ce ; 37495 $37 - call .asm_37501 + jr nz, .enemy + +; The player needs to be able to steal an item. + + call .playeritem ld a, [hl] and a ret nz - call .asm_3750c + +; The enemy needs to have an item to steal. + + call .enemyitem ld a, [hl] and a ret z + +; Can't steal mail. + ld [$d265], a ld d, a - ld a, $2e - ld hl, $5e76 - rst FarCall + callba ItemIsMail ret c + ld a, [EffectFailed] and a ret nz + ld a, [InLinkBattle] and a - jr z, .asm_374be ; 374b7 $5 + jr z, .stealenemyitem + ld a, [IsInBattle] dec a ret z -.asm_374be - call .asm_3750c + +.stealenemyitem + call .enemyitem xor a ld [hl], a ld [de], a - call .asm_37501 + + call .playeritem ld a, [$d265] ld [hl], a ld [de], a - jr .asm_374f8 ; 374cc $2a -.asm_374ce - call .asm_3750c + jr .stole + + +.enemy + +; The enemy can't already have an item. + + call .enemyitem ld a, [hl] and a ret nz - call .asm_37501 + +; The player must have an item to steal. + + call .playeritem ld a, [hl] and a ret z + +; Can't steal mail! + ld [$d265], a ld d, a - ld a, $2e - ld hl, $5e76 - rst FarCall + callba ItemIsMail ret c + ld a, [EffectFailed] and a ret nz - call .asm_37501 + +; If the enemy steals your item, +; it's gone for good if you don't get it back. + + call .playeritem xor a ld [hl], a ld [de], a - call .asm_3750c + + call .enemyitem ld a, [$d265] ld [hl], a ld [de], a -.asm_374f8 + + +.stole call GetItemName ld hl, StoleText jp StdBattleTextBox -.asm_37501 + +.playeritem ld a, 1 call BattlePartyAttr ld d, h @@ -9401,9 +9431,9 @@ BattleCommand50: ; 37492 ld hl, BattleMonItem ret -.asm_3750c +.enemyitem ld a, 1 - call $396d ; GetOTStat_Battle + call OTPartyAttr ld d, h ld e, l ld hl, EnemyMonItem @@ -9413,17 +9443,27 @@ BattleCommand50: ; 37492 BattleCommand51: ; 37517 ; arenatrap + +; Doesn't work on an absent opponent. + call CheckHiddenOpponent - jr nz, .asm_37530 ; 3751a $14 + jr nz, .failed + +; Don't trap if the opponent is already trapped. + ld a, BATTLE_VARS_SUBSTATUS5 call GetBattleVarPair - bit 7, [hl] - jr nz, .asm_37530 ; 37523 $b - set 7, [hl] + bit SUBSTATUS_CANT_RUN, [hl] + jr nz, .failed + +; Otherwise trap the opponent. + + set SUBSTATUS_CANT_RUN, [hl] call Function0x37e01 ld hl, CantEscapeNowText jp StdBattleTextBox -.asm_37530 + +.failed call Function0x37e77 jp PrintButItFailed ; 37536 @@ -9432,23 +9472,38 @@ BattleCommand51: ; 37517 BattleCommand52: ; 37536 ; nightmare +; Can't hit an absent opponent. + call CheckHiddenOpponent - jr nz, .asm_3755d ; 37539 $22 + jr nz, .failed + +; Can't hit a substitute. + call CheckSubstituteOpp - jr nz, .asm_3755d ; 3753e $1d + jr nz, .failed + +; Only works on a sleeping opponent. + ld a, BATTLE_VARS_STATUS_OPP call GetBattleVarPair - and $7 - jr z, .asm_3755d ; 37547 $14 + and SLP + jr z, .failed + +; Bail if the opponent is already having a nightmare. + ld a, BATTLE_VARS_SUBSTATUS1_OPP call GetBattleVarPair - bit 0, [hl] - jr nz, .asm_3755d ; 37550 $b - set 0, [hl] + bit SUBSTATUS_NIGHTMARE, [hl] + jr nz, .failed + +; Otherwise give the opponent a nightmare. + + set SUBSTATUS_NIGHTMARE, [hl] call Function0x37e01 ld hl, StartedNightmareText jp StdBattleTextBox -.asm_3755d + +.failed call Function0x37e77 jp PrintButItFailed ; 37563 @@ -9457,22 +9512,30 @@ BattleCommand52: ; 37536 BattleCommand53: ; 37563 ; defrost +; Thaw the user. + ld a, BATTLE_VARS_STATUS call GetBattleVarPair - bit 5, [hl] + bit FRZ, [hl] ret z - res 5, [hl] + res FRZ, [hl] + +; Don't update the enemy's party struct in a wild battle. + ld a, [hBattleTurn] and a - jr z, .asm_37578 ; 37570 $6 + jr z, .party + ld a, [IsInBattle] dec a - jr z, .asm_3757f ; 37576 $7 -.asm_37578 - ld a, $20 + jr z, .done + +.party + ld a, PartyMon1Status - PartyMon1 call UserPartyAttr - res 5, [hl] -.asm_3757f + res FRZ, [hl] + +.done call RefreshBattleHuds ld hl, WasDefrostedText jp StdBattleTextBox @@ -9486,25 +9549,40 @@ BattleCommand54: ; 37588 ld bc, PlayerStatLevels ld a, [hBattleTurn] and a - jr z, .asm_37599 ; 37591 $6 + jr z, .go ld de, EnemyMonType1 ld bc, EnemyStatLevels -.asm_37599 + +.go + +; Curse is different for Ghost-types. + ld a, [de] - cp $8 - jr z, .asm_375d7 ; 3759c $39 + cp GHOST + jr z, .ghost inc de ld a, [de] - cp $8 - jr z, .asm_375d7 ; 375a2 $33 + cp GHOST + jr z, .ghost + + +; If no stats can be increased, don't. + +; Attack ld a, [bc] - cp $d - jr c, .asm_375af ; 375a7 $6 + cp 13 ; max + jr c, .raise + +; Defense inc bc ld a, [bc] - cp $d - jr nc, .asm_3760a ; 375ad $5b -.asm_375af + cp 13 ; max + jr nc, .cantraise + +.raise + +; Raise Attack and Defense, and lower Speed. + ld a, $1 ld [$c689], a call Function0x37e01 @@ -9519,29 +9597,43 @@ BattleCommand54: ; 37588 call ResetMiss call BattleCommand71 jp BattleCommand8c -.asm_375d7 + + +.ghost + +; Cut HP in half and put a curse on the opponent. + call CheckHiddenOpponent - jr nz, .asm_37604 ; 375da $28 + jr nz, .failed + call CheckSubstituteOpp - jr nz, .asm_37604 ; 375df $23 + jr nz, .failed + ld a, BATTLE_VARS_SUBSTATUS1_OPP call GetBattleVarPair bit 1, [hl] - jr nz, .asm_37604 ; 375e8 $1a + jr nz, .failed + set 1, [hl] call Function0x37e01 - ld hl, $4c9f + ld hl, GetHalfMaxHP call CallBankF - ld hl, $4c3f + ld hl, Function3cc3f call CallBankF call UpdateUserInParty ld hl, PutACurseText jp StdBattleTextBox -.asm_37604 + +.failed call Function0x37e77 jp PrintButItFailed -.asm_3760a - ld b, $8 + + +.cantraise + +; Can't raise either stat. + + ld b, $8 ; ABILITY call GetStatName call Function0x37e77 ld hl, WontRiseAnymoreText diff --git a/extras b/extras -Subproject 016f0206b5029fc83a6200be29b0f980c76dfd9 +Subproject 276111f04dcc3e937f1a16f4b7066934409f8ad @@ -26733,11 +26733,11 @@ OpenMartDialog: ; 15a45 ; 15a57 .dialogs - dw MartDialog - dw HerbShop - dw BargainShop - dw Pharmacist - dw VendingMachine + dw MartDialog + dw HerbShop + dw BargainShop + dw Pharmacist + dw VendingMachine ; 15a61 MartDialog: ; 15a61 @@ -26865,12 +26865,12 @@ Function15b47: ; 15b47 ret .table_15b56 - dw Function15b62 - dw Function15b6e - dw Function15b8d - dw Function15b9a - dw Function15ba3 - dw Function15baf + dw Function15b62 + dw Function15b6e + dw Function15b8d + dw Function15b9a + dw Function15ba3 + dw Function15baf ; 15b62 Function15b62: ; 15b62 @@ -27107,11 +27107,11 @@ Function15ca3: ; 15ca3 ; 15cb0 .data_15cb0 ; 15cb0 - dwb $5cbf, 0 - dwb $5ccb, 0 - dwb $5cd7, 1 - dwb $5ce3, 0 - dwb $5cbf, 2 + dwb $5cbf, 0 + dwb $5ccb, 0 + dwb $5cd7, 1 + dwb $5ce3, 0 + dwb $5cbf, 2 ; 15cbf INCBIN "baserom.gbc", $15cbf, $15cef - $15cbf @@ -27791,7 +27791,7 @@ Function16798: ; 16798 ld a, [CurPartyMon] call AddNTimes ld d, [hl] - callba Functionb9e76 + callba ItemIsMail jr c, .asm_167ed ld hl, PartyMon1Nickname ld a, [CurPartyMon] @@ -30906,7 +30906,7 @@ Function24dd4: ; 24dd4 ld a, $1 call GetPartyParamLocation ld d, [hl] - callba Functionb9e76 + callba ItemIsMail pop hl ld a, $14 jr c, .asm_24e2c @@ -34522,7 +34522,7 @@ Function29bfb: ; 29bfb push hl push bc ld d, [hl] - callba Functionb9e76 + callba ItemIsMail pop bc pop hl jr c, .asm_29c5e @@ -53392,14 +53392,14 @@ StatsScreenMain: ; 0x4dcd2 INCBIN "baserom.gbc", $4dcf7, $4dd2a - $4dcf7 StatsScreenPointerTable: ; 4dd2a - dw $5d72 ; regular pokémon - dw EggStatsInit ; egg - dw $5de6 - dw $5dac - dw $5dc6 - dw $5de6 - dw $5dd6 - dw $5d6c + dw $5d72 ; regular pokémon + dw EggStatsInit ; egg + dw $5de6 + dw $5dac + dw $5dc6 + dw $5de6 + dw $5dd6 + dw $5d6c ; 4dd3a @@ -53416,10 +53416,10 @@ EggStatsInit: ; 4dda1 INCBIN "baserom.gbc", $4ddac, $4e21e - $4ddac IDNoString: ; 4e21e - db $73, "№.@" + db $73, "№.@" OTString: ; 4e222 - db "OT/@" + db "OT/@" ; 4e226 INCBIN "baserom.gbc", $4e226, $4e33a - $4e226 @@ -53481,22 +53481,22 @@ EggStatsScreen: ; 4e33a ; 0x4e3c0 EggString: ; 4e3c0 - db "EGG@" + db "EGG@" FiveQMarkString: ; 4e3c4 - db "?????@" + db "?????@" EggSoonString: ; 0x4e3ca - db "It's making sounds", $4e, "inside. It's going", $4e, "to hatch soon!@" + db "It's making sounds", $4e, "inside. It's going", $4e, "to hatch soon!@" EggCloseString: ; 0x4e3fd - db "It moves around", $4e, "inside sometimes.", $4e, "It must be close", $4e, "to hatching.@" + db "It moves around", $4e, "inside sometimes.", $4e, "It must be close", $4e, "to hatching.@" EggMoreTimeString: ; 0x4e43d - db "Wonder what's", $4e, "inside? It needs", $4e, "more time, though.@" + db "Wonder what's", $4e, "inside? It needs", $4e, "more time, though.@" EggALotMoreTimeString: ; 0x4e46e - db "This EGG needs a", $4e, "lot more time to", $4e, "hatch.@" + db "This EGG needs a", $4e, "lot more time to", $4e, "hatch.@" ; 0x4e497 @@ -55357,35 +55357,35 @@ PrintPartyMenuText: ; 5049a ; 0x504d2 PartyMenuStrings: ; 0x504d2 - dw ChooseAMonString - dw UseOnWhichPKMNString - dw WhichPKMNString - dw TeachWhichPKMNString - dw MoveToWhereString - dw UseOnWhichPKMNString - dw ChooseAMonString ; Probably used to be ChooseAFemalePKMNString - dw ChooseAMonString ; Probably used to be ChooseAMalePKMNString - dw ToWhichPKMNString + dw ChooseAMonString + dw UseOnWhichPKMNString + dw WhichPKMNString + dw TeachWhichPKMNString + dw MoveToWhereString + dw UseOnWhichPKMNString + dw ChooseAMonString ; Probably used to be ChooseAFemalePKMNString + dw ChooseAMonString ; Probably used to be ChooseAMalePKMNString + dw ToWhichPKMNString ChooseAMonString: ; 0x504e4 - db "Choose a #MON.@" + db "Choose a #MON.@" UseOnWhichPKMNString: ; 0x504f3 - db "Use on which ", $e1, $e2, "?@" + db "Use on which ", $e1, $e2, "?@" WhichPKMNString: ; 0x50504 - db "Which ", $e1, $e2, "?@" + db "Which ", $e1, $e2, "?@" TeachWhichPKMNString: ; 0x5050e - db "Teach which ", $e1, $e2, "?@" + db "Teach which ", $e1, $e2, "?@" MoveToWhereString: ; 0x5051e - db "Move to where?@" + db "Move to where?@" ChooseAFemalePKMNString: ; 0x5052d ; UNUSED - db "Choose a ♀", $e1, $e2, ".@" + db "Choose a ♀", $e1, $e2, ".@" ChooseAMalePKMNString: ; 0x5053b ; UNUSED - db "Choose a ♂", $e1, $e2, ".@" + db "Choose a ♂", $e1, $e2, ".@" ToWhichPKMNString: ; 0x50549 - db "To which ", $e1, $e2, "?@" + db "To which ", $e1, $e2, "?@" YouHaveNoPKMNString: ; 0x50556 - db "You have no ", $e1, $e2, "!@" + db "You have no ", $e1, $e2, "!@" Function50566: ; 50566 @@ -69675,14 +69675,26 @@ Functionb92b8: ; b92b8 INCBIN "baserom.gbc", $b92f7, $b9e76 - $b92f7 -Functionb9e76: ; b9e76 +ItemIsMail: ; b9e76 ld a, d - ld hl, $5e80 - ld de, $0001 + ld hl, .items + ld de, 1 jp IsInArray ; b9e80 -INCBIN "baserom.gbc", $b9e80, $b9e8b - $b9e80 +.items + db FLOWER_MAIL + db SURF_MAIL + db LITEBLUEMAIL + db PORTRAITMAIL + db LOVELY_MAIL + db EON_MAIL + db MORPH_MAIL + db BLUESKY_MAIL + db MUSIC_MAIL + db MIRAGE_MAIL + db $ff +; b9e8b SECTION "bank2F",ROMX,BANK[$2F] @@ -86674,7 +86686,7 @@ RegionCheck: ; 0x1caea1 SECTION "bank73",ROMX,BANK[$73] - ; Pokedex entries III +; Pokedex entries III ; 129-192 PokedexEntries3: INCLUDE "stats/pokedex/entries_3.asm" diff --git a/preprocessor.py b/preprocessor.py index 59850729a..188db81a6 100644 --- a/preprocessor.py +++ b/preprocessor.py @@ -3,6 +3,8 @@ import sys +import extras.pokemontools.preprocessor as preprocessor + from extras.pokemontools.crystal import ( command_classes, Warp, @@ -39,636 +41,15 @@ def load_pokecrystal_macros(): return ourmacros -chars = { -"ガ": 0x05, -"ギ": 0x06, -"グ": 0x07, -"ゲ": 0x08, -"ゴ": 0x09, -"ザ": 0x0A, -"ジ": 0x0B, -"ズ": 0x0C, -"ゼ": 0x0D, -"ゾ": 0x0E, -"ダ": 0x0F, -"ヂ": 0x10, -"ヅ": 0x11, -"デ": 0x12, -"ド": 0x13, -"バ": 0x19, -"ビ": 0x1A, -"ブ": 0x1B, -"ボ": 0x1C, -"が": 0x26, -"ぎ": 0x27, -"ぐ": 0x28, -"げ": 0x29, -"ご": 0x2A, -"ざ": 0x2B, -"じ": 0x2C, -"ず": 0x2D, -"ぜ": 0x2E, -"ぞ": 0x2F, -"だ": 0x30, -"ぢ": 0x31, -"づ": 0x32, -"で": 0x33, -"ど": 0x34, -"ば": 0x3A, -"び": 0x3B, -"ぶ": 0x3C, -"べ": 0x3D, -"ぼ": 0x3E, -"パ": 0x40, -"ピ": 0x41, -"プ": 0x42, -"ポ": 0x43, -"ぱ": 0x44, -"ぴ": 0x45, -"ぷ": 0x46, -"ぺ": 0x47, -"ぽ": 0x48, -"ア": 0x80, -"イ": 0x81, -"ウ": 0x82, -"エ": 0x83, -"ォ": 0x84, -"カ": 0x85, -"キ": 0x86, -"ク": 0x87, -"ケ": 0x88, -"コ": 0x89, -"サ": 0x8A, -"シ": 0x8B, -"ス": 0x8C, -"セ": 0x8D, -"ソ": 0x8E, -"タ": 0x8F, -"チ": 0x90, -"ツ": 0x91, -"テ": 0x92, -"ト": 0x93, -"ナ": 0x94, -"ニ": 0x95, -"ヌ": 0x96, -"ネ": 0x97, -"ノ": 0x98, -"ハ": 0x99, -"ヒ": 0x9A, -"フ": 0x9B, -"ホ": 0x9C, -"マ": 0x9D, -"ミ": 0x9E, -"ム": 0x9F, -"メ": 0xA0, -"モ": 0xA1, -"ヤ": 0xA2, -"ユ": 0xA3, -"ヨ": 0xA4, -"ラ": 0xA5, -"ル": 0xA6, -"レ": 0xA7, -"ロ": 0xA8, -"ワ": 0xA9, -"ヲ": 0xAA, -"ン": 0xAB, -"ッ": 0xAC, -"ャ": 0xAD, -"ュ": 0xAE, -"ョ": 0xAF, -"ィ": 0xB0, -"あ": 0xB1, -"い": 0xB2, -"う": 0xB3, -"え": 0xB4, -"お": 0xB5, -"か": 0xB6, -"き": 0xB7, -"く": 0xB8, -"け": 0xB9, -"こ": 0xBA, -"さ": 0xBB, -"し": 0xBC, -"す": 0xBD, -"せ": 0xBE, -"そ": 0xBF, -"た": 0xC0, -"ち": 0xC1, -"つ": 0xC2, -"て": 0xC3, -"と": 0xC4, -"な": 0xC5, -"に": 0xC6, -"ぬ": 0xC7, -"ね": 0xC8, -"の": 0xC9, -"は": 0xCA, -"ひ": 0xCB, -"ふ": 0xCC, -"へ": 0xCD, -"ほ": 0xCE, -"ま": 0xCF, -"み": 0xD0, -"む": 0xD1, -"め": 0xD2, -"も": 0xD3, -"や": 0xD4, -"ゆ": 0xD5, -"よ": 0xD6, -"ら": 0xD7, -"り": 0xD8, -"る": 0xD9, -"れ": 0xDA, -"ろ": 0xDB, -"わ": 0xDC, -"を": 0xDD, -"ん": 0xDE, -"っ": 0xDF, -"ゃ": 0xE0, -"ゅ": 0xE1, -"ょ": 0xE2, -"ー": 0xE3, -"ァ": 0xE9, - -"@": 0x50, -"#": 0x54, -"…": 0x75, - -"┌": 0x79, -"─": 0x7A, -"┐": 0x7B, -"│": 0x7C, -"└": 0x7D, -"┘": 0x7E, - -"№": 0x74, - -" ": 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, -"Ä": 0xC0, -"Ö": 0xC1, -"Ü": 0xC2, -"ä": 0xC3, -"ö": 0xC4, -"ü": 0xC5, -"'d": 0xD0, -"'l": 0xD1, -"'m": 0xD2, -"'r": 0xD3, -"'s": 0xD4, -"'t": 0xD5, -"'v": 0xD6, -"'": 0xE0, -"-": 0xE3, -"?": 0xE6, -"!": 0xE7, -".": 0xE8, -"&": 0xE9, -"é": 0xEA, -"→": 0xEB, -"▷": 0xEC, -"▶": 0xED, -"▼": 0xEE, -"♂": 0xEF, -"¥": 0xF0, -"×": 0xF1, -"/": 0xF3, -",": 0xF4, -"♀": 0xF5, -"0": 0xF6, -"1": 0xF7, -"2": 0xF8, -"3": 0xF9, -"4": 0xFA, -"5": 0xFB, -"6": 0xFC, -"7": 0xFD, -"8": 0xFE, -"9": 0xFF -} - -class PreprocessorException(Exception): - """ - There was a problem in the preprocessor. - """ - -class MacroException(PreprocessorException): - """ - There was a problem with a macro. - """ - -def separate_comment(l): - """ - Separates asm and comments on a single line. - """ - in_quotes = False - for i in xrange(len(l)): - if not in_quotes: - if l[i] == ";": - break - if l[i] == "\"": - in_quotes = not in_quotes - return l[:i], l[i:] or None - -def quote_translator(asm): - """ - Writes asm with quoted text translated into bytes. - """ - - # split by quotes - asms = asm.split('"') - - # skip asm that actually does use ASCII in quotes - if "SECTION" in asms[0]\ - or "INCBIN" in asms[0]\ - or "INCLUDE" in asms[0]: - return asm - - print_macro = False - if asms[0].strip() == 'print': - asms[0] = asms[0].replace('print','db 0,') - print_macro = True - - output = '' - even = False - for token in asms: - if even: - characters = [] - # token is a string to convert to byte values - while len(token): - # read a single UTF-8 codepoint - char = token[0] - if ord(char) < 0xc0: - token = token[1:] - # certain apostrophe-letter pairs are considered a single character - if char == "'" and token: - if token[0] in 'dlmrstv': - char += token[0] - token = token[1:] - elif ord(char) < 0xe0: - char = char + token[1:2] - token = token[2:] - elif ord(char) < 0xf0: - char = char + token[1:3] - token = token[3:] - elif ord(char) < 0xf8: - char = char + token[1:4] - token = token[4:] - elif ord(char) < 0xfc: - char = char + token[1:5] - token = token[5:] - else: - char = char + token[1:6] - token = token[6:] - characters += [char] - - if print_macro: - line = 0 - while len(characters): - last_char = 1 - if len(characters) > 18 and characters[-1] != '@': - for i, char in enumerate(characters): - last_char = i + 1 - if ' ' not in characters[i+1:18]: break - output += ", ".join("${0:02X}".format(chars[char]) for char in characters[:last_char-1]) - if characters[last_char-1] != " ": - output += ", ${0:02X}".format(characters[last_char-1]) - if not line & 1: - line_ending = 0x4f - else: - line_ending = 0x51 - output += ", ${0:02X}".format(line_ending) - line += 1 - else: - output += ", ".join(["${0:02X}".format(chars[char]) for char in characters[:last_char]]) - characters = characters[last_char:] - if len(characters): output += ", " - # end text - line_ending = 0x57 - output += ", ${0:02X}".format(line_ending) - - output += ", ".join(["${0:02X}".format(chars[char]) for char in characters]) - - else: - output += token - - even = not even - - return output - -def extract_token(asm): - return asm.split(" ")[0].strip() - -def make_macro_table(macros): - return dict(((macro.macro_name, macro) for macro in macros)) - -def macro_test(asm, macro_table): - """ - Returns a matching macro, or None/False. - """ - # macros are determined by the first symbol on the line - token = extract_token(asm) - - # skip db and dw since rgbasm handles those and they aren't macros - if token is not None and token not in ["db", "dw"] and token in macro_table: - return (macro_table[token], token) - else: - return (None, None) - -def is_based_on(something, base): - """ - Checks whether or not 'something' is a class that is a subclass of a class - by name. This is a terrible hack but it removes a direct dependency on - existing macros. - - Used by macro_translator. - """ - options = [str(klass.__name__) for klass in something.__bases__] - options += [something.__name__] - return (base in options) - -def check_macro_sanity(params, macro, original_line): - """ - Checks whether or not the correct number of arguments are being passed to a - certain macro. There are a number of possibilities based on the types of - parameters that define the macro. - - @param params: a list of parameters given to the macro - @param macro: macro klass - @param original_line: the line being preprocessed - """ - allowed_length = 0 - - for (index, param_type) in macro.param_types.items(): - param_klass = param_type["class"] - - if param_klass.byte_type == "db": - allowed_length += 1 # just one value - elif param_klass.byte_type == "dw": - if param_klass.size == 2: - allowed_length += 1 # just label - elif param_klass.size == 3: - allowed_length += 2 # bank and label - else: - raise MacroException( - "dunno what to do with a macro param with a size > 3 (size={size})" - .format(size=param_klass.size) - ) - else: - raise MacroException( - "dunno what to do with this non db/dw macro param: {klass} in line {line}" - .format(klass=param_klass, line=original_line) - ) - - # sometimes the allowed length can vary - if hasattr(macro, "allowed_lengths"): - allowed_lengths = macro.allowed_lengths + [allowed_length] - else: - allowed_lengths = [allowed_length] - - # used twice, so precompute once - params_len = len(params) - - if params_len not in allowed_lengths: - raise PreprocessorException( - "mismatched number of parameters ({count}, instead of any of {allowed}) on this line: {line}" - .format( - count=params_len, - allowed=allowed_lengths, - line=original_line, - ) - ) - - return True - -def macro_translator(macro, token, line, show_original_lines=False, do_macro_sanity_check=False): +def preprocess(macro_table, lines=None): """ - Converts a line with a macro into a rgbasm-compatible line. - - @param show_original_lines: show lines before preprocessing in stdout - @param do_macro_sanity_check: helpful for debugging macros + Entry point for the preprocessor. """ - if macro.macro_name != token: - raise MacroException("macro/token mismatch") - - original_line = line - - # remove trailing newline - if line[-1] == "\n": - line = line[:-1] - else: - original_line += "\n" - - # remove first tab - has_tab = False - if line[0] == "\t": - has_tab = True - line = line[1:] - - # remove duplicate whitespace (also trailing) - line = " ".join(line.split()) - - params = [] - - # check if the line has params - if " " in line: - # split the line into separate parameters - params = line.replace(token, "").split(",") - - # check if there are no params (redundant) - if len(params) == 1 and params[0] == "": - raise MacroException("macro has no params?") - - # write out a comment showing the original line - if show_original_lines: - sys.stdout.write("; original_line: " + original_line) - - # rgbasm can handle "db" so no preprocessing is required, plus this wont be - # reached because of earlier checks in macro_test. - if macro.macro_name in ["db", "dw"]: - sys.stdout.write(original_line) - return - - # certain macros don't need an initial byte written - # do: all scripting macros - # don't: signpost, warp_def, person_event, xy_trigger - if not macro.override_byte_check: - sys.stdout.write("db ${0:02X}\n".format(macro.id)) - - # Does the number of parameters on this line match any allowed number of - # parameters that the macro expects? - if do_macro_sanity_check: - check_macro_sanity(params, macro, original_line) - - # used for storetext - correction = 0 - - output = "" - - index = 0 - while index < len(params): - param_type = macro.param_types[index - correction] - description = param_type["name"] - param_klass = param_type["class"] - byte_type = param_klass.byte_type # db or dw - size = param_klass.size - param = params[index].strip() - - # param_klass.to_asm() won't work here because it doesn't - # include db/dw. - - # some parameters are really multiple types of bytes - if (byte_type == "dw" and size != 2) or \ - (byte_type == "db" and size != 1): - - output += ("; " + description + "\n") - - if size == 3 and is_based_on(param_klass, "PointerLabelBeforeBank"): - # write the bank first - output += ("db " + param + "\n") - # write the pointer second - output += ("dw " + params[index+1].strip() + "\n") - index += 2 - correction += 1 - elif size == 3 and is_based_on(param_klass, "PointerLabelAfterBank"): - # write the pointer first - output += ("dw " + param + "\n") - # write the bank second - output += ("db " + params[index+1].strip() + "\n") - index += 2 - correction += 1 - elif size == 3 and "from_asm" in dir(param_klass): - output += ("db " + param_klass.from_asm(param) + "\n") - index += 1 - else: - raise MacroException( - "dunno what to do with this macro param ({klass}) in line: {line}" - .format( - klass=param_klass, - line=original_line, - ) - ) - - # or just print out the byte - else: - output += (byte_type + " " + param + " ; " + description + "\n") - - index += 1 - - sys.stdout.write(output) - -def read_line(l, macro_table): - """Preprocesses a given line of asm.""" - - if l in ["\n", ""] or l[0] == ";": - sys.stdout.write(l) - return # jump out early - - # strip comments from asm - asm, comment = separate_comment(l) - - # export all labels - if ':' in asm[:asm.find('"')]: - sys.stdout.write('GLOBAL ' + asm.split(':')[0] + '\n') - - # expect preprocessed .asm files - if "INCLUDE" in asm: - asm = asm.replace('.asm','.tx') - sys.stdout.write(asm) - - # ascii string macro preserves the bytes as ascii (skip the translator) - elif len(asm) > 6 and ("ascii " == asm[:6] or "\tascii " == asm[:7]): - asm = asm.replace("ascii", "db", 1) - sys.stdout.write(asm) - - # convert text to bytes when a quote appears (not in a comment) - elif "\"" in asm: - sys.stdout.write(quote_translator(asm)) - - # check against other preprocessor features - else: - macro, token = macro_test(asm, macro_table) - if macro: - macro_translator(macro, token, asm) - else: - sys.stdout.write(asm) - - if comment: - sys.stdout.write(comment) - -def preprocess(macro_table, lines=None): - """Main entry point for the preprocessor.""" - - if not lines: - # read each line from stdin - lines = (sys.stdin.readlines()) - elif not isinstance(lines, list): - # split up the input into individual lines - lines = lines.split("\n") - - for l in lines: - read_line(l, macro_table) + return preprocessor.preprocess(macro_table, lines=lines) def main(): macros = load_pokecrystal_macros() - macro_table = make_macro_table(macros) + macro_table = preprocessor.make_macro_table(macros) preprocess(macro_table) # only run against stdin when not included as a module diff --git a/prequeue.py b/prequeue.py index 6efc519d1..5c1a9f161 100644 --- a/prequeue.py +++ b/prequeue.py @@ -11,7 +11,7 @@ import preprocessor def main(): macros = preprocessor.load_pokecrystal_macros() - macro_table = preprocessor.make_macro_table(macros) + macro_table = preprocessor.preprocessor.make_macro_table(macros) stdout = sys.stdout |