From 31c3c94d64e1ac1e40c95acfda7de8b99b4f302b Mon Sep 17 00:00:00 2001 From: vulcandth Date: Sat, 12 Mar 2022 17:34:04 -0600 Subject: [PATCH] Build the Virtual Console patch with `make crystal11_vc` (#882) Fixes #813 --- .gitattributes | 3 + .gitignore | 1 + Makefile | 55 +- README.md | 1 + docs/index.md | 1 + docs/vc_patch.md | 124 +++++ engine/battle/battle_transition.asm | 6 + engine/battle/core.asm | 1 + engine/battle_anims/anim_commands.asm | 9 + engine/events/print_unown.asm | 6 + engine/gfx/color.asm | 1 + engine/link/link.asm | 49 ++ engine/link/mystery_gift.asm | 31 ++ engine/menus/menu.asm | 2 + engine/menus/save.asm | 9 + engine/overworld/scripting.asm | 1 + engine/pokedex/pokedex.asm | 1 + engine/pokemon/mail_2.asm | 6 + home/serial.asm | 14 + macros.asm | 1 + macros/vc.asm | 27 + mobile/mobile_40.asm | 16 + roms.sha1 | 1 + tools/.gitignore | 7 +- tools/Makefile | 1 + tools/make_patch.c | 434 ++++++++++++++++ vc/pokecrystal11.constants.asm | 40 ++ vc/pokecrystal11.patch.template | 698 ++++++++++++++++++++++++++ 28 files changed, 1538 insertions(+), 8 deletions(-) create mode 100644 docs/vc_patch.md create mode 100644 macros/vc.asm create mode 100644 tools/make_patch.c create mode 100644 vc/pokecrystal11.constants.asm create mode 100644 vc/pokecrystal11.patch.template diff --git a/.gitattributes b/.gitattributes index 3cf462d95..12b953460 100644 --- a/.gitattributes +++ b/.gitattributes @@ -28,6 +28,9 @@ *.attrmap binary diff=hex *.tilemap binary diff=hex +# Declare files that will always have CRLF line endings on checkout. +*.patch.template text eol=crlf linguist-language=INI + # these are generated but just in case *.lz binary diff=hex *.2bpp binary diff=hex diff --git a/.gitignore b/.gitignore index ec594cd9d..99c530f41 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ gfx/pokemon/*/frames.asm # compiled roms *.gbc *.gb +*.patch # rgbds extras *.map diff --git a/Makefile b/Makefile index a4d7d3cab..6ee999c8f 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,9 @@ -roms := pokecrystal.gbc pokecrystal11.gbc pokecrystal_au.gbc pokecrystal_debug.gbc pokecrystal11_debug.gbc +roms := pokecrystal.gbc \ + pokecrystal11.gbc \ + pokecrystal_au.gbc \ + pokecrystal_debug.gbc \ + pokecrystal11_debug.gbc +patches := pokecrystal11.patch rom_obj := \ audio.o \ @@ -23,6 +28,7 @@ pokecrystal11_obj := $(rom_obj:.o=11.o) pokecrystal_au_obj := $(rom_obj:.o=_au.o) pokecrystal_debug_obj := $(rom_obj:.o=_debug.o) pokecrystal11_debug_obj := $(rom_obj:.o=11_debug.o) +pokecrystal11_vc_obj := $(rom_obj:.o=11_vc.o) ### Build tools @@ -54,16 +60,42 @@ crystal11: pokecrystal11.gbc crystal_au: pokecrystal_au.gbc crystal_debug: pokecrystal_debug.gbc crystal11_debug: pokecrystal11_debug.gbc +crystal11_vc: pokecrystal11.patch clean: tidy - find gfx \( -name "*.[12]bpp" -o -name "*.lz" -o -name "*.gbcpal" -o -name "*.sgb.tilemap" \) -delete - find gfx/pokemon -mindepth 1 ! -path "gfx/pokemon/unown/*" \( -name "bitmask.asm" -o -name "frames.asm" -o -name "front.animated.tilemap" -o -name "front.dimensions" \) -delete + find gfx \ + \( -name "*.[12]bpp" \ + -o -name "*.lz" \ + -o -name "*.gbcpal" \ + -o -name "*.sgb.tilemap" \) \ + -delete + find gfx/pokemon -mindepth 1 \ + ! -path "gfx/pokemon/unown/*" \ + \( -name "bitmask.asm" \ + -o -name "frames.asm" \ + -o -name "front.animated.tilemap" \ + -o -name "front.dimensions" \) \ + -delete tidy: - $(RM) $(roms) $(pokecrystal_obj) $(pokecrystal11_obj) $(pokecrystal_au_obj) $(pokecrystal_debug_obj) $(pokecrystal11_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) \ + $(pokecrystal_obj) \ + $(pokecrystal11_obj) \ + $(pokecrystal11_vc_obj) \ + $(pokecrystal_au_obj) \ + $(pokecrystal_debug_obj) \ + $(pokecrystal11_debug_obj) \ + rgbdscheck.o $(MAKE) clean -C tools/ -compare: $(roms) +compare: $(roms) $(patches) @$(SHA1) -c roms.sha1 tools: @@ -81,6 +113,12 @@ $(pokecrystal11_obj): RGBASMFLAGS += -D _CRYSTAL11 $(pokecrystal_au_obj): RGBASMFLAGS += -D _CRYSTAL11 -D _CRYSTAL_AU $(pokecrystal_debug_obj): RGBASMFLAGS += -D _DEBUG $(pokecrystal11_debug_obj): RGBASMFLAGS += -D _CRYSTAL11 -D _DEBUG +$(pokecrystal11_vc_obj): RGBASMFLAGS += -D _CRYSTAL11 -D _CRYSTAL11_VC + +%.patch: %_vc.sym vc/%.constants.sym %_vc.gbc %.gbc vc/%.patch.template + tools/make_patch $^ $@ + +%.sym: ; rgbdscheck.o: rgbdscheck.asm $(RGBASM) -o $@ $< @@ -105,6 +143,11 @@ $(foreach obj, $(pokecrystal11_obj), $(eval $(call DEP,$(obj),$(obj:11.o=.asm))) $(foreach obj, $(pokecrystal_au_obj), $(eval $(call DEP,$(obj),$(obj:_au.o=.asm)))) $(foreach obj, $(pokecrystal_debug_obj), $(eval $(call DEP,$(obj),$(obj:_debug.o=.asm)))) $(foreach obj, $(pokecrystal11_debug_obj), $(eval $(call DEP,$(obj),$(obj:11_debug.o=.asm)))) +$(foreach obj, $(pokecrystal11_vc_obj), $(eval $(call DEP,$(obj),$(obj:11_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 @@ -114,10 +157,12 @@ pokecrystal11_opt = -Cjv -t PM_CRYSTAL -i BYTE -n 1 -k 01 -l 0x33 -m 0x10 pokecrystal_au_opt = -Cjv -t PM_CRYSTAL -i BYTU -n 0 -k 01 -l 0x33 -m 0x10 -r 3 -p 0 pokecrystal_debug_opt = -Cjv -t PM_CRYSTAL -i BYTE -n 0 -k 01 -l 0x33 -m 0x10 -r 3 -p 0 pokecrystal11_debug_opt = -Cjv -t PM_CRYSTAL -i BYTE -n 1 -k 01 -l 0x33 -m 0x10 -r 3 -p 0 +pokecrystal11_vc_opt = -Cjv -t PM_CRYSTAL -i BYTE -n 1 -k 01 -l 0x33 -m 0x10 -r 3 -p 0 pokecrystal_base = us pokecrystal11_base = us pokecrystal_au_base = us +pokecrystal11_vc_base = us pokecrystal_debug_base = dbg pokecrystal11_debug_base = dbg diff --git a/README.md b/README.md index 1ce7c5a17..3b3bd6713 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ It builds the following ROMs: - Pokemon - Crystal Version (A) [C][!].gbc `sha1: a0fc810f1d4e124434f7be2c989ab5b5892ddf36` - CRYSTAL_ps3_010328d.bin `sha1: c60d57a24bbe8ecf7cba54ab3f90669f97bd330d` - CRYSTAL_ps3_us_revise_010710d.bin `sha1: 391ae86b1d5a26db712ffe6c28bbf2a1f804c3c4` +- CGBBYTE1.784.patch `sha1: a25517f60ca0e887d39ec698aa56a0040532a4b3` To set up the repository, see [INSTALL.md](INSTALL.md). diff --git a/docs/index.md b/docs/index.md index 1e1b7ad8d..649060d3c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -21,6 +21,7 @@ These pages are for documenting pieces of the [pokecrystal](https://github.com/p - [battle_anim_commands.md](battle_anim_commands.md) - [move_effect_commands.md](move_effect_commands.md) - [music_commands.md](music_commands.md) +- [vc_patch.md](vc_patch.md) ## Other subsystems diff --git a/docs/vc_patch.md b/docs/vc_patch.md new file mode 100644 index 000000000..55aa2f11d --- /dev/null +++ b/docs/vc_patch.md @@ -0,0 +1,124 @@ +# Nintendo 2DS/3DS Virtual Console Patch + +The Nintendo Virtual Console is an emulator on the 2DS and 3DS consoles. It can emulate the Game Boy Color (among other consoles), while applying enhancements or modifications to some games, such as replacing Link Cable functionality with the DS' Wireless Link capabilities, or disabling Game Boy Printer features. + +Game-specific enhancements are determined by a `.patch` file corresponding to the `.gbc` ROM file. These files are bundled together in a `.cia` file; creating such a file is outside the scope of this project. + + +## Build pokecrystal11.patch + +To build **pokecrystal11.patch**: + +```bash +make crystal11_vc +``` + +This will also create two ROM files, **pokecrystal11.gbc** and **pokecrystal11_vc.gbc**. The pokecrystal11_vc.gbc file has the patches already applied to it; do *not* use this file! The ROM file and patch file must share the same name, so use pokecrystal11.patch together with pokecrystal11.gbc. + + +## Custom files + +There are a few files involved with building the `.patch` file, in addition to the ones used for building ROMs. + +### vc/pokecrystal11.patch.template + +The `.patch.template` file is the basis for the `.patch` file. Many numeric values in the `.patch` file are derived from the values of labels, constants, and ROM content; these values are abstracted into *commands* that get evaluated by `tools/make_patch` to output symbolic names as their actual values, formatted to match the original `.patch` file. + +### vc/pokecrystal11.constants.asm + +The `.constants.asm` file is used to create a `.constants.sym` file. Typical `.sym` files only list the values of *labels* (ROM banks and addresses); this file is used to list *constants* that are needed by the `.patch.template`. Any constants that the `.patch.template` refers to must be explicitly printed here with the `vc_const` macro. + +### tools/make_patch.c + +The program used to convert a `.patch.template` into a `.patch` file. + +To convert `vc.patch.template` into `vc.patch`: + +```bash +tools/make_patch labels.sym constants.sym patched.gbc original.gbc vc.patch.template vc.patch +``` + +For example, this is what `make crystal11_vc` does: + +```bash +tools/make_patch pokecrystal11_vc.sym vc/pokecrystal11.constants.sym pokecrystal11_vc.gbc pokecrystal11.gbc vc/pokecrystal11.patch.template pokecrystal11.patch +``` + +## Patch types + +**Hooks** do not directly modify the ROM; they just identify locations within the ROM code. When the emulated code execution reaches a hook, the emulator performs an emulation function. For example, the `BiographySave_ret` hook is located after the code to add a new Hall of Fame entry, and causes the emulator to edit the save file to enable the GS Ball event. + +Hooks are defined with the `vc_hook` macro, which defines a label starting with "`.VC_`" for the patch template file to use. + +**Patches** directly modify the contents of the ROM. This is done before emulation begins. For example, the `print forbid 1` patch modifies an "`and A_BUTTON`" instruction to "`and 0`", so pressing A will not print Unown on the Game Boy Printer. + +Patches are defined with the `vc_patch` and `vc_patch_end` macros; `vc_patch` defines a label starting with "`.VC_`", `vc_patch_end` defines a corresponding label with "`_End`" appended. Between these two macros, the code or data is conditionally different depending on whether or not a patch file is being built. + +The sole purpose of creating `pokecrystal11_vc.gbc` and `pokecrystal11_vc.sym` is to make these labels and modifications available to `make_patch` for use in the patch template. + + +## Patch template syntax + +**Comments** start at a semicolon "`;`" and continue until the end of the line. They are output as-is, without interpreting commands. + +**Patch names** are contained in "`[`" brackets "`]`". They are output as-is, without interpreting commands. + +Patch names also set the **current patch label**. This is the label starting with "`.VC_`" followed by the patch name, with any invalid characters (not letters "`A-Z`", digits "`0-9`", underscore "`_`", at sign "`@`", or hash "`#`") converted to underscores "`_`". These labels are conditionally defined only when building the patch file with the `vc_hook` and `vc_patch` macros. For example, the patch name "`[fight begin]`" corresponds to the patch label "`.VC_fight_begin`", generated by the "`vc_hook fight_begin`" macro. + +**Commands** are contained in "`{`" braces "`}`". They are not output themselves, but may produce their own output when interpreted. + +Commands are interpreted with a series of arguments, separated by whitespace (spaces, tabs, or newlines). Leading and trailing whitespace is ignored; for example, "`{ hex @ 4 }`" is interpreted the same as "`{hex @ 4}`". + +Some commands may output a **value series**, which is a series of two-digit hexadecimal bytes separated by spaces, preceded by a decimal count: "a*N*: v1 v2 [...] vN". + +Some command names have variants to allow reproducing the exact formatting in a `.patch` file. If the command name is all lowercase, the output byte values use lowercase for hexadecimal digits A-F; if it is all uppercase, they use uppercase. For commands which output a value series, if the command name ends in an underscore, a space is output after the colon preceding the values; if not, then it is not. + +**Arguments** evaluate to numeric values. They may be any of the following: + +- Literal numbers in decimal (base 10, e.g. "`42`"), hexadecimal (base 16, e.g. "`0x2a`"), or octal (base 8, e.g. "`052`"). They may start with a plus sign "`+`". Numbers may not be negative. +- Comparison operators: "`==`" is 0, "`>`" is 1, "`<`" is 2, "`>=`" is 3, "`<=`" is 4, "`!=`" is 5, and "`||`" is 0x11. +- Symbol names from the two `.sym` files provided to `make_patch` may evaluate as their bank-relative address, or their absolute offset in the ROM, depending on the command. They may also be followed by a plus sign and a literal number that gets added to the value. +- "`@`" evaluates as the address or absolute offset of the current patch/hook label. + +Any other characters are output as-is. + + +## Patch template commands + + +### {patch[ offset]} + +Outputs the bytes of the current patch as a value series, or as a hexadecimal number if there is only one byte. The bytes are found between the current patch label, and the label which is the current patch label plus "`_End`". An optional argument is an *offset* to add to the current patch label before gathering the contents between it and the end label. + +For example, if "`{patch}`" outputs "`a3:ab cd ef`", then "`{patch +1}`" outputs "`a2:cd ef`", and "`{patch +2}`" outputs "`0xef`". + +Converting the patch template will print a warning if any differences exist between the original and patched ROMs, which are not covered by "`patch`" commands. + + +### {dws args...} + +Outputs its arguments as a value series of little-endian 16-bit words. + +Symbol names or "`@`" are evaluated as their relative address. + +For example, if "`{dws 42 0xabcd wCurSpecies}`" outputs "`a6:2a 00 cd ab 60 cf`", then "`{dws >= wCurSpecies+3}`" outputs "`a4:04 00 63 cf`". + + +### {db arg} + +Outputs its argument as a single-byte value series. + +Symbol names or "`@`" are evaluated as their relative address. + +For example, "`{db 0xEF}`" outputs "`a1:ef`". + + +### {hex arg[ padding]} + +Outputs its first argument as a hexadecimal number. An optional second argument is the minimum length in digits; values shorter than it will be padded with leading zeros. + +Symbol names or "`@`" are evaluated as their absolute offset. + +This command has extra variants to reproduce inconsistent output casing: "`Hex`" prints the last three digits in lowercase and the rest uppercase; "`HEx`" prints the last two digits in lowercase and the rest uppercase; "`hEX`" prints the last three digits in uppercase and the rest lowercase; and "`heX`" prints the last two digits in uppercase and the rest lowercase. + +For example, "`{hex 0xabcd 5}`" outputs "`0x0abcd`". diff --git a/engine/battle/battle_transition.asm b/engine/battle/battle_transition.asm index 12932ede5..1cbe881ef 100644 --- a/engine/battle/battle_transition.asm +++ b/engine/battle/battle_transition.asm @@ -21,6 +21,7 @@ DoBattleTransition: ld hl, hVBlank ld a, [hl] push af + vc_hook FPA_link_fight_begin ld [hl], $1 .loop @@ -58,6 +59,7 @@ DoBattleTransition: ld a, $1 ; unnecessary bankswitch? ldh [rSVBK], a pop af + vc_hook FPA_link_fight_End4 ldh [hVBlank], a call DelayFrame ret @@ -310,6 +312,7 @@ StartTrainerBattle_Flash: dc 0, 0, 0, 1 StartTrainerBattle_SetUpForWavyOutro: + vc_hook FPA_link_fight_End0 farcall RespawnPlayerAndOpponent ld a, BANK(wLYOverrides) ldh [rSVBK], a @@ -367,6 +370,7 @@ StartTrainerBattle_SineWave: ret StartTrainerBattle_SetUpForSpinOutro: + vc_hook FPA_link_fight_End1 farcall RespawnPlayerAndOpponent ld a, BANK(wLYOverrides) ldh [rSVBK], a @@ -509,6 +513,7 @@ ENDM .wedge5: db 4, 0, 3, 0, 3, 0, 2, 0, 2, 0, 1, 0, 1, 0, 1, -1 StartTrainerBattle_SetUpForRandomScatterOutro: + vc_hook FPA_link_fight_End2 farcall RespawnPlayerAndOpponent ld a, BANK(wLYOverrides) ldh [rSVBK], a @@ -763,6 +768,7 @@ StartTrainerBattle_DrawSineWave: calc_sine_wave StartTrainerBattle_ZoomToBlack: + vc_hook FPA_link_fight_End3 farcall RespawnPlayerAndOpponent ld de, .boxes diff --git a/engine/battle/core.asm b/engine/battle/core.asm index 5a6f20ce9..a8c54b77f 100644 --- a/engine/battle/core.asm +++ b/engine/battle/core.asm @@ -8940,6 +8940,7 @@ InitBattleDisplay: predef PlaceGraphic xor a ldh [hWY], a + vc_hook fight_begin ldh [rWY], a call WaitBGMap call HideSprites diff --git a/engine/battle_anims/anim_commands.asm b/engine/battle_anims/anim_commands.asm index e7ae07087..d6d874240 100644 --- a/engine/battle_anims/anim_commands.asm +++ b/engine/battle_anims/anim_commands.asm @@ -58,12 +58,20 @@ BattleAnimRunScript: farcall CheckBattleScene jr c, .disabled + vc_hook FPA_001_Begin + vc_hook FPA_002_Begin + vc_hook FPA_003_Begin + vc_hook FPA_004_Begin + vc_hook FPA_005_Begin + vc_hook FPA_006_Begin + vc_hook FPA_007_Begin call BattleAnimClearHud call RunBattleAnimScript call BattleAnimAssignPals call BattleAnimRequestPals + vc_hook FPA_001_End xor a ldh [hSCX], a ldh [hSCY], a @@ -673,6 +681,7 @@ BattleAnimCmd_5GFX: .loop ld a, [wBattleAnimGFXTempTileID] cp (vTiles1 - vTiles0) / LEN_2BPP_TILE - BATTLEANIM_BASE_TILE + vc_hook FPA_042801_Begin ret nc call GetBattleAnimByte ld [hli], a diff --git a/engine/events/print_unown.asm b/engine/events/print_unown.asm index dac7d4b97..f969b3449 100644 --- a/engine/events/print_unown.asm +++ b/engine/events/print_unown.asm @@ -74,7 +74,13 @@ _UnownPrinter: jr nz, .pressed_b ldh a, [hJoyPressed] + vc_patch print_forbid_1 +if DEF(_CRYSTAL11_VC) + and 0 +else and A_BUTTON +endc + vc_patch_end jr nz, .pressed_a call .LeftRight diff --git a/engine/gfx/color.asm b/engine/gfx/color.asm index af7b7cd3d..092980a0e 100644 --- a/engine/gfx/color.asm +++ b/engine/gfx/color.asm @@ -1033,6 +1033,7 @@ endr .FinalPush: ld hl, MltReq1Packet call _PushSGBPals + vc_hook Network_RESET jp SGBDelayCycles SGBBorder_PushBGPals: diff --git a/engine/link/link.asm b/engine/link/link.asm index 7de939430..72bc6f368 100644 --- a/engine/link/link.asm +++ b/engine/link/link.asm @@ -67,7 +67,13 @@ Gen2ToGen1LinkComms: .player_1 ld de, MUSIC_NONE call PlayMusic + vc_patch NetworkDelay1 +if DEF(_CRYSTAL11_VC) + ld c, 26 +else ld c, 3 +endc + vc_patch_end call DelayFrames xor a ldh [rIF], a @@ -77,6 +83,7 @@ Gen2ToGen1LinkComms: ld hl, wLinkBattleRNPreamble ld de, wEnemyMon ld bc, SERIAL_RN_PREAMBLE_LENGTH + SERIAL_RNS_LENGTH + vc_hook Network358 call Serial_ExchangeBytes ld a, SERIAL_NO_DATA_BYTE ld [de], a @@ -84,6 +91,7 @@ Gen2ToGen1LinkComms: ld hl, wLinkData ld de, wOTPartyData ld bc, SERIAL_PREAMBLE_LENGTH + NAME_LENGTH + 1 + PARTY_LENGTH + 1 + (REDMON_STRUCT_LENGTH + NAME_LENGTH * 2) * PARTY_LENGTH + 3 + vc_hook Network359 call Serial_ExchangeBytes ld a, SERIAL_NO_DATA_BYTE ld [de], a @@ -91,6 +99,7 @@ Gen2ToGen1LinkComms: ld hl, wPlayerPatchLists ld de, wOTPatchLists ld bc, 200 + vc_hook Network364 call Serial_ExchangeBytes xor a @@ -224,7 +233,13 @@ Gen2ToGen2LinkComms: .player_1 ld de, MUSIC_NONE call PlayMusic + vc_patch NetworkDelay4 +if DEF(_CRYSTAL11_VC) + ld c, 26 +else ld c, 3 +endc + vc_patch_end call DelayFrames xor a ldh [rIF], a @@ -234,6 +249,7 @@ Gen2ToGen2LinkComms: ld hl, wLinkBattleRNPreamble ld de, wEnemyMon ld bc, SERIAL_RN_PREAMBLE_LENGTH + SERIAL_RNS_LENGTH + vc_hook Network360 call Serial_ExchangeBytes ld a, SERIAL_NO_DATA_BYTE ld [de], a @@ -241,6 +257,7 @@ Gen2ToGen2LinkComms: ld hl, wLinkData ld de, wOTPartyData ld bc, SERIAL_PREAMBLE_LENGTH + NAME_LENGTH + 1 + PARTY_LENGTH + 1 + 2 + (PARTYMON_STRUCT_LENGTH + NAME_LENGTH * 2) * PARTY_LENGTH + 3 + vc_hook Network361 call Serial_ExchangeBytes ld a, SERIAL_NO_DATA_BYTE ld [de], a @@ -248,6 +265,7 @@ Gen2ToGen2LinkComms: ld hl, wPlayerPatchLists ld de, wOTPatchLists ld bc, 200 + vc_hook Network362 call Serial_ExchangeBytes ld a, [wLinkMode] @@ -256,6 +274,7 @@ Gen2ToGen2LinkComms: ld hl, wLinkPlayerMail ld de, wLinkOTMail ld bc, wLinkPlayerMailEnd - wLinkPlayerMail + vc_hook Network363 call ExchangeBytes .not_trading @@ -1608,6 +1627,7 @@ ExitLinkCommunications: ldh [rSC], a ld a, (1 << rSC_ON) | (1 << rSC_CLOCK) ldh [rSC], a + vc_hook ret_heya ret GSPlaceTradeScreenFooter: ; unreferenced @@ -2009,6 +2029,7 @@ LinkTrade: ld de, String_TradeCompleted call PlaceString farcall Link_WaitBGMap + vc_hook save_game_end ld c, 50 call DelayFrames ld a, [wLinkMode] @@ -2161,7 +2182,13 @@ GetIncompatibleMonName: ret EnterTimeCapsule: + vc_patch NetworkDelay2 +if DEF(_CRYSTAL11_VC) + ld c, 26 +else ld c, 10 +endc + vc_patch_end call DelayFrames ld a, $4 call Link_EnsureSync @@ -2218,6 +2245,7 @@ WaitForOtherPlayerToExit: ld [hl], a ldh [hVBlank], a ld [wLinkMode], a + vc_hook term_exit ret SetBitsForLinkTradeRequest: @@ -2282,6 +2310,7 @@ WaitForLinkedFriend: ld a, (0 << rSC_ON) | (0 << rSC_CLOCK) ldh [rSC], a ld a, (1 << rSC_ON) | (0 << rSC_CLOCK) + vc_hook linkCable_fake_begin ldh [rSC], a ld a, [wLinkTimeoutFrames] dec a @@ -2374,7 +2403,13 @@ CheckLinkTimeout_Gen2: ld a, $6 ld [wPlayerLinkAction], a ld hl, wLinkTimeoutFrames + vc_patch NetworkDelay6 +if DEF(_CRYSTAL11_VC) + ld a, $3 +else ld a, 1 +endc + vc_patch_end ld [hli], a ld [hl], 50 call Link_CheckCommunicationError @@ -2395,6 +2430,7 @@ CheckLinkTimeout_Gen2: Link_CheckCommunicationError: xor a ldh [hSerialReceivedNewData], a + vc_hook linkCable_fake_end ld a, [wLinkTimeoutFrames] ld h, a ld a, [wLinkTimeoutFrames + 1] @@ -2425,6 +2461,7 @@ Link_CheckCommunicationError: .CheckConnected: call WaitLinkTransfer ld hl, wLinkTimeoutFrames + vc_hook Network_RECHECK ld a, [hli] inc a ret nz @@ -2433,7 +2470,13 @@ Link_CheckCommunicationError: ret .AcknowledgeSerial: + vc_patch NetworkDelay3 +if DEF(_CRYSTAL11_VC) + ld b, 26 +else ld b, 10 +endc + vc_patch_end .loop call DelayFrame call LinkDataReceived @@ -2460,8 +2503,10 @@ TryQuickSave: ld a, [wChosenCableClubRoom] push af farcall Link_SaveGame + vc_hook linkCable_block_input ld a, TRUE jr nc, .return_result + vc_hook linkCable_block_input2 xor a ; FALSE .return_result ld [wScriptVar], a @@ -2498,6 +2543,7 @@ CheckBothSelectedSameRoom: ret TimeCapsule: + vc_hook to_play2_mons1 ld a, LINK_TIMECAPSULE ld [wLinkMode], a call DisableSpriteUpdates @@ -2508,6 +2554,7 @@ TimeCapsule: ret TradeCenter: + vc_hook to_play2_trade ld a, LINK_TRADECENTER ld [wLinkMode], a call DisableSpriteUpdates @@ -2518,6 +2565,7 @@ TradeCenter: ret Colosseum: + vc_hook to_play2_battle ld a, LINK_COLOSSEUM ld [wLinkMode], a call DisableSpriteUpdates @@ -2532,6 +2580,7 @@ CloseLink: ld [wLinkMode], a ld c, 3 call DelayFrames + vc_hook room_check jp Link_ResetSerialRegistersAfterLinkClosure FailedLinkToPast: diff --git a/engine/link/mystery_gift.asm b/engine/link/mystery_gift.asm index 1bc26f6a4..f3cb4e790 100644 --- a/engine/link/mystery_gift.asm +++ b/engine/link/mystery_gift.asm @@ -37,14 +37,23 @@ DoMysteryGift: ; Prepare the first of two messages for wMysteryGiftPartnerData farcall StageDataForMysteryGift call ClearMysteryGiftTrainer + vc_patch infrared_fake_0 +if DEF(_CRYSTAL11_VC) + farcall StagePartyDataForMysteryGift + call ClearMysteryGiftTrainer + nop +else ld a, 2 ld [wMysteryGiftMessageCount], a ld a, wMysteryGiftPartnerDataEnd - wMysteryGiftPartnerData ld [wMysteryGiftStagedDataLength], a +endc + vc_patch_end ldh a, [rIE] push af call ExchangeMysteryGiftData + vc_hook infrared_fake_4 ld d, a xor a ldh [rIF], a @@ -260,6 +269,26 @@ DoMysteryGift: jp CloseSRAM ExchangeMysteryGiftData: + vc_hook infrared_fake_2 + vc_patch infrared_fake_1 +if DEF(_CRYSTAL11_VC) + ld d, $ef +.loop + dec d + ld a, d + or a + jr nz, .loop + vc_hook infrared_fake_3 + nop + cp MG_CANCELED +.restart ; same location as unpatched .restart + ret z + nop + nop + cp MG_OKAY + jr nz, ExchangeMysteryGiftData + ret +else di farcall ClearChannels call InitializeIRCommunicationInterrupts @@ -268,6 +297,8 @@ ExchangeMysteryGiftData: call BeginIRCommunication call InitializeIRCommunicationRoles ldh a, [hMGStatusFlags] +endc + vc_patch_end cp MG_CANCELED jp z, EndOrContinueMysteryGiftIRCommunication cp MG_OKAY diff --git a/engine/menus/menu.asm b/engine/menus/menu.asm index f20a6dceb..edae1c982 100644 --- a/engine/menus/menu.asm +++ b/engine/menus/menu.asm @@ -362,7 +362,9 @@ Menu_WasButtonPressed: call GetMenuJoypad and a ret z + vc_hook print_forbid_3 scf + vc_hook print_forbid_2 ret _2DMenuInterpretJoypad: diff --git a/engine/menus/save.asm b/engine/menus/save.asm index 82dab1046..381e3cc7c 100644 --- a/engine/menus/save.asm +++ b/engine/menus/save.asm @@ -161,6 +161,15 @@ AddHallOfFameEntry: ld bc, wHallOfFamePokemonListEnd - wHallOfFamePokemonList + 1 call CopyBytes call CloseSRAM +; This vc_hook causes the Virtual Console to set [sMobileEventIndex] and [sMobileEventIndexBackup] +; to MOBILE_EVENT_OBJECT_GS_BALL ($b), which enables you to get the GS Ball, take it to Kurt, and +; encounter Celebi. It assumes that sMobileEventIndex and sMobileEventIndexBackup are at their +; original addresses. + vc_hook BiographySave_ret + vc_assert BANK(sMobileEventIndex) == $1 && sMobileEventIndex == $be3c, \ + "sMobileEventIndex is no longer located at 01:be3c." + vc_assert BANK(sMobileEventIndexBackup) == $1 && sMobileEventIndexBackup == $be44, \ + "sMobileEventIndexBackup is no longer located at 01:be44." ret SaveGameData: diff --git a/engine/overworld/scripting.asm b/engine/overworld/scripting.asm index b53ba3911..824f626c4 100644 --- a/engine/overworld/scripting.asm +++ b/engine/overworld/scripting.asm @@ -389,6 +389,7 @@ Script_yesorno: ld a, TRUE .no ld [wScriptVar], a + vc_hook E_YESNO ret Script_loadmenu: diff --git a/engine/pokedex/pokedex.asm b/engine/pokedex/pokedex.asm index 1a52d750f..5b356c29e 100644 --- a/engine/pokedex/pokedex.asm +++ b/engine/pokedex/pokedex.asm @@ -356,6 +356,7 @@ Pokedex_UpdateDexEntryScreen: ld a, [hl] and B_BUTTON jr nz, .return_to_prev_screen + vc_hook print_forbid_5 ld a, [hl] and A_BUTTON jr nz, .do_menu_action diff --git a/engine/pokemon/mail_2.asm b/engine/pokemon/mail_2.asm index e0eb014b8..47b662412 100644 --- a/engine/pokemon/mail_2.asm +++ b/engine/pokemon/mail_2.asm @@ -67,7 +67,13 @@ ReadAnyMail: ldh a, [hJoyPressed] and A_BUTTON | B_BUTTON | START jr z, .loop + vc_patch print_forbid_4 +if DEF(_CRYSTAL11_VC) + and 0 +else and START +endc + vc_patch_end jr nz, .pressed_start ret diff --git a/home/serial.asm b/home/serial.asm index 20a9aff72..e210ef1cb 100644 --- a/home/serial.asm +++ b/home/serial.asm @@ -290,6 +290,7 @@ Serial_SyncAndExchangeNybble:: ; unreferenced jp WaitLinkTransfer ; pointless WaitLinkTransfer:: + vc_hook send_send_buf2 ld a, $ff ld [wOtherPlayerLinkAction], a .loop @@ -317,14 +318,26 @@ WaitLinkTransfer:: inc a jr z, .loop + vc_patch Network10 +if DEF(_CRYSTAL11_VC) + ld b, 26 +else ld b, 10 +endc + vc_patch_end .receive call DelayFrame call LinkTransfer dec b jr nz, .receive + vc_patch Network11 +if DEF(_CRYSTAL11_VC) + ld b, 26 +else ld b, 10 +endc + vc_patch_end .acknowledge call DelayFrame call LinkDataReceived @@ -333,6 +346,7 @@ WaitLinkTransfer:: ld a, [wOtherPlayerLinkAction] ld [wOtherPlayerLinkMode], a + vc_hook send_send_buf2_ret ret LinkTransfer:: diff --git a/macros.asm b/macros.asm index 1c71cba88..93f424d92 100644 --- a/macros.asm +++ b/macros.asm @@ -6,6 +6,7 @@ INCLUDE "macros/data.asm" INCLUDE "macros/code.asm" INCLUDE "macros/gfx.asm" INCLUDE "macros/coords.asm" +INCLUDE "macros/vc.asm" INCLUDE "macros/scripts/audio.asm" INCLUDE "macros/scripts/maps.asm" diff --git a/macros/vc.asm b/macros/vc.asm new file mode 100644 index 000000000..781aa0b40 --- /dev/null +++ b/macros/vc.asm @@ -0,0 +1,27 @@ +vc_hook: MACRO + if DEF(_CRYSTAL11_VC) + .VC_\1:: + endc +ENDM + +vc_patch: MACRO + if DEF(_CRYSTAL11_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(_CRYSTAL11_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(_CRYSTAL11_VC) + assert \# + endc +ENDM diff --git a/mobile/mobile_40.asm b/mobile/mobile_40.asm index e01b7c8ce..94cb4f6c8 100644 --- a/mobile/mobile_40.asm +++ b/mobile/mobile_40.asm @@ -1530,6 +1530,7 @@ Function1009f3: _LinkBattleSendReceiveAction: call .StageForSend ld [wLinkBattleSentAction], a + vc_hook send_byt2 farcall PlaceWaitingText ld a, [wLinkMode] cp LINK_MOBILE @@ -1584,20 +1585,35 @@ _LinkBattleSendReceiveAction: inc a jr z, .waiting + vc_hook send_byt2_ret + vc_patch send_byt2_wait +if DEF(_CRYSTAL11_VC) + ld b, 26 +else ld b, 10 +endc + vc_patch_end .receive call DelayFrame call LinkTransfer dec b jr nz, .receive + vc_hook send_dummy + vc_patch send_dummy_wait +if DEF(_CRYSTAL11_VC) + ld b, 26 +else ld b, 10 +endc + vc_patch_end .acknowledge call DelayFrame call LinkDataReceived dec b jr nz, .acknowledge + vc_hook send_dummy_end ld a, [wOtherPlayerLinkAction] ld [wBattleAction], a ret diff --git a/roms.sha1 b/roms.sha1 index bdb13b80a..e737a347c 100644 --- a/roms.sha1 +++ b/roms.sha1 @@ -3,3 +3,4 @@ f2f52230b536214ef7c9924f483392993e226cfb *pokecrystal11.gbc a0fc810f1d4e124434f7be2c989ab5b5892ddf36 *pokecrystal_au.gbc c60d57a24bbe8ecf7cba54ab3f90669f97bd330d *pokecrystal_debug.gbc 391ae86b1d5a26db712ffe6c28bbf2a1f804c3c4 *pokecrystal11_debug.gbc +a25517f60ca0e887d39ec698aa56a0040532a4b3 *pokecrystal11.patch diff --git a/tools/.gitignore b/tools/.gitignore index 72343e9a6..080309fc2 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -1,8 +1,9 @@ +gfx lzcomp -png_dimensions -scan_includes +make_patch palette +png_dimensions pokemon_animation pokemon_animation_graphics -gfx +scan_includes stadium diff --git a/tools/Makefile b/tools/Makefile index fcacab64c..269053abc 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -6,6 +6,7 @@ CFLAGS := -O3 -std=c11 -Wall -Wextra -pedantic -Wno-missing-field-initializers tools := \ lzcomp \ gfx \ + make_patch \ png_dimensions \ pokemon_animation \ pokemon_animation_graphics \ diff --git a/tools/make_patch.c b/tools/make_patch.c new file mode 100644 index 000000000..9546349da --- /dev/null +++ b/tools/make_patch.c @@ -0,0 +1,434 @@ +#define PROGRAM_NAME "make_patch" +#define USAGE_OPTS "labels.sym constants.sym patched.gbc original.gbc vc.patch.template vc.patch" + +#include "common.h" + +#include + +struct Buffer { + size_t item_size; + size_t size; + size_t capacity; + void *data; +}; + +struct Symbol { + struct Symbol *next; + unsigned int address; + unsigned int offset; + char name[]; // C99 FAM +}; + +struct Patch { + unsigned int offset; + unsigned int size; +}; + +struct Buffer *buffer_create(size_t item_size) { + struct Buffer *buffer = xmalloc(sizeof(*buffer)); + buffer->item_size = item_size; + buffer->size = 0; + buffer->capacity = 0x10; + buffer->data = xmalloc(buffer->capacity * item_size); + return buffer; +} + +void buffer_append(struct Buffer *buffer, const void *item) { + if (buffer->size >= buffer->capacity) { + buffer->capacity = (buffer->capacity + 1) * 2; + buffer->data = xrealloc(buffer->data, buffer->capacity * buffer->item_size); + } + memcpy((char *)buffer->data + (buffer->size++ * buffer->item_size), item, buffer->item_size); +} + +void buffer_free(struct Buffer *buffer) { + free(buffer->data); + free(buffer); +} + +void symbol_append(struct Symbol **symbols, const char *name, int bank, int address) { + size_t name_len = strlen(name) + 1; + struct Symbol *symbol = xmalloc(sizeof(*symbol) + name_len); + symbol->address = address; + symbol->offset = bank > 0 && address < 0x8000 ? address + (bank - 1) * 0x4000 : address; + 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\"", 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\"", 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 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 + static const char *comparisons[] = {"==", ">", "<", ">=", "<=", "!=", "||"}; + for (unsigned int i = 0; i < sizeof(comparisons) / sizeof(*comparisons); i++) { + if (!strcmp(arg, comparisons[i])) { + return i == 6 ? 0x11 : i; // "||" 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 (!strcmp(command, "patch") || !strcmp(command, "PATCH") || !strcmp(command, "patch_") || !strcmp(command, "PATCH_")) { + if (!current_hook) { + error_exit("Error: No current patch for command: \"%s\"", 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); + } + const struct Symbol *current_hook_end = symbol_find_cat(symbols, current_hook->name, "_End"); + int 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 { + 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 (!strcmp(command, "dws") || !strcmp(command, "DWS") || !strcmp(command, "dws_") || !strcmp(command, "DWS_")) { + if (argc < 1) { + error_exit("Error: Invalid arguments for command: \"%s\"", command); + } + 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", command, value); + } + if (i) { + putc(' ', output); + } + fprintf(output, isupper((unsigned)command[0]) ? "%02X %02X": "%02x %02x", value & 0xff, value >> 8); + } + + } else if (!strcmp(command, "db") || !strcmp(command, "DB") || !strcmp(command, "db_") || !strcmp(command, "DB_")) { + if (argc != 1) { + error_exit("Error: Invalid arguments for command: \"%s\"", 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", command, value); + } + fputs(command[strlen(command) - 1] == '_' ? "a1: " : "a1:", output); + fprintf(output, isupper((unsigned)command[0]) ? "%02X" : "%02x", value); + + } else if (!strcmp(command, "hex") || !strcmp(command, "HEX") || !strcmp(command, "HEx") || !strcmp(command, "Hex") || !strcmp(command, "heX") || !strcmp(command, "hEX")) { + if (argc != 1 && argc != 2) { + error_exit("Error: Invalid arguments for command: \"%s\"", command); + } + int value = parse_arg_value(argv[0], true, symbols, current_hook->name); + int padding = argc > 1 ? parse_number(argv[1], 0) : 2; + if (!strcmp(command, "HEx")) { + fprintf(output, "0x%0*X%02x", padding - 2, value >> 8, value & 0xff); + } else if (!strcmp(command, "Hex")) { + fprintf(output, "0x%0*X%03x", padding - 3, value >> 12, value & 0xfff); + } else if (!strcmp(command, "heX")) { + fprintf(output, "0x%0*x%02X", padding - 2, value >> 8, value & 0xff); + } else if (!strcmp(command, "hEX")) { + 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); + buffer->size = 0; + for (c = getc(input); c != EOF; c = getc(input)) { + putc(c, output); + if (c == ']') { + break; + } else if (!isalnum(c) && c != '_' && c != '@' && c != '#') { + // Convert non-identifier characters to underscores + c = '_'; + } + buffer_append(buffer, &c); + } + buffer_append(buffer, &(char []){'\0'}); + // The current patch should have a corresponding ".VC_" label + current_hook = symbol_find_cat(symbols, ".VC_", buffer->data); + skip_to_next_line(input, output); + break; + + default: + putc(c, output); + } + } + + rewind(orig_rom); + rewind(new_rom); + + fclose(input); + fclose(output); + buffer_free(buffer); + return patches; +} + +int compare_patch(const void *patch1, const void *patch2) { + unsigned int offset1 = ((const struct Patch *)patch1)->offset; + unsigned int offset2 = ((const struct Patch *)patch2)->offset; + return offset1 > offset2 ? 1 : offset1 < offset2 ? -1 : 0; +} + +bool verify_completeness(FILE *restrict orig_rom, FILE *restrict new_rom, struct Buffer *patches) { + qsort(patches->data, patches->size, patches->item_size, compare_patch); + for (unsigned int offset = 0, index = 0; ; offset++) { + int orig_byte = getc(orig_rom); + int new_byte = getc(new_rom); + if (orig_byte == EOF || new_byte == EOF) { + return orig_byte == new_byte; + } + struct Patch *patch = &((struct Patch *)patches->data)[index]; + if (index < patches->size && patch->offset == offset) { + if (fseek(orig_rom, patch->size, SEEK_CUR)) { + return false; + } + if (fseek(new_rom, patch->size, SEEK_CUR)) { + return false; + } + offset += patch->size; + index++; + } else if (orig_byte != new_byte) { + fprintf(stderr, PROGRAM_NAME ": Warning: Unpatched difference at offset: 0x%x\n", offset); + fprintf(stderr, " Original ROM value: 0x%02x\n", orig_byte); + fprintf(stderr, " Patched ROM value: 0x%02x\n", new_byte); + fprintf(stderr, " Current patch offset: 0x%06x\n", patch->offset); + return false; + } + } +} + +int main(int argc, char *argv[]) { + if (argc != 7) { + usage_exit(1); + } + + struct Symbol *symbols = NULL; + parse_symbols(argv[1], &symbols); + parse_symbols(argv[2], &symbols); + + FILE *new_rom = xfopen(argv[3], 'r'); + FILE *orig_rom = xfopen(argv[4], 'r'); + struct Buffer *patches = process_template(argv[5], argv[6], new_rom, orig_rom, symbols); + + if (!verify_completeness(orig_rom, new_rom, patches)) { + fprintf(stderr, PROGRAM_NAME ": Warning: Not all ROM differences are defined by \"%s\"\n", argv[6]); + } + + symbol_free(symbols); + fclose(new_rom); + fclose(orig_rom); + buffer_free(patches); + return 0; +} diff --git a/vc/pokecrystal11.constants.asm b/vc/pokecrystal11.constants.asm new file mode 100644 index 000000000..87a3c90b4 --- /dev/null +++ b/vc/pokecrystal11.constants.asm @@ -0,0 +1,40 @@ +INCLUDE "constants.asm" + +; These are all the asm constants needed to make the crystal11_vc patch. + +vc_const: MACRO + println "00:{04x:\1} \1" ; same format as rgblink's .sym file +ENDM + +; [fight begin] + vc_const SCREEN_HEIGHT_PX + +; [print forbid 2] + vc_const A_BUTTON +; [print forbid 3] + vc_const MAPGROUP_CIANWOOD + vc_const MAP_CIANWOOD_PHOTO_STUDIO +; [print forbid 5] + vc_const NO_INPUT + vc_const B_BUTTON + vc_const D_UP + vc_const D_DOWN + +; [FPA 001 Begin] + vc_const FISSURE +; [FPA 002 Begin] + vc_const SELFDESTRUCT +; [FPA 003 Begin] + vc_const THUNDER +; [FPA 004 Begin] + vc_const FLASH +; [FPA 005 Begin] + vc_const EXPLOSION +; [FPA 006 Begin] + vc_const HORN_DRILL +; [FPA 007 Begin] + vc_const HYPER_BEAM + +; [FPA 042801 Begin] + vc_const PRESENT + vc_const anim_1gfx_command diff --git a/vc/pokecrystal11.patch.template b/vc/pokecrystal11.patch.template new file mode 100644 index 000000000..ae5dd8db6 --- /dev/null +++ b/vc/pokecrystal11.patch.template @@ -0,0 +1,698 @@ +;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) + +[Network10] +Mode = 1 +Address = {HEX @+1 4} +Fixcode = {PATCH +1} + +[Network11] +Mode = 1 +Address = {HEX @+1 4} +Fixcode = {PATCH +1} + +[send_send_buf2] +Mode = 2 +Address = {HEX @ 4} +Type = 29 + +[send_send_buf2_ret] +Mode = 2 +Address = {HEX @ 4} +Type = 30 + +[Network358] +Mode = 2 +Address = {HEX @} +Type = 4 + +[Network359] +Mode = 2 +Address = {HEX @} +Type = 4 + +[Network364] +Mode = 2 +Address = {HEX @} +;fix pokemon ?? in name +Type = 26 + +[Network360] +Mode = 2 +Address = {HEX @} +Type = 4 + +[Network361] +Mode = 2 +Address = {HEX @} +Type = 4 + +[Network362] +Mode = 2 +Address = {HEX @} +Type = 4 + +[Network363] +Mode = 2 +Address = {HEX @} +Type = 4 + +[Network_RECHECK] +Mode = 2 +Address = {HEX @} +Type = 7 + +[send_byt2] +Mode = 2 +Address = {HEX @+5} +Type = 31 + +[send_byt2_ret] +Mode = 2 +Address = {HEX @} +Type = 32 + +[send_byt2_wait] +Mode = 1 +Address = {HEX @+1} +Fixcode = {PATCH +1} + +[send_dummy] +Mode = 2 +Address = {HEX @} +Type = 33 + +[send_dummy_wait] +Mode = 1 +Address = {HEX @+1} +Fixcode = {PATCH +1} + +[send_dummy_end] +Mode = 2 +Address = {HEX @} +Type = 34 + +[NetworkDelay1] +Mode = 1 +Address = {HEX @+1} +Fixcode = {PATCH +1} + +[NetworkDelay2] +Mode = 1 +Address = {HEX @+1} +Fixcode = {PATCH +1} + +[NetworkDelay3] +Mode = 1 +Address = {HEX @+1} +Fixcode = {PATCH +1} + +[NetworkDelay4] +Mode = 1 +Address = {HEX @+1} +Fixcode = {PATCH +1} + +[NetworkDelay6] +Mode = 1 +Address = {HEX @+1} +Fixcode = {PATCH +1} + +;no use[Network_STOP] +;Mode = 2 +;Address = 0xF4D34 +;Type = 8 + +;no use[Network_END] +;Mode = 2 +;Address = 0xF4D3C +;Type = 9 + +[Network_RESET] +Mode = 2 +Address = {HEX @ 5} +Type = 10 + +[E_YESNO] +Mode = 2 +Address = {HEX @} +Type = 15 + +[linkCable fake begin] +Mode = 2 +Address = {HEX @} +Type = 16 + +[linkCable fake end] +Mode = 2 +Address = {HEX @} +Type = 17 + +;MURIYARI +[linkCable block input] +Mode = 2 +Address = {HEX @} +Type = 18 +[linkCable block input2] +Mode = 2 +Address = {HEX @} +Type = 24 +[save game end] +Mode = 2 +Address = {HEX @} +Type = 20 +[term_exit] +Mode = 2 +Address = {HEX @} +Type = 25 +[room_check] +Mode = 2 +Address = {HEX @} +Type = 27 +[to_play2_mons1] +Mode = 2 +Address = {HEX @} +Type = 11 +[to_play2_trade] +Mode = 2 +Address = {HEX @} +Type = 12 +[to_play2_battle] +Mode = 2 +Address = {HEX @} +Type = 13 +[ret_heya] +Mode = 2 +Address = {HEX @} +Type = 14 + + + + + +;ROM:3FBCD ld b, $3E ; '>' +;ROM:3FBCF inc de +;ROM:3FBD0 call unk_2D55 +;ROM:3FBD3 xor a +;ROM:3FBD4 ld [byte_FFD2], a +;ROM:3FBD6 ld [byte_FF4A], a +;ROM:3FBD8 call unk_31C2 +;ROM:3FBDB call unk_2FE2 +; +;ROM:3FBCD: 06 3E 13 CD + +;0003fbb5h: 06 3E 13 CD ; .>.? + +[fight begin] +Mode = 11 +Type = 0 +Index = 1 +Address = {HEx @} +Fixcode={db SCREEN_HEIGHT_PX} + +;12 1b 0b 79 b0 find next C9 +[BiographySave_ret] +Mode = 2 +Address = {HEX @} +Type = 60 + + +; print forbid 1 +;ROM:1758D ld a, [byte_FFA9] +;ROM:1758F and 2 +;ROM:17591 jr nz, unk_75B4 +;ROM:17593 ld a, [byte_FFA9] +;ROM:17595 and 1 ;e6 01 +;ROM:17597 jr nz, unk_75A1 +; +; change "and 1" to "and 0" +;00017595h: E6 01 20 08 CD BF 75 CD 2E 03 18 E9 FA 57 CE F5 ; ? .涂u?..辁W熙 +;00016c76h: E6 01 20 08 CD A0 6C CD 5A 04 18 E9 FA 63 CF F5 ; ? .蜖l蚙..辁c硝 +[print forbid 1] +Mode = 1 +Address = {hex @} +Fixcode={patch} + + + +[print forbid 2] +Mode = 6 +Type = 0 +Address = {hex @} +MemAddress={hex hJoyPressed} +Fixcode={db NO_INPUT} +ConditionType = 0 +ConditionValueA = {dws_ wWindowStackPointer wWindowStackPointer+1 wMenuJoypad wMenuSelection wMenuSelection wMenuCursorY hJoyPressed hJoyPressed hJoyPressed hJoyPressed} +ConditionValueB = {dws_ == == == >= <= == != != != != } +ConditionValueC = {dws_ 0xdd 0xd3 A_BUTTON 0x00 0x0f 0x03 D_DOWN D_UP B_BUTTON NO_INPUT } + +; -----ddddfffffff99999ccccc77777----0xd9c7 no ..............Mem Write: pc32 = 0x230b addr = 0xd9c7 value = 0x8 +; 0xd9c7 is the room number. + + +[print forbid 3] +Mode = 6 +Type = 0 +Address = {hex @} +MemAddress={hex hJoyPressed} +Fixcode={db NO_INPUT} +ConditionType = 0 +ConditionValueA = {dws_ wWindowStackPointer wWindowStackPointer+1 wMenuJoypad wMenuSelection wMenuCursorY wMapGroup wMapNumber wYCoord wXCoord hJoyPressed hJoyPressed hJoyPressed hJoyPressed} +ConditionValueB = {dws_ == == == == == == == == == != != != != } +ConditionValueC = {dws_ 0xaf 0xdf NO_INPUT 0x00 0x01 MAPGROUP_CIANWOOD MAP_CIANWOOD_PHOTO_STUDIO 0x04 0x02 D_DOWN D_UP B_BUTTON NO_INPUT } + + +;ROM:BB29C call unk_934 +;ROM:BB29F ld a, [byte_FFA9] +;ROM:BB2A1 and $B +;ROM:BB2A3 jr z, unk_B29C +;ROM:BB2A5 and 8 +;ROM:BB2A7 jr nz, unk_B2AA +;ROM:BB2A9 ret +; 000bb2a5h: E6 08 20 01 +; 000b92a3h: E6 08 20 01 ; ? . +; change "and 8" to "and 0" +[print forbid 4] +Mode = 1 +Address = {hex @} +Fixcode={patch} + + +;ROM:401D6 call unk_50A5 +;ROM:401D9 ld hl, $FFA9 +;ROM:401DC ld a, [hl] +;ROM:401DD and 2 +;ROM:401DF jr nz, unk_1F8 +;ROM:401E1 ld a, [hl] +;ROM:401E2 and 1 +;ROM:401E4 jr nz, unk_1EE +;ROM:401E6 call unk_4562 +;ROM:401E9 ret nc +;ROM:401EA call unk_4114 +;ROM:401ED ret +; -----6666666666ddddddddd88888----0xc6d8 no ..............Mem Write: pc32 = 0x4109b addr = 0xc6d8 value = 0x0 + +;00040266h: 7E E6 01 20 08 ; ~? . +[print forbid 5] +Mode = 6 +Type = 0 +Address = {hex @} +MemAddress={hex hJoyPressed} +Fixcode={db NO_INPUT} +ConditionType = 0 +ConditionValueA = {dws_ wWindowStackPointer wWindowStackPointer+1 wMenuJoypad wMenuSelection wDexArrowCursorPosIndex hJoyPressed hJoyPressed hJoyPressed hJoyPressed} +ConditionValueB = {dws_ == == == == == != != != != } +ConditionValueC = {dws_ 0xa1 0xdb A_BUTTON 0x00 0x03 D_DOWN D_UP B_BUTTON NO_INPUT } + + + + +;0x29e97 +; call ir_main +; ld d,a ; IR_STAT +; xor a + + +; _IRcomm_end 0x2a1b9 +; ld hl,ir_read_buf | 21 50 c7 +; ld de,ir_read_buf_stk | 11 00 c8 +; ld bc,15 | +; call block_move | +; | +;00104bf8h: FE 03 30 24 +;00104bf0h: FE 03 30 24 3E 41 21 0B 51 CF CD FB 50 ; ?0$>A!.Q贤鸓 +;00104bf0h: FE 03 30 24 3E 41 21 0B 51 CF CD FB 50 ; ?0$>A!.Q贤鸓 +;the code below is Set_send_data2 +; 3E 41 21 0A 51 CF CD FA 50 +; 3E 41 21 0B 51 CF CD FB 50 +; -------> +; BCALL G_BANK0b,set_send_data2 +; call read_buf_clr +; + +;001048dbh: 3E 02 EA 01 CA 3E 14 EA 02 CA F0 FF F5 CD 94 4A +;001048dbh: 3E 02 EA 01 CA 3E 14 EA 02 CA F0 FF F5 CD 9D 4A +[infrared fake 0] +Mode = 1 +Address = {hex @} +Fixcode={PATCH} + + +;00104c3ch: CD 66 4D CD 9E 4D CD E5 4D F0 BC FE 10 CA 24 4D +;00104a95h: F3 3E 3A 21 E9 4F CF CD 5E 4D CD 96 4D CD DD 4D ; ?:!镺贤^M蜄M洼M +;00104a9ch: CD 5E 4D CD 96 4D CD DD 4D F0 BC FE 10 ; 蚟M蜄M洼M鸺? + +[infrared fake 1] +Mode = 1 +Address = {hex @} +Fixcode={patch} + +[infrared fake 2] +Mode = 2 +Address = {hex @} +Type = 101 + +[infrared fake 3] +Mode = 2 +Address = {hex @} +Type = 102 + +[infrared fake 4] +Mode = 2 +Address = {hex @} +Type = 103 + + +;///////////// +;////fpa//////// +;/////////rangel zhang /////////// + +;PC:51-4118=20 000CC118 LY:012 AF:00A0 BC:E400 DE:E4E4 HL:FFA0 SP:DFC1 +;PC:51-411A=FA 000CC11A LY:012 AF:00A0 BC:E400 DE:E4E4 HL:FFA0 SP:DFC1 +;PC:51-411D=CB 000CC11D LY:012 AF:61A0 BC:E400 DE:E4E4 HL:FFA0 SP:DFC1 +;PC:51-411F=20 000CC11F LY:012 AF:61A0 BC:E400 DE:E4E4 HL:FFA0 SP:DFC1 +;PC:51-4121=CD 000CC121 LY:012 AF:61A0 BC:E400 DE:E4E4 HL:FFA0 SP:DFC1 +;PC:51-417A=CD 000CC17A LY:012 AF:61A0 BC:E400 DE:E4E4 HL:FFA0 SP:DFBF +;PC:51-41CA=3E 000CC1CA LY:012 AF:61A0 BC:E400 DE:E4E4 HL:FFA0 SP:DFBD +;PC:51-41CC=EA 000CC1CC LY:012 AF:01A0 BC:E400 DE:E4E4 HL:FFA0 SP:DFBD + +;ROM:CC118 jr nz, unk_C14D +;ROM:CC11A ld a, [byte_D1AB] +;ROM:CC11D bit 7, a +;ROM:CC11F jr nz, unk_C138 +;ROM:CC121 call unk_417A +;ROM:CC124 call unk_415A +;ROM:CC127 call unk_47F7 + +;000cc13eh: 6F 26 00 11 ; o&.. +;000cc156h: 6F 26 00 11 ; o&.. +;000cc137h: 38 17 CD 92 41 CD 72 41 CD 95 48 CD D3 41 AF E0 ; 8.蛼A蛂A蜁H陀A +;000cc128h: 38 17 CD A1 41 CD 63 41 CD A4 48 CD E2 41 AF E0 ; 8.汀A蚦A亭H外A + +;the 7th bit of the [byte_D1AB],decide whether the animation should be played. +;if it's zero , the game code will play fighting animation . otherwise, game code +; will jump to unk_C138 and avoiding playing animation. +; so we can begin out FPA patch right at address 0xcc121 . + + +;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) +;012532 +; +[FPA 001 Begin] +Mode = 3 +Type = 0 +Address = {hex @} +DarkEnable0 = 1 +Dark0 = 4 +MotionBEnable0 = 3 +MotionBlur0 = 11 +ConditionType = 0 +ConditionValueA = {dws_ wFXAnimID} +ConditionValueB = {dws_ == } +ConditionValueC = {dws_ FISSURE } + +;ROM:35D09 ld [byte_CFB6], a +;ROM:35D0C ld a, d +;ROM:35D0D ld [byte_CFB7], a +;ROM:35D10 ld c, 3 +;ROM:35D12 call unk_468 +;ROM:35D15 ld hl, $40E5 +;ROM:35D18 ld a, $33 ; '3' +;ROM:35D1A rst 8 +;ROM:35D1B ret +; EA B6 CF 7A EA B7 CF 0E 03 CD 68 04 21 E5 40 3E +;00035d09h: EA C2 CF 7A EA C3 CF 0E 03 CD 68 04 21 D6 40 3E +;00035d09h: EA C2 CF 7A EA C3 CF 0E 03 CD 68 04 21 D8 40 3E + +;******dc7d--------------- Mem Write: pc32 = 0x30a7 addr = 0xd066 value = 0x2c +;******dc7d--------------- Mem Write: pc32 = 0x30a7 addr = 0xd067 value = 0x3a +;******dc7d--------------- Mem Write: pc32 = 0x30a7 addr = 0xd068 value = 0xb8 +;******dc7d--------------- Mem Write: pc32 = 0x30a7 addr = 0xd069 value = 0x50 +; ------------ Mem Write: pc32 = 0x35d09 addr = 0xcfb6 value = 0x78 +;s e l d e s c +; + +[FPA 002 Begin] +Mode = 3 +Type = 0 +Address = {hex @} +DarkEnable0 = 1 +Dark0 = 4 +MotionBEnable0 = 3 +MotionBlur0 = 11 +ConditionType = 0 +ConditionValueA = {dws_ wFXAnimID } +ConditionValueB = {dws_ == } +ConditionValueC = {dws_ SELFDESTRUCT} + + +; lightening +; -------------- Mem Write: pc32 = 0x35d09 addr = 0xcfb6 value = 0x57 +[FPA 003 Begin] +Mode = 3 +Type = 0 +Address = {hex @} +DarkEnable0 = 1 +Dark0 = 4 +MotionBEnable0 = 3 +MotionBlur0 = 15 +ConditionType = 0 +ConditionValueA = {dws_ wFXAnimID} +ConditionValueB = {dws_ == } +ConditionValueC = {dws_ THUNDER } + + + + +;ji wa lei 011800 + +[FPA 004 Begin] +Mode = 3 +Type = 0 +Address = {hex @} +DarkEnable0 = 1 +Dark0 = 4 +MotionBEnable0 = 3 +MotionBlur0 = 15 +ConditionType = 0 +ConditionValueA = {dws_ wFXAnimID} +ConditionValueB = {dws_ == } +ConditionValueC = {dws_ FLASH } + + +;skill name 1 : ..............Mem Write: pc32 = 0x30db addr = 0xcf87 value = 0x2c +;skill name 2 : ..............Mem Write: pc32 = 0x30db addr = 0xcf88 value = 0x3a +;skill name 3 : ..............Mem Write: pc32 = 0x30db addr = 0xcf89 value = 0xb8 +;skill name 4 : ..............Mem Write: pc32 = 0x30db addr = 0xcf8a value = 0x50 +;skill name 5 : ..............Mem Write: pc32 = 0x30db addr = 0xcf8b value = 0x8f +;skill name 6 : ..............Mem Write: pc32 = 0x30db addr = 0xcf8c value = 0x9d +; include 2 pieces of animationl. +;ji ba lu 011607 + +[FPA 005 Begin] +Mode = 3 +Type = 0 +Address = {hex @} +DarkEnable0 = 1 +Dark0 = 4 +MotionBEnable0 = 3 +MotionBlur0 = 15 +ConditionType = 0 +ConditionValueA = {dws_ wFXAnimID} +ConditionValueB = {dws_ == } +ConditionValueC = {dws_ EXPLOSION} + + +;skill name 1 : ..............Mem Write: pc32 = 0x30db addr = 0xcf87 value = 0x30 +;skill name 2 : ..............Mem Write: pc32 = 0x30db addr = 0xcf88 value = 0xb2 +;skill name 3 : ..............Mem Write: pc32 = 0x30db addr = 0xcf89 value = 0x3a +;skill name 4 : ..............Mem Write: pc32 = 0x30db addr = 0xcf8a value = 0xb8 +;skill name 5 : ..............Mem Write: pc32 = 0x30db addr = 0xcf8b value = 0xca +;skill name 6 : ..............Mem Write: pc32 = 0x30db addr = 0xcf8c value = 0xc2 +; da yi ba ha ku ci 011441 + +[FPA 006 Begin] +Mode = 3 +Type = 0 +Address = {hex @} +DarkEnable0 = 1 +Dark0 = 4 +MotionBEnable0 = 3 +MotionBlur0 = 11 +ConditionType = 0 +ConditionValueA = {dws_ wFXAnimID } +ConditionValueB = {dws_ == } +ConditionValueC = {dws_ HORN_DRILL} + + + +;skill name 1 : ..............Mem Write: pc32 = 0x30db addr = 0xcf87 value = 0x9b +;skill name 2 : ..............Mem Write: pc32 = 0x30db addr = 0xcf88 value = 0xa5 +;skill name 3 : ..............Mem Write: pc32 = 0x30db addr = 0xcf89 value = 0xac +;skill name 4 : ..............Mem Write: pc32 = 0x30db addr = 0xcf8a value = 0x8b +;skill name 5 : ..............Mem Write: pc32 = 0x30db addr = 0xcf8b value = 0xae +;skill name 6 : ..............Mem Write: pc32 = 0x30db addr = 0xcf8c value = 0x50 +; 011251 +; +[FPA 007 Begin] +Mode = 3 +Type = 0 +Address = {hex @} +DarkEnable0 = 1 +Dark0 = 5 +MotionBEnable0 = 3 +MotionBlur0 = 7 +ConditionType = 0 +ConditionValueA = {dws_ wFXAnimID } +ConditionValueB = {dws_ == } +ConditionValueC = {dws_ HYPER_BEAM} + + + + +;-----111111111111111144444444444444----0xc902 no ..............Mem Write: pc32 = 0xcc46a addr = 0xc902 value = 0xd + +;000cc473h: FE 4F D0 cd ; 﨩? +;000cc495h: FE 4F D0 CD ; 﨩型 +;000cc497h: FE 4F D0 CD ; 﨩型 +; -------------0xd4170xd4170xd4170xd417--------------- Mem Write: pc32 = 0x3a89 addr = 0xd417 value = 0xd1 +;000cc486h: FE 4F D0 CD 7D 3A 22 FA 19 D4 22 C5 E5 6F 26 00 ; 﨩型}:"??佩o&. +[FPA 042801 Begin] +Mode = 3 +Type = 0 +Address = {HEX @} +DarkEnable0 = 1 +Dark0 = 5 +MotionBEnable0 = 3 +MotionBlur0 = 11 +ConditionType = 0 +ConditionValueA = {dws_ wFXAnimID wBattleAnimByte } +ConditionValueB = {dws_ == == } +ConditionValueC = {dws_ PRESENT anim_1gfx_command} + + + + +;ROM:CC139 call unk_4192 +;ROM:CC13C call unk_4172 +;ROM:CC13F call unk_4895 +;ROM:CC142 call unk_41D3 +;ROM:CC145 xor a + +;ROM:CC154 jr z, unk_C16E +;ROM:CC156 ld l, a +;ROM:CC157 ld h, 0 +;ROM:CC159 ld de, $10E +;ROM:CC15C add hl, de +;ROM:CC15D ld a, l +; CC156 6F 26 00 11 0E 01 + +; 000cc147h: 6F 26 00 11 0E 01 ; o&.... + +;exit point + +[FPA 001 End] +Mode = 3 +Type = 1 +Address = {hex @} + + +;-----ddddff0xff690xff69fffff----0xffa0 no ....-------------..........Mem Write: pc32 = 0x8c352 addr = 0xffa0 value = 0x1 +;-----ddddff0xff690xff69fffff----0xce57 no ....----5555555577777---------..........Mem Write: pc32 = 0x8c483 addr = 0xce57 value = 0x1a +;0008c352h: 36 01 FA 57 CE CB 7F 20 08 ; 6.鶺嗡 . +;0008c229h: 36 01 FA 57 CF CB 7F 20 08 CD 14 43 CD 5A 04 18 ; 6.鶺纤 .?C蚙.. +[FPA link fight begin] +Mode = 3 +Type = 0 +Address = {hex @} +DarkEnable0 = 1 +Dark0 = 5 +MotionBEnable0 = 3 +MotionBlur0 = 11 + +;-----ddddff0xff690xff69fffff----0xffa0 no ....-------------..........Mem Write: pc32 = 0x8c382 addr = 0xffa0 value = 0x0 +;0008c382h: E0 A0 CD 2E 03 C9 ; 酄?.? +;******ccccccccccceeeeeeeeeee55555555577777777--------------- Mem Write: pc32 = 0x8c483 addr = 0xce57 value = 0x15 +;******ccccccccccceeeeeeeeeee55555555577777777--------------- Mem Write: pc32 = 0x8c483 addr = 0xce57 value = 0x16 +;******ccccccccccceeeeeeeeeee55555555577777777--------------- Mem Write: pc32 = 0x8c483 addr = 0xce57 value = 0x17 + +;40 90 e4 01 3E at 3E +;0008c3e4h: 40 90 E4 01 3E at 3E ; @愪.> +[FPA link fight End0] +Mode = 3 +Type = 1 +Address = {HEx @} + +;3D 20 EF C9 3E 01 at 3E +; 0008c439h: 3D 20 EF C9 3E at 3e ; = 锷> +[FPA link fight End1] +Mode = 3 +Type = 1 +Address = {HEx @} + +;01 FF 3E 01 at 3E +;0008c576h: 01 FF 3E 01 ; .>. +[FPA link fight End2] +Mode = 3 +Type = 1 +Address = {HEx @} + +;32 00 19 00 3e 01 at 3e +;0008c764h: 32 00 19 00 3E 01 ; 2...>. +[FPA link fight End3] +Mode = 3 +Type = 1 +Address = {HEx @} + +;ROM:8C25A ld [byte_FFC6], a +;ROM:8C25C ld [byte_FFC7], a +;ROM:8C25E ld [byte_FFC8], a +;ROM:8C260 ld [byte_FFD0], a +;ROM:8C262 ld a, 1 +;ROM:8C264 ld [byte_FF70], a +;ROM:8C266 pop af +;ROM:8C267 ld [byte_FF9E], a --------------------at here . +;ROM:8C269 call unk_45A +;ROM:8C26C ret + + +;ROM:8C298 xor a searching code : AF 22 22 77 CD +;ROM:8C299 ldi [hl], a +;ROM:8C29A ldi [hl], a +;ROM:8C29B ld [hl], a +;ROM:8C29C call unk_46D8 +;ROM:8C29F ret +;0008c298h: AF 22 22 77 CD ; ?"w? + + +[FPA link fight End4] +Mode = 3 +Type = 1 +Address = {hex @} \ No newline at end of file