diff --git a/.gitattributes b/.gitattributes index 48b1085c..e22e2bbb 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 8643ec14..53f35143 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ # compiled roms *.gbc *.gb +*.patch # rgbds extras *.map diff --git a/Makefile b/Makefile index 08abfa12..3a23f759 100644 --- a/Makefile +++ b/Makefile @@ -1,19 +1,27 @@ -roms := pokered.gbc pokeblue.gbc pokeblue_debug.gbc +roms := \ + pokered.gbc \ + pokeblue.gbc \ + pokeblue_debug.gbc +patches := \ + pokered.patch \ + pokeblue.patch rom_obj := \ -audio.o \ -home.o \ -main.o \ -maps.o \ -text.o \ -wram.o \ -gfx/pics.o \ -gfx/sprites.o \ -gfx/tilesets.o + audio.o \ + home.o \ + main.o \ + maps.o \ + text.o \ + wram.o \ + gfx/pics.o \ + gfx/sprites.o \ + gfx/tilesets.o pokered_obj := $(rom_obj:.o=_red.o) pokeblue_obj := $(rom_obj:.o=_blue.o) pokeblue_debug_obj := $(rom_obj:.o=_blue_debug.o) +pokered_vc_obj := $(rom_obj:.o=_red_vc.o) +pokeblue_vc_obj := $(rom_obj:.o=_blue_vc.o) ### Build tools @@ -43,15 +51,34 @@ all: $(roms) red: pokered.gbc blue: pokeblue.gbc blue_debug: pokeblue_debug.gbc +red_vc: pokered.patch +blue_vc: pokeblue.patch clean: tidy - find gfx \( -iname '*.1bpp' -o -iname '*.2bpp' -o -iname '*.pic' \) -delete + find gfx \ + \( -iname '*.1bpp' \ + -o -iname '*.2bpp' \ + -o -iname '*.pic' \) \ + -delete tidy: - $(RM) $(roms) $(pokered_obj) $(pokeblue_obj) $(pokeblue_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) \ + $(pokered_obj) \ + $(pokeblue_obj) \ + $(pokered_vc_obj) \ + $(pokeblue_vc_obj) \ + $(pokeblue_debug_obj) \ + rgbdscheck.o $(MAKE) clean -C tools/ -compare: $(roms) +compare: $(roms) $(patches) @$(SHA1) -c roms.sha1 tools: @@ -67,6 +94,11 @@ endif $(pokered_obj): RGBASMFLAGS += -D _RED $(pokeblue_obj): RGBASMFLAGS += -D _BLUE $(pokeblue_debug_obj): RGBASMFLAGS += -D _BLUE -D _DEBUG +$(pokered_vc_obj): RGBASMFLAGS += -D _RED -D _RED_VC +$(pokeblue_vc_obj): RGBASMFLAGS += -D _BLUE -D _BLUE_VC + +%.patch: vc/%.constants.sym %_vc.gbc %.gbc vc/%.patch.template + tools/make_patch $*_vc.sym $^ $@ rgbdscheck.o: rgbdscheck.asm $(RGBASM) -o $@ $< @@ -89,6 +121,12 @@ $(info $(shell $(MAKE) -C tools)) $(foreach obj, $(pokered_obj), $(eval $(call DEP,$(obj),$(obj:_red.o=.asm)))) $(foreach obj, $(pokeblue_obj), $(eval $(call DEP,$(obj),$(obj:_blue.o=.asm)))) $(foreach obj, $(pokeblue_debug_obj), $(eval $(call DEP,$(obj),$(obj:_blue_debug.o=.asm)))) +$(foreach obj, $(pokered_vc_obj), $(eval $(call DEP,$(obj),$(obj:_red_vc.o=.asm)))) +$(foreach obj, $(pokeblue_vc_obj), $(eval $(call DEP,$(obj),$(obj:_blue_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 @@ -98,11 +136,15 @@ endif pokered_pad = 0x00 pokeblue_pad = 0x00 +pokered_vc_pad = 0x00 +pokeblue_vc_pad = 0x00 pokeblue_debug_pad = 0xff pokered_opt = -jsv -n 0 -k 01 -l 0x33 -m 0x13 -r 03 -t "POKEMON RED" pokeblue_opt = -jsv -n 0 -k 01 -l 0x33 -m 0x13 -r 03 -t "POKEMON BLUE" pokeblue_debug_opt = -jsv -n 0 -k 01 -l 0x33 -m 0x13 -r 03 -t "POKEMON BLUE" +pokered_vc_opt = -jsv -n 0 -k 01 -l 0x33 -m 0x13 -r 03 -t "POKEMON RED" +pokeblue_vc_opt = -jsv -n 0 -k 01 -l 0x33 -m 0x13 -r 03 -t "POKEMON BLUE" %.gbc: $$(%_obj) layout.link $(RGBLINK) -p $($*_pad) -d -m $*.map -n $*.sym -l layout.link -o $@ $(filter %.o,$^) diff --git a/README.md b/README.md index bfffd4e9..950b8b99 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ It builds the following ROMs: - Pokemon Red (UE) [S][!].gb `sha1: ea9bcae617fdf159b045185467ae58b2e4a48b9a` - Pokemon Blue (UE) [S][!].gb `sha1: d7037c83e1ae5b39bde3c30787637ba1d4c48ce2` - BLUEMONS.GB (debug build) `sha1: 5b1456177671b79b263c614ea0e7cc9ac542e9c4` +- dmgapae0.e69.patch `sha1: 0fb5f743696adfe1dbb2e062111f08f9bc5a293a` +- dmgapee0.e68.patch `sha1: ed4be94dc29c64271942c87f2157bca9ca1019c7` To set up the repository, see [**INSTALL.md**](INSTALL.md). diff --git a/data/text/text_4.asm b/data/text/text_4.asm index 6a472dc2..60ec7abe 100644 --- a/data/text/text_4.asm +++ b/data/text/text_4.asm @@ -211,9 +211,19 @@ _CableClubNPCPleaseWaitText:: text_end _CableClubNPCLinkClosedBecauseOfInactivityText:: + vc_patch Change_MSG +IF DEF(_RED_VC) || DEF(_BLUE_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 8f113adc..f1dd3d4e 100644 --- a/engine/battle/animations.asm +++ b/engine/battle/animations.asm @@ -176,8 +176,12 @@ PlayAnimation: ld h, [hl] ld l, a .animationLoop + vc_hook FPA_Thunderbolt_End ld a, [hli] + vc_hook_red FPA_007_End + vc_hook_blue FPA_009_End cp -1 + vc_hook_blue FPA_008_End jr z, .AnimationOver cp FIRST_SE_ID ; is this subanimation or a special effect? jr c, .playSubanimation @@ -246,37 +250,55 @@ PlayAnimation: ld a, [wAnimPalette] ldh [rOBP0], a call LoadAnimationTileset + vc_hook FPA_001_Begin call LoadSubanimation call PlaySubanimation + vc_hook FPA_001_End pop af + vc_hook_red FPA_008_End ldh [rOBP0], a .nextAnimationCommand + vc_hook FPA_005_End pop hl + vc_hook FPA_002_End jr .animationLoop .AnimationOver ret LoadSubanimation: + vc_hook FPA_002_Begin ld a, [wSubAnimAddrPtr + 1] + vc_hook FPA_003_Begin ld h, a + vc_hook_red FPA_131_Begin ld a, [wSubAnimAddrPtr] + vc_hook_red FPA_56_Begin ld l, a ld a, [hli] ld e, a + vc_hook FPA_76_Begin ld a, [hl] + vc_hook FPA_Thunderbolt_Begin ld d, a ; de = address of subanimation ld a, [de] + vc_hook_blue FPA_012_Begin ld b, a + vc_hook FPA_Spore_Begin and %00011111 + vc_hook FPA_Bubblebeam_Begin ld [wSubAnimCounter], a ; number of frame blocks + vc_hook_red FPA_010_Begin + vc_hook_blue FPA_009_Begin ld a, b and %11100000 cp SUBANIMTYPE_ENEMY << 5 + vc_hook_blue FPA_004_Begin jr nz, .isNotType5 .isType5 call GetSubanimationTransform2 jr .saveTransformation .isNotType5 + vc_hook FPA_Hyper_Beam_Begin call GetSubanimationTransform1 .saveTransformation ; place the upper 3 bits of a into bits 0-2 of a before storing @@ -307,6 +329,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_Reflect_Begin ld b, a ldh a, [hWhoseTurn] and a @@ -399,11 +422,15 @@ MoveAnimation: jr nz, .animationsDisabled call ShareMoveAnimations call PlayAnimation + vc_hook_red FPA_004_End + vc_hook_blue FPA_011_End jr .next4 .animationsDisabled ld c, 30 call DelayFrames .next4 + vc_hook_red FPA_010_End + vc_hook_blue FPA_012_End call PlayApplyingAttackAnimation ; shake the screen or flash the pic in and out (to show damage) .animationFinished call WaitForSoundToFinish @@ -541,6 +568,7 @@ SetAnimationPalette: .notSGB ld a, $e4 ld [wAnimPalette], a + vc_hook FPA_Dream_Eater_Begin ldh [rOBP0], a ld a, $6c ldh [rOBP1], a @@ -956,6 +984,7 @@ AnimationFlashScreenLong: ld [wFlashScreenLongCounter], a pop hl jr nz, .loop + vc_hook_red FPA_phy_End ret ; BG palettes diff --git a/engine/battle/core.asm b/engine/battle/core.asm index 4a166f7e..e7711707 100644 --- a/engine/battle/core.asm +++ b/engine/battle/core.asm @@ -3023,6 +3023,7 @@ LinkBattleExchangeData: ld a, b .doExchange ld [wSerialExchangeNybbleSendData], a + vc_hook send_byt2 callfar PrintWaitingText .syncLoop1 call Serial_ExchangeNybble @@ -3030,18 +3031,33 @@ LinkBattleExchangeData: ld a, [wSerialExchangeNybbleReceiveData] inc a jr z, .syncLoop1 + vc_hook send_byt2_ret + vc_patch FIGHT +IF DEF(_RED_VC) || DEF(_BLUE_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(_RED_VC) || DEF(_BLUE_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: @@ -6661,7 +6677,14 @@ BattleRandom: ld a, [hl] pop bc pop hl + vc_hook fight_ret_c + vc_patch fight_ret +IF DEF(_RED_VC) || DEF(_BLUE_VC) + ret +ELSE ret c +ENDC + vc_patch_end ; if we picked the last seed, we need to recalculate the nine seeds push hl @@ -6726,7 +6749,9 @@ HandleExplodingAnimation: PlayMoveAnimation: ld [wAnimationID], a + vc_hook_red FPA_conf_Begin call Delay3 + vc_hook_red FPA_phy_Begin predef_jump MoveAnimation InitBattle:: diff --git a/engine/gfx/palettes.asm b/engine/gfx/palettes.asm index a43c66ba..d26d82a6 100644 --- a/engine/gfx/palettes.asm +++ b/engine/gfx/palettes.asm @@ -484,6 +484,7 @@ CheckSGB: ldh a, [rJOYP] ldh a, [rJOYP] call Wait7000 + vc_hook Network_RESET call Wait7000 ld a, $30 ldh [rJOYP], a diff --git a/engine/link/cable_club.asm b/engine/link/cable_club.asm index cabc19ab..296900a6 100644 --- a/engine/link/cable_club.asm +++ b/engine/link/cable_club.asm @@ -124,18 +124,21 @@ CableClub_DoBattleOrTradeAgain: ld hl, wSerialRandomNumberListBlock ld de, wSerialOtherGameboyRandomNumberListBlock ld bc, $11 + vc_hook Network17 call Serial_ExchangeBytes ld a, SERIAL_NO_DATA_BYTE ld [de], a 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 @@ -859,6 +862,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 594adf08..e2a9bc88 100644 --- a/engine/link/cable_club_npc.asm +++ b/engine/link/cable_club_npc.asm @@ -27,6 +27,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 @@ -54,6 +55,7 @@ CableClubNPC:: ld a, [wCurrentMenuItem] and a jr nz, .choseNo + vc_hook linkCable_block_input callfar SaveSAVtoSRAM call WaitForSoundToFinish ld a, SFX_SAVE @@ -66,8 +68,10 @@ CableClubNPC:: xor a ld [hl], a ldh [hSerialReceivedNewData], a + vc_hook linkCable_fake_end ld [wSerialExchangeNybbleSendData], a call Serial_SyncAndExchangeNybble + vc_hook Network_RECHECK ld hl, wUnknownSerialCounter ld a, [hli] inc a diff --git a/engine/menus/main_menu.asm b/engine/menus/main_menu.asm index 7e4cc9be..0708c0ce 100644 --- a/engine/menus/main_menu.asm +++ b/engine/menus/main_menu.asm @@ -284,9 +284,11 @@ LinkMenu: .choseCancel xor a ld [wMenuJoypadPollCount], a + vc_hook Network_STOP call Delay3 call CloseLinkConnection ld hl, LinkCanceledText + vc_hook Network_END call PrintText ld hl, wd72e res 6, [hl] diff --git a/engine/menus/save.asm b/engine/menus/save.asm index 83a5ca9b..fdf69765 100644 --- a/engine/menus/save.asm +++ b/engine/menus/save.asm @@ -37,20 +37,23 @@ LoadSAV0: ld a, $1 ld [MBC1SRamBankingMode], a 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 @@ -84,11 +87,11 @@ LoadSAV1: ld a, $1 ld [MBC1SRamBankingMode], a 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 @@ -104,11 +107,11 @@ LoadSAV2: ld a, $1 ld [MBC1SRamBankingMode], a 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 @@ -219,8 +222,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 xor a @@ -239,8 +242,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 xor a @@ -262,8 +265,8 @@ SaveSAVtoSRAM2: ld de, sMainData ld bc, wPokedexSeenEnd - wPokedexOwned call CopyData - ld hl, sPlayerName - ld bc, sMainDataCheckSum - sPlayerName + ld hl, sGameData + ld bc, sGameDataEnd - sGameData call SAVCheckSum ld [sMainDataCheckSum], a xor a @@ -612,8 +615,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/home/serial.asm b/home/serial.asm index bd984d4f..7fb6b82c 100644 --- a/home/serial.asm +++ b/home/serial.asm @@ -230,6 +230,7 @@ Serial_PrintWaitingTextAndSyncAndExchangeNybble:: jp LoadScreenTilesFromBuffer1 Serial_SyncAndExchangeNybble:: + vc_hook send_send_buf2 ld a, $ff ld [wSerialExchangeNybbleReceiveData], a .loop1 @@ -253,13 +254,25 @@ Serial_SyncAndExchangeNybble:: ld a, [wSerialExchangeNybbleReceiveData] inc a jr z, .loop1 + vc_patch Network10 +IF DEF(_RED_VC) || DEF(_BLUE_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(_RED_VC) || DEF(_BLUE_VC) + ld b, 26 +ELSE ld b, 10 +ENDC + vc_patch_end .loop3 call DelayFrame call Serial_SendZeroByte @@ -267,6 +280,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 4eacddaf..466cc1ad 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 00000000..0d6b8db0 --- /dev/null +++ b/macros/vc.asm @@ -0,0 +1,39 @@ +vc_hook: MACRO +IF DEF(_RED_VC) || DEF(_BLUE_VC) +.VC_\1:: +ENDC +ENDM + +vc_hook_red: MACRO +IF DEF(_RED_VC) +.VC_\1:: +ENDC +ENDM + +vc_hook_blue: MACRO +IF DEF(_BLUE_VC) +.VC_\1:: +ENDC +ENDM + +vc_patch: MACRO +IF DEF(_RED_VC) || DEF(_BLUE_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(_RED_VC) || DEF(_BLUE_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(_RED_VC) || DEF(_BLUE_VC) + ASSERT \# +ENDC +ENDM diff --git a/roms.sha1 b/roms.sha1 index 44e94aaf..65ba4d28 100644 --- a/roms.sha1 +++ b/roms.sha1 @@ -1,3 +1,5 @@ ea9bcae617fdf159b045185467ae58b2e4a48b9a *pokered.gbc d7037c83e1ae5b39bde3c30787637ba1d4c48ce2 *pokeblue.gbc 5b1456177671b79b263c614ea0e7cc9ac542e9c4 *pokeblue_debug.gbc +0fb5f743696adfe1dbb2e062111f08f9bc5a293a *pokered.patch +ed4be94dc29c64271942c87f2157bca9ca1019c7 *pokeblue.patch diff --git a/sram.asm b/sram.asm index 776610c5..68451791 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 967af106..7955375e 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -1,3 +1,4 @@ -scan_includes gfx +make_patch pkmncompress +scan_includes diff --git a/tools/Makefile b/tools/Makefile index f30f8ef7..f00a0486 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -5,6 +5,7 @@ CFLAGS := -O3 -flto -std=c11 -Wall -Wextra -pedantic tools := \ gfx \ + make_patch \ pkmncompress \ scan_includes diff --git a/tools/make_patch.c b/tools/make_patch.c new file mode 100644 index 00000000..29d798f6 --- /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/pokeblue.constants.asm b/vc/pokeblue.constants.asm new file mode 100644 index 00000000..a197afae --- /dev/null +++ b/vc/pokeblue.constants.asm @@ -0,0 +1,71 @@ +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 "S" + vc_const "L" + vc_const "F" + vc_const "X" + vc_const MEGA_PUNCH + +; [FPA 001 End] + vc_const EXPLOSION + +; [FPA 002 Begin] + vc_const "U" + vc_const "I" + vc_const GUILLOTINE + +; [FPA 002 End] + 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 005 End] + vc_const HYPER_BEAM + +; [FPA 006 Begin] + vc_const "H" + vc_const "Y" + +; [FPA 007 Begin] + vc_const "T" + vc_const "N" + vc_const THUNDERBOLT + +; [FPA 008 Begin] + vc_const "R" + vc_const REFLECT + +; [FPA 009 Begin] + vc_const SELFDESTRUCT + +; [FPA 010 Begin] + vc_const "D" + vc_const DREAM_EATER + +; [FPA 011 Begin] + vc_const "O" + vc_const SPORE + +; [FPA 012 Begin] + vc_const "C" + vc_const ROCK_SLIDE diff --git a/vc/pokeblue.patch.template b/vc/pokeblue.patch.template new file mode 100644 index 00000000..2a8bb119 --- /dev/null +++ b/vc/pokeblue.patch.template @@ -0,0 +1,436 @@ +;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} + +[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} + +[Network17] +Mode = 2 +Address = {HEX @} +Type = 5 + +[Network424] +Mode = 2 +Address = {HEX @} +Type = 4 + +[Network200] +Mode = 2 +Address = {HEX @} +Type = 4 + +[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 = 0x59E6 +;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 + +;rsm003758 +;No151 +[FPA 001 Begin] +Mode = 3 +Type = 0 +Address = {HEX @} +MotionBEnable0 = 3 +MotionBlur0 = 27 +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 "S" "E" "L" "F" MEGA_PUNCH 00 "E" "X" "P" "L" MEGA_PUNCH } + +[FPA 001 End] +Mode = 3 +Type = 1 +Address = {HEX @} +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" EXPLOSION 00 "E" "X" "P" "L" MEGA_PUNCH } + + + +;rsm141151 +;No117 +[FPA 002 Begin] +Mode = 3 +Type = 0 +Address = {HEX @} +MotionBEnable0 = 3 +MotionBlur0 = 9 +MotionBEnable1 = 3 +MotionBlur1 = 8 +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 = 11 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID 00 wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wStringBuffer+5 wAnimationID} +ConditionValueB = {dws_ == == == == == || == == == == == == } +ConditionValueC = {dws_ "G" "U" "I" "L" GUILLOTINE 00 "M" "E" "G" "A" "K" MEGA_KICK } + + + +;rsm143918 +;No150 +[FPA 003 Begin] +Mode = 3 +Type = 0 +Address = {HEX @} +MotionBEnable0 = 3 +MotionBlur0 = 25 +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 } + + + +;rsm152422 +;No131 +[FPA 004 Begin] +Mode = 3 +Type = 0 +Address = {HEX @} +MotionBEnable0 = 3 +MotionBlur0 = 25 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "B" "L" "I" "Z" BLIZZARD } + + +;rsm160334 +;No123 +[FPA 005 Begin@FPA_Bubblebeam_Begin] +Mode = 3 +Type = 0 +Address = {hex @} +MotionBEnable0 = 3 +MotionBlur0 = 27 +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 = 11 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID 00 wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID 00 wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == || == == == == == || == == == == == } +ConditionValueC = {dws_ "B" "U" "B" "B" BUBBLEBEAM 00 "H" "Y" "P" "E" HYPER_BEAM 00 "B" "L" "I" "Z" BLIZZARD } + + +;rsm163356 +;No116 +[FPA 006 Begin@FPA_Hyper_Beam_Begin] +Mode = 3 +Type = 0 +Address = {HEX @} +MotionBEnable0 = 1 +MotionBlur0 = 5 +MotionBEnable1 = 1 +MotionBlur1 = 5 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "H" "Y" "P" "E" HYPER_BEAM } + + + +;rsm174631 +;No57 +[FPA 007 Begin@FPA_Thunderbolt_Begin] +Mode = 3 +Type = 0 +Address = {HEX @} +MotionBEnable0 = 3 +MotionBlur0 = 30 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "T" "H" "U" "N" THUNDERBOLT } + + +[FPA 007 End@FPA_Thunderbolt_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 } + + +;rsm134518 +;No159 +[FPA 008 Begin@FPA_Reflect_Begin] +Mode = 3 +Type = 0 +Address = {hex @} +MotionBEnable0 = 1 +MotionBlur0 = 5 +MotionBEnable1 = 1 +MotionBlur1 = 5 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "R" "E" "F" "L" REFLECT } + +[FPA 008 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 } + + + + +;rsm140510 +;No56 +[FPA 009 Begin] +Mode = 3 +Type = 0 +Address = {HEX @} +MotionBEnable0 = 3 +MotionBlur0 = 27 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "S" "E" "L" "F" SELFDESTRUCT} + +[FPA 009 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} + +;rsm150211 +;No156 +[FPA 010 Begin@FPA_Dream_Eater_Begin] +Mode = 3 +Type = 0 +Address = {hex @} +MotionBEnable0 = 3 +MotionBlur0 = 10 +MotionBEnable1 = 3 +MotionBlur1 = 7 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "D" "R" "E" "A" DREAM_EATER } + +;[FPA 010 End] +;Mode = 3 +;Type = 1 +;Address = 0x78176 +;ConditionType = 0 +;ConditionValueA = a10: 4b cf 4c cf 4d cf 4e cf 7c d0 +;ConditionValueB = a10: 00 00 00 00 00 00 00 00 00 00 +;ConditionValueC = a10: 83 00 91 00 84 00 80 00 8a 00 + +;rsm163334 +;No36 +[FPA 011 Begin@FPA_Spore_Begin] +Mode = 3 +Type = 0 +Address = {HEX @} +MotionBEnable0 = 3 +MotionBlur0 = 8 +MotionBEnable1 = 3 +MotionBlur1 = 8 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "S" "P" "O" "R" SPORE } + +[FPA 011 End] +Mode = 3 +Type = 1 +Address = {hex @} +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "S" "P" "O" "R" SPORE } + +;rsm012224 +;No12 +[FPA 012 Begin] +Mode = 3 +Type = 0 +Address = {HEX @} +MotionBEnable0 = 3 +MotionBlur0 = 27 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "R" "O" "C" "K" ROCK_SLIDE } + +[FPA 012 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_ "R" "O" "C" "K" ROCK_SLIDE 00 "D" "R" "E" "A" DREAM_EATER } + + + + + +;explosion +;No76 +[FPA 76 Begin] +Mode = 3 +Type = 0 +Address = 0x78186 +MotionBEnable0 = 3 +MotionBlur0 = 28 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "E" "X" "P" "L" EXPLOSION } \ No newline at end of file diff --git a/vc/pokered.constants.asm b/vc/pokered.constants.asm new file mode 100644 index 00000000..4bc62388 --- /dev/null +++ b/vc/pokered.constants.asm @@ -0,0 +1,74 @@ +INCLUDE "constants.asm" + +; These are all the asm constants needed to make the red_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 "S" + vc_const "L" + vc_const "F" + vc_const "D" + vc_const "X" + 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 BUBBLEBEAM + +; [FPA 005 Begin] + vc_const "H" + vc_const "Y" + vc_const HYPER_BEAM + +; [FPA 006 Begin] + vc_const "T" + vc_const "N" + vc_const THUNDERBOLT + +; [FPA 007 Begin] + vc_const "R" + vc_const "F" + vc_const REFLECT + +; [FPA 008 Begin] + vc_const DREAM_EATER + +; [FPA 008 End] + vc_const "Z" + vc_const BLIZZARD + +; [FPA 009 Begin] + vc_const "O" + vc_const SPORE + +; [FPA 010 Begin] + vc_const "C" + vc_const ROCK_SLIDE + +; [FPA 010 End] + vc_const SELFDESTRUCT + vc_const EXPLOSION + +; [FPA conf Begin] + vc_const CONFUSION + +; [FPA phy Begin] + vc_const PSYCHIC_M diff --git a/vc/pokered.patch.template b/vc/pokered.patch.template new file mode 100644 index 00000000..52f5d0fe --- /dev/null +++ b/vc/pokered.patch.template @@ -0,0 +1,482 @@ +;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 pokemon] +;Mode = 2 +;Address = 0x1551 +;Type = 3 + +[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} + +[Network17] +Mode = 2 +Address = {HEX @} +Type = 5 + +[Network424] +Mode = 2 +Address = {HEX @} +Type = 4 + +[Network200] +Mode = 2 +Address = {HEX @} +Type = 4 + +[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 = 0x59E6 +;Type = 19 + +[save game end] +Mode = 2 +Address = {HEX @} +Type = 20 + +;93 A7 A4 7F AB A8 AD AA 7F A7 +;at 93 +[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 + +; The effect_no decide which animation will be played. +; So we use it as a condition value. The address of effect_no is 0xd07c +; 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 + + +;rsm033659 +;no151 mega punch +[FPA 001 Begin] +Mode = 3 +Type = 0 +Address = {HEX @} +MotionBEnable0 = 3 +MotionBlur0 = 21 +MotionBEnable1 = 3 +MotionBlur1 = 21 +ConditionType = 11 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wStringBuffer+5 wAnimationID 00 wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wStringBuffer+4 wAnimationID 00 wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == == || == == == == == == || == == == == == } +ConditionValueC = {dws_ "M" "E" "G" "A" "P" MEGA_PUNCH 00 "S" "E" "L" "F" "D" MEGA_PUNCH 00 "E" "X" "P" "L" 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 } + + +;rsm032916 +;no117 guillotine +[FPA 002 Begin] +Mode = 3 +Type = 0 +Address = {HEX @} +MotionBEnable0 = 3 +MotionBlur0 = 9 +MotionBEnable1 = 3 +MotionBlur1 = 8 +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 } + + +;rsm041307 +;no150 mega kick +[FPA 003 Begin] +Mode = 3 +Type = 0 +Address = {HEX @} +MotionBEnable0 = 3 +MotionBlur0 = 25 +MotionBEnable1 = 3 +MotionBlur1 = 21 +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 } + + +;rsm001929 +;no123 bubble beam +[FPA 004 Begin@FPA_Bubblebeam_Begin] +Mode = 3 +Type = 0 +Address = {hex @} +MotionBEnable0 = 3 +MotionBlur0 = 30 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "B" "U" "B" "B" BUBBLEBEAM } + +[FPA 004 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 wStringBuffer+5 wAnimationID} +ConditionValueB = {dws_ == == == == == || == == == == == == } +ConditionValueC = {dws_ "B" "U" "B" "B" BUBBLEBEAM 00 "M" "E" "G" "A" "K" MEGA_KICK } + + +;rsm103658 +;no116 hyper beam +[FPA 005 Begin@FPA_Hyper_Beam_Begin] +Mode = 3 +Type = 0 +Address = {HEX @} +MotionBEnable0 = 1 +MotionBlur0 = 5 +MotionBEnable1 = 1 +MotionBlur1 = 5 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "H" "Y" "P" "E" HYPER_BEAM } + +[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_ "H" "Y" "P" "E" HYPER_BEAM } + + +;rsm133358 +;no57 thunderbolt +[FPA 006 Begin@FPA_Thunderbolt_Begin] +Mode = 3 +Type = 0 +Address = {HEX @} +MotionBEnable0 = 3 +MotionBlur0 = 30 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "T" "H" "U" "N" THUNDERBOLT } + + +[FPA 006 End@FPA_Thunderbolt_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 } + +;rsm152340 +;no159 reflect +[FPA 007 Begin@FPA_Reflect_Begin] +Mode = 3 +Type = 0 +Address = {hex @} +MotionBEnable0 = 1 +MotionBlur0 = 6 +MotionBEnable1 = 1 +MotionBlur1 = 5 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "R" "E" "F" "L" REFLECT } + +[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_ "R" "E" "F" "L" REFLECT } + +;rsm171812 +;no156 dream eater +[FPA 008 Begin@FPA_Dream_Eater_Begin] +Mode = 3 +Type = 0 +Address = {hex @} +MotionBEnable0 = 3 +MotionBlur0 = 10 +MotionBEnable1 = 3 +MotionBlur1 = 7 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "D" "R" "E" "A" DREAM_EATER } + +[FPA 008 End] +Mode = 3 +Type = 1 +Address = {HEX @} +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wStringBuffer+4} {dws/ wAnimationID} +ConditionValueB = {dws_ == == == == == } {dws/ == } +ConditionValueC = {dws_ "B" "L" "I" "Z" "Z" } {dws/ BLIZZARD } + +;rsm174650 +;no36 spore +[FPA 009 Begin@FPA_Spore_Begin] +Mode = 3 +Type = 0 +Address = {HEX @} +MotionBEnable0 = 3 +MotionBlur0 = 8 +MotionBEnable1 = 3 +MotionBlur1 = 8 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "S" "P" "O" "R" SPORE } + + +;rsm152115 +;no12 rock slide +[FPA 010 Begin] +Mode = 3 +Type = 0 +Address = {HEX @} +MotionBEnable0 = 3 +MotionBlur0 = 27 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID} +ConditionValueB = {dws_ == == == == == } +ConditionValueC = {dws_ "R" "O" "C" "K" ROCK_SLIDE } + +[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 00 wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID 00 wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wStringBuffer+4 wAnimationID 00 wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wStringBuffer+4 wAnimationID 00 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_ "D" "R" "E" "A" DREAM_EATER 00 "R" "O" "C" "K" ROCK_SLIDE 00 "S" "P" "O" "R" SPORE 00 "S" "E" "L" "F" "D" MEGA_PUNCH 00 "S" "E" "L" "F" "D" SELFDESTRUCT 00 "C" "O" "N" "F" "S" CONFUSION 00 "E" "X" "P" "L" EXPLOSION 00 "E" "X" "P" "L" MEGA_PUNCH } + + +;explosion +;No76 explosion +[FPA 76 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_ "E" "X" "P" "L" EXPLOSION } + + +;No56 self-destruct +[FPA 56 Begin] +Mode = 3 +Type = 0 +Address = {HEX @} +MotionBEnable0 = 3 +MotionBlur0 = 23 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wStringBuffer+4 wAnimationID} +ConditionValueB = {dws_ == == == == == == } +ConditionValueC = {dws_ "S" "E" "L" "F" "D" SELFDESTRUCT} + + +;No131 blizzard +[FPA 131 Begin] +Mode = 3 +Type = 0 +Address = {HEX @} +MotionBEnable0 = 3 +MotionBlur0 = 26 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wStringBuffer+4 wAnimationID} +ConditionValueB = {dws_ == == == == == == } +ConditionValueC = {dws_ "B" "L" "I" "Z" "Z" BLIZZARD } + + +;confusion +[FPA conf Begin] +Mode = 3 +Type = 0 +Address = {hex @} +MotionBEnable1 = 3 +MotionBlur1 = 21 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wStringBuffer+5 wAnimationID} +ConditionValueB = {dws_ == == == == == == } +ConditionValueC = {dws_ "C" "O" "N" "F" "S" CONFUSION } + +;phychic +[FPA phy Begin] +Mode = 3 +Type = 0 +Address = {hex @} +MotionBEnable1 = 3 +MotionBlur1 = 21 +ConditionType = 0 +ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wStringBuffer+5 wAnimationID} +ConditionValueB = {dws_ == == == == == == } +ConditionValueC = {dws_ "P" "S" "Y" "C" "I" PSYCHIC_M } + +[FPA phy 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_ "P" "S" "Y" "C" "I" PSYCHIC_M } + +