shithub: pokered

Download patch

ref: fe8d3c51a4056f0dd61dbef332ad9e714b82089a
parent: 07df4a5f88aa5b9927a0f8a7c317afa57a313ab9
author: vulcandth <vulcandth@gmail.com>
date: Sat Mar 26 11:59:36 EDT 2022

Build the Virtual Console patches with `make red_vc` and `make blue_vc`  (#351)


diff: cannot open b/vc//null: file does not exist: 'b/vc//null'
--- 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
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@
 # compiled roms
 *.gbc
 *.gb
+*.patch
 
 # rgbds extras
 *.map
--- 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 @@
 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,7 +94,12 @@
 $(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,7 +121,13 @@
 $(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 @@
 
 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,$^)
--- a/README.md
+++ b/README.md
@@ -7,6 +7,8 @@
 - 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).
 
--- a/data/text/text_4.asm
+++ b/data/text/text_4.asm
@@ -211,9 +211,19 @@
 	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"
--- a/engine/battle/animations.asm
+++ b/engine/battle/animations.asm
@@ -176,8 +176,12 @@
 	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 @@
 	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 @@
 ; 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 @@
 	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 @@
 .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 @@
 	ld [wFlashScreenLongCounter], a
 	pop hl
 	jr nz, .loop
+	vc_hook_red FPA_phy_End
 	ret
 
 ; BG palettes
--- a/engine/battle/core.asm
+++ b/engine/battle/core.asm
@@ -3023,6 +3023,7 @@
 	ld a, b
 .doExchange
 	ld [wSerialExchangeNybbleSendData], a
+	vc_hook send_byt2
 	callfar PrintWaitingText
 .syncLoop1
 	call Serial_ExchangeNybble
@@ -3030,18 +3031,33 @@
 	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 @@
 	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 @@
 
 PlayMoveAnimation:
 	ld [wAnimationID], a
+	vc_hook_red FPA_conf_Begin
 	call Delay3
+	vc_hook_red FPA_phy_Begin
 	predef_jump MoveAnimation
 
 InitBattle::
--- a/engine/gfx/palettes.asm
+++ b/engine/gfx/palettes.asm
@@ -484,6 +484,7 @@
 	ldh a, [rJOYP]
 	ldh a, [rJOYP]
 	call Wait7000
+	vc_hook Network_RESET
 	call Wait7000
 	ld a, $30
 	ldh [rJOYP], a
--- a/engine/link/cable_club.asm
+++ b/engine/link/cable_club.asm
@@ -124,6 +124,7 @@
 	ld hl, wSerialRandomNumberListBlock
 	ld de, wSerialOtherGameboyRandomNumberListBlock
 	ld bc, $11
+	vc_hook Network17
 	call Serial_ExchangeBytes
 	ld a, SERIAL_NO_DATA_BYTE
 	ld [de], a
@@ -130,6 +131,7 @@
 	ld hl, wSerialPlayerDataBlock
 	ld de, wSerialEnemyDataBlock
 	ld bc, $1a8
+	vc_hook Network424
 	call Serial_ExchangeBytes
 	ld a, SERIAL_NO_DATA_BYTE
 	ld [de], a
@@ -136,6 +138,7 @@
 	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 @@
 	ld de, TradeCompleted
 	call PlaceString
 	predef SaveSAVtoSRAM2
+	vc_hook save_game_end
 	ld c, 50
 	call DelayFrames
 	xor a
--- a/engine/link/cable_club_npc.asm
+++ b/engine/link/cable_club_npc.asm
@@ -27,6 +27,7 @@
 	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 @@
 	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 @@
 	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
--- a/engine/menus/main_menu.asm
+++ b/engine/menus/main_menu.asm
@@ -284,9 +284,11 @@
 .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]
--- a/engine/menus/save.asm
+++ b/engine/menus/save.asm
@@ -37,20 +37,23 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 	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]
--- a/home/serial.asm
+++ b/home/serial.asm
@@ -230,6 +230,7 @@
 	jp LoadScreenTilesFromBuffer1
 
 Serial_SyncAndExchangeNybble::
+	vc_hook send_send_buf2
 	ld a, $ff
 	ld [wSerialExchangeNybbleReceiveData], a
 .loop1
@@ -253,13 +254,25 @@
 	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 @@
 	jr nz, .loop3
 	ld a, [wSerialExchangeNybbleReceiveData]
 	ld [wSerialSyncAndExchangeNybbleReceiveData], a
+	vc_hook send_send_buf2_ret
 	ret
 
 Serial_ExchangeNybble::
--- a/macros.asm
+++ b/macros.asm
@@ -6,6 +6,7 @@
 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"
--- /dev/null
+++ b/macros/vc.asm
@@ -1,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
--- 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
--- a/sram.asm
+++ b/sram.asm
@@ -13,6 +13,7 @@
 
 	ds $598
 
+sGameData::
 sPlayerName::  ds NAME_LENGTH
 sMainData::    ds wMainDataEnd - wMainDataStart
 sSpriteData::  ds wSpriteDataEnd - wSpriteDataStart
@@ -19,6 +20,7 @@
 sPartyData::   ds wPartyDataEnd - wPartyDataStart
 sCurBoxData::  ds wBoxDataEnd - wBoxDataStart
 sTileAnimations:: db
+sGameDataEnd::
 sMainDataCheckSum:: db
 
 
--- a/tools/.gitignore
+++ b/tools/.gitignore
@@ -1,3 +1,4 @@
-scan_includes
 gfx
+make_patch
 pkmncompress
+scan_includes
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -5,6 +5,7 @@
 
 tools := \
 	gfx \
+	make_patch \
 	pkmncompress \
 	scan_includes
 
--- /dev/null
+++ b/tools/make_patch.c
@@ -1,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 <ctype.h>
+
+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;
+}
--- /dev/null
+++ b/vc/pokeblue.constants.asm
@@ -1,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
--- /dev/null
+++ b/vc/pokeblue.patch.template
@@ -1,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
--- /dev/null
+++ b/vc/pokered.constants.asm
@@ -1,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
--- /dev/null
+++ b/vc/pokered.patch.template
@@ -1,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   }   
+
+