Build the Virtual Console patch with make yellow_vc (#80)

This commit is contained in:
vulcandth 2022-03-26 21:05:06 -05:00 committed by GitHub
parent a178c57f02
commit fbaa5c9d4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 1288 additions and 42 deletions

3
.gitattributes vendored
View File

@ -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

1
.gitignore vendored
View File

@ -12,6 +12,7 @@
# compiled roms
*.gbc
*.gb
*.patch
# rgbds extras
*.map

View File

@ -1,19 +1,24 @@
roms := pokeyellow.gbc pokeyellow_debug.gbc
roms := \
pokeyellow.gbc \
pokeyellow_debug.gbc
patches := \
pokeyellow.patch
rom_obj := \
audio.o \
home.o \
main.o \
maps.o \
text.o \
wram.o \
gfx/pics.o \
gfx/pikachu.o \
gfx/sprites.o \
gfx/tilesets.o
audio.o \
home.o \
main.o \
maps.o \
text.o \
wram.o \
gfx/pics.o \
gfx/pikachu.o \
gfx/sprites.o \
gfx/tilesets.o
pokeyellow_obj := $(rom_obj)
pokeyellow_debug_obj := $(rom_obj:.o=_debug.o)
pokeyellow_vc_obj := $(rom_obj:.o=_vc.o)
### Build tools
@ -42,16 +47,34 @@ RGBLINK ?= $(RGBDS)rgblink
all: $(roms)
yellow: pokeyellow.gbc
yellow_debug: pokeyellow_debug.gbc
yellow_vc: pokeyellow.patch
clean: tidy
find gfx \( -iname '*.1bpp' -o -iname '*.2bpp' -o -iname '*.pic' \) -delete
find audio/pikachu_cries \( -iname '*.pcm' \) -delete
find gfx \
\( -iname '*.1bpp' \
-o -iname '*.2bpp' \
-o -iname '*.pic' \) \
-delete
find audio/pikachu_cries \
\( -iname '*.pcm' \) \
-delete
tidy:
$(RM) $(roms) $(pokeyellow_obj) $(pokeyellow_debug_obj) $(roms:.gbc=.map) $(roms:.gbc=.sym) rgbdscheck.o
$(RM) $(roms) \
$(roms:.gbc=.sym) \
$(roms:.gbc=.map) \
$(patches) \
$(patches:.patch=_vc.gbc) \
$(patches:.patch=_vc.sym) \
$(patches:.patch=_vc.map) \
$(patches:%.patch=vc/%.constants.sym) \
$(pokeyellow_obj) \
$(pokeyellow_vc_obj) \
$(pokeyellow_debug_obj) \
rgbdscheck.o
$(MAKE) clean -C tools/
compare: $(roms)
compare: $(roms) $(patches)
@$(SHA1) -c roms.sha1
tools:
@ -65,6 +88,10 @@ RGBASMFLAGS += -E
endif
$(pokeyellow_debug_obj): RGBASMFLAGS += -D _DEBUG
$(pokeyellow_vc_obj): RGBASMFLAGS += -D _YELLOW_VC
%.patch: vc/%.constants.sym %_vc.gbc %.gbc vc/%.patch.template
tools/make_patch $*_vc.sym $^ $@
rgbdscheck.o: rgbdscheck.asm
$(RGBASM) -o $@ $<
@ -86,6 +113,11 @@ $(info $(shell $(MAKE) -C tools))
# Dependencies for objects
$(foreach obj, $(pokeyellow_obj), $(eval $(call DEP,$(obj),$(obj:.o=.asm))))
$(foreach obj, $(pokeyellow_debug_obj), $(eval $(call DEP,$(obj),$(obj:_debug.o=.asm))))
$(foreach obj, $(pokeyellow_vc_obj), $(eval $(call DEP,$(obj),$(obj:_vc.o=.asm))))
# Dependencies for VC files that need to run scan_includes
%.constants.sym: %.constants.asm $(shell tools/scan_includes %.constants.asm) | rgbdscheck.o
$(RGBASM) $< > $@
endif
@ -95,6 +127,7 @@ endif
pokeyellow_pad = 0x00
pokeyellow_debug_pad = 0xff
pokeyellow_vc_pad = 0x00
opts = -cjsv -k 01 -l 0x33 -m 0x1b -p 0 -r 03 -t "POKEMON YELLOW"

View File

@ -6,6 +6,7 @@ It builds the following ROMs:
- Pokemon Yellow (UE) [C][!].gbc `sha1: cc7d03262ebfaf2f06772c1a480c7d9d5f4a38e1`
- YELLMONS.GB (debug build) `sha1: d44e96eddfbdad633cbe4e6e64915e9e198974b0`
- Dmgapse0.h08.patch `sha1: f3346a5559d52c296b8feab0cdbbfb0e250ac161`
To set up the repository, see [**INSTALL.md**](INSTALL.md).

View File

@ -540,7 +540,7 @@ INCLUDE "audio/music/yellowunusedsong.asm"
INCLUDE "audio/music/meetjessiejames.asm"
IF !DEF(_DEBUG)
INCBIN "garbage/bank20.bin"
INCBIN "garbage/bank20.bin"
ENDC

View File

@ -301,7 +301,7 @@ PikaPicAnimBGFrames_33:
pikaframeend
PikaPicAnimBGFrames_34:
pikaframedelay 12
pikaframedelay 12
pikaframe PikaAnimTilemap_9, 12
pikaframedelay 12
pikaframe PikaAnimTilemap_9, 100

View File

@ -3,9 +3,19 @@ _CableClubNPCPleaseWaitText::
text_end
_CableClubNPCLinkClosedBecauseOfInactivityText::
vc_patch Change_MSG
IF DEF(_YELLOW_VC)
text "Please come again!"
done
text_start
text "sed because of"
cont "inactivity."
ELSE
text "The link has been"
line "closed because of"
cont "inactivity."
ENDC
vc_patch_end
para "Please contact"
line "your friend and"

View File

@ -203,7 +203,9 @@ PlayAnimation:
ld h, [hl]
ld l, a
.animationLoop
vc_hook FPA_005_End
ld a, [hli]
vc_hook FPA_001_End
cp -1
jr z, .AnimationOver
cp FIRST_SE_ID ; is this subanimation or a special effect?
@ -277,12 +279,17 @@ PlayAnimation:
call LoadSubanimation
call PlaySubanimation
pop af
vc_hook FPA_007_End
ldh [rOBP0], a
vc_hook FPA_011_End
call UpdateGBCPal_OBP0
.nextAnimationCommand
vc_hook FPA_002_End
pop hl
vc_hook FPA_003_End
jr .animationLoop
.AnimationOver
vc_hook FPA_004_End
ret
LoadSubanimation:
@ -294,18 +301,26 @@ LoadSubanimation:
ld e, a
ld a, [hl]
ld d, a ; de = address of subanimation
vc_hook FPA_005_Begin
ld a, [de]
vc_hook FPA_003_Begin
ld b, a
vc_hook FPA_002_Begin
and %00011111
vc_hook FPA_001_Begin
ld [wSubAnimCounter], a ; number of frame blocks
vc_hook FPA_004_Begin
ld a, b
vc_hook FPA_007_Begin
and %11100000
cp SUBANIMTYPE_ENEMY << 5
vc_hook FPA_009_Begin
jr nz, .isNotType5
.isType5
call GetSubanimationTransform2
jr .saveTransformation
.isNotType5
vc_hook FPA_010_Begin
call GetSubanimationTransform1
.saveTransformation
; place the upper 3 bits of a into bits 0-2 of a before storing
@ -336,6 +351,7 @@ LoadSubanimation:
; sets the transform to SUBANIMTYPE_NORMAL if it's the player's turn
; sets the transform to the subanimation type if it's the enemy's turn
GetSubanimationTransform1:
vc_hook FPA_011_Begin
ld b, a
ldh a, [hWhoseTurn]
and a
@ -428,10 +444,12 @@ MoveAnimation:
ld c, 30
call DelayFrames
.next4
vc_hook FPA_009_End
call PlayApplyingAttackAnimation ; shake the screen or flash the pic in and out (to show damage)
.animationFinished
call WaitForSoundToFinish
xor a
vc_hook FPA_008_End
ld [wSubAnimSubEntryAddr], a
ld [wUnusedD09B], a
ld [wSubAnimTransform], a
@ -469,6 +487,7 @@ ShareMoveAnimations:
PlayApplyingAttackAnimation:
; Generic animation that shows after the move's individual animation
; Different animation depending on whether the move has an additional effect and on whose turn it is
vc_hook FPA_010_End
ld a, [wAnimationType]
and a
ret z
@ -558,8 +577,10 @@ SetAnimationPalette:
ld b, $f0
.next
ld a, b
vc_hook FPA_006_Begin
ldh [rOBP0], a
ld a, $6c
vc_hook FPA_008_Begin
ldh [rOBP1], a
call UpdateGBCPal_OBP0
call UpdateGBCPal_OBP1

View File

@ -3192,6 +3192,7 @@ LinkBattleExchangeData:
ld a, b
.doExchange
ld [wSerialExchangeNybbleSendData], a
vc_hook send_byt2
callfar PrintWaitingText
.syncLoop1
call Serial_ExchangeNybble
@ -3199,18 +3200,33 @@ LinkBattleExchangeData:
ld a, [wSerialExchangeNybbleReceiveData]
inc a
jr z, .syncLoop1
vc_hook send_byt2_ret
vc_patch FIGHT
IF DEF(_YELLOW_VC)
ld b, 26
ELSE
ld b, 10
ENDC
vc_patch_end
.syncLoop2
call DelayFrame
call Serial_ExchangeNybble
dec b
jr nz, .syncLoop2
vc_hook send_dummy
vc_patch FIGHT2
IF DEF(_YELLOW_VC)
ld b, 26
ELSE
ld b, 10
ENDC
vc_patch_end
.syncLoop3
call DelayFrame
call Serial_SendZeroByte
dec b
jr nz, .syncLoop3
vc_hook send_dummy_end
ret
ExecutePlayerMove:
@ -6844,7 +6860,14 @@ BattleRandom:
ld a, [hl]
pop bc
pop hl
vc_hook fight_ret_c
vc_patch fight_ret
IF DEF(_YELLOW_VC)
ret
ELSE
ret c
ENDC
vc_patch_end
; if we picked the last seed, we need to recalculate the nine seeds
push hl

View File

@ -657,6 +657,7 @@ CheckSGB:
SendMltReq1Packet:
ld hl, MltReq1Packet
call SendSGBPacket
vc_hook Network_RESET
jp Wait7000
CopyGfxToSuperNintendoVRAM:

View File

@ -130,12 +130,14 @@ CableClub_DoBattleOrTradeAgain:
ld hl, wSerialPlayerDataBlock
ld de, wSerialEnemyDataBlock
ld bc, $1a8
vc_hook Network424
call Serial_ExchangeBytes
ld a, SERIAL_NO_DATA_BYTE
ld [de], a
ld hl, wSerialPartyMonsPatchList
ld de, wSerialEnemyMonsPatchList
ld bc, $c8
vc_hook Network200
call Serial_ExchangeBytes
ld a, (1 << SERIAL) | (1 << TIMER) | (1 << VBLANK)
ldh [rIE], a
@ -866,6 +868,7 @@ TradeCenter_Trade:
ld de, TradeCompleted
call PlaceString
predef SaveSAVtoSRAM2
vc_hook save_game_end
ld c, 50
call DelayFrames
xor a

View File

@ -30,6 +30,7 @@ CableClubNPC::
xor a
ldh [hSerialReceiveData], a
ld a, START_TRANSFER_EXTERNAL_CLOCK
vc_hook linkCable_fake_begin
ldh [rSC], a
ld a, [wLinkTimeoutCounter]
dec a
@ -57,6 +58,7 @@ CableClubNPC::
ld a, [wCurrentMenuItem]
and a
jr nz, .choseNo
vc_hook linkCable_block_input
callfar SaveSAVtoSRAM
call WaitForSoundToFinish
ld a, SFX_SAVE
@ -70,7 +72,9 @@ CableClubNPC::
ld [hl], a
ldh [hSerialReceivedNewData], a
ld [wSerialExchangeNybbleSendData], a
vc_hook linkCable_fake_end
call Serial_SyncAndExchangeNybble
vc_hook Network_RECHECK
ld hl, wUnknownSerialCounter
ld a, [hli]
inc a

View File

@ -794,9 +794,11 @@ LinkMenu:
call Delay3
callfar CloseLinkConnection
ld hl, ColosseumCanceledText
vc_hook Network_STOP
call PrintText
ld hl, wd72e
res 6, [hl]
vc_hook Network_END
ret
.asm_f5963

View File

@ -111,7 +111,13 @@ HandlePokedexSideMenu:
dec a
jr z, .choseArea
dec a
vc_patch disable_print
IF DEF (_YELLOW_VC)
jr z, .handleMenuInput
ELSE
jr z, .chosePrint
ENDC
vc_patch_end
.choseQuit
ld b, 1
.exitSideMenu

View File

@ -35,20 +35,23 @@ LoadSAV0:
call EnableSRAMAndLatchClockData
ld a, $1
ld [MBC1SRamBank], a
ld hl, sPlayerName ; hero name located in SRAM
ld bc, sMainDataCheckSum - sPlayerName ; but here checks the full SAV
; This vc_hook does not have to be in any particular location.
; It is defined here because it refers to the same labels as the two lines below.
vc_hook SaveLimit
ld hl, sGameData
ld bc, sGameDataEnd - sGameData
call SAVCheckSum
ld c, a
ld a, [sMainDataCheckSum] ; SAV's checksum
ld a, [sMainDataCheckSum]
cp c
jp z, .checkSumsMatched
; If the computed checksum didn't match the saved on, try again.
ld hl, sPlayerName
ld bc, sMainDataCheckSum - sPlayerName
ld hl, sGameData
ld bc, sGameDataEnd - sGameData
call SAVCheckSum
ld c, a
ld a, [sMainDataCheckSum] ; SAV's checksum
ld a, [sMainDataCheckSum]
cp c
jp nz, SAVBadCheckSum
@ -80,11 +83,11 @@ LoadSAV1:
call EnableSRAMAndLatchClockData
ld a, $1
ld [MBC1SRamBank], a
ld hl, sPlayerName ; hero name located in SRAM
ld bc, sMainDataCheckSum - sPlayerName ; but here checks the full SAV
ld hl, sGameData
ld bc, sGameDataEnd - sGameData
call SAVCheckSum
ld c, a
ld a, [sMainDataCheckSum] ; SAV's checksum
ld a, [sMainDataCheckSum]
cp c
jr nz, SAVBadCheckSum
ld hl, sCurBoxData
@ -98,11 +101,11 @@ LoadSAV2:
call EnableSRAMAndLatchClockData
ld a, $1
ld [MBC1SRamBank], a
ld hl, sPlayerName ; hero name located in SRAM
ld bc, sMainDataCheckSum - sPlayerName ; but here checks the full SAV
ld hl, sGameData
ld bc, sGameDataEnd - sGameData
call SAVCheckSum
ld c, a
ld a, [sMainDataCheckSum] ; SAV's checksum
ld a, [sMainDataCheckSum]
cp c
jp nz, SAVBadCheckSum
ld hl, sPartyData
@ -213,8 +216,8 @@ SaveSAVtoSRAM0:
call CopyData
ldh a, [hTileAnimations]
ld [sTileAnimations], a
ld hl, sPlayerName
ld bc, sMainDataCheckSum - sPlayerName
ld hl, sGameData
ld bc, sGameDataEnd - sGameData
call SAVCheckSum
ld [sMainDataCheckSum], a
call DisableSRAMAndPrepareClockData
@ -229,8 +232,8 @@ SaveSAVtoSRAM1:
ld de, sCurBoxData
ld bc, wBoxDataEnd - wBoxDataStart
call CopyData
ld hl, sPlayerName
ld bc, sMainDataCheckSum - sPlayerName
ld hl, sGameData
ld bc, sGameDataEnd - sGameData
call SAVCheckSum
ld [sMainDataCheckSum], a
call DisableSRAMAndPrepareClockData
@ -255,8 +258,8 @@ SaveSAVtoSRAM2:
inc de
ld a, [hl]
ld [de], a
ld hl, sPlayerName
ld bc, sMainDataCheckSum - sPlayerName
ld hl, sGameData
ld bc, sGameDataEnd - sGameData
call SAVCheckSum
ld [sMainDataCheckSum], a
call DisableSRAMAndPrepareClockData
@ -584,8 +587,8 @@ SAVCheckRandomID:
ld a, [sPlayerName]
and a
jr z, .next
ld hl, sPlayerName
ld bc, sMainDataCheckSum - sPlayerName
ld hl, sGameData
ld bc, sGameDataEnd - sGameData
call SAVCheckSum
ld c, a
ld a, [sMainDataCheckSum]

View File

@ -30,6 +30,7 @@ PlayIntroScene:
jr .loop
.go_to_title_screen
vc_hook FPA_031801_End3
call YellowIntro_BlankPalettes
xor a
ldh [hLCDCPointer], a
@ -123,6 +124,7 @@ Jumptable_f9906:
YellowIntro_NextScene:
ld hl, wYellowIntroCurrentScene
inc [hl]
vc_hook FPA_031801_Begin1
ret
YellowIntroScene0:
@ -601,6 +603,7 @@ YellowIntroScene14:
call YellowIntro_SpawnAnimatedObjectAndSavePointer
call YellowIntro_NextScene
ld a, $28
vc_hook FPA_031801_Begin2
ld [wYellowIntroSceneTimer], a
ret
@ -705,12 +708,14 @@ YellowIntro_CheckFrameTimerDecrement:
ret
.asm_f9e4b
vc_hook FPA_031801_End2
scf
ret
YellowIntro_LoadDMGPalAndIncrementCounter:
ld hl, wYellowIntroSceneTimer
ld a, [hl]
vc_hook FPA_031801_End1
inc [hl]
ld l, a
ld h, $0

View File

@ -1,4 +1,5 @@
PlayPikachuSoundClip::
vc_hook PLAY_NEW
ld a, e
ld e, a
ld d, $0
@ -39,7 +40,13 @@ PlayPikachuSoundClip::
ld a, l
cp $40 ; end of wave data
jr nz, .saveWaveDataLoop
vc_patch PLAY_MUTE
IF DEF(_YELLOW_VC)
ld a, 0
ELSE
ld a, $80
ENDC
vc_patch_end
ldh [rNR30], a
ldh a, [rNR51]
or $44

View File

@ -1,4 +1,5 @@
PlayPikachuPCM::
vc_hook PLAY_HACK
ldh a, [hLoadedROMBank]
push af
ld a, b

View File

@ -233,6 +233,7 @@ Serial_PrintWaitingTextAndSyncAndExchangeNybble::
jp LoadScreenTilesFromBuffer1
Serial_SyncAndExchangeNybble::
vc_hook send_send_buf2
ld a, $ff
ld [wSerialExchangeNybbleReceiveData], a
.loop1
@ -256,13 +257,25 @@ Serial_SyncAndExchangeNybble::
ld a, [wSerialExchangeNybbleReceiveData]
inc a
jr z, .loop1
vc_patch Network10
IF DEF(_YELLOW_VC)
ld b, 26
ELSE
ld b, 10
ENDC
vc_patch_end
.loop2
call DelayFrame
call Serial_ExchangeNybble
dec b
jr nz, .loop2
vc_patch Network11
IF DEF(_YELLOW_VC)
ld b, 26
ELSE
ld b, 10
ENDC
vc_patch_end
.loop3
call DelayFrame
call Serial_SendZeroByte
@ -270,6 +283,7 @@ Serial_SyncAndExchangeNybble::
jr nz, .loop3
ld a, [wSerialExchangeNybbleReceiveData]
ld [wSerialSyncAndExchangeNybbleReceiveData], a
vc_hook send_send_buf2_ret
ret
Serial_ExchangeNybble::

View File

@ -6,6 +6,7 @@ INCLUDE "macros/data.asm"
INCLUDE "macros/code.asm"
INCLUDE "macros/gfx.asm"
INCLUDE "macros/coords.asm"
INCLUDE "macros/vc.asm"
INCLUDE "macros/scripts/audio.asm"
INCLUDE "macros/scripts/maps.asm"

27
macros/vc.asm Normal file
View File

@ -0,0 +1,27 @@
vc_hook: MACRO
IF DEF(_YELLOW_VC)
.VC_\1::
ENDC
ENDM
vc_patch: MACRO
IF DEF(_YELLOW_VC)
ASSERT !DEF(CURRENT_VC_PATCH), "Already started a vc_patch"
CURRENT_VC_PATCH EQUS "\1"
.VC_{CURRENT_VC_PATCH}::
ENDC
ENDM
vc_patch_end: MACRO
IF DEF(_YELLOW_VC)
ASSERT DEF(CURRENT_VC_PATCH), "No vc_patch started"
.VC_{CURRENT_VC_PATCH}_End::
PURGE CURRENT_VC_PATCH
ENDC
ENDM
vc_assert: MACRO
IF DEF(_YELLOW_VC)
ASSERT \#
ENDC
ENDM

View File

@ -314,7 +314,7 @@ INCLUDE "data/battle_anims/frame_blocks.asm"
SECTION "BG Map Attributes (Debug)", ROMX
IF DEF(_DEBUG)
INCLUDE "engine/gfx/bg_map_attributes.asm"
INCLUDE "engine/gfx/bg_map_attributes.asm"
ENDC
@ -322,7 +322,7 @@ SECTION "BG Map Attributes", ROMX
INCLUDE "data/cgb/bg_map_attributes.asm"
IF !DEF(_DEBUG)
INCLUDE "engine/gfx/bg_map_attributes.asm"
INCLUDE "engine/gfx/bg_map_attributes.asm"
ENDC
@ -330,7 +330,7 @@ SECTION "bank30", ROMX
; This whole bank is garbage data.
IF !DEF(_DEBUG)
INCBIN "garbage/bank30.bin"
INCBIN "garbage/bank30.bin"
ENDC

View File

@ -1,2 +1,3 @@
cc7d03262ebfaf2f06772c1a480c7d9d5f4a38e1 *pokeyellow.gbc
d44e96eddfbdad633cbe4e6e64915e9e198974b0 *pokeyellow_debug.gbc
f3346a5559d52c296b8feab0cdbbfb0e250ac161 *pokeyellow.patch

View File

@ -13,7 +13,13 @@ SummerBeachHouse_TextPointers:
SurfinDudeText:
text_asm
ld a, [wd472]
vc_patch MiniGame
IF DEF (_YELLOW_VC)
bit 7, a
ELSE
bit 6, a
ENDC
vc_patch_end
jr nz, .next
ld hl, .SurfinDudeText4
call PrintText
@ -130,7 +136,13 @@ SummerBeachHouseSign4Text:
ld a, 1
ld [wDoNotWaitForButtonPressAfterDisplayingText], a
ld a, [wd472]
vc_patch MiniGameHighScorePC
IF DEF(_YELLOW_VC)
bit 7, a
ELSE
bit 6, a
ENDC
vc_patch_end
jr z, .asm_f2369
ld hl, wd492

View File

@ -13,12 +13,14 @@ SECTION "Save Data", SRAM
ds $598
sGameData::
sPlayerName:: ds NAME_LENGTH
sMainData:: ds wMainDataEnd - wMainDataStart
sSpriteData:: ds wSpriteDataEnd - wSpriteDataStart
sPartyData:: ds wPartyDataEnd - wPartyDataStart
sCurBoxData:: ds wBoxDataEnd - wBoxDataStart
sTileAnimations:: db
sGameDataEnd::
sMainDataCheckSum:: db

5
tools/.gitignore vendored
View File

@ -1,4 +1,5 @@
scan_includes
gfx
pkmncompress
make_patch
pcm
pkmncompress
scan_includes

View File

@ -5,6 +5,7 @@ CFLAGS := -O3 -flto -std=c11 -Wall -Wextra -pedantic
tools := \
gfx \
make_patch \
pcm \
pkmncompress \
scan_includes

469
tools/make_patch.c Normal file
View File

@ -0,0 +1,469 @@
#define PROGRAM_NAME "make_patch"
#define USAGE_OPTS "labels.sym constants.sym patched.gbc original.gbc vc.patch.template vc.patch"
#include "common.h"
#include <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;
}

View File

@ -0,0 +1,61 @@
INCLUDE "constants.asm"
; These are all the asm constants needed to make the blue_vc patch.
vc_const: MACRO
x = \1
PRINTLN "00:{04x:x} \1" ; same format as rgblink's .sym file
ENDM
; [FPA 001 Begin]
vc_const "M"
vc_const "E"
vc_const "G"
vc_const "A"
vc_const "P"
vc_const "X"
vc_const "L"
vc_const "S"
vc_const "F"
vc_const MEGA_PUNCH
; [FPA 002 Begin]
vc_const "U"
vc_const "I"
vc_const GUILLOTINE
; [FPA 003 Begin]
vc_const "K"
vc_const MEGA_KICK
; [FPA 004 Begin]
vc_const "B"
vc_const "Z"
vc_const BLIZZARD
; [FPA 005 Begin]
vc_const BUBBLEBEAM
; [FPA 006 Begin]
vc_const "H"
vc_const "Y"
vc_const HYPER_BEAM
; [FPA 007 Begin]
vc_const "T"
vc_const "N"
vc_const THUNDERBOLT
; [FPA 008 Begin]
vc_const HAZE
; [FPA 009 Begin]
vc_const "R"
vc_const "F"
vc_const REFLECT
; [FPA 010 Begin]
vc_const SELFDESTRUCT
; [FPA 011 Begin]
vc_const EXPLOSION

View File

@ -0,0 +1,533 @@
;Format Sample
;[xxxx] ;User-defined Name (Max:31 chars)
;Mode = 1 ;1:Fixcode; 2:Fixvalue; 3:Mask; 4:Palette; 5:Double Frame Buffer
;Type = 0 ;0:Begin 1:End
;Index = 0 ;Index
;Address = x1F8000 ;ROM Address
;MemAddress = x2000 ;RAM Address
;Fixcode = 0 ;Mode1: Fixed Rom Code; Mode2: Fixed Value
;DelayFrame = 0 ;Delay Frame
;FadeFrame = 0 ;Fade Frame 0:Off
;DarkEnable0 = 0 ;0:Off, 1:On (for Normal Mode)
;ReduceEnable0 = 0 ;0:Off, 1:On (for Normal Mode)
;MotionBEnable0 = 0 ;0:Off, 1:Black Fade, 2:, 3:Frame Blend (for Normal Mode)
;Dark0 = 10 ;0~10 (for Normal Mode)
;ReduceColorR0 = 0 ;0~31 (for Normal Mode)
;ReduceColorG0 = 0 ;0~31 (for Normal Mode)
;ReduceColorB0 = 0 ;0~31 (for Normal Mode)
;MotionBlur0 = 31 ;0~31 (for Normal Mode)
;DarkEnable1 = 0 ;0:Off, 1:On (for Green Mode)
;ReduceEnable1 = 0 ;0:Off, 1:On (for Green Mode)
;MotionBEnable1 = 0 ;0:Off, 1:Black Fade, 2:, 3:Frame Blend (for Green Mode)
;Dark1 = 10 ;0~10 (for Green Mode)
;ReduceColorR1 = 0 ;0~31 (for Green Mode)
;ReduceColorG1 = 0 ;0~31 (for Green Mode)
;ReduceColorB1 = 0 ;0~31 (for Green Mode)
;MotionBlur1 = 31 ;0~31 (for Green Mode)
;PaletteX = c31,31,31 ;X:0~15, cR,G,B (0~31)
[SaveLimit]
Mode = 12
Type = 1
Index = {hex sGameData}
Address = {hex sGameDataEnd}
;[Fix_print]
;Mode = 1
;Address = 0x40130
;Fixcode = a3: 00 00 00
[disable_print]
Mode = 1
Address = {hex @+1}
Fixcode = {PATCH +1}
;[Fix pokemon]
;Mode = 2
;Address = 0x1551
;Type = 3
[MiniGame]
Mode = 1
Address = {HEX @+1}
Fixcode = {PATCH +1}
[MiniGame-HighScorePC@MiniGameHighScorePC]
Mode = 1
Address = {HEX @+1}
Fixcode = {PATCH +1}
[send_send_buf2]
Mode = 2
Address = {HEX @}
Type = 29
[send_send_buf2_ret]
Mode = 2
Address = {HEX @}
Type = 30
[send_byt2]
Mode = 2
Address = {HEX @+5}
Type = 31
[send_byt2_ret]
Mode = 2
Address = {HEX @}
Type = 32
[send_dummy]
Mode = 2
Address = {HEX @}
Type = 33
[send_dummy_end]
Mode = 2
Address = {HEX @}
Type = 34
[FIGHT]
Mode = 1
Address = {HEX @+1}
Fixcode = {PATCH +1}
[FIGHT2]
Mode = 1
Address = {HEX @+1}
Fixcode = {PATCH +1}
[Network10]
Mode = 1
Address = {HEX @+1}
Fixcode = {PATCH +1}
[Network11]
Mode = 1
Address = {HEX @+1}
Fixcode = {PATCH +1}
[Network424]
Mode = 2
Address = {HEX @}
Type = 4
[Network200]
Mode = 2
Address = {HEX @}
Type = 4
;[RND_HACK]
;Mode = 1
;Address = 0x3E70
;Fixcode = a10: 3E 3D 00 00 00 00 00 00 00 00
;[PLAY_HACK]
;Mode = 2
;Address = 0x2238
;Type = 5
;[PLAY_MUTE]
;Mode = 1
;Address = 0x150
;Fixcode = 0xC9
[PLAY_MUTE]
Mode = 1
Address = {HEX @+1}
Fixcode = {PATCH +1}
[PLAY_HACK]
Mode = 2
Address = {HEX @}
Type = 6
[PLAY_NEW]
Mode = 2
Address = {HEX @}
Type = 21
[Network_RECHECK]
Mode = 2
Address = {HEX @}
Type = 7
[Network_STOP]
Mode = 2
Address = {HEX @}
Type = 8
[Network_END]
Mode = 2
Address = {HEX @}
Type = 9
[Network_RESET]
Mode = 2
Address = {HEX @}
Type = 10
[linkCable fake begin]
Mode = 2
Address = {HEX @}
Type = 16
[linkCable fake end]
Mode = 2
Address = {HEX @}
Type = 17
[linkCable block input]
Mode = 2
Address = {HEX @+5}
Type = 18
;[save game start]
;Mode = 2
;Address = 0x5A84
;Type = 19
[save game end]
Mode = 2
Address = {HEX @}
Type = 20
[Change_MSG]
Mode = 1
Address = {HEX @+1}
Fixcode = {PATCH_ +1 20}
[fight_ret]
Mode = 1
Address = {hex @}
Fixcode = {PATCH}
[fight_ret_c]
Mode = 2
Address = {hex @}
Type = 98
;Mem Write: pc32 = 0xf992d addr = 0xc634 value = 0xe
;Mem Write: pc32 = 0xf9e51 addr = 0xc635 value = 0x1
[FPA 031801 Begin1]
Mode = 3
Type = 0
Address = {hex @}
MotionBEnable0 = 3
MotionBlur0 = 28
ConditionType = 0
ConditionValueA = {dws_ wYellowIntroCurrentScene}
ConditionValueB = {dws_ == }
ConditionValueC = {dws_ 0x0e }
;34 6F 26 00
;Mem Write: pc32 = 0xf9e51 addr = 0xc635 value = 0x33
[FPA 031801 End1]
Mode = 3
Type = 1
Address = {hex @}
ConditionType = 0
ConditionValueA = {dws_ wYellowIntroCurrentScene wYellowIntroSceneTimer}
ConditionValueB = {dws_ == == }
ConditionValueC = {dws_ 0x0e 0x33 }
;rsm095822
;Mem Write: pc32 = 0xf9d8b addr = 0xc635 value = 0x28
;30
[FPA 031801 Begin2]
Mode = 3
Type = 0
Address = {hex @}
MotionBEnable0 = 3
MotionBlur0 = 4
ConditionType = 0
ConditionValueA = {dws_ wYellowIntroCurrentScene}
ConditionValueB = {dws_ == }
ConditionValueC = {dws_ 0x0f }
[FPA 031801 End2]
Mode = 3
Type = 1
Address = 0xF9e4b
ConditionType = 0
ConditionValueA = {dws_ wYellowIntroCurrentScene}
ConditionValueB = {dws_ == }
ConditionValueC = {dws_ 0x0f }
;222222aaaaaaaaaaaaaaaa: pc32 = 0xf984b addr = 0xffb3 value = 0x0
;000f984bh: F0 B3 E6 0B
[FPA 031801 End3]
Mode = 3
Type = 1
Address = {hex @}
ConditionType = 11
ConditionValueA = {dws_ wYellowIntroCurrentScene 00 wYellowIntroCurrentScene}
ConditionValueB = {dws_ == || == }
ConditionValueC = {dws_ 0x0e 00 0x0f }
; The effect_no decide which animation will be played.
; So we use it as a condition value. The address of effect_no is 0xd076
; a7 c0 3e 05 ea
; and a A7
; ret nz C0
;
; ld a,5 3E 05
; ld (anime_buf),a EA
;
;
;effect_select_rdy:
; ld (effect_no),a
;
; call put_wait
;
; ld a,B_EFFECT_SELECT
;rsm002551
;Mem Write: pc32 = 0x781c5 addr = 0xd086 value = 0x3
;17 --13
;No151 Mega Punch
[FPA 001 Begin]
Mode = 3
Type = 0
Address = {hex @}
MotionBEnable0 = 3
MotionBlur0 = 11
ConditionType = 11
ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wStringBuffer+5 wAnimationID 00 wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID 00 wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID}
ConditionValueB = {dws_ == == == == == == || == == == == == || == == == == == }
ConditionValueC = {dws_ "M" "E" "G" "A" "P" MEGA_PUNCH 00 "E" "X" "P" "L" MEGA_PUNCH 00 "S" "E" "L" "F" MEGA_PUNCH }
[FPA 001 End]
Mode = 3
Type = 1
Address = {hex @}
ConditionType = 0
ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wStringBuffer+5 wAnimationID}
ConditionValueB = {dws_ == == == == == == }
ConditionValueC = {dws_ "M" "E" "G" "A" "P" MEGA_PUNCH }
;rsm103725
;Mem Write: pc32 = 0x781c5 addr = 0xd086 value = 0x3
;No117 Guillotine
[FPA 002 Begin]
Mode = 3
Type = 0
Address = {hex @}
MotionBEnable0 = 3
MotionBlur0 = 7
ConditionType = 0
ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID}
ConditionValueB = {dws_ == == == == == }
ConditionValueC = {dws_ "G" "U" "I" "L" GUILLOTINE }
[FPA 002 End]
Mode = 3
Type = 1
Address = {hex @}
ConditionType = 0
ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID}
ConditionValueB = {dws_ == == == == == }
ConditionValueC = {dws_ "G" "U" "I" "L" GUILLOTINE }
;rsm142438
;17 --13
;No150 Mega Kick
[FPA 003 Begin]
Mode = 3
Type = 0
Address = {hex @}
MotionBEnable0 = 3
MotionBlur0 = 10
ConditionType = 0
ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wStringBuffer+5 wAnimationID}
ConditionValueB = {dws_ == == == == == == }
ConditionValueC = {dws_ "M" "E" "G" "A" "K" MEGA_KICK }
[FPA 003 End]
Mode = 3
Type = 1
Address = {hex @}
ConditionType = 0
ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wStringBuffer+5 wAnimationID}
ConditionValueB = {dws_ == == == == == == }
ConditionValueC = {dws_ "M" "E" "G" "A" "K" MEGA_KICK }
;rsm154217 blizzard
;No131
[FPA 004 Begin]
Mode = 3
Type = 0
Address = {hex @}
MotionBEnable0 = 3
MotionBlur0 = 11
ConditionType = 0
ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID}
ConditionValueB = {dws_ == == == == == }
ConditionValueC = {dws_ "B" "L" "I" "Z" BLIZZARD }
[FPA 004 End]
Mode = 3
Type = 1
Address = {hex @}
ConditionType = 0
ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID}
ConditionValueB = {dws_ == == == == == }
ConditionValueC = {dws_ "B" "L" "I" "Z" BLIZZARD }
;rsm163638 bubblebeam
;No123
[FPA 005 Begin]
Mode = 3
Type = 0
Address = {hex @}
MotionBEnable0 = 3
MotionBlur0 = 15
ConditionType = 0
ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID}
ConditionValueB = {dws_ == == == == == }
ConditionValueC = {dws_ "B" "U" "B" "B" BUBBLEBEAM }
[FPA 005 End]
Mode = 3
Type = 1
Address = {hex @}
ConditionType = 0
ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID}
ConditionValueB = {dws_ == == == == == }
ConditionValueC = {dws_ "B" "U" "B" "B" BUBBLEBEAM }
;Mem Write: pc32 = 0xc2 addr = 0xcfd1 value = 0x3f
;Pallte Write: pc32 =00078e79 00000048 e4
;No116 hyper beam
[FPA 006 Begin]
Mode = 3
Type = 0
Address = {hex @}
MotionBEnable0 = 3
MotionBlur0 = 6
ConditionType = 0
ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wAnimationID}
ConditionValueB = {dws_ == == == == }
ConditionValueC = {dws_ "H" "Y" "P" HYPER_BEAM }
;rsm112921 thunderbolt
;15 -- 13
;No57 thunderbolt
[FPA 007 Begin]
Mode = 3
Type = 0
Address = {hex @}
MotionBEnable0 = 3
MotionBlur0 = 13
ConditionType = 0
ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID}
ConditionValueB = {dws_ == == == == == }
ConditionValueC = {dws_ "T" "H" "U" "N" THUNDERBOLT }
[FPA 007 End]
Mode = 3
Type = 1
Address = {hex @}
ConditionType = 0
ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID}
ConditionValueB = {dws_ == == == == == }
ConditionValueC = {dws_ "T" "H" "U" "N" THUNDERBOLT }
;
;rsm130806 haze
;No40 haze
[FPA 008 Begin]
Mode = 3
Type = 0
Address = {hex @}
MotionBEnable0 = 3
MotionBlur0 = 28
ConditionType = 0
ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID}
ConditionValueB = {dws_ == == == == == }
ConditionValueC = {dws_ "H" "A" "Z" "E" HAZE }
[FPA 008 End]
Mode = 3
Type = 1
Address = {hex @}
ConditionType = 11
ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID 00 wStringBuffer wStringBuffer+1 wStringBuffer+2 wAnimationID}
ConditionValueB = {dws_ == == == == == || == == == == }
ConditionValueC = {dws_ "H" "A" "Z" "E" HAZE 00 "H" "Y" "P" HYPER_BEAM }
;rsm131656 reflect
;No159
[FPA 009 Begin]
Mode = 3
Type = 0
Address = {hex @}
MotionBEnable0 = 3
MotionBlur0 = 6
ConditionType = 0
ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID}
ConditionValueB = {dws_ == == == == == }
ConditionValueC = {dws_ "R" "E" "F" "L" REFLECT }
[FPA 009 End]
Mode = 3
Type = 1
Address = {hex @}
ConditionType = 0
ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID}
ConditionValueB = {dws_ == == == == == }
ConditionValueC = {dws_ "R" "E" "F" "L" REFLECT }
;rsm132935 selfdestruct
;No56
[FPA 010 Begin]
Mode = 3
Type = 0
Address = {hex @}
MotionBEnable0 = 3
MotionBlur0 = 11
ConditionType = 0
ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID}
ConditionValueB = {dws_ == == == == == }
ConditionValueC = {dws_ "S" "E" "L" "F" SELFDESTRUCT}
[FPA 010 End]
Mode = 3
Type = 1
Address = {hex @}
ConditionType = 11
ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID 00 wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID}
ConditionValueB = {dws_ == == == == == || == == == == == }
ConditionValueC = {dws_ "S" "E" "L" "F" MEGA_PUNCH 00 "S" "E" "L" "F" SELFDESTRUCT}
;rsm013740 explosion
;No76
[FPA 011 Begin]
Mode = 3
Type = 0
Address = {hex @}
MotionBEnable0 = 3
MotionBlur0 = 11
ConditionType = 0
ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID}
ConditionValueB = {dws_ == == == == == }
ConditionValueC = {dws_ "E" "X" "P" "L" EXPLOSION }
[FPA 011 End]
Mode = 3
Type = 1
Address = {hex @}
ConditionType = 11
ConditionValueA = {dws_ wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID 00 wStringBuffer wStringBuffer+1 wStringBuffer+2 wStringBuffer+3 wAnimationID}
ConditionValueB = {dws_ == == == == == || == == == == == }
ConditionValueC = {dws_ "E" "X" "P" "L" MEGA_PUNCH 00 "E" "X" "P" "L" EXPLOSION }