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)
--- 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 }
+
+