From fbaa5c9d4b48c000a52860a8392fc423c4e312f9 Mon Sep 17 00:00:00 2001 From: vulcandth Date: Sat, 26 Mar 2022 21:05:06 -0500 Subject: [PATCH] Build the Virtual Console patch with `make yellow_vc` (#80) --- .gitattributes | 3 + .gitignore | 1 + Makefile | 63 +++- README.md | 1 + audio.asm | 2 +- data/pikachu/pikachu_pic_objects.asm | 2 +- data/text/text_8.asm | 10 + engine/battle/animations.asm | 21 ++ engine/battle/core.asm | 23 ++ engine/gfx/palettes.asm | 1 + engine/link/cable_club.asm | 3 + engine/link/cable_club_npc.asm | 4 + engine/menus/link_menu.asm | 2 + engine/menus/pokedex.asm | 6 + engine/menus/save.asm | 43 ++- engine/movie/intro_yellow.asm | 5 + engine/pikachu/pikachu_pcm.asm | 7 + home/pikachu_cries.asm | 1 + home/serial.asm | 14 + macros.asm | 1 + macros/vc.asm | 27 ++ main.asm | 6 +- roms.sha1 | 1 + scripts/SummerBeachHouse.asm | 12 + sram.asm | 2 + tools/.gitignore | 5 +- tools/Makefile | 1 + tools/make_patch.c | 469 +++++++++++++++++++++++ vc/pokeyellow.constants.asm | 61 +++ vc/pokeyellow.patch.template | 533 +++++++++++++++++++++++++++ 30 files changed, 1288 insertions(+), 42 deletions(-) create mode 100644 macros/vc.asm create mode 100644 tools/make_patch.c create mode 100644 vc/pokeyellow.constants.asm create mode 100644 vc/pokeyellow.patch.template diff --git a/.gitattributes b/.gitattributes index 48b1085c8..e22e2bbbb 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15,3 +15,6 @@ *.wav binary *.blk binary *.pic binary + +# Declare files that will always have CRLF line endings on checkout. +*.patch.template text eol=crlf linguist-language=INI diff --git a/.gitignore b/.gitignore index 80e4aad8c..8952a16f3 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ # compiled roms *.gbc *.gb +*.patch # rgbds extras *.map diff --git a/Makefile b/Makefile index 9225dc4c7..c5ac37e3f 100644 --- a/Makefile +++ b/Makefile @@ -1,19 +1,24 @@ -roms := pokeyellow.gbc pokeyellow_debug.gbc +roms := \ + pokeyellow.gbc \ + pokeyellow_debug.gbc +patches := \ + pokeyellow.patch rom_obj := \ -audio.o \ -home.o \ -main.o \ -maps.o \ -text.o \ -wram.o \ -gfx/pics.o \ -gfx/pikachu.o \ -gfx/sprites.o \ -gfx/tilesets.o + audio.o \ + home.o \ + main.o \ + maps.o \ + text.o \ + wram.o \ + gfx/pics.o \ + gfx/pikachu.o \ + gfx/sprites.o \ + gfx/tilesets.o pokeyellow_obj := $(rom_obj) pokeyellow_debug_obj := $(rom_obj:.o=_debug.o) +pokeyellow_vc_obj := $(rom_obj:.o=_vc.o) ### Build tools @@ -42,16 +47,34 @@ RGBLINK ?= $(RGBDS)rgblink all: $(roms) yellow: pokeyellow.gbc yellow_debug: pokeyellow_debug.gbc +yellow_vc: pokeyellow.patch clean: tidy - find gfx \( -iname '*.1bpp' -o -iname '*.2bpp' -o -iname '*.pic' \) -delete - find audio/pikachu_cries \( -iname '*.pcm' \) -delete + find gfx \ + \( -iname '*.1bpp' \ + -o -iname '*.2bpp' \ + -o -iname '*.pic' \) \ + -delete + find audio/pikachu_cries \ + \( -iname '*.pcm' \) \ + -delete tidy: - $(RM) $(roms) $(pokeyellow_obj) $(pokeyellow_debug_obj) $(roms:.gbc=.map) $(roms:.gbc=.sym) rgbdscheck.o + $(RM) $(roms) \ + $(roms:.gbc=.sym) \ + $(roms:.gbc=.map) \ + $(patches) \ + $(patches:.patch=_vc.gbc) \ + $(patches:.patch=_vc.sym) \ + $(patches:.patch=_vc.map) \ + $(patches:%.patch=vc/%.constants.sym) \ + $(pokeyellow_obj) \ + $(pokeyellow_vc_obj) \ + $(pokeyellow_debug_obj) \ + rgbdscheck.o $(MAKE) clean -C tools/ -compare: $(roms) +compare: $(roms) $(patches) @$(SHA1) -c roms.sha1 tools: @@ -65,6 +88,10 @@ RGBASMFLAGS += -E endif $(pokeyellow_debug_obj): RGBASMFLAGS += -D _DEBUG +$(pokeyellow_vc_obj): RGBASMFLAGS += -D _YELLOW_VC + +%.patch: vc/%.constants.sym %_vc.gbc %.gbc vc/%.patch.template + tools/make_patch $*_vc.sym $^ $@ rgbdscheck.o: rgbdscheck.asm $(RGBASM) -o $@ $< @@ -86,6 +113,11 @@ $(info $(shell $(MAKE) -C tools)) # Dependencies for objects $(foreach obj, $(pokeyellow_obj), $(eval $(call DEP,$(obj),$(obj:.o=.asm)))) $(foreach obj, $(pokeyellow_debug_obj), $(eval $(call DEP,$(obj),$(obj:_debug.o=.asm)))) +$(foreach obj, $(pokeyellow_vc_obj), $(eval $(call DEP,$(obj),$(obj:_vc.o=.asm)))) + +# Dependencies for VC files that need to run scan_includes +%.constants.sym: %.constants.asm $(shell tools/scan_includes %.constants.asm) | rgbdscheck.o + $(RGBASM) $< > $@ endif @@ -95,6 +127,7 @@ endif pokeyellow_pad = 0x00 pokeyellow_debug_pad = 0xff +pokeyellow_vc_pad = 0x00 opts = -cjsv -k 01 -l 0x33 -m 0x1b -p 0 -r 03 -t "POKEMON YELLOW" diff --git a/README.md b/README.md index 7fe3a4737..cc34067af 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ It builds the following ROMs: - Pokemon Yellow (UE) [C][!].gbc `sha1: cc7d03262ebfaf2f06772c1a480c7d9d5f4a38e1` - YELLMONS.GB (debug build) `sha1: d44e96eddfbdad633cbe4e6e64915e9e198974b0` +- Dmgapse0.h08.patch `sha1: f3346a5559d52c296b8feab0cdbbfb0e250ac161` To set up the repository, see [**INSTALL.md**](INSTALL.md). diff --git a/audio.asm b/audio.asm index a3a32c12b..ef82fd60b 100644 --- a/audio.asm +++ b/audio.asm @@ -540,7 +540,7 @@ INCLUDE "audio/music/yellowunusedsong.asm" INCLUDE "audio/music/meetjessiejames.asm" IF !DEF(_DEBUG) - INCBIN "garbage/bank20.bin" + INCBIN "garbage/bank20.bin" ENDC diff --git a/data/pikachu/pikachu_pic_objects.asm b/data/pikachu/pikachu_pic_objects.asm index c3a44bc92..7a1f27464 100644 --- a/data/pikachu/pikachu_pic_objects.asm +++ b/data/pikachu/pikachu_pic_objects.asm @@ -301,7 +301,7 @@ PikaPicAnimBGFrames_33: pikaframeend PikaPicAnimBGFrames_34: - pikaframedelay 12 + pikaframedelay 12 pikaframe PikaAnimTilemap_9, 12 pikaframedelay 12 pikaframe PikaAnimTilemap_9, 100 diff --git a/data/text/text_8.asm b/data/text/text_8.asm index 12550c8e0..d3294170c 100644 --- a/data/text/text_8.asm +++ b/data/text/text_8.asm @@ -3,9 +3,19 @@ _CableClubNPCPleaseWaitText:: text_end _CableClubNPCLinkClosedBecauseOfInactivityText:: + vc_patch Change_MSG +IF DEF(_YELLOW_VC) + text "Please come again!" + done + text_start + text "sed because of" + cont "inactivity." +ELSE text "The link has been" line "closed because of" cont "inactivity." +ENDC + vc_patch_end para "Please contact" line "your friend and" diff --git a/engine/battle/animations.asm b/engine/battle/animations.asm index edffadb6c..a46f11df8 100644 --- a/engine/battle/animations.asm +++ b/engine/battle/animations.asm @@ -203,7 +203,9 @@ PlayAnimation: ld h, [hl] ld l, a .animationLoop + vc_hook FPA_005_End ld a, [hli] + vc_hook FPA_001_End cp -1 jr z, .AnimationOver cp FIRST_SE_ID ; is this subanimation or a special effect? @@ -277,12 +279,17 @@ PlayAnimation: call LoadSubanimation call PlaySubanimation pop af + vc_hook FPA_007_End ldh [rOBP0], a + vc_hook FPA_011_End call UpdateGBCPal_OBP0 .nextAnimationCommand + vc_hook FPA_002_End pop hl + vc_hook FPA_003_End jr .animationLoop .AnimationOver + vc_hook FPA_004_End ret LoadSubanimation: @@ -294,18 +301,26 @@ LoadSubanimation: ld e, a ld a, [hl] ld d, a ; de = address of subanimation + vc_hook FPA_005_Begin ld a, [de] + vc_hook FPA_003_Begin ld b, a + vc_hook FPA_002_Begin and %00011111 + vc_hook FPA_001_Begin ld [wSubAnimCounter], a ; number of frame blocks + vc_hook FPA_004_Begin ld a, b + vc_hook FPA_007_Begin and %11100000 cp SUBANIMTYPE_ENEMY << 5 + vc_hook FPA_009_Begin jr nz, .isNotType5 .isType5 call GetSubanimationTransform2 jr .saveTransformation .isNotType5 + vc_hook FPA_010_Begin call GetSubanimationTransform1 .saveTransformation ; place the upper 3 bits of a into bits 0-2 of a before storing @@ -336,6 +351,7 @@ LoadSubanimation: ; sets the transform to SUBANIMTYPE_NORMAL if it's the player's turn ; sets the transform to the subanimation type if it's the enemy's turn GetSubanimationTransform1: + vc_hook FPA_011_Begin ld b, a ldh a, [hWhoseTurn] and a @@ -428,10 +444,12 @@ MoveAnimation: ld c, 30 call DelayFrames .next4 + vc_hook FPA_009_End call PlayApplyingAttackAnimation ; shake the screen or flash the pic in and out (to show damage) .animationFinished call WaitForSoundToFinish xor a + vc_hook FPA_008_End ld [wSubAnimSubEntryAddr], a ld [wUnusedD09B], a ld [wSubAnimTransform], a @@ -469,6 +487,7 @@ ShareMoveAnimations: PlayApplyingAttackAnimation: ; Generic animation that shows after the move's individual animation ; Different animation depending on whether the move has an additional effect and on whose turn it is + vc_hook FPA_010_End ld a, [wAnimationType] and a ret z @@ -558,8 +577,10 @@ SetAnimationPalette: ld b, $f0 .next ld a, b + vc_hook FPA_006_Begin ldh [rOBP0], a ld a, $6c + vc_hook FPA_008_Begin ldh [rOBP1], a call UpdateGBCPal_OBP0 call UpdateGBCPal_OBP1 diff --git a/engine/battle/core.asm b/engine/battle/core.asm index fa1a8d56f..e86242898 100644 --- a/engine/battle/core.asm +++ b/engine/battle/core.asm @@ -3192,6 +3192,7 @@ LinkBattleExchangeData: ld a, b .doExchange ld [wSerialExchangeNybbleSendData], a + vc_hook send_byt2 callfar PrintWaitingText .syncLoop1 call Serial_ExchangeNybble @@ -3199,18 +3200,33 @@ LinkBattleExchangeData: ld a, [wSerialExchangeNybbleReceiveData] inc a jr z, .syncLoop1 + vc_hook send_byt2_ret + vc_patch FIGHT +IF DEF(_YELLOW_VC) + ld b, 26 +ELSE ld b, 10 +ENDC + vc_patch_end .syncLoop2 call DelayFrame call Serial_ExchangeNybble dec b jr nz, .syncLoop2 + vc_hook send_dummy + vc_patch FIGHT2 +IF DEF(_YELLOW_VC) + ld b, 26 +ELSE ld b, 10 +ENDC + vc_patch_end .syncLoop3 call DelayFrame call Serial_SendZeroByte dec b jr nz, .syncLoop3 + vc_hook send_dummy_end ret ExecutePlayerMove: @@ -6844,7 +6860,14 @@ BattleRandom: ld a, [hl] pop bc pop hl + vc_hook fight_ret_c + vc_patch fight_ret +IF DEF(_YELLOW_VC) + ret +ELSE ret c +ENDC + vc_patch_end ; if we picked the last seed, we need to recalculate the nine seeds push hl diff --git a/engine/gfx/palettes.asm b/engine/gfx/palettes.asm index fa7bb7c57..4d4e6d02c 100644 --- a/engine/gfx/palettes.asm +++ b/engine/gfx/palettes.asm @@ -657,6 +657,7 @@ CheckSGB: SendMltReq1Packet: ld hl, MltReq1Packet call SendSGBPacket + vc_hook Network_RESET jp Wait7000 CopyGfxToSuperNintendoVRAM: diff --git a/engine/link/cable_club.asm b/engine/link/cable_club.asm index 0c653cc81..aa2ff15a7 100644 --- a/engine/link/cable_club.asm +++ b/engine/link/cable_club.asm @@ -130,12 +130,14 @@ CableClub_DoBattleOrTradeAgain: ld hl, wSerialPlayerDataBlock ld de, wSerialEnemyDataBlock ld bc, $1a8 + vc_hook Network424 call Serial_ExchangeBytes ld a, SERIAL_NO_DATA_BYTE ld [de], a ld hl, wSerialPartyMonsPatchList ld de, wSerialEnemyMonsPatchList ld bc, $c8 + vc_hook Network200 call Serial_ExchangeBytes ld a, (1 << SERIAL) | (1 << TIMER) | (1 << VBLANK) ldh [rIE], a @@ -866,6 +868,7 @@ TradeCenter_Trade: ld de, TradeCompleted call PlaceString predef SaveSAVtoSRAM2 + vc_hook save_game_end ld c, 50 call DelayFrames xor a diff --git a/engine/link/cable_club_npc.asm b/engine/link/cable_club_npc.asm index 4d0a0a8ab..b65acfdcc 100644 --- a/engine/link/cable_club_npc.asm +++ b/engine/link/cable_club_npc.asm @@ -30,6 +30,7 @@ CableClubNPC:: xor a ldh [hSerialReceiveData], a ld a, START_TRANSFER_EXTERNAL_CLOCK + vc_hook linkCable_fake_begin ldh [rSC], a ld a, [wLinkTimeoutCounter] dec a @@ -57,6 +58,7 @@ CableClubNPC:: ld a, [wCurrentMenuItem] and a jr nz, .choseNo + vc_hook linkCable_block_input callfar SaveSAVtoSRAM call WaitForSoundToFinish ld a, SFX_SAVE @@ -70,7 +72,9 @@ CableClubNPC:: ld [hl], a ldh [hSerialReceivedNewData], a ld [wSerialExchangeNybbleSendData], a + vc_hook linkCable_fake_end call Serial_SyncAndExchangeNybble + vc_hook Network_RECHECK ld hl, wUnknownSerialCounter ld a, [hli] inc a diff --git a/engine/menus/link_menu.asm b/engine/menus/link_menu.asm index 2f2db5739..6d9021e47 100644 --- a/engine/menus/link_menu.asm +++ b/engine/menus/link_menu.asm @@ -794,9 +794,11 @@ LinkMenu: call Delay3 callfar CloseLinkConnection ld hl, ColosseumCanceledText + vc_hook Network_STOP call PrintText ld hl, wd72e res 6, [hl] + vc_hook Network_END ret .asm_f5963 diff --git a/engine/menus/pokedex.asm b/engine/menus/pokedex.asm index 7b1a204a9..d0e8cf2bb 100644 --- a/engine/menus/pokedex.asm +++ b/engine/menus/pokedex.asm @@ -111,7 +111,13 @@ HandlePokedexSideMenu: dec a jr z, .choseArea dec a + vc_patch disable_print +IF DEF (_YELLOW_VC) + jr z, .handleMenuInput +ELSE jr z, .chosePrint +ENDC + vc_patch_end .choseQuit ld b, 1 .exitSideMenu diff --git a/engine/menus/save.asm b/engine/menus/save.asm index 55ae9e471..a623ee1aa 100644 --- a/engine/menus/save.asm +++ b/engine/menus/save.asm @@ -35,20 +35,23 @@ LoadSAV0: call EnableSRAMAndLatchClockData ld a, $1 ld [MBC1SRamBank], a - ld hl, sPlayerName ; hero name located in SRAM - ld bc, sMainDataCheckSum - sPlayerName ; but here checks the full SAV +; This vc_hook does not have to be in any particular location. +; It is defined here because it refers to the same labels as the two lines below. + vc_hook SaveLimit + ld hl, sGameData + ld bc, sGameDataEnd - sGameData call SAVCheckSum ld c, a - ld a, [sMainDataCheckSum] ; SAV's checksum + ld a, [sMainDataCheckSum] cp c jp z, .checkSumsMatched ; If the computed checksum didn't match the saved on, try again. - ld hl, sPlayerName - ld bc, sMainDataCheckSum - sPlayerName + ld hl, sGameData + ld bc, sGameDataEnd - sGameData call SAVCheckSum ld c, a - ld a, [sMainDataCheckSum] ; SAV's checksum + ld a, [sMainDataCheckSum] cp c jp nz, SAVBadCheckSum @@ -80,11 +83,11 @@ LoadSAV1: call EnableSRAMAndLatchClockData ld a, $1 ld [MBC1SRamBank], a - ld hl, sPlayerName ; hero name located in SRAM - ld bc, sMainDataCheckSum - sPlayerName ; but here checks the full SAV + ld hl, sGameData + ld bc, sGameDataEnd - sGameData call SAVCheckSum ld c, a - ld a, [sMainDataCheckSum] ; SAV's checksum + ld a, [sMainDataCheckSum] cp c jr nz, SAVBadCheckSum ld hl, sCurBoxData @@ -98,11 +101,11 @@ LoadSAV2: call EnableSRAMAndLatchClockData ld a, $1 ld [MBC1SRamBank], a - ld hl, sPlayerName ; hero name located in SRAM - ld bc, sMainDataCheckSum - sPlayerName ; but here checks the full SAV + ld hl, sGameData + ld bc, sGameDataEnd - sGameData call SAVCheckSum ld c, a - ld a, [sMainDataCheckSum] ; SAV's checksum + ld a, [sMainDataCheckSum] cp c jp nz, SAVBadCheckSum ld hl, sPartyData @@ -213,8 +216,8 @@ SaveSAVtoSRAM0: call CopyData ldh a, [hTileAnimations] ld [sTileAnimations], a - ld hl, sPlayerName - ld bc, sMainDataCheckSum - sPlayerName + ld hl, sGameData + ld bc, sGameDataEnd - sGameData call SAVCheckSum ld [sMainDataCheckSum], a call DisableSRAMAndPrepareClockData @@ -229,8 +232,8 @@ SaveSAVtoSRAM1: ld de, sCurBoxData ld bc, wBoxDataEnd - wBoxDataStart call CopyData - ld hl, sPlayerName - ld bc, sMainDataCheckSum - sPlayerName + ld hl, sGameData + ld bc, sGameDataEnd - sGameData call SAVCheckSum ld [sMainDataCheckSum], a call DisableSRAMAndPrepareClockData @@ -255,8 +258,8 @@ SaveSAVtoSRAM2: inc de ld a, [hl] ld [de], a - ld hl, sPlayerName - ld bc, sMainDataCheckSum - sPlayerName + ld hl, sGameData + ld bc, sGameDataEnd - sGameData call SAVCheckSum ld [sMainDataCheckSum], a call DisableSRAMAndPrepareClockData @@ -584,8 +587,8 @@ SAVCheckRandomID: ld a, [sPlayerName] and a jr z, .next - ld hl, sPlayerName - ld bc, sMainDataCheckSum - sPlayerName + ld hl, sGameData + ld bc, sGameDataEnd - sGameData call SAVCheckSum ld c, a ld a, [sMainDataCheckSum] diff --git a/engine/movie/intro_yellow.asm b/engine/movie/intro_yellow.asm index 8a2e6f4c9..f054f5765 100644 --- a/engine/movie/intro_yellow.asm +++ b/engine/movie/intro_yellow.asm @@ -30,6 +30,7 @@ PlayIntroScene: jr .loop .go_to_title_screen + vc_hook FPA_031801_End3 call YellowIntro_BlankPalettes xor a ldh [hLCDCPointer], a @@ -123,6 +124,7 @@ Jumptable_f9906: YellowIntro_NextScene: ld hl, wYellowIntroCurrentScene inc [hl] + vc_hook FPA_031801_Begin1 ret YellowIntroScene0: @@ -601,6 +603,7 @@ YellowIntroScene14: call YellowIntro_SpawnAnimatedObjectAndSavePointer call YellowIntro_NextScene ld a, $28 + vc_hook FPA_031801_Begin2 ld [wYellowIntroSceneTimer], a ret @@ -705,12 +708,14 @@ YellowIntro_CheckFrameTimerDecrement: ret .asm_f9e4b + vc_hook FPA_031801_End2 scf ret YellowIntro_LoadDMGPalAndIncrementCounter: ld hl, wYellowIntroSceneTimer ld a, [hl] + vc_hook FPA_031801_End1 inc [hl] ld l, a ld h, $0 diff --git a/engine/pikachu/pikachu_pcm.asm b/engine/pikachu/pikachu_pcm.asm index 5ef6fbc36..d971ea854 100644 --- a/engine/pikachu/pikachu_pcm.asm +++ b/engine/pikachu/pikachu_pcm.asm @@ -1,4 +1,5 @@ PlayPikachuSoundClip:: + vc_hook PLAY_NEW ld a, e ld e, a ld d, $0 @@ -39,7 +40,13 @@ PlayPikachuSoundClip:: ld a, l cp $40 ; end of wave data jr nz, .saveWaveDataLoop + vc_patch PLAY_MUTE +IF DEF(_YELLOW_VC) + ld a, 0 +ELSE ld a, $80 +ENDC + vc_patch_end ldh [rNR30], a ldh a, [rNR51] or $44 diff --git a/home/pikachu_cries.asm b/home/pikachu_cries.asm index 84889ef90..58bf2bdb7 100644 --- a/home/pikachu_cries.asm +++ b/home/pikachu_cries.asm @@ -1,4 +1,5 @@ PlayPikachuPCM:: + vc_hook PLAY_HACK ldh a, [hLoadedROMBank] push af ld a, b diff --git a/home/serial.asm b/home/serial.asm index ef9149337..b4546fbd2 100644 --- a/home/serial.asm +++ b/home/serial.asm @@ -233,6 +233,7 @@ Serial_PrintWaitingTextAndSyncAndExchangeNybble:: jp LoadScreenTilesFromBuffer1 Serial_SyncAndExchangeNybble:: + vc_hook send_send_buf2 ld a, $ff ld [wSerialExchangeNybbleReceiveData], a .loop1 @@ -256,13 +257,25 @@ Serial_SyncAndExchangeNybble:: ld a, [wSerialExchangeNybbleReceiveData] inc a jr z, .loop1 + vc_patch Network10 +IF DEF(_YELLOW_VC) + ld b, 26 +ELSE ld b, 10 +ENDC + vc_patch_end .loop2 call DelayFrame call Serial_ExchangeNybble dec b jr nz, .loop2 + vc_patch Network11 +IF DEF(_YELLOW_VC) + ld b, 26 +ELSE ld b, 10 +ENDC + vc_patch_end .loop3 call DelayFrame call Serial_SendZeroByte @@ -270,6 +283,7 @@ Serial_SyncAndExchangeNybble:: jr nz, .loop3 ld a, [wSerialExchangeNybbleReceiveData] ld [wSerialSyncAndExchangeNybbleReceiveData], a + vc_hook send_send_buf2_ret ret Serial_ExchangeNybble:: diff --git a/macros.asm b/macros.asm index 7b64db918..5dd71f71e 100644 --- a/macros.asm +++ b/macros.asm @@ -6,6 +6,7 @@ INCLUDE "macros/data.asm" INCLUDE "macros/code.asm" INCLUDE "macros/gfx.asm" INCLUDE "macros/coords.asm" +INCLUDE "macros/vc.asm" INCLUDE "macros/scripts/audio.asm" INCLUDE "macros/scripts/maps.asm" diff --git a/macros/vc.asm b/macros/vc.asm new file mode 100644 index 000000000..2fbdf9d62 --- /dev/null +++ b/macros/vc.asm @@ -0,0 +1,27 @@ +vc_hook: MACRO +IF DEF(_YELLOW_VC) +.VC_\1:: +ENDC +ENDM + +vc_patch: MACRO +IF DEF(_YELLOW_VC) + ASSERT !DEF(CURRENT_VC_PATCH), "Already started a vc_patch" +CURRENT_VC_PATCH EQUS "\1" +.VC_{CURRENT_VC_PATCH}:: +ENDC +ENDM + +vc_patch_end: MACRO +IF DEF(_YELLOW_VC) + ASSERT DEF(CURRENT_VC_PATCH), "No vc_patch started" +.VC_{CURRENT_VC_PATCH}_End:: + PURGE CURRENT_VC_PATCH +ENDC +ENDM + +vc_assert: MACRO +IF DEF(_YELLOW_VC) + ASSERT \# +ENDC +ENDM diff --git a/main.asm b/main.asm index 30ce58251..3f79c571a 100644 --- a/main.asm +++ b/main.asm @@ -314,7 +314,7 @@ INCLUDE "data/battle_anims/frame_blocks.asm" SECTION "BG Map Attributes (Debug)", ROMX IF DEF(_DEBUG) - INCLUDE "engine/gfx/bg_map_attributes.asm" + INCLUDE "engine/gfx/bg_map_attributes.asm" ENDC @@ -322,7 +322,7 @@ SECTION "BG Map Attributes", ROMX INCLUDE "data/cgb/bg_map_attributes.asm" IF !DEF(_DEBUG) - INCLUDE "engine/gfx/bg_map_attributes.asm" + INCLUDE "engine/gfx/bg_map_attributes.asm" ENDC @@ -330,7 +330,7 @@ SECTION "bank30", ROMX ; This whole bank is garbage data. IF !DEF(_DEBUG) - INCBIN "garbage/bank30.bin" + INCBIN "garbage/bank30.bin" ENDC diff --git a/roms.sha1 b/roms.sha1 index 624d47f3e..952e98742 100644 --- a/roms.sha1 +++ b/roms.sha1 @@ -1,2 +1,3 @@ cc7d03262ebfaf2f06772c1a480c7d9d5f4a38e1 *pokeyellow.gbc d44e96eddfbdad633cbe4e6e64915e9e198974b0 *pokeyellow_debug.gbc +f3346a5559d52c296b8feab0cdbbfb0e250ac161 *pokeyellow.patch diff --git a/scripts/SummerBeachHouse.asm b/scripts/SummerBeachHouse.asm index 45e8032d3..f31c04fea 100644 --- a/scripts/SummerBeachHouse.asm +++ b/scripts/SummerBeachHouse.asm @@ -13,7 +13,13 @@ SummerBeachHouse_TextPointers: SurfinDudeText: text_asm ld a, [wd472] + vc_patch MiniGame +IF DEF (_YELLOW_VC) + bit 7, a +ELSE bit 6, a +ENDC + vc_patch_end jr nz, .next ld hl, .SurfinDudeText4 call PrintText @@ -130,7 +136,13 @@ SummerBeachHouseSign4Text: ld a, 1 ld [wDoNotWaitForButtonPressAfterDisplayingText], a ld a, [wd472] + vc_patch MiniGameHighScorePC +IF DEF(_YELLOW_VC) + bit 7, a +ELSE bit 6, a +ENDC + vc_patch_end jr z, .asm_f2369 ld hl, wd492 diff --git a/sram.asm b/sram.asm index 776610c54..684517912 100644 --- a/sram.asm +++ b/sram.asm @@ -13,12 +13,14 @@ SECTION "Save Data", SRAM ds $598 +sGameData:: sPlayerName:: ds NAME_LENGTH sMainData:: ds wMainDataEnd - wMainDataStart sSpriteData:: ds wSpriteDataEnd - wSpriteDataStart sPartyData:: ds wPartyDataEnd - wPartyDataStart sCurBoxData:: ds wBoxDataEnd - wBoxDataStart sTileAnimations:: db +sGameDataEnd:: sMainDataCheckSum:: db diff --git a/tools/.gitignore b/tools/.gitignore index cf5f5adb9..25faebc80 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -1,4 +1,5 @@ -scan_includes gfx -pkmncompress +make_patch pcm +pkmncompress +scan_includes diff --git a/tools/Makefile b/tools/Makefile index 1ed253ec2..ed748bafc 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -5,6 +5,7 @@ CFLAGS := -O3 -flto -std=c11 -Wall -Wextra -pedantic tools := \ gfx \ + make_patch \ pcm \ pkmncompress \ scan_includes diff --git a/tools/make_patch.c b/tools/make_patch.c new file mode 100644 index 000000000..29d798f61 --- /dev/null +++ b/tools/make_patch.c @@ -0,0 +1,469 @@ +#define PROGRAM_NAME "make_patch" +#define USAGE_OPTS "labels.sym constants.sym patched.gbc original.gbc vc.patch.template vc.patch" + +#include "common.h" + +#include + +struct Buffer { + size_t item_size; + size_t size; + size_t capacity; + void *data; +}; + +struct Symbol { + struct Symbol *next; + unsigned int address; + unsigned int offset; + char name[]; // C99 FAM +}; + +struct Patch { + unsigned int offset; + unsigned int size; +}; + +struct Buffer *buffer_create(size_t item_size) { + struct Buffer *buffer = xmalloc(sizeof(*buffer)); + buffer->item_size = item_size; + buffer->size = 0; + buffer->capacity = 0x10; + buffer->data = xmalloc(buffer->capacity * item_size); + return buffer; +} + +void buffer_append(struct Buffer *buffer, const void *item) { + if (buffer->size >= buffer->capacity) { + buffer->capacity = (buffer->capacity + 1) * 2; + buffer->data = xrealloc(buffer->data, buffer->capacity * buffer->item_size); + } + memcpy((char *)buffer->data + (buffer->size++ * buffer->item_size), item, buffer->item_size); +} + +void buffer_free(struct Buffer *buffer) { + free(buffer->data); + free(buffer); +} + +void symbol_append(struct Symbol **symbols, const char *name, int bank, int address) { + size_t name_len = strlen(name) + 1; + struct Symbol *symbol = xmalloc(sizeof(*symbol) + name_len); + symbol->address = address; + symbol->offset = address < 0x8000 + ? (bank > 0 ? address + (bank - 1) * 0x4000 : address) // ROM addresses are relative to their bank + : address - 0x8000; // RAM addresses are relative to the start of all RAM + memcpy(symbol->name, name, name_len); + symbol->next = *symbols; + *symbols = symbol; +} + +void symbol_free(struct Symbol *symbols) { + for (struct Symbol *next; symbols; symbols = next) { + next = symbols->next; + free(symbols); + } +} + +const struct Symbol *symbol_find(const struct Symbol *symbols, const char *name) { + size_t name_len = strlen(name); + for (const struct Symbol *symbol = symbols; symbol; symbol = symbol->next) { + size_t sym_name_len = strlen(symbol->name); + if (name_len > sym_name_len) { + continue; + } + const char *sym_name = symbol->name; + if (name[0] == '.') { + // If `name` is a local label, compare it to the local part of `symbol->name` + sym_name += sym_name_len - name_len; + } + if (!strcmp(sym_name, name)) { + return symbol; + } + } + error_exit("Error: Unknown symbol: \"%s\"\n", name); +} + +const struct Symbol *symbol_find_cat(const struct Symbol *symbols, const char *prefix, const char *suffix) { + char *sym_name = xmalloc(strlen(prefix) + strlen(suffix) + 1); + sprintf(sym_name, "%s%s", prefix, suffix); + const struct Symbol *symbol = symbol_find(symbols, sym_name); + free(sym_name); + return symbol; +} + +int parse_number(const char *input, int base) { + char *endptr; + int n = (int)strtol(input, &endptr, base); + if (endptr == input || *endptr || n < 0) { + error_exit("Error: Cannot parse number: \"%s\"\n", input); + } + return n; +} + +void parse_symbol_value(char *input, int *restrict bank, int *restrict address) { + char *colon = strchr(input, ':'); + if (!colon) { + error_exit("Error: Cannot parse bank+address: \"%s\"\n", input); + } + *colon++ = '\0'; + *bank = parse_number(input, 16); + *address = parse_number(colon, 16); +} + +void parse_symbols(const char *filename, struct Symbol **symbols) { + FILE *file = xfopen(filename, 'r'); + struct Buffer *buffer = buffer_create(1); + + enum { SYM_PRE, SYM_VALUE, SYM_SPACE, SYM_NAME } state = SYM_PRE; + int bank = 0; + int address = 0; + + for (;;) { + int c = getc(file); + if (c == EOF || c == '\n' || c == '\r' || c == ';' || (state == SYM_NAME && (c == ' ' || c == '\t'))) { + if (state == SYM_NAME) { + // The symbol name has ended; append the buffered symbol + buffer_append(buffer, &(char []){'\0'}); + symbol_append(symbols, buffer->data, bank, address); + } + // Skip to the next line, ignoring anything after the symbol value and name + state = SYM_PRE; + while (c != EOF && c != '\n' && c != '\r') { + c = getc(file); + } + if (c == EOF) { + break; + } + } else if (c != ' ' && c != '\t') { + if (state == SYM_PRE || state == SYM_SPACE) { + // The symbol value or name has started; buffer its contents + if (++state == SYM_NAME) { + // The symbol name has started; parse the buffered value + buffer_append(buffer, &(char []){'\0'}); + parse_symbol_value(buffer->data, &bank, &address); + } + buffer->size = 0; + } + buffer_append(buffer, &c); + } else if (state == SYM_VALUE) { + // The symbol value has ended; wait to see if a name comes after it + state = SYM_SPACE; + } + } + + fclose(file); + buffer_free(buffer); +} + +int strfind(const char *s, const char *list[], int count) { + for (int i = 0; i < count; i++) { + if (!strcmp(s, list[i])) { + return i; + } + } + return -1; +} + +#define vstrfind(s, ...) strfind(s, (const char *[]){__VA_ARGS__}, sizeof (const char *[]){__VA_ARGS__} / sizeof(const char *)) + +int parse_arg_value(const char *arg, bool absolute, const struct Symbol *symbols, const char *patch_name) { + // Comparison operators for "ConditionValueB" evaluate to their particular values + int op = vstrfind(arg, "==", ">", "<", ">=", "<=", "!=", "||"); + if (op >= 0) { + return op == 6 ? 0x11 : op; // "||" is 0x11 + } + + // Literal numbers evaluate to themselves + if (isdigit((unsigned)arg[0]) || arg[0] == '+') { + return parse_number(arg, 0); + } + + // Symbols evaluate to their offset or address, plus an optional offset mod + int offset_mod = 0; + char *plus = strchr(arg, '+'); + if (plus) { + offset_mod = parse_number(plus, 0); + *plus = '\0'; + } + const char *sym_name = !strcmp(arg, "@") ? patch_name : arg; // "@" is the current patch label + const struct Symbol *symbol = symbol_find(symbols, sym_name); + return (absolute ? symbol->offset : symbol->address) + offset_mod; +} + +void interpret_command(char *command, const struct Symbol *current_hook, const struct Symbol *symbols, struct Buffer *patches, FILE *restrict new_rom, FILE *restrict orig_rom, FILE *restrict output) { + // Strip all leading spaces and all but one trailing space + int x = 0; + for (int i = 0; command[i]; i++) { + if (!isspace((unsigned)command[i]) || (i > 0 && !isspace((unsigned)command[i - 1]))) { + command[x++] = command[i]; + } + } + command[x - (x > 0 && isspace((unsigned)command[x - 1]))] = '\0'; + + // Count the arguments + int argc = 0; + for (const char *c = command; *c; c++) { + if (isspace((unsigned)*c)) { + argc++; + } + } + + // Get the arguments + char *argv[argc]; // VLA + char *arg = command; + for (int i = 0; i < argc; i++) { + while (*arg && !isspace((unsigned)*arg)) { + arg++; + } + if (!*arg) { + break; + } + *arg++ = '\0'; + argv[i] = arg; + } + + // Use the arguments + if (vstrfind(command, "patch", "PATCH", "patch_", "PATCH_", "patch/", "PATCH/") >= 0) { + if (argc > 2) { + error_exit("Error: Invalid arguments for command: \"%s\"\n", command); + } + if (!current_hook) { + error_exit("Error: No current patch for command: \"%s\"\n", command); + } + int current_offset = current_hook->offset + (argc > 0 ? parse_number(argv[0], 0) : 0); + if (fseek(orig_rom, current_offset, SEEK_SET)) { + error_exit("Error: Cannot seek to \"vc_patch %s\" in the original ROM\n", current_hook->name); + } + if (fseek(new_rom, current_offset, SEEK_SET)) { + error_exit("Error: Cannot seek to \"vc_patch %s\" in the new ROM\n", current_hook->name); + } + int length; + if (argc == 2) { + length = parse_number(argv[1], 0); + } else { + const struct Symbol *current_hook_end = symbol_find_cat(symbols, current_hook->name, "_End"); + length = current_hook_end->offset - current_offset; + } + buffer_append(patches, &(struct Patch){current_offset, length}); + bool modified = false; + if (length == 1) { + int c = getc(new_rom); + modified = c != getc(orig_rom); + fprintf(output, isupper((unsigned)command[0]) ? "0x%02X" : "0x%02x", c); + } else { + if (command[strlen(command) - 1] != '/') { + fprintf(output, command[strlen(command) - 1] == '_' ? "a%d: " : "a%d:", length); + } + for (int i = 0; i < length; i++) { + if (i) { + putc(' ', output); + } + int c = getc(new_rom); + modified |= c != getc(orig_rom); + fprintf(output, isupper((unsigned)command[0]) ? "%02X" : "%02x", c); + } + } + if (!modified) { + fprintf(stderr, PROGRAM_NAME ": Warning: \"vc_patch %s\" doesn't alter the ROM\n", current_hook->name); + } + + } else if (vstrfind(command, "dws", "DWS", "dws_", "DWS_", "dws/", "DWS/") >= 0) { + if (argc < 1) { + error_exit("Error: Invalid arguments for command: \"%s\"\n", command); + } + if (command[strlen(command) - 1] != '/') { + fprintf(output, command[strlen(command) - 1] == '_' ? "a%d: " : "a%d:", argc * 2); + } + for (int i = 0; i < argc; i++) { + int value = parse_arg_value(argv[i], false, symbols, current_hook->name); + if (value > 0xffff) { + error_exit("Error: Invalid value for \"%s\" argument: 0x%x\n", command, value); + } + if (i) { + putc(' ', output); + } + fprintf(output, isupper((unsigned)command[0]) ? "%02X %02X": "%02x %02x", value & 0xff, value >> 8); + } + + } else if (vstrfind(command, "db", "DB", "db_", "DB_", "db/", "DB/") >= 0) { + if (argc != 1) { + error_exit("Error: Invalid arguments for command: \"%s\"\n", command); + } + int value = parse_arg_value(argv[0], false, symbols, current_hook->name); + if (value > 0xff) { + error_exit("Error: Invalid value for \"%s\" argument: 0x%x\n", command, value); + } + if (command[strlen(command) - 1] != '/') { + fputs(command[strlen(command) - 1] == '_' ? "a1: " : "a1:", output); + } + fprintf(output, isupper((unsigned)command[0]) ? "%02X" : "%02x", value); + + } else if (vstrfind(command, "hex", "HEX", "HEx", "Hex", "heX", "hEX", "hex~", "HEX~", "HEx~", "Hex~", "heX~", "hEX~") >= 0) { + if (argc != 1 && argc != 2) { + error_exit("Error: Invalid arguments for command: \"%s\"\n", command); + } + int value = parse_arg_value(argv[0], command[strlen(command) - 1] != '~', symbols, current_hook->name); + int padding = argc > 1 ? parse_number(argv[1], 0) : 2; + if (vstrfind(command, "HEx", "HEx~") >= 0) { + fprintf(output, "0x%0*X%02x", padding - 2, value >> 8, value & 0xff); + } else if (vstrfind(command, "Hex", "Hex~") >= 0) { + fprintf(output, "0x%0*X%03x", padding - 3, value >> 12, value & 0xfff); + } else if (vstrfind(command, "heX", "heX~") >= 0) { + fprintf(output, "0x%0*x%02X", padding - 2, value >> 8, value & 0xff); + } else if (vstrfind(command, "hEX", "hEX~") >= 0) { + fprintf(output, "0x%0*x%03X", padding - 3, value >> 12, value & 0xfff); + } else { + fprintf(output, isupper((unsigned)command[0]) ? "0x%0*X" : "0x%0*x", padding, value); + } + + } else { + error_exit("Error: Unknown command: \"%s\"\n", command); + } +} + +void skip_to_next_line(FILE *restrict input, FILE *restrict output) { + for (int c = getc(input); c != EOF; c = getc(input)) { + putc(c, output); + if (c == '\n' || c == '\r') { + break; + } + } +} + +struct Buffer *process_template(const char *template_filename, const char *patch_filename, FILE *restrict new_rom, FILE *restrict orig_rom, const struct Symbol *symbols) { + FILE *input = xfopen(template_filename, 'r'); + FILE *output = xfopen(patch_filename, 'w'); + + struct Buffer *patches = buffer_create(sizeof(struct Patch)); + struct Buffer *buffer = buffer_create(1); + + // The ROM checksum will always differ + buffer_append(patches, &(struct Patch){0x14e, 2}); + // The Stadium data (see stadium.c) will always differ + unsigned int rom_size = (unsigned int)xfsize("", orig_rom); + unsigned int stadium_size = 24 + 6 + 2 + (rom_size / 0x2000) * 2; + buffer_append(patches, &(struct Patch){rom_size - stadium_size, stadium_size}); + + // Fill in the template + const struct Symbol *current_hook = NULL; + for (int c = getc(input); c != EOF; c = getc(input)) { + switch (c) { + case ';': + // ";" comments until the end of the line + putc(c, output); + skip_to_next_line(input, output); + break; + + case '{': + // "{...}" is a template command; buffer its contents + buffer->size = 0; + for (c = getc(input); c != EOF && c != '}'; c = getc(input)) { + buffer_append(buffer, &c); + } + buffer_append(buffer, &(char []){'\0'}); + // Interpret the command in the context of the current patch + interpret_command(buffer->data, current_hook, symbols, patches, new_rom, orig_rom, output); + break; + + case '[': + // "[...]" is a patch label; buffer its contents + putc(c, output); + bool alternate = false; + buffer->size = 0; + for (c = getc(input); c != EOF; c = getc(input)) { + if (!alternate && c == '@') { + // "@" designates an alternate name for the ".VC_" label + alternate = true; + buffer->size = 0; + } else if (c == ']') { + putc(c, output); + break; + } else { + if (!alternate) { + putc(c, output); + if (!isalnum(c) && c != '_') { + // Convert non-identifier characters to underscores + c = '_'; + } + } + buffer_append(buffer, &c); + } + } + buffer_append(buffer, &(char []){'\0'}); + // The current patch should have a corresponding ".VC_" label + current_hook = symbol_find_cat(symbols, ".VC_", buffer->data); + skip_to_next_line(input, output); + break; + + default: + putc(c, output); + } + } + + rewind(orig_rom); + rewind(new_rom); + + fclose(input); + fclose(output); + buffer_free(buffer); + return patches; +} + +int compare_patch(const void *patch1, const void *patch2) { + unsigned int offset1 = ((const struct Patch *)patch1)->offset; + unsigned int offset2 = ((const struct Patch *)patch2)->offset; + return offset1 > offset2 ? 1 : offset1 < offset2 ? -1 : 0; +} + +bool verify_completeness(FILE *restrict orig_rom, FILE *restrict new_rom, struct Buffer *patches) { + qsort(patches->data, patches->size, patches->item_size, compare_patch); + for (unsigned int offset = 0, index = 0; ; offset++) { + int orig_byte = getc(orig_rom); + int new_byte = getc(new_rom); + if (orig_byte == EOF || new_byte == EOF) { + return orig_byte == new_byte; + } + struct Patch *patch = &((struct Patch *)patches->data)[index]; + if (index < patches->size && patch->offset == offset) { + if (fseek(orig_rom, patch->size, SEEK_CUR)) { + return false; + } + if (fseek(new_rom, patch->size, SEEK_CUR)) { + return false; + } + offset += patch->size; + index++; + } else if (orig_byte != new_byte) { + fprintf(stderr, PROGRAM_NAME ": Warning: Unpatched difference at offset: 0x%x\n", offset); + fprintf(stderr, " Original ROM value: 0x%02x\n", orig_byte); + fprintf(stderr, " Patched ROM value: 0x%02x\n", new_byte); + fprintf(stderr, " Current patch offset: 0x%06x\n", patch->offset); + return false; + } + } +} + +int main(int argc, char *argv[]) { + if (argc != 7) { + usage_exit(1); + } + + struct Symbol *symbols = NULL; + parse_symbols(argv[1], &symbols); + parse_symbols(argv[2], &symbols); + + FILE *new_rom = xfopen(argv[3], 'r'); + FILE *orig_rom = xfopen(argv[4], 'r'); + struct Buffer *patches = process_template(argv[5], argv[6], new_rom, orig_rom, symbols); + + if (!verify_completeness(orig_rom, new_rom, patches)) { + fprintf(stderr, PROGRAM_NAME ": Warning: Not all ROM differences are defined by \"%s\"\n", argv[6]); + } + + symbol_free(symbols); + fclose(new_rom); + fclose(orig_rom); + buffer_free(patches); + return 0; +} diff --git a/vc/pokeyellow.constants.asm b/vc/pokeyellow.constants.asm new file mode 100644 index 000000000..89fb598ff --- /dev/null +++ b/vc/pokeyellow.constants.asm @@ -0,0 +1,61 @@ +INCLUDE "constants.asm" + +; These are all the asm constants needed to make the blue_vc patch. + +vc_const: MACRO +x = \1 + PRINTLN "00:{04x:x} \1" ; same format as rgblink's .sym file +ENDM + +; [FPA 001 Begin] + vc_const "M" + vc_const "E" + vc_const "G" + vc_const "A" + vc_const "P" + vc_const "X" + vc_const "L" + vc_const "S" + vc_const "F" + vc_const MEGA_PUNCH + +; [FPA 002 Begin] + vc_const "U" + vc_const "I" + vc_const GUILLOTINE + +; [FPA 003 Begin] + vc_const "K" + vc_const MEGA_KICK + +; [FPA 004 Begin] + vc_const "B" + vc_const "Z" + vc_const BLIZZARD + +; [FPA 005 Begin] + vc_const BUBBLEBEAM + +; [FPA 006 Begin] + vc_const "H" + vc_const "Y" + vc_const HYPER_BEAM + +; [FPA 007 Begin] + vc_const "T" + vc_const "N" + vc_const THUNDERBOLT + +; [FPA 008 Begin] + vc_const HAZE + +; [FPA 009 Begin] + vc_const "R" + vc_const "F" + vc_const REFLECT + +; [FPA 010 Begin] + vc_const SELFDESTRUCT + +; [FPA 011 Begin] + vc_const EXPLOSION diff --git a/vc/pokeyellow.patch.template b/vc/pokeyellow.patch.template new file mode 100644 index 000000000..699a3685c --- /dev/null +++ b/vc/pokeyellow.patch.template @@ -0,0 +1,533 @@ +;Format Sample +;[xxxx] ;User-defined Name (Max:31 chars) +;Mode = 1 ;1:Fixcode; 2:Fixvalue; 3:Mask; 4:Palette; 5:Double Frame Buffer +;Type = 0 ;0:Begin 1:End +;Index = 0 ;Index +;Address = x1F8000 ;ROM Address +;MemAddress = x2000 ;RAM Address +;Fixcode = 0 ;Mode1: Fixed Rom Code; Mode2: Fixed Value +;DelayFrame = 0 ;Delay Frame +;FadeFrame = 0 ;Fade Frame 0:Off +;DarkEnable0 = 0 ;0:Off, 1:On (for Normal Mode) +;ReduceEnable0 = 0 ;0:Off, 1:On (for Normal Mode) +;MotionBEnable0 = 0 ;0:Off, 1:Black Fade, 2:, 3:Frame Blend (for Normal Mode) +;Dark0 = 10 ;0~10 (for Normal Mode) +;ReduceColorR0 = 0 ;0~31 (for Normal Mode) +;ReduceColorG0 = 0 ;0~31 (for Normal Mode) +;ReduceColorB0 = 0 ;0~31 (for Normal Mode) +;MotionBlur0 = 31 ;0~31 (for Normal Mode) +;DarkEnable1 = 0 ;0:Off, 1:On (for Green Mode) +;ReduceEnable1 = 0 ;0:Off, 1:On (for Green Mode) +;MotionBEnable1 = 0 ;0:Off, 1:Black Fade, 2:, 3:Frame Blend (for Green Mode) +;Dark1 = 10 ;0~10 (for Green Mode) +;ReduceColorR1 = 0 ;0~31 (for Green Mode) +;ReduceColorG1 = 0 ;0~31 (for Green Mode) +;ReduceColorB1 = 0 ;0~31 (for Green Mode) +;MotionBlur1 = 31 ;0~31 (for Green Mode) +;PaletteX = c31,31,31 ;X:0~15, cR,G,B (0~31) +[SaveLimit] +Mode = 12 +Type = 1 +Index = {hex sGameData} +Address = {hex sGameDataEnd} + +;[Fix_print] +;Mode = 1 +;Address = 0x40130 +;Fixcode = a3: 00 00 00 + +[disable_print] +Mode = 1 +Address = {hex @+1} +Fixcode = {PATCH +1} + +;[Fix pokemon] +;Mode = 2 +;Address = 0x1551 +;Type = 3 + +[MiniGame] +Mode = 1 +Address = {HEX @+1} +Fixcode = {PATCH +1} + +[MiniGame-HighScorePC@MiniGameHighScorePC] +Mode = 1 +Address = {HEX @+1} +Fixcode = {PATCH +1} + +[send_send_buf2] +Mode = 2 +Address = {HEX @} +Type = 29 + +[send_send_buf2_ret] +Mode = 2 +Address = {HEX @} +Type = 30 + +[send_byt2] +Mode = 2 +Address = {HEX @+5} +Type = 31 + +[send_byt2_ret] +Mode = 2 +Address = {HEX @} +Type = 32 + +[send_dummy] +Mode = 2 +Address = {HEX @} +Type = 33 + +[send_dummy_end] +Mode = 2 +Address = {HEX @} +Type = 34 + +[FIGHT] +Mode = 1 +Address = {HEX @+1} +Fixcode = {PATCH +1} + +[FIGHT2] +Mode = 1 +Address = {HEX @+1} +Fixcode = {PATCH +1} + +[Network10] +Mode = 1 +Address = {HEX @+1} +Fixcode = {PATCH +1} + +[Network11] +Mode = 1 +Address = {HEX @+1} +Fixcode = {PATCH +1} + +[Network424] +Mode = 2 +Address = {HEX @} +Type = 4 + +[Network200] +Mode = 2 +Address = {HEX @} +Type = 4 + +;[RND_HACK] +;Mode = 1 +;Address = 0x3E70 +;Fixcode = a10: 3E 3D 00 00 00 00 00 00 00 00 + +;[PLAY_HACK] +;Mode = 2 +;Address = 0x2238 +;Type = 5 + + +;[PLAY_MUTE] +;Mode = 1 +;Address = 0x150 +;Fixcode = 0xC9 + +[PLAY_MUTE] +Mode = 1 +Address = {HEX @+1} +Fixcode = {PATCH +1} + +[PLAY_HACK] +Mode = 2 +Address = {HEX @} +Type = 6 + +[PLAY_NEW] +Mode = 2 +Address = {HEX @} +Type = 21 + +[Network_RECHECK] +Mode = 2 +Address = {HEX @} +Type = 7 + +[Network_STOP] +Mode = 2 +Address = {HEX @} +Type = 8 + +[Network_END] +Mode = 2 +Address = {HEX @} +Type = 9 + +[Network_RESET] +Mode = 2 +Address = {HEX @} +Type = 10 + +[linkCable fake begin] +Mode = 2 +Address = {HEX @} +Type = 16 + +[linkCable fake end] +Mode = 2 +Address = {HEX @} +Type = 17 + +[linkCable block input] +Mode = 2 +Address = {HEX @+5} +Type = 18 + +;[save game start] +;Mode = 2 +;Address = 0x5A84 +;Type = 19 + +[save game end] +Mode = 2 +Address = {HEX @} +Type = 20 + +[Change_MSG] +Mode = 1 +Address = {HEX @+1} +Fixcode = {PATCH_ +1 20} + +[fight_ret] +Mode = 1 +Address = {hex @} +Fixcode = {PATCH} + +[fight_ret_c] +Mode = 2 +Address = {hex @} +Type = 98 + +;Mem Write: pc32 = 0xf992d addr = 0xc634 value = 0xe +;Mem Write: pc32 = 0xf9e51 addr = 0xc635 value = 0x1 +[FPA 031801 Begin1] +Mode = 3 +Type = 0 +Address = {hex @} +MotionBEnable0 = 3 +MotionBlur0 = 28 +ConditionType = 0 +ConditionValueA = {dws_ wYellowIntroCurrentScene} +ConditionValueB = {dws_ == } +ConditionValueC = {dws_ 0x0e } + +;34 6F 26 00 +;Mem Write: pc32 = 0xf9e51 addr = 0xc635 value = 0x33 +[FPA 031801 End1] +Mode = 3 +Type = 1 +Address = {hex @} +ConditionType = 0 +ConditionValueA = {dws_ wYellowIntroCurrentScene wYellowIntroSceneTimer} +ConditionValueB = {dws_ == == } +ConditionValueC = {dws_ 0x0e 0x33 } + + + + +;rsm095822 +;Mem Write: pc32 = 0xf9d8b addr = 0xc635 value = 0x28 +;30 +[FPA 031801 Begin2] +Mode = 3 +Type = 0 +Address = {hex @} +MotionBEnable0 = 3 +MotionBlur0 = 4 +ConditionType = 0 +ConditionValueA = {dws_ wYellowIntroCurrentScene} +ConditionValueB = {dws_ == } +ConditionValueC = {dws_ 0x0f } + + +[FPA 031801 End2] +Mode = 3 +Type = 1 +Address = 0xF9e4b +ConditionType = 0 +ConditionValueA = {dws_ wYellowIntroCurrentScene} +ConditionValueB = {dws_ == } +ConditionValueC = {dws_ 0x0f } + +;222222aaaaaaaaaaaaaaaa: pc32 = 0xf984b addr = 0xffb3 value = 0x0 +;000f984bh: F0 B3 E6 0B +[FPA 031801 End3] +Mode = 3 +Type = 1 +Address = {hex @} +ConditionType = 11 +ConditionValueA = {dws_ wYellowIntroCurrentScene 00 wYellowIntroCurrentScene} +ConditionValueB = {dws_ == || == } +ConditionValueC = {dws_ 0x0e 00 0x0f } + + +; The effect_no decide which animation will be played. +; So we use it as a condition value. The address of effect_no is 0xd076 +; a7 c0 3e 05 ea +; and a A7 +; ret nz C0 +; +; ld a,5 3E 05 +; ld (anime_buf),a EA +; +; +;effect_select_rdy: +; ld (effect_no),a +; +; call put_wait +; +; ld a,B_EFFECT_SELECT + + + +;rsm002551 +;Mem Write: pc32 = 0x781c5 addr = 0xd086 value = 0x3 +;17 --13 +;No151 Mega Punch +[FPA 001 Begin] +Mode = 3 +Type = 0 +Address = {hex @} +MotionBEnable0 = 3 +MotionBlur0 = 11 +ConditionType = 11 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wStringBuffer+5 wAnimationID 00 wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID 00 wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == == || == == == == == || == == == == == } +ConditionValueC = {dws_ "M" "E" "G" "A" "P" MEGA_PUNCH 00 "E" "X" "P" "L" MEGA_PUNCH 00 "S" "E" "L" "F" MEGA_PUNCH } + +[FPA 001 End] +Mode = 3 +Type = 1 +Address = {hex @} +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wStringBuffer+5 wAnimationID} +ConditionValueB = {dws_ == == == == == == } +ConditionValueC = {dws_ "M" "E" "G" "A" "P" MEGA_PUNCH } + +;rsm103725 +;Mem Write: pc32 = 0x781c5 addr = 0xd086 value = 0x3 +;No117 Guillotine +[FPA 002 Begin] +Mode = 3 +Type = 0 +Address = {hex @} +MotionBEnable0 = 3 +MotionBlur0 = 7 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "G" "U" "I" "L" GUILLOTINE } + +[FPA 002 End] +Mode = 3 +Type = 1 +Address = {hex @} +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "G" "U" "I" "L" GUILLOTINE } + + +;rsm142438 +;17 --13 +;No150 Mega Kick +[FPA 003 Begin] +Mode = 3 +Type = 0 +Address = {hex @} +MotionBEnable0 = 3 +MotionBlur0 = 10 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wStringBuffer+5 wAnimationID} +ConditionValueB = {dws_ == == == == == == } +ConditionValueC = {dws_ "M" "E" "G" "A" "K" MEGA_KICK } + +[FPA 003 End] +Mode = 3 +Type = 1 +Address = {hex @} +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wStringBuffer+5 wAnimationID} +ConditionValueB = {dws_ == == == == == == } +ConditionValueC = {dws_ "M" "E" "G" "A" "K" MEGA_KICK } + +;rsm154217 blizzard +;No131 +[FPA 004 Begin] +Mode = 3 +Type = 0 +Address = {hex @} +MotionBEnable0 = 3 +MotionBlur0 = 11 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "B" "L" "I" "Z" BLIZZARD } + +[FPA 004 End] +Mode = 3 +Type = 1 +Address = {hex @} +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "B" "L" "I" "Z" BLIZZARD } + + + +;rsm163638 bubblebeam +;No123 +[FPA 005 Begin] +Mode = 3 +Type = 0 +Address = {hex @} +MotionBEnable0 = 3 +MotionBlur0 = 15 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "B" "U" "B" "B" BUBBLEBEAM } + +[FPA 005 End] +Mode = 3 +Type = 1 +Address = {hex @} +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "B" "U" "B" "B" BUBBLEBEAM } + +;Mem Write: pc32 = 0xc2 addr = 0xcfd1 value = 0x3f +;Pallte Write: pc32 =00078e79 00000048 e4 +;No116 hyper beam +[FPA 006 Begin] +Mode = 3 +Type = 0 +Address = {hex @} +MotionBEnable0 = 3 +MotionBlur0 = 6 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wAnimationID} +ConditionValueB = {dws_ == == == == } +ConditionValueC = {dws_ "H" "Y" "P" HYPER_BEAM } + +;rsm112921 thunderbolt +;15 -- 13 +;No57 thunderbolt +[FPA 007 Begin] +Mode = 3 +Type = 0 +Address = {hex @} +MotionBEnable0 = 3 +MotionBlur0 = 13 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "T" "H" "U" "N" THUNDERBOLT } + +[FPA 007 End] +Mode = 3 +Type = 1 +Address = {hex @} +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "T" "H" "U" "N" THUNDERBOLT } +; +;rsm130806 haze +;No40 haze +[FPA 008 Begin] +Mode = 3 +Type = 0 +Address = {hex @} +MotionBEnable0 = 3 +MotionBlur0 = 28 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "H" "A" "Z" "E" HAZE } + +[FPA 008 End] +Mode = 3 +Type = 1 +Address = {hex @} +ConditionType = 11 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID 00 wStringBuffer wStringBuffer+1 wStringBuffer+2 wAnimationID} +ConditionValueB = {dws_ == == == == == || == == == == } +ConditionValueC = {dws_ "H" "A" "Z" "E" HAZE 00 "H" "Y" "P" HYPER_BEAM } + + +;rsm131656 reflect +;No159 +[FPA 009 Begin] +Mode = 3 +Type = 0 +Address = {hex @} +MotionBEnable0 = 3 +MotionBlur0 = 6 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "R" "E" "F" "L" REFLECT } + +[FPA 009 End] +Mode = 3 +Type = 1 +Address = {hex @} +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "R" "E" "F" "L" REFLECT } + +;rsm132935 selfdestruct +;No56 +[FPA 010 Begin] +Mode = 3 +Type = 0 +Address = {hex @} +MotionBEnable0 = 3 +MotionBlur0 = 11 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "S" "E" "L" "F" SELFDESTRUCT} + +[FPA 010 End] +Mode = 3 +Type = 1 +Address = {hex @} +ConditionType = 11 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID 00 wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == || == == == == == } +ConditionValueC = {dws_ "S" "E" "L" "F" MEGA_PUNCH 00 "S" "E" "L" "F" SELFDESTRUCT} + +;rsm013740 explosion +;No76 +[FPA 011 Begin] +Mode = 3 +Type = 0 +Address = {hex @} +MotionBEnable0 = 3 +MotionBlur0 = 11 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "E" "X" "P" "L" EXPLOSION } + +[FPA 011 End] +Mode = 3 +Type = 1 +Address = {hex @} +ConditionType = 11 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID 00 wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == || == == == == == } +ConditionValueC = {dws_ "E" "X" "P" "L" MEGA_PUNCH 00 "E" "X" "P" "L" EXPLOSION } \ No newline at end of file