├── .dockerignore ├── .github └── workflows │ ├── main.yml │ └── pullrequest.yml ├── .gitignore ├── .vscode └── settings.json ├── Dockerfile ├── LICENSE ├── README.md ├── TODO.txt ├── build.bat ├── build.sh ├── build_PAL.bat ├── build_PAL.sh ├── build_dev.bat ├── build_dev.sh ├── build_docker.bat ├── build_docker.sh ├── enemies ├── clear_enemies.txt └── create_clear_enemies_data.py ├── layout ├── create_layout.py ├── layoutmenutemplate.asm └── portals.txt ├── names ├── create_names.py └── default_names.txt ├── requirements.txt ├── resources ├── HUDfont.tbl ├── bk2_demo_to_input_log.py ├── bk2_input_log_to_demo.py ├── cm_brb_gfx.bin ├── cm_gfx.bin ├── cm_gfx2.bin ├── create_dummies.py ├── create_ips.py ├── create_ram_symbols.py ├── dos_to_unix.py ├── header.tbl ├── hudgfx.bin ├── mapgfx.bin ├── normal.tbl ├── ntsc_to_pal_abridged_pseudo_patch.txt ├── ntsc_to_pal_raw_pseudo_patch.txt ├── num_gfx_choice.bin ├── palettes_00_upper_crateria.bin ├── palettes_01_red_crateria.bin ├── palettes_02_lower_crateria.bin ├── palettes_03_old_tourian.bin ├── palettes_04_wrecked_ship_power_on.bin ├── palettes_05_wrecked_ship_power_off.bin ├── palettes_06_green_blue_brinstar.bin ├── palettes_07_red_brinstar.bin ├── palettes_08_tourian_entrance.bin ├── palettes_09_heated_norfair.bin ├── palettes_10_unheated_norfair.bin ├── palettes_11_sandless_maridia.bin ├── palettes_12_sandy_maridia.bin ├── palettes_13_tourian.bin ├── palettes_14_mother_brain.bin ├── palettes_15_17_19_blue_ceres.bin ├── palettes_16_18_20_white_ceres.bin ├── palettes_21_map_room.bin ├── palettes_22_map_room_power_off.bin ├── palettes_23_blue_refill_room.bin ├── palettes_24_yellow_refill_room.bin ├── palettes_25_save_room.bin ├── palettes_26_kraid.bin ├── palettes_27_croc.bin ├── palettes_28_draygon.bin ├── sort_debug_symbols.py ├── spritegfx.bin ├── tile_table_00_01_upper_crateria.bin ├── tile_table_02_03_lower_crateria.bin ├── tile_table_04_05_wrecked_ship.bin ├── tile_table_06_green_blue_brinstar.bin ├── tile_table_07_08_red_brinstar.bin ├── tile_table_09_10_norfair.bin ├── tile_table_11_sandless_maridia.bin ├── tile_table_12_sandy_maridia.bin ├── tile_table_13_14_tourian.bin ├── tile_table_15_20_ceres.bin ├── tile_table_21_25_utility.bin ├── tile_table_26_kraid.bin ├── tile_table_27_croc.bin ├── tile_table_28_draygon.bin ├── tile_table_CRE.bin ├── tiles_00_01_upper_crateria.bin ├── tiles_02_03_lower_crateria.bin ├── tiles_04_05_wrecked_ship.bin ├── tiles_06_green_blue_brinstar.bin ├── tiles_07_08_red_brinstar.bin ├── tiles_09_10_norfair.bin ├── tiles_11_sandless_maridia.bin ├── tiles_12_sandy_maridia.bin ├── tiles_13_14_tourian.bin ├── tiles_15_16_ceres.bin ├── tiles_17_18_ceres_elevator.bin ├── tiles_19_20_ceres_ridley.bin ├── tiles_21_25_utility.bin ├── tiles_26_kraid.bin ├── tiles_27_croc.bin ├── tiles_28_draygon.bin └── tiles_CRE.bin ├── src ├── BRBmenu.asm ├── clearenemies.asm ├── crash.asm ├── cropmenu.asm ├── customizemenu.asm ├── custompresets.asm ├── cutscenes.asm ├── damage.asm ├── defines.asm ├── demos.asm ├── enemy_rng.asm ├── fanfare.asm ├── flagmenu.asm ├── freespace.asm ├── gamemenu.asm ├── gamemode.asm ├── infohud.asm ├── infohudmodes.asm ├── init.asm ├── layout.asm ├── layoutmenu.asm ├── layoutportaltables.asm ├── macros.asm ├── main.asm ├── mainmenu.asm ├── menu.asm ├── minimap.asm ├── misc.asm ├── physics.asm ├── presets.asm ├── presets │ ├── 100early_data.asm │ ├── 100early_menu.asm │ ├── 100map_data.asm │ ├── 100map_menu.asm │ ├── 14ice_data.asm │ ├── 14ice_menu.asm │ ├── 14speed_data.asm │ ├── 14speed_menu.asm │ ├── allbosskpdr_data.asm │ ├── allbosskpdr_menu.asm │ ├── allbosspkdr_data.asm │ ├── allbosspkdr_menu.asm │ ├── allbossprkd_data.asm │ ├── allbossprkd_menu.asm │ ├── gtclassic_data.asm │ ├── gtclassic_menu.asm │ ├── gtmax_data.asm │ ├── gtmax_menu.asm │ ├── hundo_data.asm │ ├── hundo_menu.asm │ ├── kpdr21_data.asm │ ├── kpdr21_menu.asm │ ├── kpdr22_data.asm │ ├── kpdr22_menu.asm │ ├── kpdr23_data.asm │ ├── kpdr23_menu.asm │ ├── kpdr25_data.asm │ ├── kpdr25_menu.asm │ ├── nghyper_data.asm │ ├── nghyper_menu.asm │ ├── ngplasma_data.asm │ ├── ngplasma_menu.asm │ ├── nintendopower_data.asm │ ├── nintendopower_menu.asm │ ├── pkrd_data.asm │ ├── pkrd_menu.asm │ ├── prkd19_data.asm │ ├── prkd19_menu.asm │ ├── prkd20_data.asm │ ├── prkd20_menu.asm │ ├── rbo_data.asm │ ├── rbo_menu.asm │ ├── suitless_data.asm │ └── suitless_menu.asm ├── ramwatchmenu.asm ├── roomnames.asm ├── save.asm ├── spritefeat.asm ├── spriteprio.asm ├── symbols.asm ├── tilegraphics.asm ├── tinystates.asm └── wram_symbols.asm ├── tools ├── pauselag.lua └── sm_teledump.lua └── web ├── .eslintrc ├── .gitignore ├── components ├── layout.js ├── mdx.js ├── patch.js └── upload.js ├── data ├── changelog.mdx ├── config.json ├── help.mdx └── infohudmode.mdx ├── lib └── crc32.js ├── next.config.js ├── package-lock.json ├── package.json ├── pages ├── _app.js ├── changelog.js ├── help.js ├── index.js └── infohudmode.js ├── postcss.config.js ├── public ├── favicon.ico └── smhud.png ├── styles └── globals.css └── tailwind.config.js /.dockerignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .git 3 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Build and deploy patches and website 4 | 5 | # Controls when the action will run. Triggers the workflow on push or pull request 6 | # events but only for the master branch 7 | on: 8 | push: 9 | branches: [ master ] 10 | 11 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | 20 | - name: Install packages 21 | run: | 22 | sudo apt-get update -yq && sudo apt-get install cmake build-essential python3.10 -yq 23 | mkdir asar 24 | 25 | - name: Download and build asar 26 | working-directory: ./asar 27 | run: | 28 | curl -sSL "https://github.com/RPGHacker/asar/archive/v1.81.tar.gz" -o v1.81.tar.gz 29 | tar xfz v1.81.tar.gz 30 | cd asar-1.81 31 | cmake src 32 | make 33 | 34 | - name: Build practice hack patches 35 | run: | 36 | cp ./asar/asar-1.81/asar/asar-standalone ./tools/asar 37 | chmod +x ./build.sh 38 | chmod +x ./build_PAL.sh 39 | ./build.sh 40 | ./build_PAL.sh 41 | mkdir ./web/public/patches 42 | cp build/smhack20_sd2snes.ips ./web/public/patches/sd2snes-ntsc.ips 43 | cp build/smhack20_tinystates.ips ./web/public/patches/tinystates-ntsc.ips 44 | cp build/smhack20.ips ./web/public/patches/emulator-ntsc.ips 45 | cp build/smpalhack20_sd2snes.ips ./web/public/patches/sd2snes-pal.ips 46 | cp build/smpalhack20_tinystates.ips ./web/public/patches/tinystates-pal.ips 47 | cp build/smpalhack20.ips ./web/public/patches/emulator-pal.ips 48 | 49 | - name: Upload artifacts 50 | uses: actions/upload-artifact@v4 51 | with: 52 | name: Patches & Debug Symbols 53 | path: ./build 54 | 55 | - name: Set up node 56 | uses: actions/setup-node@v4 57 | 58 | - name: Build and export web application 59 | working-directory: ./web 60 | run: | 61 | npm i 62 | npx next build 63 | npx next export 64 | touch ./out/.nojekyll 65 | 66 | - name: Deploy 🚀 67 | uses: JamesIves/github-pages-deploy-action@v4 68 | with: 69 | branch: gh-pages # The branch the action should deploy to. 70 | folder: ./web/out/ # The folder the action should deploy. 71 | -------------------------------------------------------------------------------- /.github/workflows/pullrequest.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: PR Build 4 | 5 | # Controls when the action will run. Triggers the workflow on push or pull request 6 | # events but only for the master branch 7 | on: 8 | - pull_request 9 | 10 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | 19 | - name: Install packages 20 | run: | 21 | sudo apt-get update -yq && sudo apt-get install cmake build-essential python3.10 -yq 22 | mkdir asar 23 | 24 | - name: Download and build asar 25 | working-directory: ./asar 26 | run: | 27 | curl -sSL "https://github.com/RPGHacker/asar/archive/v1.81.tar.gz" -o v1.81.tar.gz 28 | tar xfz v1.81.tar.gz 29 | cd asar-1.81 30 | cmake src 31 | make 32 | 33 | - name: Build practice hack patches 34 | run: | 35 | cp ./asar/asar-1.81/asar/asar-standalone ./tools/asar 36 | chmod +x ./build_dev.sh 37 | chmod +x ./build.sh 38 | chmod +x ./build_PAL.sh 39 | ./build_dev.sh 40 | ./build.sh -DPRERELEASE=$(git rev-parse --short HEAD) 41 | ./build_PAL.sh -DPRERELEASE=$(git rev-parse --short HEAD) 42 | mkdir ./web/public/patches 43 | cp build/smhack20_sd2snes.ips ./web/public/patches/sd2snes-ntsc.ips 44 | cp build/smhack20_tinystates.ips ./web/public/patches/tinystates-ntsc.ips 45 | cp build/smhack20.ips ./web/public/patches/emulator-ntsc.ips 46 | cp build/smpalhack20_sd2snes.ips ./web/public/patches/sd2snes-pal.ips 47 | cp build/smpalhack20_tinystates.ips ./web/public/patches/tinystates-pal.ips 48 | cp build/smpalhack20.ips ./web/public/patches/emulator-pal.ips 49 | 50 | - name: Upload artifacts 51 | uses: actions/upload-artifact@v4 52 | with: 53 | name: Patches & Debug Symbols 54 | path: ./build 55 | 56 | - name: Set up node 57 | uses: actions/setup-node@v4 58 | 59 | - name: Build and export web application 60 | working-directory: ./web 61 | run: | 62 | npm i 63 | npx next build 64 | npx next export 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.srm 2 | *.bst 3 | *.s[mf]c 4 | *.sw[op] 5 | build/ 6 | tools/*.exe 7 | tools/asar* 8 | tools/dll/* 9 | .vscode/launch.json 10 | .vscode/tasks.json 11 | /names/custom_names.txt 12 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "git.ignoreLimitWarning": true 3 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-slim-buster AS build-stage 2 | 3 | WORKDIR /app 4 | RUN apt-get update -yq \ 5 | && apt-get install -yq build-essential wget 6 | 7 | RUN python -m pip install --upgrade pip 8 | COPY requirements.txt requirements.txt 9 | RUN pip3 install -r requirements.txt 10 | 11 | WORKDIR /app/asar 12 | RUN wget https://github.com/RPGHacker/asar/archive/v1.81.tar.gz \ 13 | && tar xfz v1.81.tar.gz \ 14 | && cd asar-1.81 \ 15 | && cmake src \ 16 | && make \ 17 | && mkdir -p /app/tools \ 18 | && cp asar/asar-standalone /app/tools/asar 19 | 20 | RUN mkdir -p /app/build \ 21 | && mkdir -p /app/enemies \ 22 | && mkdir -p /app/layout \ 23 | && mkdir -p /app/names \ 24 | && mkdir -p /app/resources \ 25 | && mkdir -p /app/src 26 | 27 | COPY build.sh /app/build.sh 28 | COPY build_dev.sh /app/build_dev.sh 29 | COPY build_PAL.sh /app/build_PAL.sh 30 | 31 | ARG now 32 | 33 | WORKDIR /app 34 | RUN rm -rf build 35 | RUN mkdir -p build 36 | RUN rm -rf enemies 37 | COPY enemies/ enemies/ 38 | RUN rm -rf layout 39 | COPY layout/ layout/ 40 | RUN rm -rf names 41 | COPY names/ names/ 42 | RUN rm -rf resources 43 | COPY resources/ resources/ 44 | RUN rm -rf src 45 | COPY src/ src/ 46 | 47 | RUN ./build_dev.sh 48 | RUN ./build_PAL.sh 49 | RUN ./build.sh 50 | 51 | FROM scratch AS export-stage 52 | COPY --from=build-stage /app/build . 53 | 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Super Metroid Practice Hack 2 | 3 | This page hosts the source material for the Super Metroid Practice Hack. It is intended for users that wish to contribute to the project, customize their own Practice Hacks, adapt it for SM romhacks, or just to see how it works. Visit https://smpractice.speedga.me for details on patching your SM rom with the latest version of InfoHUD. 4 | 5 | ## Getting Started: 6 | 7 | 1. Download asar from https://github.com/RPGHacker/asar and place it in the \tools\ folder. 8 | 2. Download and install Python 3 from https://python.org. Windows users will need to set the PATH environmental variable to point to their Python installation folder. 9 | 3. Run build.bat to create IPS patch files. 10 | 4. Locate the patch files in \build\. 11 | 12 | Alternatively if you can use docker, then running the build_docker script will build the IPS patch files and copy them to \build\. Running it again will rebuild changes in the names, resources, or src directories. 13 | 14 | ## Which rom should I use? 15 | 16 | The build script will create three IPS patch files for the practice hack. The patch that features "sd2snes" in the filename will have the built-in savestate feature enabled. This is only intended for use with the SD2SNES/FXPAK cartridge and will likely cause crashes if used with Everdrives and most emulators (including Virtual Console). The Super NT is compatible, although it may require a firmware update. The patch that features "tinystates" in the filename has a modified version of the savestate feature with wider compatibility with emulators and Everdrives. Virtual Console players (including SNES Classic) will need to use the third option without built-in savestates. 17 | 18 | You will need an IPS patcher utility, such as [Lunar IPS](http://fusoya.eludevisibility.org/lips/) or [Floating IPS](https://www.romhacking.net/utilities/1040/), to apply the patch to your vanilla SM rom. Regardless of which tool you use, the process is the same: you select the .IPS file and a copy of your SM ROM file, and it will replace the ROM with a patched practice hack ROM. Then simply load up the patched ROM in your emulator or flash cart of choice. If all goes well, you're done and can delete the .IPS file if you like. 19 | -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- 1 | 2 | ================================================================ 3 | Porting features from IFB CustomROMBase branch 4 | For other TODO items, recommend using the github issue system 5 | ================================================================ 6 | 7 | ================================================================ 8 | Features that are being ported now 9 | ================================================================ 10 | 11 | - Capture Cropping 12 | 13 | - Count Boss Damage 14 | 15 | - Demo Timer 16 | 17 | - Factory Reset option 18 | 19 | - Force Stand shortcut 20 | 21 | - Infinite Ammo 22 | 23 | - No Steam Collision option 24 | 25 | - Phantoon always visible 26 | 27 | - Random bubble SFX 28 | 29 | - Randomize on load 30 | 31 | - Randomize RNG shortcut 32 | 33 | - Reveal Boss Damage shortcut 34 | 35 | ================================================================ 36 | Features intentionally not ported 37 | This isn't a forever list, nor is it an indictment 38 | Most of these items are simply differences where both work 39 | and there isn't much benefit in porting or backporting them 40 | ================================================================ 41 | 42 | - Resources folder structure and unusued resources 43 | 44 | - Order of included sources and bank freespace locations 45 | 46 | - Structure of defines.asm and main.asm (and separating printdebug.asm from main.asm) 47 | 48 | - The SM Practice Hack title screen (the difference helps identify which practice hack is being used) 49 | 50 | - Various sound effect macros 51 | 52 | - Walk Through Walls option and unfinished dboost infohud mode 53 | 54 | - Detect emulator inaccuracy developer option 55 | 56 | - The taller menu (this is available as a build option) 57 | 58 | - Leading > or arrow on menu items, or menuing layout and text differences in general 59 | 60 | - Differences in how menu options can be scrolled through 61 | 62 | - AREA_ID stored in custom presets 63 | 64 | - Additional custom flags in the preset data 65 | 66 | - Differences in room names 67 | 68 | - Differences in preset file names or organization 69 | 70 | - The separate ih_update_timers routine (too much duplication with ih_update_hud_code) 71 | 72 | - Tiny states fast flag (that was removed from main branch) 73 | 74 | - Differences in how gamemode is implemented 75 | 76 | - Differences in magic/space/loud pants implementation 77 | 78 | - Differences in how scroll offsets are fixed 79 | 80 | - Differences in how WRAM and SRAM are initialized 81 | 82 | - Differences in PAL debug movement implementation 83 | 84 | - Replacing TDC with LDA #$0000 85 | 86 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | echo Building SM NTSC Practice Hack 4 | if not exist build mkdir build 5 | python enemies\create_clear_enemies_data.py ..\src\clearenemies.asm clear_enemies.txt 6 | python layout\create_layout.py portals.txt layoutmenutemplate.asm ..\src\layoutmenu.asm ..\src\layoutportaltables.asm 7 | python names\create_names.py ..\src\roomnames.asm default_names.txt custom_names.txt 8 | cd resources 9 | python create_ram_symbols.py ..\src\defines.asm ..\src\symbols.asm 10 | python create_dummies.py 00.sfc ff.sfc 11 | 12 | echo Building saveless version 13 | if exist ..\build\smhack20.ips del ..\build\smhack20.ips 14 | copy 00.sfc ..\build 15 | copy ff.sfc ..\build 16 | ..\tools\asar --no-title-check --symbols=wla --symbols-path=..\build\symbols.sym -DFEATURE_SD2SNES=0 -DFEATURE_PAL=0 -DFEATURE_TINYSTATES=0 ..\src\main.asm ..\build\00.sfc 17 | if ERRORLEVEL 1 goto end_build_saveless 18 | ..\tools\asar --no-title-check -DFEATURE_SD2SNES=0 -DFEATURE_PAL=0 -DFEATURE_TINYSTATES=0 ..\src\main.asm ..\build\ff.sfc 19 | python dos_to_unix.py ..\build\symbols.sym 20 | python sort_debug_symbols.py ..\build\symbols.sym x ..\build\smhack20.sym 21 | python create_ips.py ..\build\00.sfc ..\build\ff.sfc ..\build\smhack20.ips 22 | :end_build_saveless 23 | 24 | echo Building SD2SNES version 25 | if exist ..\build\smhack20_sd2snes.ips del ..\build\smhack20_sd2snes.ips 26 | copy 00.sfc ..\build 27 | copy ff.sfc ..\build 28 | ..\tools\asar --no-title-check --symbols=wla --symbols-path=..\build\symbols.sym -DFEATURE_SD2SNES=1 -DFEATURE_PAL=0 -DFEATURE_TINYSTATES=0 ..\src\main.asm ..\build\00.sfc 29 | if ERRORLEVEL 1 goto end_build_sd2snes 30 | ..\tools\asar --no-title-check -DFEATURE_SD2SNES=1 -DFEATURE_PAL=0 -DFEATURE_TINYSTATES=0 ..\src\main.asm ..\build\ff.sfc 31 | python dos_to_unix.py ..\build\symbols.sym 32 | python sort_debug_symbols.py ..\build\symbols.sym x ..\build\smhack20_sd2snes.sym 33 | python create_ips.py ..\build\00.sfc ..\build\ff.sfc ..\build\smhack20_sd2snes.ips 34 | :end_build_sd2snes 35 | 36 | echo Building TinyStates version 37 | if exist ..\build\smhack20_tinystates.ips del ..\build\smhack20_tinystates.ips 38 | copy 00.sfc ..\build 39 | copy ff.sfc ..\build 40 | ..\tools\asar --no-title-check --symbols=wla --symbols-path=..\build\symbols.sym -DFEATURE_SD2SNES=0 -DFEATURE_PAL=0 -DFEATURE_TINYSTATES=1 ..\src\main.asm ..\build\00.sfc 41 | if ERRORLEVEL 1 goto end_build_tinystates 42 | ..\tools\asar --no-title-check -DFEATURE_SD2SNES=0 -DFEATURE_PAL=0 -DFEATURE_TINYSTATES=1 ..\src\main.asm ..\build\ff.sfc 43 | python dos_to_unix.py ..\build\symbols.sym 44 | python sort_debug_symbols.py ..\build\symbols.sym x ..\build\smhack20_tinystates.sym 45 | python create_ips.py ..\build\00.sfc ..\build\ff.sfc ..\build\smhack20_tinystates.ips 46 | :end_build_tinystates 47 | 48 | del 00.sfc ff.sfc ..\build\00.sfc ..\build\ff.sfc ..\build\symbols.sym 49 | cd .. 50 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Return an error if any command fails 4 | success=0 5 | trap success=1 ERR 6 | 7 | echo "Building SM NTSC Practice Hack" 8 | mkdir -p build 9 | python3 enemies/create_clear_enemies_data.py ../src/clearenemies.asm clear_enemies.txt 10 | python3 layout/create_layout.py portals.txt layoutmenutemplate.asm ../src/layoutmenu.asm ../src/layoutportaltables.asm 11 | python3 names/create_names.py ../src/roomnames.asm default_names.txt custom_names.txt 12 | cd resources 13 | python3 create_ram_symbols.py ../src/defines.asm ../src/symbols.asm 14 | python3 create_dummies.py 00.sfc ff.sfc 15 | 16 | echo "Building saveless version" 17 | rm -f ../build/smhack20.ips 18 | cp 00.sfc ../build 19 | cp ff.sfc ../build 20 | ../tools/asar --no-title-check --symbols=wla --symbols-path=../build/symbols.sym -DFEATURE_SD2SNES=0 -DFEATURE_PAL=0 -DFEATURE_TINYSTATES=0 "$@" ../src/main.asm ../build/00.sfc 21 | ../tools/asar --no-title-check -DFEATURE_SD2SNES=0 -DFEATURE_PAL=0 -DFEATURE_TINYSTATES=0 "$@" ../src/main.asm ../build/ff.sfc 22 | python3 sort_debug_symbols.py ../build/symbols.sym x ../build/smhack20.sym 23 | python3 create_ips.py ../build/00.sfc ../build/ff.sfc ../build/smhack20.ips 24 | 25 | echo "Building SD2SNES version" 26 | rm -f ../build/smhack20_sd2snes.ips 27 | cp 00.sfc ../build 28 | cp ff.sfc ../build 29 | ../tools/asar --no-title-check --symbols=wla --symbols-path=../build/symbols.sym -DFEATURE_SD2SNES=1 -DFEATURE_PAL=0 -DFEATURE_TINYSTATES=0 "$@" ../src/main.asm ../build/00.sfc 30 | ../tools/asar --no-title-check -DFEATURE_SD2SNES=1 -DFEATURE_PAL=0 -DFEATURE_TINYSTATES=0 "$@" ../src/main.asm ../build/ff.sfc 31 | python3 sort_debug_symbols.py ../build/symbols.sym x ../build/smhack20_sd2snes.sym 32 | python3 create_ips.py ../build/00.sfc ../build/ff.sfc ../build/smhack20_sd2snes.ips 33 | 34 | echo "Building TinyStates version" 35 | rm -f ../build/smhack20_tinystates.ips 36 | cp 00.sfc ../build 37 | cp ff.sfc ../build 38 | ../tools/asar --no-title-check --symbols=wla --symbols-path=../build/symbols.sym -DFEATURE_SD2SNES=0 -DFEATURE_PAL=0 -DFEATURE_TINYSTATES=1 "$@" ../src/main.asm ../build/00.sfc 39 | ../tools/asar --no-title-check -DFEATURE_SD2SNES=0 -DFEATURE_PAL=0 -DFEATURE_TINYSTATES=1 "$@" ../src/main.asm ../build/ff.sfc 40 | python3 sort_debug_symbols.py ../build/symbols.sym x ../build/smhack20_tinystates.sym 41 | python3 create_ips.py ../build/00.sfc ../build/ff.sfc ../build/smhack20_tinystates.ips 42 | 43 | rm 00.sfc ff.sfc ../build/00.sfc ../build/ff.sfc ../build/symbols.sym 44 | cd .. 45 | 46 | exit $success 47 | -------------------------------------------------------------------------------- /build_PAL.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | echo Building SM PAL Practice Hack 4 | if not exist build mkdir build 5 | python enemies\create_clear_enemies_data.py ..\src\clearenemies.asm clear_enemies.txt 6 | python layout\create_layout.py portals.txt layoutmenutemplate.asm ..\src\layoutmenu.asm ..\src\layoutportaltables.asm 7 | python names\create_names.py ..\src\roomnames.asm default_names.txt custom_names.txt 8 | cd resources 9 | python create_ram_symbols.py ..\src\defines.asm ..\src\symbols.asm 10 | python create_dummies.py 00.sfc ff.sfc 11 | 12 | echo Building saveless version 13 | if exist ..\build\smpalhack20.ips del ..\build\smpalhack20.ips 14 | copy 00.sfc ..\build 15 | copy ff.sfc ..\build 16 | ..\tools\asar --no-title-check --symbols=wla --symbols-path=..\build\symbols.sym -DFEATURE_SD2SNES=0 -DFEATURE_PAL=1 -DFEATURE_TINYSTATES=0 ..\src\main.asm ..\build\00.sfc 17 | if ERRORLEVEL 1 goto end_build_saveless 18 | ..\tools\asar --no-title-check -DFEATURE_SD2SNES=0 -DFEATURE_PAL=1 -DFEATURE_TINYSTATES=0 ..\src\main.asm ..\build\ff.sfc 19 | python dos_to_unix.py ..\build\symbols.sym 20 | python sort_debug_symbols.py ..\build\symbols.sym x ..\build\smpalhack20.sym 21 | python create_ips.py ..\build\00.sfc ..\build\ff.sfc ..\build\smpalhack20.ips 22 | :end_build_saveless 23 | 24 | echo Building SD2SNES version 25 | if exist ..\build\smpalhack20_sd2snes.ips del ..\build\smpalhack20_sd2snes.ips 26 | copy 00.sfc ..\build 27 | copy ff.sfc ..\build 28 | ..\tools\asar --no-title-check --symbols=wla --symbols-path=..\build\symbols.sym -DFEATURE_SD2SNES=1 -DFEATURE_PAL=1 -DFEATURE_TINYSTATES=0 ..\src\main.asm ..\build\00.sfc 29 | if ERRORLEVEL 1 goto end_build_sd2snes 30 | ..\tools\asar --no-title-check -DFEATURE_SD2SNES=1 -DFEATURE_PAL=1 -DFEATURE_TINYSTATES=0 ..\src\main.asm ..\build\ff.sfc 31 | python dos_to_unix.py ..\build\symbols.sym 32 | python sort_debug_symbols.py ..\build\symbols.sym x ..\build\smpalhack20_sd2snes.sym 33 | python create_ips.py ..\build\00.sfc ..\build\ff.sfc ..\build\smpalhack20_sd2snes.ips 34 | :end_build_sd2snes 35 | 36 | echo Building TinyStates version 37 | if exist ..\build\smpalhack20_tinystates.ips del ..\build\smpalhack20_tinystates.ips 38 | copy 00.sfc ..\build 39 | copy ff.sfc ..\build 40 | ..\tools\asar --no-title-check --symbols=wla --symbols-path=..\build\symbols.sym -DFEATURE_SD2SNES=0 -DFEATURE_PAL=1 -DFEATURE_TINYSTATES=1 ..\src\main.asm ..\build\00.sfc 41 | if ERRORLEVEL 1 goto end_build_tinystates 42 | ..\tools\asar --no-title-check -DFEATURE_SD2SNES=0 -DFEATURE_PAL=1 -DFEATURE_TINYSTATES=1 ..\src\main.asm ..\build\ff.sfc 43 | python dos_to_unix.py ..\build\symbols.sym 44 | python sort_debug_symbols.py ..\build\symbols.sym x ..\build\smpalhack20_tinystates.sym 45 | python create_ips.py ..\build\00.sfc ..\build\ff.sfc ..\build\smpalhack20_tinystates.ips 46 | :end_build_tinystates 47 | 48 | del 00.sfc ff.sfc ..\build\00.sfc ..\build\ff.sfc ..\build\symbols.sym 49 | cd .. 50 | -------------------------------------------------------------------------------- /build_PAL.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Return an error if any command fails. 4 | success=0 5 | trap success=1 ERR 6 | 7 | echo "Building SM PAL Practice Hack" 8 | mkdir -p build 9 | python3 enemies/create_clear_enemies_data.py ../src/clearenemies.asm clear_enemies.txt 10 | python3 layout/create_layout.py portals.txt layoutmenutemplate.asm ../src/layoutmenu.asm ../src/layoutportaltables.asm 11 | python3 names/create_names.py ../src/roomnames.asm default_names.txt custom_names.txt 12 | cd resources 13 | python3 create_ram_symbols.py ../src/defines.asm ../src/symbols.asm 14 | python3 create_dummies.py 00.sfc ff.sfc 15 | 16 | echo "Building saveless version" 17 | rm -f ../build/smpalhack20.ips 18 | cp 00.sfc ../build 19 | cp ff.sfc ../build 20 | ../tools/asar --no-title-check --symbols=wla --symbols-path=../build/symbols.sym -DFEATURE_SD2SNES=0 -DFEATURE_PAL=1 -DFEATURE_TINYSTATES=0 "$@" ../src/main.asm ../build/00.sfc 21 | ../tools/asar --no-title-check -DFEATURE_SD2SNES=0 -DFEATURE_PAL=1 -DFEATURE_TINYSTATES=0 "$@" ../src/main.asm ../build/ff.sfc 22 | python3 sort_debug_symbols.py ../build/symbols.sym x ../build/smpalhack20.sym 23 | python3 create_ips.py ../build/00.sfc ../build/ff.sfc ../build/smpalhack20.ips 24 | 25 | echo "Building SD2SNES version" 26 | rm -f ../build/smpalhack20_sd2snes.ips 27 | cp 00.sfc ../build 28 | cp ff.sfc ../build 29 | ../tools/asar --no-title-check --symbols=wla --symbols-path=../build/symbols.sym -DFEATURE_SD2SNES=1 -DFEATURE_PAL=1 -DFEATURE_TINYSTATES=0 "$@" ../src/main.asm ../build/00.sfc 30 | ../tools/asar --no-title-check -DFEATURE_SD2SNES=1 -DFEATURE_PAL=1 -DFEATURE_TINYSTATES=0 "$@" ../src/main.asm ../build/ff.sfc 31 | python3 sort_debug_symbols.py ../build/symbols.sym x ../build/smpalhack20_sd2snes.sym 32 | python3 create_ips.py ../build/00.sfc ../build/ff.sfc ../build/smpalhack20_sd2snes.ips 33 | 34 | echo "Building TinyStates version" 35 | rm -f ../build/smpalhack20_tinystates.ips 36 | cp 00.sfc ../build 37 | cp ff.sfc ../build 38 | ../tools/asar --no-title-check --symbols=wla --symbols-path=../build/symbols.sym -DFEATURE_SD2SNES=0 -DFEATURE_PAL=1 -DFEATURE_TINYSTATES=1 "$@" ../src/main.asm ../build/00.sfc 39 | ../tools/asar --no-title-check -DFEATURE_SD2SNES=0 -DFEATURE_PAL=1 -DFEATURE_TINYSTATES=1 "$@" ../src/main.asm ../build/ff.sfc 40 | python3 sort_debug_symbols.py ../build/symbols.sym x ../build/smpalhack20_tinystates.sym 41 | python3 create_ips.py ../build/00.sfc ../build/ff.sfc ../build/smpalhack20_tinystates.ips 42 | 43 | rm 00.sfc ff.sfc ../build/00.sfc ../build/ff.sfc ../build/symbols.sym 44 | cd .. 45 | 46 | exit $success 47 | -------------------------------------------------------------------------------- /build_dev.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cls 3 | 4 | echo Building SM NTSC Dev Practice Hack 5 | if not exist build mkdir build 6 | python enemies\create_clear_enemies_data.py ..\src\clearenemies.asm clear_enemies.txt 7 | python layout\create_layout.py portals.txt layoutmenutemplate.asm ..\src\layoutmenu.asm ..\src\layoutportaltables.asm 8 | python names\create_names.py ..\src\roomnames.asm default_names.txt custom_names.txt 9 | cd resources 10 | python create_ram_symbols.py ..\src\defines.asm ..\src\symbols.asm 11 | python create_dummies.py 00.sfc ff.sfc 12 | 13 | echo Building SD2SNES Dev version 14 | if exist ..\build\print_pc.log del ..\build\print_pc.log 15 | if exist ..\build\smhack20_sd2snes_dev.ips del ..\build\smhack20_sd2snes_dev.ips 16 | copy 00.sfc ..\build 17 | copy ff.sfc ..\build 18 | ..\tools\asar --no-title-check --symbols=wla --symbols-path=..\build\Debugging_Symbols.sym -DFEATURE_SD2SNES=1 -DFEATURE_DEV=1 -DFEATURE_PAL=0 -DFEATURE_TINYSTATES=0 ..\src\main.asm ..\build\00.sfc > ..\build\print_pc.log 2>&1 19 | if ERRORLEVEL 1 goto fail_build_dev 20 | ..\tools\asar --no-title-check -DFEATURE_SD2SNES=1 -DFEATURE_DEV=1 -DFEATURE_PAL=0 -DFEATURE_TINYSTATES=0 ..\src\main.asm ..\build\ff.sfc 21 | python dos_to_unix.py ..\build\Debugging_Symbols.sym 22 | python sort_debug_symbols.py ..\build\Debugging_Symbols.sym ..\build\Debugging_Sorted.sym ..\build\Debugging_Combined.sym 23 | python create_ips.py ..\build\00.sfc ..\build\ff.sfc ..\build\smhack20_sd2snes_dev.ips 24 | goto end_build_dev 25 | 26 | :fail_build_dev 27 | type ..\build\print_pc.log 28 | :end_build_dev 29 | 30 | del 00.sfc ff.sfc ..\build\00.sfc ..\build\ff.sfc 31 | cd .. 32 | 33 | if exist build\smhack20_sd2snes_dev.sfc del build\smhack20_sd2snes_dev.sfc 34 | if not exist build\smhack20_sd2snes_dev.ips goto end_patch_dev 35 | copy "build\Super Metroid NTSC.sfc" build\smhack20_sd2snes_dev.sfc 36 | "tools\Lunar IPS.exe" -ApplyIPS build\smhack20_sd2snes_dev.ips build\smhack20_sd2snes_dev.sfc 37 | :end_patch_dev 38 | 39 | -------------------------------------------------------------------------------- /build_dev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Return an error if any command fails 4 | success=0 5 | trap success=1 ERR 6 | 7 | echo "Building SM NTSC Dev Practice Hack" 8 | mkdir -p build 9 | python3 enemies/create_clear_enemies_data.py ../src/clearenemies.asm clear_enemies.txt 10 | python3 layout/create_layout.py portals.txt layoutmenutemplate.asm ../src/layoutmenu.asm ../src/layoutportaltables.asm 11 | python3 names/create_names.py ../src/roomnames.asm default_names.txt custom_names.txt 12 | cd resources 13 | python3 create_ram_symbols.py ../src/defines.asm ../src/symbols.asm 14 | python3 create_dummies.py 00.sfc ff.sfc 15 | 16 | echo "Building tinystates Dev version" 17 | rm -f ../build/smhack20_tinystates_dev.ips 18 | cp 00.sfc ../build 19 | cp ff.sfc ../build 20 | if git --version &>/dev/null; then 21 | PRERELEASE=$(git rev-parse --short HEAD) 22 | ../tools/asar --no-title-check --symbols=wla --symbols-path=../build/Debugging_Symbols.sym -DFEATURE_SD2SNES=0 -DFEATURE_DEV=1 -DFEATURE_PAL=0 -DFEATURE_TINYSTATES=1 -DPRERELEASE=$PRERELEASE "$@" ../src/main.asm ../build/00.sfc 23 | ../tools/asar --no-title-check -DFEATURE_SD2SNES=0 -DFEATURE_DEV=1 -DFEATURE_PAL=0 -DFEATURE_TINYSTATES=1 -DPRERELEASE=$PRERELEASE "$@" ../src/main.asm ../build/ff.sfc 24 | else 25 | ../tools/asar --no-title-check --symbols=wla --symbols-path=../build/Debugging_Symbols.sym -DFEATURE_SD2SNES=0 -DFEATURE_DEV=1 -DFEATURE_PAL=0 -DFEATURE_TINYSTATES=1 "$@" ../src/main.asm ../build/00.sfc 26 | ../tools/asar --no-title-check -DFEATURE_SD2SNES=0 -DFEATURE_DEV=1 -DFEATURE_PAL=0 -DFEATURE_TINYSTATES=1 "$@" ../src/main.asm ../build/ff.sfc 27 | fi 28 | python3 sort_debug_symbols.py ../build/Debugging_Symbols.sym ../build/Debugging_Sorted.sym ../build/Debugging_Combined.sym 29 | python3 create_ips.py ../build/00.sfc ../build/ff.sfc ../build/smhack20_tinystates_dev.ips 30 | 31 | rm 00.sfc ff.sfc ../build/00.sfc ../build/ff.sfc 32 | cd .. 33 | 34 | exit $success 35 | -------------------------------------------------------------------------------- /build_docker.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | for /f "tokens=2-4 delims=/ " %%a in ('DATE /T') do (set mydate=%%c-%%a-%%b) 3 | if exist build\smhack20.ips del build\smhack20.ips 4 | if exist build\smhack20_sd2snes.ips del build\smhack20_sd2snes.ips 5 | if exist build\smhack20_tinystates.ips del build\smhack20_tinystates.ips 6 | if exist build\smhack20_tinystates_dev.ips del build\smhack20_tinystates_dev.ips 7 | if exist build\smpalhack20.ips del build\smpalhack20.ips 8 | if exist build\smpalhack20_sd2snes.ips del build\smpalhack20_sd2snes.ips 9 | if exist build\smpalhack20_tinystates.ips del build\smpalhack20_tinystates.ips 10 | docker build --tag sm_practice_hack --build-arg now="%mydate%_%TIME%" --output build . 11 | -------------------------------------------------------------------------------- /build_docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -f build/smhack20.ips 3 | rm -f build/smhack20_sd2snes.ips 4 | rm -f build/smhack20_tinystates.ips 5 | rm -f build/smhack20_tinystates_dev.ips 6 | rm -f build/smpalhack20.ips 7 | rm -f build/smpalhack20_sd2snes.ips 8 | rm -f build/smpalhack20_tinystates.ips 9 | docker build --tag sm_practice_hack --build-arg now="$(date +%s)" --output build . 10 | -------------------------------------------------------------------------------- /enemies/clear_enemies.txt: -------------------------------------------------------------------------------- 1 | CEBF=1 ; BOYON (boyon, bouncing gooball) 2 | CEFF=1 ; STOKE (mini-Crocomire) 3 | CF3F=1 ; kame is Japanese for turtle) 4 | CF7F=1 ; (mini-tatori) 5 | CFBF=1 ; PUYO (puyo, thin hopping blobs) 6 | CFFF=1 ; saboten is Japanese for cactus) 7 | D03F=1 ; toge is Japanese for thorn) 8 | D07F=0 ; (gunship top) 9 | D0BF=0 ; (gunship bottom / entrance pad) 10 | D0FF=1 ; MERO (mellow, pre-Bomb Torizo fly) 11 | D13F=1 ; MELLA (mella, under ice beam fly) 12 | D17F=1 ; MEMU (memu, pre-spring ball fly) 13 | D1BF=1 ; MULTI (multiviola, Norfair erratic fireball) 14 | D1FF=1 ; POLYP (lavaquake rocks) 15 | D23F=1 ; rinko is Japanese for outline) 16 | D27F=1 ; RIO (rio, Brinstar flying enemy) 17 | D2BF=1 ; SQUEEWPT (squeept, Norfair lava-jumping enemy) 18 | D2FF=1 ; GERUDA (geruta, Norfair rio) 19 | D33F=1 ; HOLTZ (holtz, Lower Norfair rio) 20 | D37F=1 ; OUM (oum, Maridia large indestructible snails) 21 | D3BF=1 ; hiru is Japanese for leech) 22 | D3FF=1 ; RIPPER2 (gripper, grapplable jet powered ripper) 23 | D43F=1 ; RIPPER2 (ripper ii, jet powered ripper) 24 | D47F=1 ; RIPPER (ripper) 25 | D4BF=1 ; DRAGON (dragon, lava seahorse) 26 | D4FF=0 ; SHUTTER (timed shutter) 27 | D53F=0 ; SHUTTER2 (shootable shutter) 28 | D57F=0 ; SHUTTER2 (horizontal shootable shutter) 29 | D5BF=0 ; SHUTTER2 (destroyable timed shutter) 30 | D5FF=0 ; SHUTTER2 (rising and falling platform) 31 | D63F=1 ; WAVER (waver, wave-like path travelling enemy) 32 | D67F=1 ; METALEE (metaree, metal skree) 33 | D6BF=1 ; hotaru is Japanese for firefly) 34 | D6FF=1 ; FISH (skultera, Maridia fish) 35 | D73F=0 ; (elevator) 36 | D77F=1 ; kani is Japanese for crab) 37 | D7BF=1 ; OUMU (zero, slug) 38 | D7FF=0 ; KAMER (tripper, fast-moving slowly-sinking platform) 39 | D83F=0 ; KAMER (platform that falls with Samus' weight) 40 | D87F=1 ; SBUG (roach, flies away from Samus) 41 | D8BF=1 ; SBUG (roach, unused/broken) 42 | D8FF=1 ; METMOD (mochtroid, pre-Botwoon fake metroids) 43 | D93F=1 ; SSIDE (sidehopper) 44 | D97F=1 ; SDEATH (desgeega) 45 | D9BF=1 ; SIDE (super-sidehopper) 46 | D9FF=1 ; SIDE (Tourian super-sidehopper) 47 | DA3F=1 ; DESSGEEGA (super-desgeega) 48 | DA7F=1 ; ZOA (zoa, Maridia refill candy) 49 | DABF=1 ; VIOLA (viola, Norfair slow fireball) 50 | DAFF=1 ; (respawning enemy placeholder) 51 | DB3F=1 ; BANG (bang) 52 | DB7F=1 ; SKREE (skree) 53 | DBBF=1 ; YARD (yard, Maridia snail) 54 | DBFF=1 ; REFLEC (reflec) 55 | DC3F=0 ; HZOOMER (Wrecked Ship orange zoomer) 56 | DC7F=1 ; ZEELA (zeela, big eye bugs) 57 | DCBF=1 ; NOVA (sova, fire zoomer) 58 | DCFF=1 ; ZOOMER (zoomer) 59 | DD3F=1 ; MZOOMER (stone zoomer) 60 | DD7F=1 ; METROID (metroid) 61 | DDBF=0 ; (Crocomire) 62 | DDFF=0 ; (Crocomire's tongue) 63 | DE3F=0 ; (Draygon body) 64 | DE7F=0 ; (Draygon eye) 65 | DEBF=0 ; (Draygon tail) 66 | DEFF=0 ; (Draygon arms) 67 | DF3F=0 ; (Spore Spawn) 68 | DF7F=0 ; (Spore Spawn) 69 | DFBF=1 ; RSTONE (boulder) 70 | DFFF=0 ; KZAN (spikey platform top) 71 | E03F=0 ; (spikey platform bottom) 72 | E07F=0 ; HIBASHI (fire geyser) 73 | E0BF=1 ; PUROMI (nuclear waffle) 74 | E0FF=1 ; SCLAYD (mini-Kraid) 75 | E13F=0 ; (Ceres Ridley) 76 | E17F=0 ; (Ridley) 77 | E1BF=0 ; (Ridley's explosion) 78 | E1FF=1 ; (Ceres steam) 79 | E23F=0 ; (Ceres door) 80 | E27F=0 ; (zebetites) 81 | E2BF=0 ; (Kraid) 82 | E2FF=0 ; (Kraid's arm) 83 | E33F=0 ; (Kraid top lint) 84 | E37F=0 ; (Kraid middle lint) 85 | E3BF=0 ; (Kraid bottom lint) 86 | E3FF=0 ; (Kraid's foot) 87 | E43F=0 ; (Kraid good fingernail) 88 | E47F=0 ; (Kraid bad fingernail) 89 | E4BF=0 ; (Phantoon body) 90 | E4FF=0 ; (Phantoon eye) 91 | E53F=0 ; (Phantoon tentacles) 92 | E57F=0 ; (Phantoon mouth) 93 | E5BF=0 ; (etecoon) 94 | E5FF=0 ; (dachora) 95 | E63F=1 ; ebi is Japanese for prawn) 96 | E67F=1 ; (evir projectile) 97 | E6BF=0 ; EYE (morph ball eye) 98 | E6FF=1 ; fune is Japanese for boat) 99 | E73F=0 ; nami is Japanese for wave) 100 | E77F=1 ; GAI (coven, Wrecked Ship ghost) 101 | E7BF=1 ; HAND (yapping maw) 102 | E7FF=1 ; kago is Japanese for cage) 103 | E83F=1 ; LAVAMAN (magdollite, Norfair lava creature) 104 | E87F=1 ; nomi is Japanese for flea) 105 | E8BF=1 ; PUU (powamp, Maridia floater) 106 | E8FF=0 ; ROBO (Wrecked Ship robot) 107 | E93F=0 ; ROBO2 (Wrecked Ship robot, deactivated) 108 | E97F=1 ; PIPE (bull, Maridia puffer) 109 | E9BF=1 ; NDRA (alcoon, walking lava seahorse) 110 | E9FF=1 ; ATOMIC (atomic, Wrecked Ship orbs) 111 | EA3F=0 ; SPA (Wrecked Ship spark) 112 | EA7F=0 ; KOMA (blue Brinstar face block) 113 | EABF=1 ; hachi is Japanese for bee) 114 | EAFF=1 ; (green ki-hunter wings) 115 | EB3F=1 ; HACHI2 (yellow ki-hunter) 116 | EB7F=1 ; (yellow ki-hunter wings) 117 | EBBF=1 ; HACHI3 (red ki-hunter) 118 | EBFF=1 ; (red ki-hunter wings) 119 | EC3F=0 ; (Mother Brain's brain) 120 | EC7F=0 ; (Mother Brain's body) 121 | ECBF=0 ; (Shitroid in cutscene) 122 | ECFF=0 ; (Mother Brain's tubes falling) 123 | ED3F=0 ; (dead Torizo) 124 | ED7F=0 ; (dead sidehopper) 125 | EDBF=0 ; (dead sidehopper, part 2). Used only to load more tiles 126 | EDFF=0 ; (dead zoomer) 127 | EE3F=0 ; (dead ripper) 128 | EE7F=0 ; (dead skree) 129 | EEBF=0 ; (Shitroid) 130 | EEFF=0 ; (Bomb Torizo) 131 | EF3F=0 ; (Bomb Torizo orb) 132 | EF7F=0 ; (Gold Torizo) 133 | EFBF=0 ; (Gold Torizo orb) 134 | EFFF=0 ; (Tourian entrance statue) 135 | F03F=0 ; (Tourian entrance statue ghost) 136 | F07F=0 ; doriyuku is Japanese for effort) 137 | F0BF=0 ; (n00b tube cracks) 138 | F0FF=0 ; (Chozo statue) 139 | F153=0 ; (unused spinning turtle eye: just runs a graphics instruction loop) 140 | F193=1 ; ZEB (zeb, Brinstar red pipe bug) 141 | F1D3=1 ; ZEBBO (zebbo, Brinstar green pipe bug) 142 | F213=1 ; GAMET (gamet, Norfair pipe bug) 143 | F253=1 ; GEEGA (geega, Brinstar yellow pipe bug) 144 | F293=0 ; BOTOON (Botwoon) 145 | F2D3=0 ; (escape etecoon) 146 | F313=0 ; (escape dachora) 147 | F353=1 ; BATTA1 (old Tourian grey wall space pirate) 148 | F393=1 ; BATTA1Br (Kraid green wall space pirate) 149 | F3D3=1 ; BATTA1No (Norfair red wall space pirate) 150 | F413=1 ; BATTA1Na (lower Norfair gold wall space pirate) 151 | F453=1 ; BATTA1Ma (Maridia wall space pirate) 152 | F493=1 ; BATTA1Tu (escape silver wall space pirate) 153 | F4D3=1 ; BATTA2 (grey ninja space pirate) 154 | F513=1 ; BATTA2Br (green ninja space pirate) 155 | F553=1 ; BATTA2No (red ninja space pirate) 156 | F593=1 ; BATTA2Na (gold ninja space pirate) 157 | F5D3=1 ; BATTA2Ma (magenta ninja space pirate) 158 | F613=1 ; BATTA2Tu (escape silver ninja space pirate) 159 | F653=1 ; BATTA3 (grey walking space pirate) 160 | F693=1 ; BATTA3Br (green walking space pirate) 161 | F6D3=1 ; BATTA3No (red walking space pirate) 162 | F713=1 ; BATTA3Na (gold walking space pirate) 163 | F753=1 ; BATTA3Ma (magenta walking space pirate) 164 | F793=1 ; BATTA3Tu (escape silver walking space pirate) 165 | -------------------------------------------------------------------------------- /enemies/create_clear_enemies_data.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import io 3 | import os 4 | import sys 5 | 6 | if len(sys.argv) != 3: 7 | print("create_clear_enemies_data.py ") 8 | sys.exit() 9 | 10 | output_asm = sys.argv[1] 11 | input_txt = sys.argv[2] 12 | 13 | f_input = io.open(os.path.join(os.path.dirname(os.path.realpath(__file__)), input_txt), "r") 14 | input_lines = f_input.readlines() 15 | f_input.close() 16 | 17 | enemies = {} 18 | 19 | for line in input_lines: 20 | parts = line.split('=') 21 | if len(parts) == 2: 22 | enemy_id = parts[0].strip() 23 | if len(enemy_id) == 4: 24 | shifted_enemy_id = (int(enemy_id, 16) + 196605) >> 2 25 | enemies[shifted_enemy_id] = parts[1].rstrip() 26 | 27 | f_output = io.open(os.path.join(os.path.dirname(os.path.realpath(__file__)), output_asm), "w", newline='\n') 28 | f_output.write("\n; ---------------") 29 | f_output.write("\n; Clear Enemies") 30 | f_output.write("\n; (autogenerated)") 31 | f_output.write("\n; ---------------") 32 | 33 | last_enemy_id = None 34 | for enemy_id in sorted(enemies): 35 | if not last_enemy_id: 36 | f_output.write("\n\n%startfree(E5)\n") 37 | f_output.write("ClearEnemiesTable:\n\n") 38 | enemy_id_pal = enemy_id + 8 39 | f_output.write("") 40 | f_output.write(f'if !FEATURE_PAL\norg $E5{enemy_id_pal:04X}\n') 41 | f_output.write(f'else\norg $E5{enemy_id:04X}\nendif\n') 42 | f_output.write("dw $000" + enemies[enemy_id] + "\n\n") 43 | last_enemy_id = enemy_id 44 | 45 | f_output.write("%endfree(E5)\n\n") 46 | f_output.close() 47 | 48 | -------------------------------------------------------------------------------- /names/create_names.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import io 3 | import os 4 | import re 5 | import sys 6 | 7 | if len(sys.argv) < 3 or len(sys.argv) > 4: 8 | print("create_names.py ") 9 | sys.exit() 10 | 11 | output_asm = sys.argv[1] 12 | default_txt = sys.argv[2] 13 | custom_txt = "" 14 | if len(sys.argv) > 3: 15 | custom_txt = sys.argv[3] 16 | 17 | f_default = io.open(os.path.join(os.path.dirname(os.path.realpath(__file__)), default_txt), "r") 18 | default_lines = f_default.readlines() 19 | f_default.close() 20 | 21 | custom_lines = [] 22 | if custom_txt != "": 23 | custom_filepath = os.path.join(os.path.dirname(os.path.realpath(__file__)), custom_txt) 24 | if os.path.isfile(custom_filepath): 25 | f_custom = io.open(custom_filepath, "r") 26 | custom_lines = f_custom.readlines() 27 | f_custom.close() 28 | 29 | names = {} 30 | labels = {} 31 | 32 | for line in default_lines: 33 | parts = line.split('=') 34 | if len(parts) == 2: 35 | room_id = parts[0].strip() 36 | if len(room_id) == 4: 37 | names[room_id] = parts[1].strip() 38 | labels[room_id] = ''.join(s[:1].upper() + s[1:] for s in re.split(r"[ '-\.]", names[room_id].replace("'s", ""))) 39 | 40 | for line in custom_lines: 41 | parts = line.split('=') 42 | if len(parts) == 2: 43 | room_id = parts[0].strip() 44 | if room_id in names: 45 | names[room_id] = parts[1].strip() 46 | 47 | f_output = io.open(os.path.join(os.path.dirname(os.path.realpath(__file__)), output_asm), "w", newline='\n') 48 | f_output.write("\n; ---------------") 49 | f_output.write("\n; Room Names") 50 | f_output.write("\n; (autogenerated)") 51 | f_output.write("\n; ---------------") 52 | f_output.write("\n\n%startfree(E5)\n") 53 | f_output.write("RoomNameTextTable:\n") 54 | f_output.write("ROOM:\n\n") 55 | 56 | max_characters = 24 57 | max_roomname_space = max_characters + 2 58 | last_room_id = None 59 | for room_id in sorted(names): 60 | if last_room_id: 61 | last_room_end = int(last_room_id, 16) + max_roomname_space 62 | if last_room_end > int(room_id, 16): 63 | last_room_end = room_id 64 | f_output.write("warnpc $E5" + f'{last_room_end:04X}' + "\n\n") 65 | f_output.write("org $E5" + room_id + "\n") 66 | f_output.write(" ." + labels[room_id] + "\n") 67 | f_output.write(" db $28, \"" + names[room_id] + "\", $FF\n") 68 | last_room_id = room_id 69 | 70 | if last_room_id: 71 | last_room_end = int(last_room_id, 16) + max_characters 72 | f_output.write("warnpc $E5" + f'{last_room_end:04X}' + "\n\n") 73 | 74 | f_output.write("%endfree(E5)\n\n") 75 | f_output.close() 76 | 77 | -------------------------------------------------------------------------------- /names/default_names.txt: -------------------------------------------------------------------------------- 1 | 91F8=Landing Site 2 | 92B3=Gauntlet Entrance 3 | 92FD=Parlor and Alcatraz 4 | 93AA=Crateria Power Bombs 5 | 93D5=Crateria Save Room 6 | 93FE=West Ocean 7 | 9461=Bowling Alley Path 8 | 948C=Crateria Kihunter Room 9 | 94CC=Forgotten Highway Elev 10 | 94FD=East Ocean 11 | 9552=Forgotten Highway Kagos 12 | 957D=Crab Maze 13 | 95A8=Forgotten Highway Elbow 14 | 95D4=Crateria Tube 15 | 95FF=The Moat 16 | 962A=Red Brinstar Elev Room 17 | 965B=Gauntlet E-Tank Room 18 | 968F=Pre-Bowling 19 | 96BA=Climb 20 | 975C=Pit Room 21 | 97B5=Elev to Blue Brinstar 22 | 9804=Bomb Torizo Room 23 | 9879=Flyway 24 | 98E2=Pre-Map Flyway 25 | 990D=Terminator Room 26 | 9938=Green Brin Elev Room 27 | 9969=Lower Mushrooms 28 | 9994=Crateria Map Room 29 | 99BD=Green Pirates Shaft 30 | 99F9=Crateria Super Room 31 | 9A44=Final Missile Bombway 32 | 9A90=The Final Missile 33 | 9AD9=Green Brin Main Shaft 34 | 9B5B=Spore Spawn Super Room 35 | 9B9D=Brinstar Pre-Map Room 36 | 9BC8=Early Supers Room 37 | 9C07=Brinstar Reserve Tank 38 | 9C35=Brinstar Map Room 39 | 9C5E=Green Brin Firefleas 40 | 9C89=Green Brin Missile Refi 41 | 9CB3=Dachora Room 42 | 9D19=Big Pink 43 | 9D9C=Spore Spawn Kihunters 44 | 9DC7=Spore Spawn Room 45 | 9E11=Pink Brin Power Bombs 46 | 9E52=Green Hill Zone 47 | 9E9F=Morph Ball Room 48 | 9F11=Construction Zone 49 | 9F64=Blue Brinstar E-Tank 50 | 9FBA=Noob Bridge 51 | 9FE5=Green Brinstar Beetoms 52 | A011=Etecoon E-Tank Room 53 | A051=Etecoon Super Room 54 | A07B=Dachora Energy Refill 55 | A0A4=Spore Spawn Farming 56 | A0D2=Waterway E-Tank Room 57 | A107=First Missile Room 58 | A130=Pink Brinstar Hoppers 59 | A15B=Hopper Energy Tank Room 60 | A184=Big Pink Save Room 61 | A1AD=Blue Brinstar Boulders 62 | A1D8=Billy Mays Room 63 | A201=Green Brin Save Room 64 | A22A=Etecoon Save Room 65 | A253=Red Tower 66 | A293=Red Brinstar Firefleas 67 | A2CE=X-Ray Scope Room 68 | A2F7=Hellway 69 | A322=Caterpillar Room 70 | A37C=Beta Power Bomb Room 71 | A3AE=Alpha Power Bomb Room 72 | A3DD=Skree Boost Room 73 | A408=Below Spazer 74 | A447=Spazer Room 75 | A471=Warehouse Zeela Room 76 | A4B1=Warehouse E-Tank Room 77 | A4DA=Warehouse Kihunter Room 78 | A521=Baby Kraid Room 79 | A56B=Kraid Eye Door Room 80 | A59F=Kraid Room 81 | A5ED=Statues Hallway 82 | A618=Sloaters Refill 83 | A641=Kraid Refill Stations 84 | A66A=Statues Room 85 | A6A1=Warehouse Entrance 86 | A6E2=Varia Suit Room 87 | A70B=Warehouse Save Room 88 | A734=Red Brinstar Save Room 89 | A75D=Ice Beam Acid Room 90 | A788=Cathedral 91 | A7B3=Cathedral Entrance 92 | A7DE=Business Center 93 | A815=Ice Beam Gate Room 94 | A865=Ice Beam Tutorial Room 95 | A890=Ice Beam Room 96 | A8B9=Ice Beam Snake Room 97 | A8F8=Crumble Shaft 98 | A923=Crocomire Speedway 99 | A98D=Crocomire's Room 100 | A9E5=Hi Jump Boots Room 101 | AA0E=Crocomire Escape 102 | AA41=Hi Jump E-Tank Room 103 | AA82=Post Croc Farming Room 104 | AAB5=Post Croc Save Room 105 | AADE=Post Croc Power Bombs 106 | AB07=Post Crocomire Shaft 107 | AB3B=Post Croc Missile Room 108 | AB64=Grapple Tutorial Room 3 109 | AB8F=Post Croc Jump Room 110 | ABD2=Grapple Tutorial Room 2 111 | AC00=Grapple Tutorial Room 1 112 | AC2B=Grapple Beam Room 113 | AC5A=Norfair Reserve Tank 114 | AC83=Green Bubbles Missiles 115 | ACB3=Bubble Mountain 116 | ACF0=Speed Booster Hall 117 | AD1B=Speed Booster Room 118 | AD5E=Single Chamber 119 | ADAD=Double Chamber 120 | ADDE=Wave Beam Room 121 | AE07=Spiky Platforms Tunnel 122 | AE32=Volcano Room 123 | AE74=Kronic Boost Room 124 | AEB4=Magdollite Tunnel 125 | AEDF=Purple Shaft 126 | AF14=Lava Dive Room 127 | AF3F=Lower Norfair Elevator 128 | AF72=Upper Norf Farming Room 129 | AFA3=Rising Tide 130 | AFCE=Acid Snakes Tunnel 131 | AFFB=Spiky Acid Snakes Tunnel 132 | B026=Nutella Refill 133 | B051=Purple Farming Room 134 | B07A=Bat Cave 135 | B0B4=Norfair Map Room 136 | B0DD=Bubble Mountain Save 137 | B106=Frog Speedway 138 | B139=Red Pirate Shaft 139 | B167=Frog Savestation 140 | B192=Crocomire Save Room 141 | B1BB=LN Elevator Save Room 142 | B1E5=Acid Statue Room 143 | B236=Main Hall 144 | B283=Golden Torizo's Room 145 | B2DA=Fast Ripper Room 146 | B305=GT Energy Recharge 147 | B32E=Ridley's Room 148 | B37A=LN Farming Room 149 | B3A5=Fast Pillars Setup Room 150 | B40A=Mickey Mouse Room 151 | B457=Pillar Room 152 | B482=Plowerhouse Room 153 | B4AD=Worst Room In The Game 154 | B4E5=Amphitheatre 155 | B510=LN Spring Ball Maze 156 | B55A=LN Escape Power Bombs 157 | B585=Red Kihunter Shaft 158 | B5D5=Wasteland 159 | B62B=Metal Pirates Room 160 | B656=Three Musketeers' Room 161 | B698=Ridley Tank Room 162 | B6C1=Screw Attack Room 163 | B6EE=LN Fireflea Room 164 | B741=Lower Norfair Save Room 165 | C98E=Bowling Alley 166 | CA08=Wrecked Ship Entrance 167 | CA52=Attic 168 | CAAE=Assembly Line 169 | CAF6=Wrecked Ship Main Shaft 170 | CB8B=Spiky Death Room 171 | CBD5=Electric Death Room 172 | CC27=Wrecked Ship E-Tank 173 | CC6F=Basement 174 | CCCB=Wrecked Ship Map Room 175 | CD13=Phantoon's Room 176 | CD5C=Sponge Bath 177 | CDA8=WS West Super Room 178 | CDF1=WS East Super Room 179 | CE40=Gravity Suit Room 180 | CE8A=Wrecked Ship Save Room 181 | CED2=Glass Tunnel Save Room 182 | CEFB=Glass Tunnel 183 | CF54=West Tunnel 184 | CF80=East Tunnel 185 | CFC9=Main Street 186 | D017=Fish Tank 187 | D055=Mama Turtle Room 188 | D08A=Crab Tunnel 189 | D0B9=Mt. Everest 190 | D104=Red Fish Room 191 | D13B=Watering Hole 192 | D16D=N.West Maridia Bug Room 193 | D1A3=Crab Shaft 194 | D1DD=Pseudo Plasma Spark 195 | D21C=Crab Hole 196 | D252=West Sand Hall Tunnel 197 | D27E=Plasma Tutorial Room 198 | D2AA=Plasma Room 199 | D2D9=Thread The Needle Room 200 | D30B=Maridia Elevator Room 201 | D340=Plasma Spark Room 202 | D387=Kassiuz Room 203 | D3B6=Maridia Map Room 204 | D3DF=Forgotten Highway Save 205 | D408=Toilet 206 | D433=Bug Sand Hole 207 | D461=West Sand Hall 208 | D48E=Oasis 209 | D4C2=East Sand Hall 210 | D4EF=West Sand Hole 211 | D51E=East Sand Hole 212 | D54D=West Aqueduct Quicksand 213 | D57A=East Aqueduct Quicksand 214 | D5A7=Aqueduct 215 | D5EC=Butterfly Room 216 | D617=Botwoon Hallway 217 | D646=Pants Room 218 | D69A=East Pants Room 219 | D6D0=Spring Ball Room 220 | D6FD=Below Botwoon E-Tank 221 | D72A=Colosseum 222 | D765=Aqueduct Save Room 223 | D78F=The Precious Room 224 | D7E4=Botwoon E-Tank Room 225 | D81A=Draygon Save Room 226 | D845=Maridia Missile Refill 227 | D86E=Plasma Beach Quicksand 228 | D898=Botwoon Quicksand Room 229 | D8C5=Shaktool Room 230 | D913=Halfie Climb Room 231 | D95E=Botwoon's Room 232 | D9AA=Space Jump Room 233 | D9D4=Maridia Energy Refill 234 | D9FE=West Cactus Alley Room 235 | DA2B=East Cactus Alley Room 236 | DA60=Draygon's Room 237 | DAAE=Tourian First Room 238 | DAE1=Metroid Room 1 239 | DB31=Metroid Room 2 240 | DB7D=Metroid Room 3 241 | DBCD=Metroid Room 4 242 | DC19=Blue Hopper Room 243 | DC65=Dust Torizo Room 244 | DCB1=Big Boy Room 245 | DCFF=Seaweed Room 246 | DD2E=Tourian Recharge Room 247 | DD58=Mother Brain Room 248 | DDC4=Tourian Eye Door Room 249 | DDF3=Rinka Shaft 250 | DE23=Mother Brain Save Room 251 | DE4D=Tourian Escape Room 1 252 | DE7A=Tourian Escape Room 2 253 | DEA7=Tourian Escape Room 3 254 | DEDE=Tourian Escape Room 4 255 | DF1B=Upper Tourian Save Room 256 | DF45=Ceres Elevator Room 257 | DF8D=Falling Tile Room 258 | DFD7=Magnet Stairs Room 259 | E021=Dead Scientist Room 260 | E06B=58 Escape 261 | E0B5=Ceres Ridley's Room 262 | E82C=Debug Room 263 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | cmake==3.22.6 2 | 3 | -------------------------------------------------------------------------------- /resources/HUDfont.tbl: -------------------------------------------------------------------------------- 1 | =4E 2 | <=60 3 | ^=61 4 | >=62 5 | v=63 6 | -=CF 7 | A=E0 8 | B=E1 9 | C=E2 10 | D=E3 11 | E=E4 12 | F=E5 13 | G=E6 14 | H=E7 15 | I=E8 16 | J=E9 17 | K=EA 18 | L=EB 19 | M=EC 20 | N=ED 21 | O=EE 22 | P=EF 23 | Q=F0 24 | R=F1 25 | S=F2 26 | T=F3 27 | U=F4 28 | V=F5 29 | W=F6 30 | X=F7 31 | Y=F8 32 | Z=F9 33 | .=FA 34 | ?=FE 35 | -------------------------------------------------------------------------------- /resources/bk2_demo_to_input_log.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import io 3 | import os 4 | import sys 5 | 6 | if len(sys.argv) != 3: 7 | print("bk2_demo_to_input_log.py ") 8 | sys.exit() 9 | else: 10 | asm_name = sys.argv[1] 11 | output_name = sys.argv[2] 12 | 13 | asm_file = io.open(os.path.join(os.path.dirname(os.path.realpath(__file__)), asm_name), "r") 14 | asm_lines = asm_file.readlines() 15 | asm_file.close() 16 | 17 | output_file = io.open(os.path.join(os.path.dirname(os.path.realpath(__file__)), output_name), "w", newline='\n') 18 | output_file.write("\n; ---------------") 19 | output_file.write("\n; Log Export") 20 | output_file.write("\n; (autogenerated)") 21 | output_file.write("\n; ---------------\n\n") 22 | 23 | demo_input_started = False 24 | for line in asm_lines: 25 | if line.startswith("DemoInputInstructionLists"): 26 | demo_input_started = True 27 | if demo_input_started: 28 | if len(line) < 20 or not line.startswith(" dw $"): 29 | output_file.write(line) 30 | else: 31 | count = int(line[8:12], 16) 32 | keys = int(line[15:19], 16) 33 | output_line = bytearray("|..|............|\n", "ascii") 34 | if keys >= 0x8000: 35 | keys -= 0x8000 36 | output_line[11] = ord('B') 37 | if keys >= 0x4000: 38 | keys -= 0x4000 39 | output_line[10] = ord('Y') 40 | if keys >= 0x2000: 41 | keys -= 0x2000 42 | output_line[8] = ord('s') 43 | if keys >= 0x1000: 44 | keys -= 0x1000 45 | output_line[9] = ord('S') 46 | if keys >= 0x800: 47 | keys -= 0x800 48 | output_line[4] = ord('U') 49 | if keys >= 0x400: 50 | keys -= 0x400 51 | output_line[5] = ord('D') 52 | if keys >= 0x200: 53 | keys -= 0x200 54 | output_line[6] = ord('L') 55 | if keys >= 0x100: 56 | keys -= 0x100 57 | output_line[7] = ord('R') 58 | if keys >= 0x80: 59 | keys -= 0x80 60 | output_line[13] = ord('A') 61 | if keys >= 0x40: 62 | keys -= 0x40 63 | output_line[12] = ord('X') 64 | if keys >= 0x20: 65 | keys -= 0x20 66 | output_line[14] = ord('l') 67 | if keys >= 0x10: 68 | keys -= 0x10 69 | output_line[15] = ord('r') 70 | finished_line = output_line.decode() 71 | while count > 0: 72 | output_file.write(finished_line) 73 | count -= 1 74 | 75 | output_file.close() 76 | 77 | -------------------------------------------------------------------------------- /resources/bk2_input_log_to_demo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import io 3 | import os 4 | import sys 5 | 6 | if len(sys.argv) != 3: 7 | print("bk2_input_log_to_demo.py ") 8 | sys.exit() 9 | else: 10 | input_name = sys.argv[1] 11 | demo_name = sys.argv[2] 12 | 13 | input_file = io.open(os.path.join(os.path.dirname(os.path.realpath(__file__)), input_name), "r") 14 | input_lines = input_file.readlines() 15 | input_file.close() 16 | 17 | demo_file = io.open(os.path.join(os.path.dirname(os.path.realpath(__file__)), demo_name), "w", newline='\n') 18 | demo_file.write("\n; ---------------") 19 | demo_file.write("\n; Demo Export") 20 | demo_file.write("\n; (autogenerated)") 21 | demo_file.write("\n; ---------------\n\n") 22 | 23 | current_input = 0 24 | previous_line = None 25 | previous_count = 0 26 | for line in input_lines: 27 | if len(line) < 17 or '|' != line[0] or '|' != line[3] or '|' != line[16]: 28 | if previous_count > 0: 29 | demo_file.write(f' dw ${previous_count:04X}, ${current_input:04X} ; {previous_line[4:16]}\n') 30 | previous_line = None 31 | previous_count = 0 32 | demo_file.write(line) 33 | else: 34 | if previous_count > 0 and previous_line != line: 35 | demo_file.write(f' dw ${previous_count:04X}, ${current_input:04X} ; {previous_line[4:16]}\n') 36 | previous_line = None 37 | previous_count = 0 38 | if previous_line is None: 39 | previous_line = line 40 | current_input = 0 41 | if 'U' == line[4]: 42 | current_input += 0x800 43 | if 'D' == line[5]: 44 | current_input += 0x400 45 | if 'L' == line[6]: 46 | current_input += 0x200 47 | if 'R' == line[7]: 48 | current_input += 0x100 49 | if 's' == line[8]: 50 | current_input += 0x2000 51 | if 'S' == line[9]: 52 | current_input += 0x1000 53 | if 'Y' == line[10]: 54 | current_input += 0x4000 55 | if 'B' == line[11]: 56 | current_input += 0x8000 57 | if 'X' == line[12]: 58 | current_input += 0x40 59 | if 'A' == line[13]: 60 | current_input += 0x80 61 | if 'l' == line[14]: 62 | current_input += 0x20 63 | if 'r' == line[15]: 64 | current_input += 0x10 65 | previous_count = previous_count + 1 66 | if previous_count > 0: 67 | demo_file.write(f' dw ${previous_count:04X}, ${current_input:04X} ; {previous_line[4:16]}\n\n') 68 | 69 | demo_file.close() 70 | 71 | -------------------------------------------------------------------------------- /resources/cm_brb_gfx.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/cm_brb_gfx.bin -------------------------------------------------------------------------------- /resources/cm_gfx.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/cm_gfx.bin -------------------------------------------------------------------------------- /resources/cm_gfx2.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/cm_gfx2.bin -------------------------------------------------------------------------------- /resources/create_dummies.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | import os 4 | 5 | if len(sys.argv) != 3: 6 | print("create_dummies.py <00_file> ") 7 | sys.exit() 8 | else: 9 | zero_name = sys.argv[1] 10 | ff_name = sys.argv[2] 11 | 12 | fo_z = open(os.path.join(os.path.dirname(os.path.realpath(__file__)), zero_name), "wb") 13 | fo_f = open(os.path.join(os.path.dirname(os.path.realpath(__file__)), ff_name), "wb") 14 | 15 | fo_z.write(bytearray([0x00] * 1024 * 1024 * 4)) 16 | fo_f.write(bytearray([0xff] * 1024 * 1024 * 4)) 17 | 18 | fo_z.close() 19 | fo_f.close() 20 | -------------------------------------------------------------------------------- /resources/create_ips.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | import os 4 | 5 | if len(sys.argv) != 4: 6 | print("ips.py <00_file> ") 7 | sys.exit() 8 | else: 9 | zero_name = sys.argv[1] 10 | ff_name = sys.argv[2] 11 | output_name = sys.argv[3] 12 | 13 | f_zero = open(os.path.join(os.path.dirname(os.path.realpath(__file__)), zero_name), "rb") 14 | f_ff = open(os.path.join(os.path.dirname(os.path.realpath(__file__)), ff_name), "rb") 15 | fo = open(os.path.join(os.path.dirname(os.path.realpath(__file__)), output_name), "wb") 16 | 17 | d_zero = f_zero.read() 18 | d_ff = f_ff.read() 19 | patches = {} 20 | 21 | for i in range(0, len(d_zero), 1): 22 | if d_zero[i] == d_ff[i]: 23 | patches[i] = [d_zero[i]] 24 | 25 | base_m = -100 26 | prev_m = -100 27 | for m in list(patches.keys()): 28 | if prev_m == m - 1: 29 | if len(patches[base_m]) < 0xFFFF: 30 | patches[base_m] += patches[m] 31 | del patches[m] 32 | prev_m = m 33 | else: 34 | base_m = m 35 | prev_m = m 36 | else: 37 | base_m = m 38 | prev_m = m 39 | 40 | d_patch = [] 41 | for k in patches: 42 | l = len(patches[k]) 43 | d_patch += [(k >> 16) & 0xff, (k >> 8) & 0xFF, k & 0xFF, (l >> 8) & 0xFF, l & 0xFF] + patches[k] 44 | 45 | 46 | d_patch = [0x50, 0x41, 0x54, 0x43, 0x48] + d_patch + [0x45, 0x4f, 0x46] 47 | fo.write(bytearray(d_patch)) 48 | fo.close() 49 | f_ff.close() 50 | f_zero.close() 51 | -------------------------------------------------------------------------------- /resources/create_ram_symbols.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import io 3 | import os 4 | import sys 5 | 6 | if len(sys.argv) != 3: 7 | print("create_ram_symbols.py ") 8 | sys.exit() 9 | else: 10 | defines_name = sys.argv[1] 11 | new_symbols_name = sys.argv[2] 12 | 13 | defines_file = io.open(os.path.join(os.path.dirname(os.path.realpath(__file__)), defines_name), "r") 14 | define_lines = defines_file.readlines() 15 | defines_file.close() 16 | 17 | symbols_file = io.open(os.path.join(os.path.dirname(os.path.realpath(__file__)), new_symbols_name), "w", newline='\n') 18 | symbols_file.write("\n; ---------------") 19 | symbols_file.write("\n; Symbol Export") 20 | symbols_file.write("\n; (autogenerated)") 21 | symbols_file.write("\n; ---------------") 22 | symbols_file.write("\n\nincsrc wram_symbols.asm\n\n") 23 | 24 | last_line_was_new_line = True 25 | for line in define_lines: 26 | if len(line) <= 2: 27 | if not last_line_was_new_line: 28 | symbols_file.write(line) 29 | last_line_was_new_line = True 30 | elif line[0] == ';': 31 | if line.startswith("; Vanilla Labels"): 32 | break 33 | symbols_file.write(line) 34 | last_line_was_new_line = False 35 | elif line[0] == '!' and line[1].lower() == line[1]: 36 | parts = line.split('=') 37 | if len(parts) == 2: 38 | ram_symbol = parts[0].strip() 39 | symbols_file.write(ram_symbol[1:]) 40 | symbols_file.write(" = ") 41 | symbols_file.write(ram_symbol) 42 | symbols_file.write(" ;") 43 | symbols_file.write(parts[1]) 44 | last_line_was_new_line = False 45 | 46 | symbols_file.close() 47 | 48 | -------------------------------------------------------------------------------- /resources/dos_to_unix.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | 4 | if len(sys.argv) != 2: 5 | print("dos_to_unix.py ") 6 | sys.exit() 7 | 8 | file_path = sys.argv[1] 9 | 10 | with open(file_path, 'rb') as open_file: 11 | content = open_file.read() 12 | 13 | content = content.replace(b'\r\n', b'\n') 14 | 15 | with open(file_path, 'wb') as open_file: 16 | open_file.write(content) 17 | 18 | -------------------------------------------------------------------------------- /resources/header.tbl: -------------------------------------------------------------------------------- 1 | =1F 2 | A=50 3 | B=51 4 | C=52 5 | D=53 6 | E=54 7 | F=55 8 | G=56 9 | H=57 10 | I=58 11 | J=59 12 | K=5A 13 | L=5B 14 | M=5C 15 | N=5D 16 | O=5E 17 | P=5F 18 | Q=60 19 | R=61 20 | S=62 21 | T=63 22 | U=64 23 | V=65 24 | W=66 25 | X=67 26 | Y=68 27 | Z=69 28 | a=50 29 | b=51 30 | c=52 31 | d=53 32 | e=54 33 | f=55 34 | g=56 35 | h=57 36 | i=58 37 | j=59 38 | k=5A 39 | l=5B 40 | m=5C 41 | n=5D 42 | o=5E 43 | p=5F 44 | q=60 45 | r=61 46 | s=62 47 | t=63 48 | u=64 49 | v=92 50 | w=66 51 | x=67 52 | y=68 53 | z=69 54 | :=4A 55 | !=6A 56 | .=6B 57 | -=6C 58 | ,=6D 59 | ?=6E 60 | #=6F 61 | 0=70 62 | 1=71 63 | 2=72 64 | 3=73 65 | 4=74 66 | 5=75 67 | 6=76 68 | 7=77 69 | 8=78 70 | 9=79 71 | '=7E 72 | (=88 73 | )=89 74 | %=8A 75 | +=8B 76 | $=99 77 | _=9E 78 | /=9F 79 | -------------------------------------------------------------------------------- /resources/hudgfx.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/hudgfx.bin -------------------------------------------------------------------------------- /resources/mapgfx.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/mapgfx.bin -------------------------------------------------------------------------------- /resources/normal.tbl: -------------------------------------------------------------------------------- 1 | A=00 2 | B=01 3 | C=02 4 | D=03 5 | E=04 6 | F=05 7 | G=06 8 | H=07 9 | I=08 10 | J=09 11 | K=0A 12 | L=0B 13 | M=0C 14 | N=0D 15 | O=7D 16 | P=0F 17 | Q=10 18 | R=11 19 | S=12 20 | T=13 21 | U=14 22 | V=15 23 | W=16 24 | X=17 25 | Y=18 26 | Z=19 27 | !=1A 28 | .=1B 29 | -=1C 30 | ,=1D 31 | ?=1E 32 | =1F 33 | 0=20 34 | 1=21 35 | 2=22 36 | 3=23 37 | 4=24 38 | 5=25 39 | 6=26 40 | 7=27 41 | 8=28 42 | 9=29 43 | a=2A 44 | b=2B 45 | c=2C 46 | d=2D 47 | e=2E 48 | f=2F 49 | g=30 50 | h=31 51 | i=32 52 | j=33 53 | k=34 54 | l=35 55 | m=36 56 | n=37 57 | o=38 58 | p=39 59 | q=3A 60 | r=3B 61 | s=3C 62 | t=3D 63 | u=3E 64 | v=3F 65 | w=40 66 | x=41 67 | y=42 68 | z=43 69 | #=44 70 | (=45 71 | )=46 72 | '=47 73 | :=49 74 | $=4E 75 | +=4F 76 | %=7C 77 | /=7F 78 | >=93 79 | _=9E 80 | -------------------------------------------------------------------------------- /resources/num_gfx_choice.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/num_gfx_choice.bin -------------------------------------------------------------------------------- /resources/palettes_00_upper_crateria.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_00_upper_crateria.bin -------------------------------------------------------------------------------- /resources/palettes_01_red_crateria.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_01_red_crateria.bin -------------------------------------------------------------------------------- /resources/palettes_02_lower_crateria.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_02_lower_crateria.bin -------------------------------------------------------------------------------- /resources/palettes_03_old_tourian.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_03_old_tourian.bin -------------------------------------------------------------------------------- /resources/palettes_04_wrecked_ship_power_on.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_04_wrecked_ship_power_on.bin -------------------------------------------------------------------------------- /resources/palettes_05_wrecked_ship_power_off.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_05_wrecked_ship_power_off.bin -------------------------------------------------------------------------------- /resources/palettes_06_green_blue_brinstar.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_06_green_blue_brinstar.bin -------------------------------------------------------------------------------- /resources/palettes_07_red_brinstar.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_07_red_brinstar.bin -------------------------------------------------------------------------------- /resources/palettes_08_tourian_entrance.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_08_tourian_entrance.bin -------------------------------------------------------------------------------- /resources/palettes_09_heated_norfair.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_09_heated_norfair.bin -------------------------------------------------------------------------------- /resources/palettes_10_unheated_norfair.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_10_unheated_norfair.bin -------------------------------------------------------------------------------- /resources/palettes_11_sandless_maridia.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_11_sandless_maridia.bin -------------------------------------------------------------------------------- /resources/palettes_12_sandy_maridia.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_12_sandy_maridia.bin -------------------------------------------------------------------------------- /resources/palettes_13_tourian.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_13_tourian.bin -------------------------------------------------------------------------------- /resources/palettes_14_mother_brain.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_14_mother_brain.bin -------------------------------------------------------------------------------- /resources/palettes_15_17_19_blue_ceres.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_15_17_19_blue_ceres.bin -------------------------------------------------------------------------------- /resources/palettes_16_18_20_white_ceres.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_16_18_20_white_ceres.bin -------------------------------------------------------------------------------- /resources/palettes_21_map_room.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_21_map_room.bin -------------------------------------------------------------------------------- /resources/palettes_22_map_room_power_off.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_22_map_room_power_off.bin -------------------------------------------------------------------------------- /resources/palettes_23_blue_refill_room.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_23_blue_refill_room.bin -------------------------------------------------------------------------------- /resources/palettes_24_yellow_refill_room.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_24_yellow_refill_room.bin -------------------------------------------------------------------------------- /resources/palettes_25_save_room.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_25_save_room.bin -------------------------------------------------------------------------------- /resources/palettes_26_kraid.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_26_kraid.bin -------------------------------------------------------------------------------- /resources/palettes_27_croc.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_27_croc.bin -------------------------------------------------------------------------------- /resources/palettes_28_draygon.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/palettes_28_draygon.bin -------------------------------------------------------------------------------- /resources/sort_debug_symbols.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import io 3 | import os 4 | import re 5 | import sys 6 | 7 | if len(sys.argv) != 4: 8 | print("sort_debug_symbols.py ") 9 | sys.exit() 10 | 11 | original_name = sys.argv[1] 12 | new_name = sys.argv[2] 13 | combined_name = sys.argv[3] 14 | 15 | original_file = None 16 | new_file = None 17 | combined_file = None 18 | 19 | try: 20 | original_file = io.open(os.path.join(os.path.dirname(os.path.realpath(__file__)), original_name), "r") 21 | except OSError as e: 22 | print(f"Unable to open {original_name}: {e}", file=sys.stderr) 23 | sys.exit() 24 | 25 | if len(new_name) > 1: 26 | try: 27 | new_file = io.open(os.path.join(os.path.dirname(os.path.realpath(__file__)), new_name), "w", newline='\n') 28 | except OSError as e: 29 | print(f"Unable to open {new_name}: {e}", file=sys.stderr) 30 | sys.exit() 31 | 32 | if len(combined_name) > 1: 33 | try: 34 | combined_file = io.open(os.path.join(os.path.dirname(os.path.realpath(__file__)), combined_name), "w", newline='\n') 35 | except OSError as e: 36 | print(f"Unable to open {combined_name}: {e}", file=sys.stderr) 37 | sys.exit() 38 | 39 | longest_label = "" 40 | rows = original_file.readlines() 41 | rows_to_sort = [] 42 | combined_rows_to_sort = [] 43 | sources_dict = {} 44 | in_addr_to_line = False 45 | in_labels = False 46 | in_source_files = False 47 | unnamed_symbol_found = False 48 | for row in rows: 49 | if len(row) <= 2: 50 | if 0 != len(rows_to_sort): 51 | sorted_rows = sorted(rows_to_sort) 52 | last_addr = None 53 | for sorted_row in sorted_rows: 54 | if new_file: 55 | new_file.write(sorted_row) 56 | if in_addr_to_line: 57 | addr = sorted_row.split(' ')[0] 58 | if last_addr and last_addr == addr: 59 | print("sort_debug_symbols.py WARNING duplicate addr-to-line mapping %s" % addr) 60 | sys.exit() 61 | last_addr = addr 62 | in_addr_to_line = False 63 | in_source_files = False 64 | in_labels = False 65 | rows_to_sort = [] 66 | if new_file: 67 | new_file.write(row) 68 | elif in_addr_to_line: 69 | parts = re.split(" |:|\n", row) 70 | if len(parts) != 5: 71 | rows_to_sort.append(row.upper()) 72 | combined_rows_to_sort.append(row.upper()) 73 | else: 74 | named_source = parts[2] 75 | if named_source in sources_dict: 76 | named_source = sources_dict[named_source] 77 | named_line = "%s:%s %s:%d\n" % (parts[0].upper(), parts[1].upper(), named_source, int(parts[3], 16)) 78 | rows_to_sort.append(named_line) 79 | named_line = "%s:%s_%s:%d\n" % (parts[0].upper(), parts[1].upper(), named_source, int(parts[3], 16)) 80 | combined_rows_to_sort.append(named_line) 81 | elif in_labels: 82 | rows_to_sort.append(row) 83 | combined_rows_to_sort.append(row) 84 | if " :" in row: 85 | unnamed_symbol_found = True 86 | if len(row) > 8: 87 | # for longest label, ignore presets and tile table names 88 | if row[0] < 'E' or row[1] == '0': 89 | label = row[8:-1] 90 | if len(label) > len(longest_label): 91 | longest_label = label 92 | elif in_source_files: 93 | if new_file: 94 | new_file.write(row) 95 | parts = re.split(" |\.|\/", row) 96 | if len(parts) > 3: 97 | named_source = parts[len(parts) - 2] 98 | if named_source in sources_dict.values(): 99 | print("sort_debug_symbols.py WARNING multiple source files have same name: " + named_source) 100 | sys.exit() 101 | sources_dict[parts[0]] = named_source 102 | else: 103 | if new_file: 104 | new_file.write(row) 105 | if "[addr-to-line mapping]" in row: 106 | in_addr_to_line = True 107 | elif "[labels]" in row: 108 | in_labels = True 109 | elif "[source files]" in row: 110 | in_source_files = True 111 | 112 | if 0 != len(rows_to_sort): 113 | sorted_rows = sorted(rows_to_sort) 114 | last_addr = None 115 | for sorted_row in sorted_rows: 116 | if new_file: 117 | new_file.write(sorted_row) 118 | if in_addr_to_line: 119 | addr = sorted_row.split(' ')[0] 120 | if last_addr and last_addr == addr: 121 | print("sort_debug_symbols.py WARNING duplicate addr-to-line mapping %s" % addr) 122 | sys.exit() 123 | last_addr = addr 124 | 125 | original_file.close() 126 | 127 | if new_file: 128 | new_file.close() 129 | 130 | if combined_file: 131 | sorted_rows = sorted(combined_rows_to_sort) 132 | for sorted_row in sorted_rows: 133 | if len(sorted_row) > 8: 134 | bank = int(sorted_row[:2], 16) 135 | addr = int(sorted_row[3:7], 16) 136 | pc_addr = (bank - 129) * 32768 + addr 137 | pc_rom_addr = " " if pc_addr < 0 else f'{pc_addr:08x}' 138 | combined_file.write("%s %s %s" % (pc_rom_addr, sorted_row[:7], sorted_row[8:])) 139 | else: 140 | combined_file.write(sorted_row) 141 | combined_file.close() 142 | 143 | if unnamed_symbol_found: 144 | print("sort_debug_symbols.py WARNING unnamed debug symbols detected") 145 | sys.exit() 146 | 147 | recommended_max_label_length = 48 148 | if len(longest_label) >= recommended_max_label_length: 149 | print("sort_debug_symbols.py WARNING labels at or exceeding recommended length of %d detected" % recommended_max_label_length) 150 | print("longest label (length %d) = %s" % (len(longest_label), longest_label)) 151 | sys.exit() 152 | 153 | -------------------------------------------------------------------------------- /resources/spritegfx.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/spritegfx.bin -------------------------------------------------------------------------------- /resources/tile_table_00_01_upper_crateria.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tile_table_00_01_upper_crateria.bin -------------------------------------------------------------------------------- /resources/tile_table_02_03_lower_crateria.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tile_table_02_03_lower_crateria.bin -------------------------------------------------------------------------------- /resources/tile_table_04_05_wrecked_ship.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tile_table_04_05_wrecked_ship.bin -------------------------------------------------------------------------------- /resources/tile_table_06_green_blue_brinstar.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tile_table_06_green_blue_brinstar.bin -------------------------------------------------------------------------------- /resources/tile_table_07_08_red_brinstar.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tile_table_07_08_red_brinstar.bin -------------------------------------------------------------------------------- /resources/tile_table_09_10_norfair.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tile_table_09_10_norfair.bin -------------------------------------------------------------------------------- /resources/tile_table_11_sandless_maridia.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tile_table_11_sandless_maridia.bin -------------------------------------------------------------------------------- /resources/tile_table_12_sandy_maridia.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tile_table_12_sandy_maridia.bin -------------------------------------------------------------------------------- /resources/tile_table_13_14_tourian.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tile_table_13_14_tourian.bin -------------------------------------------------------------------------------- /resources/tile_table_15_20_ceres.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tile_table_15_20_ceres.bin -------------------------------------------------------------------------------- /resources/tile_table_21_25_utility.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tile_table_21_25_utility.bin -------------------------------------------------------------------------------- /resources/tile_table_26_kraid.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tile_table_26_kraid.bin -------------------------------------------------------------------------------- /resources/tile_table_27_croc.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tile_table_27_croc.bin -------------------------------------------------------------------------------- /resources/tile_table_28_draygon.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tile_table_28_draygon.bin -------------------------------------------------------------------------------- /resources/tile_table_CRE.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tile_table_CRE.bin -------------------------------------------------------------------------------- /resources/tiles_00_01_upper_crateria.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tiles_00_01_upper_crateria.bin -------------------------------------------------------------------------------- /resources/tiles_02_03_lower_crateria.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tiles_02_03_lower_crateria.bin -------------------------------------------------------------------------------- /resources/tiles_04_05_wrecked_ship.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tiles_04_05_wrecked_ship.bin -------------------------------------------------------------------------------- /resources/tiles_06_green_blue_brinstar.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tiles_06_green_blue_brinstar.bin -------------------------------------------------------------------------------- /resources/tiles_07_08_red_brinstar.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tiles_07_08_red_brinstar.bin -------------------------------------------------------------------------------- /resources/tiles_09_10_norfair.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tiles_09_10_norfair.bin -------------------------------------------------------------------------------- /resources/tiles_11_sandless_maridia.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tiles_11_sandless_maridia.bin -------------------------------------------------------------------------------- /resources/tiles_12_sandy_maridia.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tiles_12_sandy_maridia.bin -------------------------------------------------------------------------------- /resources/tiles_13_14_tourian.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tiles_13_14_tourian.bin -------------------------------------------------------------------------------- /resources/tiles_15_16_ceres.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tiles_15_16_ceres.bin -------------------------------------------------------------------------------- /resources/tiles_17_18_ceres_elevator.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tiles_17_18_ceres_elevator.bin -------------------------------------------------------------------------------- /resources/tiles_19_20_ceres_ridley.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tiles_19_20_ceres_ridley.bin -------------------------------------------------------------------------------- /resources/tiles_21_25_utility.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tiles_21_25_utility.bin -------------------------------------------------------------------------------- /resources/tiles_26_kraid.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tiles_26_kraid.bin -------------------------------------------------------------------------------- /resources/tiles_27_croc.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tiles_27_croc.bin -------------------------------------------------------------------------------- /resources/tiles_28_draygon.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tiles_28_draygon.bin -------------------------------------------------------------------------------- /resources/tiles_CRE.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/resources/tiles_CRE.bin -------------------------------------------------------------------------------- /src/BRBmenu.asm: -------------------------------------------------------------------------------- 1 | 2 | %startfree(8E) 3 | 4 | !BRB_TOTAL_SCREENS = #$0007 5 | 6 | ; -------- 7 | ; BRB Data 8 | ; -------- 9 | cm_brb_table: 10 | ; 1000h bytes transferred 11 | incbin ../resources/cm_brb_gfx.bin 12 | 13 | 14 | ; -------- 15 | ; BRB Menu 16 | ; -------- 17 | 18 | BRBMenu: 19 | dw #brb_menu_screen 20 | dw #$FFFF 21 | dw #brb_menu_timer_mode 22 | dw #brb_menu_timer_min 23 | dw #brb_menu_timer_sec 24 | dw #brb_menu_timer_clear 25 | dw #$FFFF 26 | dw #brb_menu_cycle_timer 27 | dw #brb_menu_palette_cycle 28 | dw #$FFFF 29 | dw #brb_menu_scroll 30 | dw #$FFFF 31 | dw #brb_menu_music_toggle 32 | dw #$0000 33 | %cm_header("BRB SCREEN MENU") 34 | 35 | brb_menu_screen: 36 | %cm_jsl("Launch BRB Screen", .routine, #0) 37 | .routine 38 | JSL cm_brb_loop 39 | JSL cm_wait_for_lag_frame 40 | JSL cm_transfer_custom_tileset 41 | JML refresh_cgram_long 42 | 43 | brb_menu_timer_mode: 44 | dw !ACTION_CHOICE 45 | dl #!ram_cm_brb_timer_mode 46 | dw #$0000 47 | db #$28, "Timer Mode", #$FF 48 | db #$28, " OFF", #$FF 49 | db #$28, " COUNT UP", #$FF 50 | db #$28, " COUNT DOWN", #$FF 51 | db #$FF 52 | 53 | brb_menu_timer_min: 54 | %cm_numfield("Minutes on Timer", !ram_cm_brb_mins, 0, 99, 1, 5, #0) 55 | 56 | brb_menu_timer_sec: 57 | %cm_numfield("Seconds on Timer", !ram_cm_brb_secs, 0, 59, 1, 5, #0) 58 | 59 | brb_menu_timer_clear: 60 | %cm_jsl("Clear Timer", .routine, #0) 61 | .routine 62 | TYA : STA !ram_cm_brb_mins 63 | STA !ram_cm_brb_secs : STA !ram_cm_brb_frames 64 | RTL 65 | 66 | brb_menu_cycle_timer: 67 | %cm_numfield_word("Cycle Timer (seconds)", !ram_cm_brb_set_cycle, 1, 512, 1, 10, .routine) 68 | .routine 69 | LDA !ram_cm_brb_set_cycle : BNE .setTime 70 | LDA #$0009 ; default to ~10s 71 | .setTime 72 | ASL #6 : STA !ram_cm_brb_cycle_time 73 | RTL 74 | 75 | brb_menu_palette_cycle: 76 | %cm_toggle_bit_inverted("Cycle Palettes", !ram_cm_brb_palette, #$FFFF, #0) 77 | 78 | brb_menu_scroll: 79 | %cm_toggle("Screen Scrolling", !ram_cm_brb_scroll, #$01, #0) 80 | 81 | brb_menu_music_toggle: 82 | dw !ACTION_CHOICE 83 | dl #!sram_music_toggle 84 | dw .routine 85 | db #$28, "Music", #$FF 86 | db #$28, " OFF", #$FF 87 | db #$28, " ON", #$FF 88 | db #$28, " FAST OFF", #$FF 89 | db #$28, " PRESET OFF", #$FF 90 | db #$FF 91 | .routine 92 | ; Clear music queue 93 | STZ !MUSIC_QUEUE_TIMERS : STZ !MUSIC_QUEUE_TIMERS+$2 : STZ !MUSIC_QUEUE_TIMERS+$4 : STZ !MUSIC_QUEUE_TIMERS+$6 94 | STZ !MUSIC_QUEUE_TIMERS+$8 : STZ !MUSIC_QUEUE_TIMERS+$A : STZ !MUSIC_QUEUE_TIMERS+$C : STZ !MUSIC_QUEUE_TIMERS+$E 95 | STZ !MUSIC_QUEUE_NEXT : STZ !MUSIC_QUEUE_START : STZ !MUSIC_ENTRY : STZ !MUSIC_TIMER 96 | CMP #$0001 : BEQ .resume_music 97 | STZ $2140 98 | RTL 99 | .resume_music 100 | LDA !MUSIC_DATA : CLC : ADC #$FF00 : PHA : STZ !MUSIC_DATA : PLA : JSL !MUSIC_ROUTINE 101 | LDA !MUSIC_TRACK : PHA : STZ !MUSIC_TRACK : PLA : JSL !MUSIC_ROUTINE 102 | RTL 103 | 104 | 105 | ; --------- 106 | ; BRB Logic 107 | ; --------- 108 | 109 | cm_brb_loop: 110 | { 111 | ; Initial setup 112 | %ai16() 113 | LDA !DP_CurrentMenu : PHA 114 | LDA !DP_CurrentMenu+2 : PHA 115 | PHX : PHY 116 | 117 | LDA !ram_cm_brb_palette : PHA ; preserve menu palette 118 | CMP #$FFFF : BEQ .continue_init ; check if palette swapping disabled 119 | TDC : INC : STA !ram_cm_brb_palette 120 | 121 | .continue_init 122 | TDC : STA !ram_cm_brb_screen 123 | STA !ram_cm_brb_frames : STA !ram_cm_brb_timer 124 | STA !ram_cm_brb_scroll_H : STA !ram_cm_brb_scroll_V 125 | STA !ram_cm_brb_scroll_X 126 | LDA #$0008 : STA !ram_cm_brb_scroll_Y 127 | 128 | JSR cm_transfer_brb_tileset 129 | JSL cm_wait_for_lag_frame 130 | JSL $808F0C ; Music queue 131 | JSL $8289EF ; Sound fx queue 132 | 133 | .loop 134 | ; [do entertaining stuff here] 135 | JSR cm_draw_brb 136 | 137 | ; [do loop stuff down here] 138 | 139 | LDA !ram_cm_leave : BEQ .check_input 140 | 141 | .exit 142 | ; Exit brb loop 143 | TDC : STA !ram_cm_leave 144 | PLA : STA !ram_cm_brb_palette ; restore menu palette 145 | LDA #$0046 : JSL !SFX_LIB2 ; lavaquake sfx 146 | JSL cm_wait_for_lag_frame 147 | 148 | LDA !REG_2111_BG3_X 149 | %a8() 150 | STA $2111 : XBA : STA $2111 151 | %a16() 152 | LDA !REG_2112_BG3_Y 153 | %a8() 154 | STA $2112 : XBA : STA $2112 155 | %a16() 156 | 157 | PLY : PLX 158 | PLA : STA !DP_CurrentMenu+2 159 | PLA : STA !DP_CurrentMenu 160 | RTL 161 | 162 | .check_input 163 | ; Make sure we don't read joysticks twice in the same frame 164 | LDA !FRAME_COUNTER : CMP !ram_cm_input_counter : PHP 165 | STA !ram_cm_input_counter 166 | PLP : BNE .input_read 167 | JSL $809459 ; Read controller input 168 | 169 | .input_read 170 | LDA !IH_CONTROLLER_PRI_NEW : BEQ .loop 171 | 172 | ; A = cycle now, B = exit 173 | CMP !CTRL_B : BEQ .exit 174 | CMP !CTRL_A : BNE .loop 175 | 176 | LDA !ram_cm_brb_screen : INC : STA !ram_cm_brb_screen 177 | CMP !BRB_TOTAL_SCREENS : BMI .loop 178 | TDC : STA !ram_cm_brb_screen : STA !ram_cm_brb_timer 179 | BRA .loop 180 | } 181 | 182 | cm_draw_brb: 183 | { 184 | JSL cm_tilemap_bg_interior_long 185 | JSR cm_tilemap_brb 186 | JSL cm_tilemap_transfer_long 187 | JMP cm_brb_scroll_BG3 188 | } 189 | 190 | cm_tilemap_brb: 191 | { 192 | ; Handle timer for cycling text and palette 193 | ; Timer is only incremented once every other frame 194 | LDA !ram_cm_brb_timer : CMP !ram_cm_brb_cycle_time : BMI .draw_brb_stuff 195 | TDC : STA !ram_cm_brb_timer 196 | JSL MenuRNG ; Get new RNG values 197 | 198 | ; Cycle screen text 199 | LDA !ram_cm_brb_screen : INC : STA !ram_cm_brb_screen 200 | CMP !BRB_TOTAL_SCREENS : BMI .done_cycle_text 201 | TDC : STA !ram_cm_brb_screen 202 | 203 | .done_cycle_text 204 | ; Cycle menu palette 205 | LDA !ram_cm_brb_palette : CMP #$FFFF : BEQ .draw_brb_stuff 206 | INC : CMP !PROFILE_COUNT : BMI .done_cycle_palette 207 | TDC : INC 208 | 209 | .done_cycle_palette 210 | STA !ram_cm_brb_palette 211 | JSL PrepMenuPalette_standard 212 | JSR cm_transfer_brb_cgram 213 | 214 | .draw_brb_stuff 215 | ; Drawing whatever manually 216 | LDA !ram_cm_brb_timer : INC : STA !ram_cm_brb_timer 217 | LDA !ram_seed_X : AND #$07FE : TAX 218 | LDA !BRB_METROID : STA !ram_tilemap_buffer,X 219 | 220 | LDA !ram_seed_X : XBA : AND #$07FE : TAX 221 | LDA !BRB_METROID : STA !ram_tilemap_buffer,X 222 | 223 | LDA !ram_seed_Y : AND #$07FE : TAX 224 | LDA !BRB_METROID : STA !ram_tilemap_buffer,X 225 | 226 | LDA !ram_seed_Y : XBA : AND #$07FE : TAX 227 | LDA !BRB_METROID : STA !ram_tilemap_buffer,X 228 | 229 | .draw_text 230 | ; Same bank for all of the BRB text 231 | PHK : PHK : PLA : STA !DP_CurrentMenu+2 232 | 233 | LDA.w #BRB_common_1 : STA !DP_CurrentMenu 234 | LDX #$01C6 235 | JSR cm_draw_brb_text 236 | 237 | LDA.w #BRB_common_2 : STA !DP_CurrentMenu 238 | LDX #$0286 239 | JSR cm_draw_brb_text 240 | 241 | ; Handle timer 242 | LDA !ram_cm_brb_timer_mode : BEQ .draw_cycling_text 243 | DEC : BEQ .countup 244 | JSR brb_handle_countdown_timer 245 | BRA .draw_timer 246 | 247 | .countup 248 | JSR brb_handle_countup_timer 249 | 250 | .draw_timer 251 | ; Seconds 252 | LDA !ram_cm_brb_secs : ASL : TAX 253 | LDA.l TimerNumberGFX1,X : STA !ram_tilemap_buffer+$360 254 | LDA.l TimerNumberGFX2,X : STA !ram_tilemap_buffer+$362 255 | 256 | ; Minutes 257 | LDA !ram_cm_brb_mins : ASL : TAX 258 | LDA.l TimerNumberGFX1,X : STA !ram_tilemap_buffer+$35A 259 | LDA.l TimerNumberGFX2,X : STA !ram_tilemap_buffer+$35C 260 | 261 | ; Draw colon seperator 262 | LDA #$2849 : STA !ram_tilemap_buffer+$35E 263 | 264 | ; Draw +/- after countdown expires 265 | LDA !ram_cm_brb_timer_mode : BEQ .draw_cycling_text 266 | DEC : BEQ .draw_countup 267 | LDA #$286C : STA !ram_tilemap_buffer+$358 268 | BRA .draw_cycling_text 269 | 270 | .draw_countup 271 | LDA #$288B : STA !ram_tilemap_buffer+$358 272 | 273 | .draw_cycling_text 274 | ; Draw cycling text 275 | ; first cycled line 276 | LDA !ram_cm_brb_screen : ASL : TAX 277 | LDA.l BRBTilemapAddress,X : STA !DP_CurrentMenu 278 | LDX #$04C6 279 | JSR cm_draw_brb_text 280 | 281 | ; second cycled line 282 | LDA !ram_cm_brb_screen : ASL : TAX 283 | LDA.l BRBTilemapAddress2,X : STA !DP_CurrentMenu 284 | LDX #$0586 285 | JSR cm_draw_brb_text 286 | 287 | RTS 288 | } 289 | 290 | brb_handle_countup_timer: 291 | { 292 | LDA !ram_cm_brb_frames : INC : STA !ram_cm_brb_frames 293 | CMP !FRAMERATE : BNE .done 294 | TDC : STA !ram_cm_brb_frames 295 | LDA !ram_cm_brb_secs : INC : STA !ram_cm_brb_secs 296 | CMP #$003C : BNE .done 297 | TDC : STA !ram_cm_brb_secs 298 | LDA !ram_cm_brb_mins : INC : STA !ram_cm_brb_mins 299 | .done 300 | RTS 301 | } 302 | 303 | brb_handle_countdown_timer: 304 | { 305 | LDA !ram_cm_brb_frames : DEC : STA !ram_cm_brb_frames : BPL .done 306 | LDA !FRAMERATE-1 : STA !ram_cm_brb_frames 307 | LDA !ram_cm_brb_secs : DEC : STA !ram_cm_brb_secs : BPL .done 308 | LDA #$003B : STA !ram_cm_brb_secs 309 | LDA !ram_cm_brb_mins : DEC : STA !ram_cm_brb_mins : BPL .done 310 | TDC : STA !ram_cm_brb_secs : STA !ram_cm_brb_mins 311 | INC : STA !ram_cm_brb_timer_mode : STA !ram_cm_brb_frames 312 | .done 313 | RTS 314 | } 315 | 316 | cm_transfer_brb_cgram: 317 | { 318 | TDC : STA $7EC000 319 | LDA !ram_cm_palette_border : STA $7EC00A 320 | LDA !ram_cm_palette_headeroutline : STA $7EC012 321 | LDA !ram_cm_palette_text : STA $7EC014 322 | LDA !ram_cm_palette_background : STA $7EC016 : STA $7EC00E 323 | LDA !ram_cm_palette_numoutline : STA $7EC01A 324 | LDA !ram_cm_palette_numfill : STA $7EC01C 325 | LDA !ram_cm_palette_toggleon : STA $7EC032 326 | LDA !ram_cm_palette_seltext : STA $7EC034 327 | LDA !ram_cm_palette_seltextbg : STA $7EC036 328 | LDA !ram_cm_palette_numseloutline : STA $7EC03A 329 | LDA !ram_cm_palette_numsel : STA $7EC03C 330 | JSL transfer_cgram_long 331 | RTS 332 | } 333 | 334 | cm_draw_brb_text: 335 | { 336 | ; X = pointer to tilemap area (STA !ram_tilemap_buffer,X) 337 | ; !DP_CurrentMenu[0x3] = address 338 | %a8() 339 | LDY #$0000 340 | ; terminator 341 | LDA [!DP_CurrentMenu],Y : INY : CMP #$FF : BEQ .end 342 | ; ORA with palette info 343 | ORA !DP_Palette : STA !DP_Palette 344 | 345 | .loop 346 | LDA [!DP_CurrentMenu],Y : CMP #$FF : BEQ .end ; terminator 347 | STA !ram_tilemap_buffer,X : INX ; tile 348 | LDA !DP_Palette : STA !ram_tilemap_buffer,X : INX ; palette 349 | INY : BRA .loop 350 | 351 | .end 352 | %a16() 353 | RTS 354 | } 355 | 356 | cm_transfer_brb_tileset: 357 | { 358 | PHP 359 | 360 | ; Load custom vram to normal location 361 | %a8() 362 | LDA #$80 : STA $2100 ; enable forced blanking 363 | LDA #!DP_CurrentMenu : STA $210C 364 | LDA #$80 : STA $2115 ; word-access, incr by 1 365 | LDX #$4000 : STX $2116 ; VRAM address (8000 in vram) 366 | LDX.w #cm_brb_table : STX $4302 ; Source offset 367 | LDA.b #cm_brb_table>>16 : STA $4304 ; Source bank 368 | LDX #$1000 : STX $4305 ; Size (0x10 = 1 tile) 369 | LDA #$01 : STA $4300 ; word, normal increment (DMA MODE) 370 | LDA #$18 : STA $4301 ; destination (VRAM write) 371 | LDA #$01 : STA $420B ; initiate DMA (channel 1) 372 | LDA #$0F : STA $2100 ; disable forced blanking 373 | PLP 374 | RTS 375 | } 376 | 377 | cm_brb_scroll_BG3: 378 | { 379 | LDA !ram_cm_brb_scroll : BNE .incrementTimer 380 | 381 | .skip 382 | RTS 383 | 384 | .incrementTimer 385 | LDA !ram_cm_brb_scroll_timer : INC : STA !ram_cm_brb_scroll_timer 386 | CMP #$0007 : BNE .chooseScrolling 387 | TDC : STA !ram_cm_brb_scroll_timer 388 | 389 | .chooseScrolling 390 | PHP : %a16() 391 | ; vertical scrolling at 3, 5 392 | LDA !ram_cm_brb_scroll_timer : CMP #$0003 : BEQ .verticalScrolling 393 | CMP #$0005 : BNE .horizontalScrolling 394 | 395 | .verticalScrolling 396 | LDA !ram_cm_brb_scroll_V : BNE .moveUp 397 | LDA !ram_cm_brb_scroll_Y : INC : STA !ram_cm_brb_scroll_Y 398 | CMP #$0011 : BNE .horizontalScrolling 399 | 400 | .reverseV 401 | LDA !ram_cm_brb_scroll_V : BEQ .incV 402 | TDC : STA !ram_cm_brb_scroll_V 403 | BRA .horizontalScrolling 404 | 405 | .incV 406 | INC : STA !ram_cm_brb_scroll_V 407 | BRA .horizontalScrolling 408 | 409 | .moveUp 410 | LDA !ram_cm_brb_scroll_Y : DEC : STA !ram_cm_brb_scroll_Y 411 | BPL .horizontalScrolling 412 | LDA #$0001 : STA !ram_cm_brb_scroll_Y 413 | BRA .reverseV 414 | 415 | .horizontalScrolling 416 | ; horizontal scrolling at 1, 3, 5 417 | LDA !ram_cm_brb_scroll_timer : AND #$0001 : BEQ .applyScrolls 418 | LDA !ram_cm_brb_scroll_H : BNE .moveLeft 419 | LDA !ram_cm_brb_scroll_X : INC : STA !ram_cm_brb_scroll_X 420 | CMP #$000F : BEQ .reverseH 421 | CMP #$0400 : BNE .applyScrolls 422 | TDC : STA !ram_cm_brb_scroll_X 423 | BRA .applyScrolls 424 | 425 | .moveLeft 426 | LDA !ram_cm_brb_scroll_X : DEC : STA !ram_cm_brb_scroll_X 427 | BMI .dec3FF 428 | CMP #$03F1 : BNE .applyScrolls 429 | 430 | .reverseH 431 | LDA !ram_cm_brb_scroll_H : BEQ .incH 432 | TDC : STA !ram_cm_brb_scroll_H 433 | BRA .applyScrolls 434 | 435 | .incH 436 | INC : STA !ram_cm_brb_scroll_H 437 | BRA .applyScrolls 438 | 439 | .dec3FF 440 | LDA #$03FF : STA !ram_cm_brb_scroll_X 441 | 442 | .applyScrolls 443 | ; force blank and apply scrolls 444 | %i8() 445 | LDX #$80 : STX $2100 446 | 447 | LDA !ram_cm_brb_scroll_X 448 | %a8() 449 | STA $2111 : XBA : STA $2111 450 | %a16() 451 | 452 | LDA !ram_cm_brb_scroll_Y : DEC 453 | %a8() 454 | STA $2112 : XBA : STA $2112 455 | 456 | LDA #$0F : STA $2100 457 | 458 | PLP 459 | RTS 460 | } 461 | 462 | 463 | ; ------------- 464 | ; BRB Text Data 465 | ; ------------- 466 | 467 | BRB_common_1: 468 | table ../resources/header.tbl 469 | db #$28, " The Streamer", #$FF 470 | 471 | BRB_common_2: 472 | db #$28, " Will Be Right Back", #$FF 473 | table ../resources/normal.tbl 474 | 475 | BRBTilemapAddress: 476 | dw #BRB_screen_01 477 | dw #BRB_screen_02 478 | dw #BRB_screen_03 479 | dw #BRB_screen_04 480 | dw #BRB_screen_05 481 | dw #BRB_screen_06 482 | dw #BRB_screen_07 483 | 484 | BRB_screen_01: 485 | db #$28, " SM Speedrunning Wiki", #$FF 486 | 487 | BRB_screen_02: 488 | db #$28, " SM Speedrunning Discord", #$FF 489 | 490 | BRB_screen_03: 491 | db #$28, "Find the practice hack at", #$FF 492 | 493 | BRB_screen_04: 494 | db #$28, " Control Schemes for SM", #$FF 495 | 496 | BRB_screen_05: 497 | db #$28, "Support FUNtoon on Patreon", #$FF 498 | 499 | BRB_screen_06: 500 | db #$28, " Crazy chain damage clips", #$FF 501 | 502 | BRB_screen_07: 503 | db #$28, " Customized practice hacks", #$FF 504 | 505 | BRBTilemapAddress2: 506 | dw #BRB_screen2_01 507 | dw #BRB_screen2_02 508 | dw #BRB_screen2_03 509 | dw #BRB_screen2_04 510 | dw #BRB_screen2_05 511 | dw #BRB_screen2_06 512 | dw #BRB_screen2_07 513 | 514 | BRB_screen2_01: 515 | db #$28, " wiki.supermetroid.run", #$FF 516 | 517 | BRB_screen2_02: 518 | db #$28, " SMDiscord.spazer.link", #$FF 519 | 520 | BRB_screen2_03: 521 | db #$28, " smpractice.speedga.me", #$FF 522 | 523 | BRB_screen2_04: 524 | db #$28, " controls.spazer.link", #$FF 525 | 526 | BRB_screen2_05: 527 | ; !funtoonpatreon 528 | db #$28, " ", #$1A, "funtoonpatreon", #$FF 529 | 530 | BRB_screen2_06: 531 | db #$28, " chain.spazer.link", #$FF 532 | 533 | BRB_screen2_07: 534 | db #$28, " by InsaneFirebat", #$FF 535 | 536 | %endfree(8E) 537 | 538 | -------------------------------------------------------------------------------- /src/cropmenu.asm: -------------------------------------------------------------------------------- 1 | 2 | %startfree(8E) 3 | 4 | ; ---------------- 5 | ; Capture Cropping 6 | ; ---------------- 7 | 8 | CaptureCroppingMenu: 9 | dw #ccm_launch_crop_mode 10 | dw #$FFFF 11 | dw #ccm_crop_mode 12 | dw #ccm_crop_tile 13 | dw #$0000 14 | %cm_header("CAPTURE CROPPING MODE") 15 | 16 | ccm_launch_crop_mode: 17 | %cm_jsl("Capture Crop Mode", .routine, #0) 18 | .routine 19 | JSL cm_crop_mode 20 | JML refresh_cgram_long 21 | 22 | ccm_crop_mode: 23 | dw !ACTION_CHOICE 24 | dl #!ram_cm_crop_mode 25 | dw #$0000 26 | db #$28, "Drawing Method", #$FF 27 | db #$28, " BORDER", #$FF 28 | db #$28, " FILL", #$FF 29 | db #$FF 30 | 31 | ccm_crop_tile: 32 | dw !ACTION_CHOICE 33 | dl #!ram_cm_crop_tile 34 | dw #$0000 35 | db #$28, "Cropping Tile", #$FF 36 | db #$28, " ", #$90, #$FF 37 | db #$28, " ", #$91, #$FF 38 | db #$FF 39 | 40 | cm_crop_mode: 41 | { 42 | PHP : %a16() : %i8() 43 | 44 | ; turn on forced blank 45 | LDX #$80 : STX $2100 46 | 47 | ; fix BG3 scroll offset 48 | LDX #$FF : STX $2112 49 | LDX #$03 : STX $2112 50 | 51 | TDC : STA !ram_crash_palette 52 | 53 | ; pattern or solid color? 54 | LDA !ram_cm_crop_tile : BEQ .boxes 55 | LDA #$2891 : STA $C1 56 | BRA .draw 57 | 58 | .boxes 59 | LDA #$2890 : STA $C1 60 | 61 | .draw 62 | ; wait for lag frame 63 | LDX !NMI_COUNTER : CPX !NMI_COUNTER : BEQ .draw 64 | 65 | ; draw around the border or fill the screen? 66 | LDA !ram_cm_crop_mode : BNE .fill 67 | JSR cm_crop_border 68 | BRA .drawingdone 69 | 70 | .fill 71 | JSR cm_crop_fill 72 | 73 | .drawingdone 74 | ; turn off forced blank 75 | LDX #$0F : STX $2100 76 | 77 | .loop 78 | ; Make sure we don't read joysticks twice in the same frame 79 | ; wait for lag frame 80 | LDX !NMI_COUNTER : CPX !NMI_COUNTER : BEQ .loop 81 | LDA !FRAME_COUNTER : CMP !ram_cm_input_counter : PHP 82 | STA !ram_cm_input_counter 83 | PLP : BNE .inputRead 84 | JSL $809459 ; Read controller input 85 | 86 | .inputRead 87 | LDA !IH_CONTROLLER_PRI_NEW : BEQ .loop 88 | CMP !CTRL_B : BEQ .end 89 | CMP !CTRL_L : BEQ .decPalette 90 | CMP !CTRL_R : BEQ .incPalette 91 | BRA .loop 92 | 93 | .decPalette 94 | LDA !ram_crash_palette : BNE .decSetPalette 95 | LDA #$0008 96 | .decSetPalette 97 | DEC : STA !ram_crash_palette 98 | JSL crash_cgram_transfer 99 | BRA .loop 100 | 101 | .incPalette 102 | LDA !ram_crash_palette : CMP #$0007 : BMI .incSetPalette 103 | LDA #$FFFF 104 | .incSetPalette 105 | INC : STA !ram_crash_palette 106 | JSL crash_cgram_transfer 107 | BRA .loop 108 | 109 | .end 110 | ; restore BG3 scroll offset 111 | LDA !REG_2112_BG3_Y 112 | %ai8() 113 | LDX #$80 : STX $2100 114 | STA $2112 : XBA : STA $2112 115 | LDX #$0F : STX $2100 116 | 117 | PLP 118 | RTL 119 | } 120 | 121 | cm_crop_border: 122 | { 123 | ; top 124 | LDX #$80 : STX $2115 125 | LDA #$5800 : STA $2116 126 | LDA $C1 : LDX #$00 127 | .topLoop 128 | STA $2118 129 | INX : CPX #$20 : BNE .topLoop 130 | 131 | ; bottom 132 | LDX #$80 : STX $2115 133 | LDA #$5B60 : STA $2116 134 | LDA $C1 : LDX #$00 135 | .bottomLoop 136 | STA $2118 137 | INX : CPX #$40 : BNE .bottomLoop 138 | 139 | ; left 140 | LDX #$81 : STX $2115 141 | LDA #$5820 : STA $2116 142 | LDA $C1 : LDX #$00 143 | .leftLoop 144 | STA $2118 145 | INX : CPX #$1D : BNE .leftLoop 146 | 147 | ; right 148 | LDX #$81 : STX $2115 149 | LDA #$583F : STA $2116 150 | LDA $C1 : LDX #$00 151 | .rightLoop 152 | STA $2118 153 | INX : CPX #$1D : BNE .rightLoop 154 | 155 | RTS 156 | } 157 | 158 | cm_crop_fill: 159 | { 160 | LDX #$80 : STX $2115 161 | LDA #$5800 : STA $2116 162 | PHP : %i16() 163 | LDA $C1 : LDX #$0400 164 | 165 | .loop 166 | STA $2118 167 | DEX : BPL .loop 168 | 169 | PLP 170 | RTS 171 | } 172 | 173 | %endfree(8E) 174 | 175 | -------------------------------------------------------------------------------- /src/damage.asm: -------------------------------------------------------------------------------- 1 | 2 | ; Suit periodic damage 3 | org $8DE37C 4 | AND !ram_suits_heat_damage_check : BNE $29 5 | LDA !ram_suits_heat_damage_value : BEQ $23 6 | CLC : ADC $0A4E : STA $0A4E 7 | BCC $03 : INC $0A50 8 | warnpc $8DE394 9 | 10 | 11 | ; Lava suit check 12 | org $9081DB 13 | ; The AND/CMP replaces a BIT operation 14 | ; Everything else is vanilla but needs to be shifted down three bytes 15 | AND !SAMUS_LAVA_DAMAGE_SUITS 16 | CMP !SAMUS_LAVA_DAMAGE_SUITS : BEQ $2C 17 | LDA $09DA : BIT #$0007 : BNE $0F 18 | LDA $09C2 : CMP #$0047 : BMI $07 19 | LDA #$002D : JSL $809139 20 | LDA $0A4E : CLC : ADC $9E8B : STA $0A4E 21 | LDA $0A50 : ADC $9E8D 22 | ; Originally STA $0A50 and BRA $40 to $90824C 23 | ; Conveniently the command at $908249 is STA $0A50 so we can save three bytes 24 | BRA $3D 25 | warnpc $90820C 26 | 27 | org $90B8E8 28 | JSL damage_init_beam 29 | 30 | org $90B9E2 31 | JSL damage_init_beam 32 | 33 | ; We now have three separate periodic damage routines, 34 | ; so we need to load an index to jump to the correct routine 35 | org $90E72B 36 | LDA !sram_suit_properties : ASL : PHA 37 | JSR damage_overwritten_movement_routine 38 | 39 | ; Handle periodic damage based on suit properties 40 | ; Overwritten logic will be transferred 41 | org $90E74D 42 | PLA : PHX : TAX 43 | JSR (periodic_damage_table,X) 44 | PLX : NOP : NOP 45 | 46 | ; Transfer logic here by overwriting redundant end of periodic damage 47 | ; Also repoint jump and branch to avoid the redundant section 48 | if !FEATURE_PAL 49 | org $90E9D3 50 | JMP $EA32 51 | else 52 | org $90E9D6 53 | JMP $EA35 54 | endif 55 | 56 | if !FEATURE_PAL 57 | org $90EA2A 58 | else 59 | org $90EA2D 60 | endif 61 | BPL $06 62 | 63 | ; Optimize CPU by overwriting our PLP/RTS 64 | ; and skipping over the PHP/REP #$30 in the pause check routine 65 | if !FEATURE_PAL 66 | org $90EA38 67 | else 68 | org $90EA3B 69 | endif 70 | BRA $0B 71 | 72 | ; Optimize CPU by removing RTS so we go straight to the low health check 73 | if !FEATURE_PAL 74 | org $90EA7B 75 | else 76 | org $90EA7E 77 | endif 78 | NOP 79 | 80 | 81 | ; Turn off health alarm 82 | if !FEATURE_PAL 83 | org $90EA89 84 | else 85 | org $90EA8C 86 | endif 87 | LDA !sram_healthalarm : ASL : PHX : TAX 88 | JMP (healthalarm_turn_off_table,X) 89 | 90 | ; Turn on health alarm 91 | if !FEATURE_PAL 92 | org $90EA9A 93 | else 94 | org $90EA9D 95 | endif 96 | LDA !sram_healthalarm : ASL : PHX : TAX 97 | JMP (healthalarm_turn_on_table,X) 98 | 99 | ; Turn on health alarm 100 | if !FEATURE_PAL 101 | org $90F336 102 | JSR $EA9A 103 | else 104 | org $90F339 105 | JSR $EA9D 106 | endif 107 | BRA $02 108 | 109 | ; Turn on health alarm from bank 91 110 | if !FEATURE_PAL 111 | org $91E63F 112 | else 113 | org $91E6DA 114 | endif 115 | JML healthalarm_turn_on_remote 116 | 117 | 118 | ; Suit enemy damage 119 | if !FEATURE_PAL 120 | org $A0A473 121 | else 122 | org $A0A463 123 | endif 124 | suit_enemy_damage: 125 | { 126 | BIT #$0020 : BEQ .checkSuit 127 | LSR $12 128 | .checkSuit 129 | AND !ram_suits_enemy_damage_check : BEQ .return 130 | LSR $12 131 | .return 132 | LDA $12 133 | RTL 134 | } 135 | 136 | 137 | ; Suit metroid damage 138 | if !FEATURE_PAL 139 | org $A3EEF4 140 | else 141 | org $A3EED8 142 | endif 143 | suit_metroid_damage: 144 | { 145 | LDA #$C000 : STA $12 146 | LDA $09A2 : AND !ram_suits_enemy_damage_check : BEQ .checkGrav 147 | LSR $12 148 | .checkGrav 149 | LDA $09A2 : BIT #$0020 : BEQ .noGrav 150 | LSR $12 151 | .noGrav 152 | ; Continue vanilla routine 153 | } 154 | 155 | 156 | %startfree(90) 157 | 158 | damage_overwritten_movement_routine: 159 | ; We overwrote an unnecessary JSR, a STZ command, and a jump to the movement routine 160 | STZ $0A6E 161 | JMP ($0A58) 162 | 163 | periodic_damage_table: 164 | if !FEATURE_PAL 165 | dw $E9CB 166 | else ; vanilla routine 167 | dw $E9CE 168 | endif 169 | dw periodic_damage_balanced 170 | dw periodic_damage_progressive 171 | dw periodic_damage_progressive 172 | dw periodic_damage_dash_recall 173 | dw periodic_damage_heat_shield 174 | if !FEATURE_PAL 175 | dw $E9CB 176 | dw $E9CB 177 | else ; vanilla routine 178 | dw $E9CE 179 | dw $E9CE 180 | endif 181 | dw periodic_damage_pal_debug 182 | dw periodic_damage_pal_debug 183 | dw periodic_damage_pal_debug 184 | dw periodic_damage_pal_debug 185 | dw periodic_damage_pal_debug 186 | dw periodic_damage_pal_debug 187 | dw periodic_damage_pal_debug 188 | dw periodic_damage_pal_debug 189 | 190 | periodic_damage_pal_debug: 191 | { 192 | ; If we are here then the PAL debug flag was set 193 | ; First execute the correct periodic damage routine 194 | LDA !sram_suit_properties : AND !SUIT_PROPERTIES_MASK 195 | ASL : TAX : JSR (periodic_damage_table,X) 196 | 197 | ; Now jump to demo recorder routine, but first fix the stack 198 | ; Skip over an RTS and pull X from stack 199 | PLA : PLX 200 | if !FEATURE_PAL 201 | JMP $E75B 202 | else 203 | JMP $E75E 204 | endif 205 | } 206 | 207 | periodic_damage_balanced: 208 | { 209 | PHP : REP #$30 210 | LDA $0A78 : BEQ $03 211 | ; Nothing to do, jump back to vanilla routine 212 | if !FEATURE_PAL 213 | JMP $EA32 214 | else 215 | JMP $EA35 216 | endif 217 | LDA $09A2 : BIT #$0001 : BNE $03 218 | ; Either jump to gravity (75% reduction) or power suit (no reduction) 219 | if !FEATURE_PAL 220 | JMP $EA0E ; Varia not equipped 221 | JMP $E9F9 ; Varia equipped 222 | else 223 | JMP $EA11 ; Varia not equipped 224 | JMP $E9FC ; Varia equipped 225 | endif 226 | } 227 | 228 | periodic_damage_progressive: 229 | { 230 | PHP : REP #$30 231 | LDA $0A78 : BEQ $03 232 | ; Nothing to do, jump back to vanilla routine 233 | if !FEATURE_PAL 234 | JMP $EA32 235 | else 236 | JMP $EA35 237 | endif 238 | 239 | LDA $09A2 : BIT #$0020 : BEQ .nogravity 240 | ; Gravity equipped, so halve damage 241 | LDA $0A4F : LSR 242 | PHA : XBA : AND #$FF00 : STA $0A4E 243 | PLA : XBA : AND #$00FF : STA $0A50 244 | 245 | .nogravity 246 | LDA $09A2 : BIT #$0001 : BEQ .novaria 247 | ; Varia equipped, so halve damage 248 | LDA $0A4F : LSR 249 | PHA : XBA : AND #$FF00 : STA $0A4E 250 | PLA : XBA : AND #$00FF : STA $0A50 251 | 252 | .novaria 253 | ; Jump back into the vanilla routine 254 | if !FEATURE_PAL 255 | JMP $EA0E 256 | else 257 | JMP $EA11 258 | endif 259 | } 260 | 261 | periodic_damage_dash_recall: 262 | { 263 | PHP : REP #$30 264 | LDA $0A78 : BEQ $03 265 | ; Nothing to do, jump back to vanilla routine 266 | if !FEATURE_PAL 267 | JMP $EA32 268 | else 269 | JMP $EA35 270 | endif 271 | 272 | LDA $09A2 : BIT #$0001 : BEQ .novaria 273 | ; Jump back to gravity vanilla routine for 75% reduction 274 | if !FEATURE_PAL 275 | JMP $E9F9 276 | else 277 | JMP $E9FC 278 | endif 279 | 280 | .novaria 281 | LDA $09A2 : BIT #$0020 : BEQ .nogravity 282 | ; Gravity equipped, so halve damage 283 | LDA $0A4F : LSR 284 | PHA : XBA : AND #$FF00 : STA $0A4E 285 | PLA : XBA : AND #$00FF : STA $0A50 286 | 287 | .nogravity 288 | ; Jump back into the vanilla routine 289 | if !FEATURE_PAL 290 | JMP $EA0E 291 | else 292 | JMP $EA11 293 | endif 294 | } 295 | 296 | periodic_damage_heat_shield: 297 | { 298 | PHP : REP #$30 299 | LDA $0A78 : BEQ $03 300 | ; Nothing to do, jump back to vanilla routine 301 | if !FEATURE_PAL 302 | JMP $EA32 303 | else 304 | JMP $EA35 305 | endif 306 | 307 | LDA $09A2 : BIT #$0020 : BEQ .nogravity 308 | ; Gravity equipped, so halve damage 309 | LDA $0A4F : LSR 310 | PHA : XBA : AND #$FF00 : STA $0A4E 311 | PLA : XBA : AND #$00FF : STA $0A50 312 | 313 | .nogravity 314 | ; Jump back into the vanilla routine 315 | if !FEATURE_PAL 316 | JMP $EA0E 317 | else 318 | JMP $EA11 319 | endif 320 | } 321 | 322 | 323 | healthalarm_turn_on_table: 324 | dw healthalarm_turn_on_never 325 | dw healthalarm_turn_on_vanilla 326 | dw healthalarm_turn_on_pb_fix 327 | dw healthalarm_turn_on_improved 328 | dw healthalarm_turn_on_always_on 329 | 330 | healthalarm_turn_on_improved: 331 | ; Do not sound alarm until below 30 combined health 332 | LDA $09C2 : CLC : ADC $09D6 : CMP #$001E : BPL healthalarm_turn_on_done 333 | 334 | healthalarm_turn_on_always_on: 335 | healthalarm_turn_on_pb_fix: 336 | ; Do not sound alarm if it won't play due to power bomb explosion 337 | LDA $0592 : BMI healthalarm_turn_on_done 338 | 339 | healthalarm_turn_on_vanilla: 340 | LDA #$0002 : JSL $80914D 341 | 342 | healthalarm_turn_on_never: 343 | LDA #$0001 : STA !SAMUS_HEALTH_WARNING 344 | 345 | healthalarm_turn_on_done: 346 | PLX : RTS 347 | 348 | 349 | healthalarm_turn_off_table: 350 | dw healthalarm_turn_off_never 351 | dw healthalarm_turn_off_vanilla 352 | dw healthalarm_turn_off_pb_fix 353 | dw healthalarm_turn_off_improved 354 | dw healthalarm_turn_off_always_on 355 | 356 | healthalarm_turn_off_improved: 357 | healthalarm_turn_off_pb_fix: 358 | ; Do not stop alarm if it won't stop due to power bomb explosion 359 | LDA $0592 : BMI healthalarm_turn_off_done 360 | 361 | healthalarm_turn_off_vanilla: 362 | LDA #$0001 : JSL $80914D 363 | 364 | healthalarm_turn_off_never: 365 | STZ !SAMUS_HEALTH_WARNING 366 | 367 | healthalarm_turn_off_done: 368 | PLX : RTS 369 | 370 | healthalarm_turn_off_always_on: 371 | ; Do not sound alarm if it won't play due to power bomb explosion 372 | LDA $0592 : BMI healthalarm_turn_off_done 373 | LDA #$0002 : JSL $80914D 374 | BRA healthalarm_turn_off_never 375 | 376 | 377 | healthalarm_turn_on_remote: 378 | if !FEATURE_PAL 379 | JSR $EA9A 380 | else 381 | JSR $EA9D 382 | endif 383 | PLB : PLP : RTL 384 | 385 | %endfree(90) 386 | 387 | 388 | %startfree(93) 389 | 390 | damage_init_beam: 391 | { 392 | ; Based on $938000 initialize projectile method, 393 | ; but optimized for beam shots 394 | ; so we can inject custom damage without a CPU hit 395 | PHP : PHB : PHK : PLB 396 | %ai16() 397 | LDA $0C04,X : AND #$000F 398 | ASL : STA $12 399 | LDA $0C18,X 400 | BIT #$0010 : BNE .charged 401 | AND #$000F : ASL 402 | TAY : LDA $83C1,Y : TAY 403 | LDA !sram_custom_damage : BNE .nonVanillaUncharged 404 | LDA $0000,Y : STA $0C2C,X 405 | JMP $8048 406 | 407 | .charged 408 | AND #$000F : ASL 409 | TAY : LDA $83D9,Y : TAY 410 | LDA !sram_custom_damage : BNE .nonVanillaCharged 411 | LDA $0000,Y : STA $0C2C,X 412 | JMP $8048 413 | 414 | .nonVanillaUncharged 415 | DEC : BEQ .customUncharged 416 | 417 | .dashCharge0 418 | PHX : JSL compute_dash_charge_0_damage : PLX : STA $0C2C,X 419 | JMP $8048 420 | 421 | .customUncharged 422 | LDA !sram_custom_uncharge_damage : STA $0C2C,X 423 | JMP $8048 424 | 425 | .nonVanillaCharged 426 | DEC : BEQ .customCharged 427 | DEC : BEQ .dashCharge0 428 | DEC : BEQ .dashCharge1 429 | DEC : BEQ .dashCharge2 430 | DEC : BEQ .dashCharge3 431 | PHX : JSL compute_dash_charge_4_damage : PLX : STA $0C2C,X 432 | JMP $8048 433 | 434 | .customCharged 435 | LDA !sram_custom_charge_damage : STA $0C2C,X 436 | JMP $8048 437 | 438 | .dashCharge1 439 | PHX : JSL compute_dash_charge_1_damage : PLX : STA $0C2C,X 440 | JMP $8048 441 | 442 | .dashCharge2 443 | PHX : JSL compute_dash_charge_2_damage : PLX : STA $0C2C,X 444 | JMP $8048 445 | 446 | .dashCharge3 447 | PHX : JSL compute_dash_charge_3_damage : PLX : STA $0C2C,X 448 | JMP $8048 449 | } 450 | 451 | compute_vanilla_uncharged_damage: 452 | { 453 | LDA !SAMUS_BEAMS_EQUIPPED : AND #$000F : ASL : TAX 454 | LDA.l vanilla_uncharged_damage_table,X 455 | RTL 456 | } 457 | 458 | compute_vanilla_charged_damage: 459 | { 460 | LDA !SAMUS_BEAMS_EQUIPPED : AND #$000F : ASL : TAX 461 | LDA.l vanilla_charged_damage_table,X 462 | RTL 463 | } 464 | 465 | compute_dash_charge_0_damage: 466 | { 467 | LDA !SAMUS_BEAMS_EQUIPPED : AND #$000F : ASL : TAX 468 | LDA.l dash_charge_0_damage_table,X 469 | RTL 470 | } 471 | 472 | compute_dash_charge_1_damage: 473 | { 474 | LDA !SAMUS_BEAMS_EQUIPPED : AND #$000F : ASL : TAX 475 | LDA.l dash_charge_1_damage_table,X 476 | RTL 477 | } 478 | 479 | compute_dash_charge_2_damage: 480 | { 481 | LDA !SAMUS_BEAMS_EQUIPPED : AND #$000F : ASL : TAX 482 | LDA.l dash_charge_2_damage_table,X 483 | RTL 484 | } 485 | 486 | compute_dash_charge_3_damage: 487 | { 488 | LDA !SAMUS_BEAMS_EQUIPPED : AND #$000F : ASL : TAX 489 | LDA.l dash_charge_3_damage_table,X 490 | RTL 491 | } 492 | 493 | compute_dash_charge_4_damage: 494 | { 495 | LDA !SAMUS_BEAMS_EQUIPPED : AND #$000F : ASL : TAX 496 | LDA.l dash_charge_4_damage_table,X 497 | RTL 498 | } 499 | 500 | vanilla_uncharged_damage_table: 501 | dw #$0014, #$0032, #$001E, #$003C, #$0028, #$0046, #$003C, #$0064, #$0096, #$00FA, #$00C8, #$012C, #$0000, #$0000, #$0000, #$0000 502 | vanilla_charged_damage_table: 503 | dw #$003C, #$0096, #$005A, #$00B4, #$0078, #$00D2, #$00B4, #$012C, #$01C2, #$02EE, #$0258, #$0384, #$0000, #$0000, #$0000, #$0000 504 | 505 | dash_charge_0_damage_table: 506 | dw #$0014, #$0032, #$001E, #$003C, #$0028, #$0050, #$0032, #$0064, #$0064, #$0064, #$0064, #$0064, #$0000, #$0000, #$0000, #$0000 507 | dash_charge_1_damage_table: 508 | dw #$0028, #$0064, #$003C, #$0078, #$0050, #$00B4, #$0064, #$00C8, #$00C8, #$00C8, #$00C8, #$00C8, #$0000, #$0000, #$0000, #$0000 509 | dash_charge_2_damage_table: 510 | dw #$003C, #$0096, #$005A, #$00B4, #$0078, #$00FA, #$0096, #$012C, #$012C, #$012C, #$012C, #$012C, #$0000, #$0000, #$0000, #$0000 511 | dash_charge_3_damage_table: 512 | dw #$0050, #$00C8, #$0078, #$00F0, #$00A0, #$0168, #$00C8, #$0190, #$0190, #$0190, #$0190, #$0190, #$0000, #$0000, #$0000, #$0000 513 | dash_charge_4_damage_table: 514 | dw #$0064, #$00FA, #$0096, #$012C, #$00C8, #$01C2, #$00FA, #$01F4, #$01F4, #$01F4, #$01F4, #$01F4, #$0000, #$0000, #$0000, #$0000 515 | 516 | %endfree(93) 517 | 518 | 519 | if !FEATURE_PAL 520 | org $A0A55C 521 | else ; shinespark damage 522 | org $A0A54C 523 | endif 524 | JSR EnemyDamageShinespark 525 | 526 | if !FEATURE_PAL 527 | org $A0A63B 528 | else ; power bomb damage 529 | org $A0A62B 530 | endif 531 | JSR EnemyDamagePowerBomb 532 | 533 | if !FEATURE_PAL 534 | org $A0A872 535 | else ; general damage hijack 536 | org $A0A862 537 | endif 538 | JSR EnemyDamage 539 | 540 | 541 | %startfree(A0) 542 | 543 | EnemyDamage: 544 | { 545 | LDA !ram_pacifist : BNE .no_damage 546 | LDA !DAMAGE_COUNTER : CLC : ADC $187A : STA !DAMAGE_COUNTER 547 | LDA !ENEMY_HP,X ; original code 548 | RTS 549 | 550 | .no_damage 551 | ; pull return address and jump past saving enemy hp 552 | PLA 553 | if !FEATURE_PAL 554 | JMP $A8CA 555 | else 556 | JMP $A8BA 557 | endif 558 | } 559 | 560 | EnemyDamageShinespark: 561 | { 562 | LDA !ram_pacifist : BNE .no_damage 563 | LDA !DAMAGE_COUNTER : CLC : ADC $12 : STA !DAMAGE_COUNTER 564 | LDA !ENEMY_HP,X ; original code 565 | RTS 566 | 567 | .no_damage 568 | ; pull return address and jump past saving enemy hp 569 | PLA 570 | if !FEATURE_PAL 571 | JMP $A86A 572 | else 573 | JMP $A55A 574 | endif 575 | } 576 | 577 | EnemyDamagePowerBomb: 578 | { 579 | LDA !ram_pacifist : BNE .no_damage 580 | LDA !DAMAGE_COUNTER : CLC : ADC $12 : STA !DAMAGE_COUNTER 581 | LDA !ENEMY_HP,X ; original code 582 | RTS 583 | 584 | .no_damage 585 | ; pull return address and jump past saving enemy hp 586 | PLA 587 | if !FEATURE_PAL 588 | JMP $A64C 589 | else 590 | JMP $A63C 591 | endif 592 | } 593 | 594 | %endfree(A0) 595 | 596 | -------------------------------------------------------------------------------- /src/fanfare.asm: -------------------------------------------------------------------------------- 1 | ; $82:9396: Queue Samus movement sound effects 2 | org $829396 3 | JSL hook_unpause_play_sound 4 | 5 | ; $82:E126: Logic to queue room music after fanfare 6 | org $82E126 7 | JSL hook_resume_room_music 8 | BRA $08 9 | 10 | ; $84:8BDD: Instruction - clear music queue and queue music track [[Y]] ;;; 11 | org $848BDD 12 | JML play_or_skip_fanfare 13 | 14 | ; $85:80BA: End message box routine 15 | org $8580BA 16 | JML hook_end_fanfare 17 | 18 | ; $85:8493: Handle message box interaction 19 | org $858493 20 | JSR hook_message_box_wait 21 | BRA $0B 22 | 23 | 24 | if !ORIGINAL_MESSAGE_TEXT 25 | else 26 | 27 | org $858412 28 | ; Relocated this table to make room for more message box types 29 | LDA SpecialButtonTilemapOffsets,X 30 | 31 | org $858749 32 | original_button_tilemap_offset_table: 33 | ; Expand message definitions 34 | dw #$8436, #$8289, EnemiesKilledText 35 | dw #$8436, #$8289, BrainBusterText 36 | dw #$8436, #$8289, GhostBusterText 37 | dw #$8436, #$8289, ShipBusterText 38 | ; Currently room for four more types 39 | ; The last one must remain reserved 40 | dw #$8436, #$8289, EndFanfareText 41 | dw #$8436, #$8289, EndFanfareText 42 | dw #$8436, #$8289, EndFanfareText 43 | dw #$8436, #$8289, EndFanfareText 44 | dw #$8436, #$8289, EndFanfareText 45 | 46 | 47 | ;;; Message text must be listed in order 48 | org $859643 49 | table ../resources/HUDfont.tbl 50 | EnemiesKilledText: 51 | dw #$000E, #$000E, #$000E, #$000E, #$000E, #$000E 52 | dw #$2800|' ' 53 | dw #$2800|' ' 54 | dw #$2800|' ' 55 | dw #$2800|'E' 56 | dw #$2800|'N' 57 | dw #$2800|'E' 58 | dw #$2800|'M' 59 | dw #$2800|'I' 60 | dw #$2800|'E' 61 | dw #$2800|'S' 62 | dw #$2800|' ' 63 | dw #$2800|'K' 64 | dw #$2800|'I' 65 | dw #$2800|'L' 66 | dw #$2800|'L' 67 | dw #$2800|'E' 68 | dw #$2800|'D' 69 | dw #$2800|' ' 70 | dw #$2800|' ' 71 | dw #$000E, #$000E, #$000E, #$000E, #$000E, #$000E, #$000E 72 | 73 | BrainBusterText: 74 | dw #$000E, #$000E, #$000E, #$000E, #$000E, #$000E 75 | dw #$2800|' ' 76 | dw #$2800|' ' 77 | dw #$2800|' ' 78 | dw #$2800|'B' 79 | dw #$2800|'R' 80 | dw #$2800|'A' 81 | dw #$2800|'I' 82 | dw #$2800|'N' 83 | dw #$2800|' ' 84 | dw #$2800|'B' 85 | dw #$2800|'U' 86 | dw #$2800|'S' 87 | dw #$2800|'T' 88 | dw #$2800|'E' 89 | dw #$2800|'R' 90 | dw #$2800|' ' 91 | dw #$2800|' ' 92 | dw #$2800|' ' 93 | dw #$2800|' ' 94 | dw #$000E, #$000E, #$000E, #$000E, #$000E, #$000E, #$000E 95 | 96 | GhostBusterText: 97 | dw #$000E, #$000E, #$000E, #$000E, #$000E, #$000E 98 | dw #$2800|' ' 99 | dw #$2800|'W' 100 | dw #$2800|'H' 101 | dw #$2800|'O' 102 | dw #$2800|' ' 103 | dw #$2800|'Y' 104 | dw #$2800|'A' 105 | dw #$2800|' ' 106 | dw #$2800|'G' 107 | dw #$2800|'O' 108 | dw #$2800|'N' 109 | dw #$2800|'N' 110 | dw #$2800|'A' 111 | dw #$2800|' ' 112 | dw #$2800|'C' 113 | dw #$2800|'A' 114 | dw #$2800|'L' 115 | dw #$2800|'L' 116 | dw #$2800|'?' 117 | dw #$000E, #$000E, #$000E, #$000E, #$000E, #$000E, #$000E 118 | 119 | ShipBusterText: 120 | dw #$000E, #$000E, #$000E, #$000E, #$000E, #$000E 121 | dw #$2800|' ' 122 | dw #$2800|' ' 123 | dw #$2800|'S' 124 | dw #$2800|'H' 125 | dw #$2800|'I' 126 | dw #$2800|'P' 127 | dw #$2800|' ' 128 | dw #$2800|'C' 129 | dw #$2800|'L' 130 | dw #$2800|'O' 131 | dw #$2800|'A' 132 | dw #$2800|'K' 133 | dw #$2800|'I' 134 | dw #$2800|'N' 135 | dw #$2800|'G' 136 | dw #$2800|' ' 137 | dw #$2800|' ' 138 | dw #$2800|' ' 139 | dw #$2800|' ' 140 | dw #$000E, #$000E, #$000E, #$000E, #$000E, #$000E, #$000E 141 | dw #$000E, #$000E, #$000E, #$000E, #$000E, #$000E 142 | dw #$2800|' ' 143 | dw #$2800|' ' 144 | dw #$2800|'D' 145 | dw #$2800|'E' 146 | dw #$2800|'V' 147 | dw #$2800|'I' 148 | dw #$2800|'C' 149 | dw #$2800|'E' 150 | dw #$2800|' ' 151 | dw #$2800|'E' 152 | dw #$2800|'N' 153 | dw #$2800|'G' 154 | dw #$2800|'A' 155 | dw #$2800|'G' 156 | dw #$2800|'E' 157 | dw #$2800|'D' 158 | dw #$2800|' ' 159 | dw #$2800|' ' 160 | dw #$2800|' ' 161 | dw #$000E, #$000E, #$000E, #$000E, #$000E, #$000E, #$000E 162 | 163 | EndFanfareText: 164 | table ../resources/normal.tbl 165 | 166 | ;;; Recreate $8749: Special button tilemap offsets ;;; 167 | SpecialButtonTilemapOffsets: 168 | dw #$0000 ; 1: Energy tank 169 | dw #$012A ; 2: Missile 170 | dw #$012A ; 3: Super missile 171 | dw #$012C ; 4: Power bomb 172 | dw #$012C ; 5: Grappling beam 173 | dw #$012C ; 6: X-ray scope 174 | dw #$0000 ; 7: Varia suit 175 | dw #$0000 ; 8: Spring ball 176 | dw #$0000 ; 9: Morphing ball 177 | dw #$0000 ; Ah: Screw attack 178 | dw #$0000 ; Bh: Hi-jump boots 179 | dw #$0000 ; Ch: Space jump 180 | dw #$0120 ; Dh: Speed booster 181 | dw #$0000 ; Eh: Charge beam 182 | dw #$0000 ; Fh: Ice beam 183 | dw #$0000 ; 10h: Wave beam 184 | dw #$0000 ; 11h: Spazer 185 | dw #$0000 ; 12h: Plasma beam 186 | dw #$012A ; 13h: Bomb 187 | dw #$0000 ; 14h: Map data access completed 188 | dw #$0000 ; 15h: Energy recharge completed 189 | dw #$0000 ; 16h: Missile reload completed 190 | dw #$0000 ; 17h: Would you like to save? 191 | dw #$0000 ; 18h: Save completed 192 | dw #$0000 ; 19h: Reserve tank 193 | dw #$0000 ; 1Ah: Gravity suit 194 | dw #$0000 ; 1Bh: Terminator 195 | dw #$000E ; 1Ch: Would you like to save? (Used by gunship) 196 | dw #$000E ; 1Dh: Reserved (Used by gunship) 197 | dw #$0000 ; 1Eh: Enemies killed 198 | dw #$0000 ; 1Fh: Brain buster 199 | dw #$0000 ; 20h: Ghost buster 200 | dw #$0000 ; 21h: Ship buster 201 | dw #$0000 ; 22h: 202 | dw #$0000 ; 23h: 203 | dw #$0000 ; 24h: 204 | dw #$0000 ; 25h: 205 | dw #$0000 ; 26h: Reserved 206 | 207 | warnpc $85A000 208 | endif 209 | 210 | 211 | %startfree(85) 212 | 213 | ; List this first since it affects bank $84 where we are trying to minimize change 214 | play_or_skip_fanfare: 215 | { 216 | PHA 217 | LDA !sram_fanfare : BNE .playfanfare 218 | PLA 219 | JML $848C05 220 | 221 | .playfanfare 222 | PLA 223 | 224 | ; overwritten logic 225 | PHX 226 | LDX #$000E 227 | JML $848BE1 228 | } 229 | 230 | prepare_fanfare_from_non_plm: 231 | { 232 | LDA #$0002 : STA $C1 : LDY #$00C1 233 | ; $848BDD ends with an RTS, but we need an RTL 234 | ; Have the RTS return to $848031 which is an RTL 235 | PEA $8030 : JML $848BDD 236 | } 237 | 238 | kill_enemies: 239 | { 240 | TDC 241 | .kill_loop 242 | TAX : LDA !ENEMY_PROPERTIES,X : ORA #$0200 : STA !ENEMY_PROPERTIES,X 243 | TXA : CLC : ADC #$0040 : CMP #$0800 : BNE .kill_loop 244 | 245 | if !ORIGINAL_MESSAGE_TEXT 246 | RTL 247 | else 248 | JSL prepare_fanfare_from_non_plm 249 | ; Play room music track after 360 frames 250 | LDA #$0168 : JSL $82E118 251 | 252 | ; Open message box 253 | LDA !ROOM_ID : CMP.w #ROOM_MotherBrainRoom : BEQ .kill_mb 254 | CMP.w #ROOM_PhantoonRoom : BEQ .kill_phantoon 255 | CMP.w #ROOM_LandingSite : BEQ .kill_ship 256 | LDA #$001E : JML $858080 257 | .kill_mb 258 | LDA #$001F : JML $858080 259 | .kill_phantoon 260 | LDA #$0020 : JML $858080 261 | .kill_ship 262 | LDA #$0021 : JML $858080 263 | endif 264 | } 265 | 266 | hook_message_box_wait: 267 | { 268 | LDA !sram_fanfare : BNE .fanfareloop 269 | ; shorten message box length 270 | LDX #$0020 271 | 272 | .nofanfareloop 273 | ; skipping fanfare, so no need to mess with sound 274 | JSR hook_msg_wait_for_lag_frame 275 | DEX : BNE .nofanfareloop 276 | RTS 277 | 278 | .fanfareloop 279 | ; original logic 280 | JSR hook_msg_wait_for_lag_frame 281 | PHX 282 | JSL $808F0C ; Handle music queue 283 | JSL $8289EF ; Handle sounds 284 | PLX : DEX : BNE .fanfareloop 285 | RTS 286 | } 287 | 288 | hook_msg_wait_for_lag_frame: 289 | { 290 | PHP 291 | if !FEATURE_SD2SNES 292 | %a8() 293 | .wait_joypad 294 | LDA $4212 : BIT #$01 : BNE .wait_joypad 295 | 296 | %a16() 297 | LDA $4218 : BEQ .done 298 | CMP !sram_ctrl_load_state : BNE .done 299 | LDA !SRAM_SAVED_STATE : CMP !SAFEWORD : BNE .done 300 | PHB : PHK : PLB 301 | JML load_state 302 | 303 | .done 304 | endif 305 | ; Jump to vanilla routine 306 | JMP $8137 307 | } 308 | 309 | hook_resume_room_music: 310 | { 311 | LDA !sram_fanfare : BNE .resume 312 | 313 | ; This method is also used when starting game at Ceres 314 | LDA !AREA_ID : CMP #$0006 : BEQ .resume 315 | RTL 316 | 317 | .resume 318 | ; original logic to queue room music after fanfare 319 | TDC : JSL $808FF7 320 | LDA !MUSIC_TRACK 321 | JSL !MUSIC_ROUTINE 322 | RTL 323 | } 324 | 325 | hook_unpause_play_sound: 326 | { 327 | JSL $82BE2F ; original logic to queue Samus movement sound effects 328 | LDA !sram_healthalarm : CMP #$0004 : BNE .done_health_alarm 329 | LDA #$0002 : JSL $80914D 330 | .done_health_alarm 331 | RTL 332 | } 333 | 334 | hook_end_fanfare: 335 | { 336 | LDA !sram_healthalarm : CMP #$0004 : BNE .done_health_alarm 337 | LDA #$0002 : JSL $80914D 338 | .done_health_alarm 339 | PLY : PLX ; original logic 340 | PLB : PLP : RTL 341 | } 342 | 343 | %endfree(85) 344 | 345 | -------------------------------------------------------------------------------- /src/freespace.asm: -------------------------------------------------------------------------------- 1 | 2 | ; Original design by cout https://github.com/cout/baby_metroid/blob/main/src/freespace.asm 3 | 4 | ; Assign start of freespace per bank 5 | ; Only one chunk allowed per bank 6 | ; Ommited banks have no freespace 7 | !START_FREESPACE_80 = $80CD8E ; $3232 8 | !START_FREESPACE_81 = $81EF1A ; $FE6 9 | !START_FREESPACE_82 = $82F70F ; $8F1 10 | ; Logic expects custom doors are placed at or after $83C000 11 | !START_FREESPACE_83 = $83C000 ; $529A 12 | !START_FREESPACE_84 = $84EFD9 ; $1027 (PAL) 13 | ; Leave space for custom fanfare text to immediately follow vanilla text 14 | !START_FREESPACE_85 = $85A000 ; $69BF 15 | !START_FREESPACE_86 = $86F4E2 ; $B1E (PAL) 16 | !START_FREESPACE_87 = $87C964 ; $369C 17 | !START_FREESPACE_88 = $88EE32 ; $11CE 18 | !START_FREESPACE_89 = $89AEFD ; $5103 19 | !START_FREESPACE_8A = $8AE980 ; $1680 20 | !START_FREESPACE_8B = $8BF760 ; $8A0 21 | if !FEATURE_PAL 22 | !START_FREESPACE_8C = $8CF79D ; $C17 (PAL) 23 | else 24 | ; NTSC needs more freespace for cutscenes 25 | !START_FREESPACE_8C = $8CF3E9 ; $C17 26 | endif 27 | !START_FREESPACE_8D = $8DFFF1 ; $F 28 | !START_FREESPACE_8E = $8EE600 ; $1A00 29 | !START_FREESPACE_8F = $8FE99B ; $1665 30 | !START_FREESPACE_90 = $90F63A ; $9C6 31 | !START_FREESPACE_91 = $91FFEE ; $12 32 | !START_FREESPACE_92 = $92EDF4 ; $120C 33 | !START_FREESPACE_93 = $93F61D ; $9E3 34 | !START_FREESPACE_94 = $94B19F ; $1661 35 | !START_FREESPACE_99 = $99EE21 ; $11DF 36 | !START_FREESPACE_9A = $9AFC20 ; $3E0 37 | !START_FREESPACE_9B = $9BCBFB ; $1405 38 | !START_FREESPACE_9C = $9CFA80 ; $580 39 | !START_FREESPACE_9D = $9DF780 ; $880 40 | !START_FREESPACE_9E = $9EF6C0 ; $940 41 | !START_FREESPACE_9F = $9FF740 ; $8C0 42 | !START_FREESPACE_A0 = $A0F7F3 ; $80D (PAL) 43 | !START_FREESPACE_A1 = $A1EBD1 ; $142F 44 | !START_FREESPACE_A2 = $A2F4B0 ; $B50 (PAL) 45 | !START_FREESPACE_A3 = $A3F32D ; $CD3 (PAL) 46 | !START_FREESPACE_A4 = $A4F6D9 ; $927 (PAL) 47 | !START_FREESPACE_A5 = $A5F99F ; $661 (PAL) 48 | !START_FREESPACE_A6 = $A6FEBC ; $144 49 | !START_FREESPACE_A7 = $A7FFB6 ; $4A (PAL) 50 | !START_FREESPACE_A8 = $A8F9CD ; $633 (PAL) 51 | !START_FREESPACE_A9 = $A9FBBD ; $443 (PAL) 52 | !START_FREESPACE_AA = $AAF7E3 ; $81D (PAL) 53 | !START_FREESPACE_AB = $ABF800 ; $800 54 | !START_FREESPACE_AC = $ACEE00 ; $1200 55 | !START_FREESPACE_AD = $ADF710 ; $8F0 (PAL) 56 | !START_FREESPACE_AE = $AEFD20 ; $2E0 57 | !START_FREESPACE_AF = $AFEC00 ; $1400 58 | !START_FREESPACE_B0 = $B0EE00 ; $1200 59 | !START_FREESPACE_B2 = $B2FF23 ; $DD (PAL) 60 | !START_FREESPACE_B3 = $B3ED87 ; $1279 (PAL) 61 | !START_FREESPACE_B4 = $B4F4B8 ; $B48 62 | !START_FREESPACE_B5 = $B5F000 ; $1000 63 | !START_FREESPACE_B6 = $B6F200 ; $E00 64 | !START_FREESPACE_B7 = $B7FFC0 ; $40 (PAL) 65 | !START_FREESPACE_B8 = $B88000 ; $8000 66 | !START_FREESPACE_CE = $CEB22E ; $4DD2 67 | !START_FREESPACE_DE = $DED1C0 ; $2E40 68 | !START_FREESPACE_DF = $DFD500 ; $2B21 69 | !START_FREESPACE_E0 = $E08000 ; $8000 70 | !START_FREESPACE_E1 = $E18000 ; $8000 71 | !START_FREESPACE_E2 = $E28000 ; $8000 72 | !START_FREESPACE_E3 = $E38000 ; $8000 73 | !START_FREESPACE_E4 = $E48000 ; $8000 74 | !START_FREESPACE_E5 = $E58000 ; $8000 75 | !START_FREESPACE_E6 = $E68000 ; $8000 76 | !START_FREESPACE_E7 = $E78000 ; $8000 77 | !START_FREESPACE_E8 = $E88000 ; $8000 78 | !START_FREESPACE_E9 = $E98000 ; $8000 79 | !START_FREESPACE_EA = $EA8000 ; $8000 80 | !START_FREESPACE_EB = $EB8000 ; $8000 81 | !START_FREESPACE_EC = $EC8000 ; $8000 82 | !START_FREESPACE_ED = $ED8000 ; $8000 83 | !START_FREESPACE_EE = $EE8000 ; $8000 84 | !START_FREESPACE_EF = $EF8000 ; $8000 85 | !START_FREESPACE_F0 = $F08000 ; $8000 86 | !START_FREESPACE_F1 = $F18000 ; $8000 87 | !START_FREESPACE_F2 = $F28000 ; $8000 88 | !START_FREESPACE_F3 = $F38000 ; $8000 89 | !START_FREESPACE_F4 = $F48000 ; $8000 90 | !START_FREESPACE_F5 = $F58000 ; $8000 91 | !START_FREESPACE_F6 = $F68000 ; $8000 92 | !START_FREESPACE_F7 = $F78000 ; $8000 93 | !START_FREESPACE_F8 = $F88000 ; $8000 94 | !START_FREESPACE_F9 = $F98000 ; $8000 95 | !START_FREESPACE_FA = $FA8000 ; $8000 96 | !START_FREESPACE_FB = $FB8000 ; $8000 97 | !START_FREESPACE_FC = $FC8000 ; $8000 98 | !START_FREESPACE_FD = $FD8000 ; $8000 99 | !START_FREESPACE_FE = $FE8000 ; $8000 100 | !START_FREESPACE_FF = $FF8000 ; $8000 101 | 102 | ; These defines will be reassigned by the endfree macro 103 | ; This leaves our starting location untouched for later evaluation 104 | !FREESPACE_80 = !START_FREESPACE_80 105 | !FREESPACE_81 = !START_FREESPACE_81 106 | !FREESPACE_82 = !START_FREESPACE_82 107 | !FREESPACE_83 = !START_FREESPACE_83 108 | !FREESPACE_84 = !START_FREESPACE_84 109 | !FREESPACE_85 = !START_FREESPACE_85 110 | !FREESPACE_86 = !START_FREESPACE_86 111 | !FREESPACE_87 = !START_FREESPACE_87 112 | !FREESPACE_88 = !START_FREESPACE_88 113 | !FREESPACE_89 = !START_FREESPACE_89 114 | !FREESPACE_8A = !START_FREESPACE_8A 115 | !FREESPACE_8B = !START_FREESPACE_8B 116 | !FREESPACE_8C = !START_FREESPACE_8C 117 | !FREESPACE_8D = !START_FREESPACE_8D 118 | !FREESPACE_8E = !START_FREESPACE_8E 119 | !FREESPACE_8F = !START_FREESPACE_8F 120 | !FREESPACE_90 = !START_FREESPACE_90 121 | !FREESPACE_91 = !START_FREESPACE_91 122 | !FREESPACE_92 = !START_FREESPACE_92 123 | !FREESPACE_93 = !START_FREESPACE_93 124 | !FREESPACE_94 = !START_FREESPACE_94 125 | !FREESPACE_99 = !START_FREESPACE_99 126 | !FREESPACE_9A = !START_FREESPACE_9A 127 | !FREESPACE_9B = !START_FREESPACE_9B 128 | !FREESPACE_9C = !START_FREESPACE_9C 129 | !FREESPACE_9D = !START_FREESPACE_9D 130 | !FREESPACE_9E = !START_FREESPACE_9E 131 | !FREESPACE_9F = !START_FREESPACE_9F 132 | !FREESPACE_A0 = !START_FREESPACE_A0 133 | !FREESPACE_A1 = !START_FREESPACE_A1 134 | !FREESPACE_A2 = !START_FREESPACE_A2 135 | !FREESPACE_A3 = !START_FREESPACE_A3 136 | !FREESPACE_A4 = !START_FREESPACE_A4 137 | !FREESPACE_A5 = !START_FREESPACE_A5 138 | !FREESPACE_A6 = !START_FREESPACE_A6 139 | !FREESPACE_A7 = !START_FREESPACE_A7 140 | !FREESPACE_A8 = !START_FREESPACE_A8 141 | !FREESPACE_A9 = !START_FREESPACE_A9 142 | !FREESPACE_AA = !START_FREESPACE_AA 143 | !FREESPACE_AB = !START_FREESPACE_AB 144 | !FREESPACE_AC = !START_FREESPACE_AC 145 | !FREESPACE_AD = !START_FREESPACE_AD 146 | !FREESPACE_AE = !START_FREESPACE_AE 147 | !FREESPACE_AF = !START_FREESPACE_AF 148 | !FREESPACE_B0 = !START_FREESPACE_B0 149 | !FREESPACE_B2 = !START_FREESPACE_B2 150 | !FREESPACE_B3 = !START_FREESPACE_B3 151 | !FREESPACE_B4 = !START_FREESPACE_B4 152 | !FREESPACE_B5 = !START_FREESPACE_B5 153 | !FREESPACE_B6 = !START_FREESPACE_B6 154 | !FREESPACE_B7 = !START_FREESPACE_B7 155 | !FREESPACE_B8 = !START_FREESPACE_B8 156 | !FREESPACE_CE = !START_FREESPACE_CE 157 | !FREESPACE_DE = !START_FREESPACE_DE 158 | !FREESPACE_DF = !START_FREESPACE_DF 159 | !FREESPACE_E0 = !START_FREESPACE_E0 160 | !FREESPACE_E1 = !START_FREESPACE_E1 161 | !FREESPACE_E2 = !START_FREESPACE_E2 162 | !FREESPACE_E3 = !START_FREESPACE_E3 163 | !FREESPACE_E4 = !START_FREESPACE_E4 164 | !FREESPACE_E5 = !START_FREESPACE_E5 165 | !FREESPACE_E6 = !START_FREESPACE_E6 166 | !FREESPACE_E7 = !START_FREESPACE_E7 167 | !FREESPACE_E8 = !START_FREESPACE_E8 168 | !FREESPACE_E9 = !START_FREESPACE_E9 169 | !FREESPACE_EA = !START_FREESPACE_EA 170 | !FREESPACE_EB = !START_FREESPACE_EB 171 | !FREESPACE_EC = !START_FREESPACE_EC 172 | !FREESPACE_ED = !START_FREESPACE_ED 173 | !FREESPACE_EE = !START_FREESPACE_EE 174 | !FREESPACE_EF = !START_FREESPACE_EF 175 | !FREESPACE_F0 = !START_FREESPACE_F0 176 | !FREESPACE_F1 = !START_FREESPACE_F1 177 | !FREESPACE_F2 = !START_FREESPACE_F2 178 | !FREESPACE_F3 = !START_FREESPACE_F3 179 | !FREESPACE_F4 = !START_FREESPACE_F4 180 | !FREESPACE_F5 = !START_FREESPACE_F5 181 | !FREESPACE_F6 = !START_FREESPACE_F6 182 | !FREESPACE_F7 = !START_FREESPACE_F7 183 | !FREESPACE_F8 = !START_FREESPACE_F8 184 | !FREESPACE_F9 = !START_FREESPACE_F9 185 | !FREESPACE_FA = !START_FREESPACE_FA 186 | !FREESPACE_FB = !START_FREESPACE_FB 187 | !FREESPACE_FC = !START_FREESPACE_FC 188 | !FREESPACE_FD = !START_FREESPACE_FD 189 | !FREESPACE_FE = !START_FREESPACE_FE 190 | !FREESPACE_FF = !START_FREESPACE_FF 191 | 192 | ; Assign end of freespace per bank 193 | ; Set for freespace that doesn't end at the bank border 194 | !END_FREESPACE_80 = $80FFC0 ; Game header 195 | !END_FREESPACE_81 = $81FF00 ; Thanks Genji! 196 | !END_FREESPACE_82 = $820000+$10000 197 | !END_FREESPACE_83 = $830000+$10000 198 | !END_FREESPACE_84 = $840000+$10000 199 | !END_FREESPACE_85 = $850000+$10000 200 | !END_FREESPACE_86 = $860000+$10000 201 | !END_FREESPACE_87 = $870000+$10000 202 | !END_FREESPACE_88 = $880000+$10000 203 | !END_FREESPACE_89 = $890000+$10000 204 | !END_FREESPACE_8A = $8A0000+$10000 205 | !END_FREESPACE_8B = $8B0000+$10000 206 | !END_FREESPACE_8C = $8C0000+$10000 207 | !END_FREESPACE_8D = $8D0000+$10000 208 | !END_FREESPACE_8E = $8E0000+$10000 209 | !END_FREESPACE_8F = $8F0000+$10000 210 | !END_FREESPACE_90 = $900000+$10000 211 | !END_FREESPACE_91 = $910000+$10000 212 | !END_FREESPACE_92 = $920000+$10000 213 | !END_FREESPACE_93 = $930000+$10000 214 | !END_FREESPACE_94 = $94C800 ; Ship tiles 215 | !END_FREESPACE_99 = $990000+$10000 216 | !END_FREESPACE_9A = $9A0000+$10000 217 | !END_FREESPACE_9B = $9BE000 ; Samus tiles 218 | !END_FREESPACE_9C = $9C0000+$10000 219 | !END_FREESPACE_9D = $9D0000+$10000 220 | !END_FREESPACE_9E = $9E0000+$10000 221 | !END_FREESPACE_9F = $9F0000+$10000 222 | !END_FREESPACE_A0 = $A00000+$10000 223 | !END_FREESPACE_A1 = $A10000+$10000 224 | !END_FREESPACE_A2 = $A20000+$10000 225 | !END_FREESPACE_A3 = $A30000+$10000 226 | !END_FREESPACE_A4 = $A40000+$10000 227 | !END_FREESPACE_A5 = $A50000+$10000 228 | !END_FREESPACE_A6 = $A60000+$10000 229 | !END_FREESPACE_A7 = $A70000+$10000 230 | !END_FREESPACE_A8 = $A80000+$10000 231 | !END_FREESPACE_A9 = $A90000+$10000 232 | !END_FREESPACE_AA = $AA0000+$10000 233 | !END_FREESPACE_AB = $AB0000+$10000 234 | !END_FREESPACE_AC = $AC0000+$10000 235 | !END_FREESPACE_AD = $AD0000+$10000 236 | !END_FREESPACE_AE = $AE0000+$10000 237 | !END_FREESPACE_AF = $AF0000+$10000 238 | !END_FREESPACE_B0 = $B00000+$10000 239 | !END_FREESPACE_B2 = $B20000+$10000 240 | !END_FREESPACE_B3 = $B30000+$10000 241 | !END_FREESPACE_B4 = $B40000+$10000 242 | !END_FREESPACE_B5 = $B50000+$10000 243 | !END_FREESPACE_B6 = $B60000+$10000 244 | !END_FREESPACE_B7 = $B70000+$10000 245 | !END_FREESPACE_B8 = $B80000+$10000 246 | !END_FREESPACE_CE = $CE0000+$10000 247 | !END_FREESPACE_DE = $DE0000+$10000 248 | !END_FREESPACE_DF = $DF0000+$10000 249 | !END_FREESPACE_E0 = $E00000+$10000 250 | !END_FREESPACE_E1 = $E10000+$10000 251 | !END_FREESPACE_E2 = $E20000+$10000 252 | !END_FREESPACE_E3 = $E30000+$10000 253 | !END_FREESPACE_E4 = $E40000+$10000 254 | !END_FREESPACE_E5 = $E50000+$10000 255 | !END_FREESPACE_E6 = $E68000 ; tilegraphics.asm 256 | !END_FREESPACE_E7 = $E78000 ; tilegraphics.asm 257 | !END_FREESPACE_E8 = $E88000 ; tilegraphics.asm + presets.asm 258 | !END_FREESPACE_E9 = $E98000 ; presets.asm 259 | !END_FREESPACE_EA = $EA8000 ; presets.asm 260 | !END_FREESPACE_EB = $EB8000 ; presets.asm 261 | !END_FREESPACE_EC = $EC8000 ; presets.asm 262 | !END_FREESPACE_ED = $ED8000 ; presets.asm 263 | !END_FREESPACE_EE = $EE8000 ; presets.asm 264 | !END_FREESPACE_EF = $EF0000+$10000 265 | !END_FREESPACE_F0 = $F00000+$10000 266 | !END_FREESPACE_F1 = $F10000+$10000 267 | !END_FREESPACE_F2 = $F20000+$10000 268 | !END_FREESPACE_F3 = $F30000+$10000 269 | !END_FREESPACE_F4 = $F4D800 ; tilegraphics.asm 270 | !END_FREESPACE_F5 = $F58000 ; tilegraphics.asm 271 | !END_FREESPACE_F6 = $F68000 ; tilegraphics.asm 272 | !END_FREESPACE_F7 = $F78000 ; tilegraphics.asm 273 | !END_FREESPACE_F8 = $F88000 ; tilegraphics.asm 274 | !END_FREESPACE_F9 = $F98000 ; tilegraphics.asm 275 | !END_FREESPACE_FA = $FA8000 ; tilegraphics.asm 276 | !END_FREESPACE_FB = $FB8000 ; tilegraphics.asm 277 | !END_FREESPACE_FC = $FC8000 ; tilegraphics.asm 278 | !END_FREESPACE_FD = $FD8000 ; tilegraphics.asm 279 | !END_FREESPACE_FE = $FE8000 ; tilegraphics.asm 280 | !END_FREESPACE_FF = $1000000 281 | 282 | ; Allows us to setup warnings for mishandled macros 283 | !FREESPACE_BANK = -1 284 | 285 | macro startfree(bank) 286 | ; Allows us to assign freespace without gaps from different files 287 | assert !FREESPACE_BANK < 0, "You forgot to close out bank !FREESPACE_BANK" 288 | org !FREESPACE_ 289 | !FREESPACE_BANK = $ 290 | endmacro 291 | 292 | macro endfree(bank) 293 | ; Used to close out an org and track the next free byte 294 | assert !FREESPACE_BANK >= 0, "No matching startfree()" 295 | assert $ = !FREESPACE_BANK, "You closed out the wrong bank. (Check bank !FREESPACE_BANK)" 296 | !FREESPACE_COUNTER_ ?= 0 297 | FreespaceLabel_!FREESPACE_COUNTER_: 298 | !FREESPACE_ := FreespaceLabel_!FREESPACE_COUNTER_ 299 | !FREESPACE_COUNTER_ #= !FREESPACE_COUNTER_+1 300 | !FREESPACE_BANK = -1 301 | warnpc !END_FREESPACE_ 302 | endmacro 303 | 304 | macro printfreespacebank(bank) 305 | ; Print some numbers about our freespace usage 306 | org !FREESPACE_ 307 | !FREESPACE_COUNTER_ ?= 0 308 | if !FREESPACE_COUNTER_ 309 | print "Bank $ ended at $", pc, " with $", hex(!END_FREESPACE_-!FREESPACE_), " bytes remaining" 310 | endif 311 | endmacro 312 | 313 | macro printfreespace() 314 | ; Hide this long list in a single macro 315 | %printfreespacebank(80) 316 | %printfreespacebank(81) 317 | %printfreespacebank(82) 318 | %printfreespacebank(83) 319 | %printfreespacebank(84) 320 | %printfreespacebank(85) 321 | %printfreespacebank(86) 322 | %printfreespacebank(87) 323 | %printfreespacebank(88) 324 | %printfreespacebank(89) 325 | %printfreespacebank(8A) 326 | %printfreespacebank(8B) 327 | %printfreespacebank(8C) 328 | %printfreespacebank(8D) 329 | %printfreespacebank(8E) 330 | %printfreespacebank(8F) 331 | %printfreespacebank(90) 332 | %printfreespacebank(91) 333 | %printfreespacebank(92) 334 | %printfreespacebank(93) 335 | %printfreespacebank(99) 336 | %printfreespacebank(9A) 337 | %printfreespacebank(9C) 338 | %printfreespacebank(9D) 339 | %printfreespacebank(9E) 340 | %printfreespacebank(9F) 341 | %printfreespacebank(A0) 342 | %printfreespacebank(A1) 343 | %printfreespacebank(A2) 344 | %printfreespacebank(A3) 345 | %printfreespacebank(A4) 346 | %printfreespacebank(A5) 347 | %printfreespacebank(A6) 348 | %printfreespacebank(A7) 349 | %printfreespacebank(A8) 350 | %printfreespacebank(A9) 351 | %printfreespacebank(AA) 352 | %printfreespacebank(AB) 353 | %printfreespacebank(AC) 354 | %printfreespacebank(AD) 355 | %printfreespacebank(AE) 356 | %printfreespacebank(AF) 357 | %printfreespacebank(B0) 358 | %printfreespacebank(B2) 359 | %printfreespacebank(B3) 360 | %printfreespacebank(B4) 361 | %printfreespacebank(B5) 362 | %printfreespacebank(B6) 363 | %printfreespacebank(B7) 364 | %printfreespacebank(B8) 365 | %printfreespacebank(CE) 366 | %printfreespacebank(DE) 367 | %printfreespacebank(DF) 368 | %printfreespacebank(E0) 369 | %printfreespacebank(E1) 370 | %printfreespacebank(E2) 371 | %printfreespacebank(E3) 372 | %printfreespacebank(E4) 373 | %printfreespacebank(E5) 374 | %printfreespacebank(E6) 375 | %printfreespacebank(E7) 376 | %printfreespacebank(E8) 377 | %printfreespacebank(E9) 378 | %printfreespacebank(EA) 379 | %printfreespacebank(EB) 380 | %printfreespacebank(EC) 381 | %printfreespacebank(ED) 382 | %printfreespacebank(EE) 383 | %printfreespacebank(EF) 384 | %printfreespacebank(F0) 385 | %printfreespacebank(F1) 386 | %printfreespacebank(F2) 387 | %printfreespacebank(F3) 388 | %printfreespacebank(F4) 389 | %printfreespacebank(F5) 390 | %printfreespacebank(F6) 391 | %printfreespacebank(F7) 392 | %printfreespacebank(F8) 393 | %printfreespacebank(F9) 394 | %printfreespacebank(FA) 395 | %printfreespacebank(FB) 396 | %printfreespacebank(FC) 397 | %printfreespacebank(FD) 398 | %printfreespacebank(FE) 399 | %printfreespacebank(FF) 400 | endmacro 401 | 402 | -------------------------------------------------------------------------------- /src/init.asm: -------------------------------------------------------------------------------- 1 | 2 | ; hijack, runs as game is starting, JSR to RAM initialization to avoid bad values 3 | org $808455 4 | JML init_code 5 | 6 | 7 | ; hijack when clearing bank 7E 8 | org $808490 9 | clear_bank: 10 | ; Save quickboot state since it needs to distinguish between a soft and hard reset 11 | LDY.w !ram_quickboot_spc_state 12 | LDX #$3FFE 13 | .loop 14 | STZ $0000,X 15 | STZ $4000,X 16 | STZ $8000,X 17 | STZ $C000,X 18 | DEX #2 : BPL .loop 19 | JSL init_nonzero_wram 20 | STY.w !ram_quickboot_spc_state 21 | BRA .end 22 | warnpc $8084AF 23 | 24 | org $8084AF 25 | .end 26 | 27 | org $80856E 28 | JML init_post_boot 29 | 30 | 31 | %startfree(81) 32 | 33 | init_code: 34 | { 35 | %ai16() 36 | PHA 37 | 38 | ; Initialize RAM (Bank 7E required) 39 | TDC : STA !ram_slowdown_mode 40 | 41 | ; Check if we should initialize SRAM 42 | LDA !sram_initialized : CMP !SRAM_VERSION : BEQ .sram_initialized 43 | BCC .sram_init_routine 44 | TDC 45 | .sram_init_routine 46 | ASL : TAX 47 | JSR (init_sram_routine_table,X) 48 | 49 | .sram_initialized 50 | if !FEATURE_SD2SNES 51 | JSL validate_sram_for_savestates 52 | endif 53 | PLA 54 | ; Execute overwritten logic and return 55 | if !FEATURE_PAL 56 | JSL $8B90EF 57 | else 58 | JSL $8B9146 59 | endif 60 | JML $808459 61 | } 62 | 63 | init_nonzero_wram: 64 | { 65 | ; RAM $7E0000 fluctuates so it is not a good default value 66 | LDA #!ENEMY_HP : STA !ram_watch_left 67 | LDA #!SAMUS_HP : STA !ram_watch_right 68 | LDA #$007E : STA !ram_watch_bank 69 | LDA !sram_seed_X : STA !ram_seed_X 70 | LDA !sram_seed_Y : STA !ram_seed_Y 71 | 72 | TDC : STA !ram_cm_watch_enemy_side 73 | STA !ram_cm_watch_enemy_property : STA !ram_cm_watch_enemy_index 74 | STA !ram_watch_left_index : STA !ram_watch_right_index 75 | 76 | INC : STA !ram_cm_sfxlib1 77 | STA !ram_cm_sfxlib2 : STA !ram_cm_sfxlib3 78 | 79 | JML init_wram_based_on_sram 80 | } 81 | 82 | init_sram_routine_table: 83 | dw init_sram ; Version 0 treated same as 0-9 84 | dw init_sram ; Version 1 treated same as 0-9 85 | dw init_sram ; Version 2 treated same as 0-9 86 | dw init_sram ; Version 3 treated same as 0-9 87 | dw init_sram ; Version 4 treated same as 0-9 88 | dw init_sram ; Version 5 treated same as 0-9 89 | dw init_sram ; Version 6 treated same as 0-9 90 | dw init_sram ; Version 7 treated same as 0-9 91 | dw init_sram ; Version 8 treated same as 0-9 92 | dw init_sram ; Version 9 treated same as 0-9 93 | dw init_sram_upgrade_9toA 94 | dw init_sram_upgrade_AtoB 95 | dw init_sram_upgrade_BtoC 96 | dw init_sram_upgrade_CtoD 97 | dw init_sram_upgrade_DtoE 98 | dw init_sram_upgrade_EtoF 99 | dw init_sram_upgrade_Fto10 100 | dw init_sram_upgrade_10to11 101 | dw init_sram_upgrade_11to12 102 | dw init_sram_upgrade_12to13 103 | dw init_sram_upgrade_13to14 104 | dw init_sram_upgrade_14to15 105 | dw init_sram_upgrade_15to16 106 | dw init_sram_upgrade_16to17 107 | dw init_sram_upgrade_17to18 108 | dw init_sram_upgrade_18to19 109 | 110 | init_sram: 111 | { 112 | JSL init_sram_controller_shortcuts 113 | LDA #$0015 : STA !sram_artificial_lag 114 | TDC : STA !sram_fanfare 115 | STA !sram_frame_counter_mode 116 | STA !sram_display_mode 117 | STA !sram_last_preset 118 | STA !sram_save_has_set_rng 119 | STA !sram_preset_category 120 | STA !sram_custom_preset_slot 121 | STA !sram_room_strat 122 | STA !sram_sprite_prio_flag 123 | INC : STA !sram_rerandomize 124 | STA !sram_music_toggle 125 | INC : STA !sram_metronome_sfx 126 | LDA #$000A : STA !sram_metronome_tickrate 127 | 128 | .upgrade_9toA 129 | TDC : STA !sram_ctrl_toggle_tileviewer 130 | STA !sram_status_icons 131 | STA !sram_suit_properties 132 | 133 | .upgrade_AtoB 134 | TDC : STA !sram_ctrl_update_timers 135 | 136 | .upgrade_BtoC 137 | TDC : STA !sram_top_display_mode 138 | STA !sram_room_layout 139 | INC : STA !sram_healthalarm 140 | 141 | if !FEATURE_DEV 142 | LDA !CUTSCENE_QUICKBOOT|$0003 : STA !sram_cutscenes 143 | else 144 | LDA #$0003 : STA !sram_cutscenes 145 | endif 146 | 147 | .upgrade_CtoD 148 | TDC : STA !sram_preset_options 149 | STA !sram_lag_counter_mode 150 | 151 | .upgrade_DtoE 152 | TDC : STA !sram_fast_doors 153 | 154 | .upgrade_EtoF 155 | TDC : STA !sram_suppress_flashing 156 | 157 | .upgrade_Fto10 158 | TDC : STA !sram_fast_elevators 159 | 160 | .upgrade_10to11 161 | TDC : STA !sram_custom_damage 162 | STA !sram_custom_charge_damage 163 | STA !sram_custom_uncharge_damage 164 | STA !sram_water_physics 165 | STA !sram_double_jump 166 | 167 | .upgrade_11to12 168 | JSL init_menu_customization 169 | TDC : STA !sram_ctrl_auto_save_state 170 | 171 | .upgrade_12to13 172 | TDC : STA !sram_custom_header 173 | 174 | .upgrade_13to14 175 | ; "skip fanfares, but adjust timer" option has been replaced with "speedrun" timer mode 176 | LDA !sram_fanfare : BIT #$0002 : BEQ .upgrade_14to15 177 | LDA !sram_fanfare : AND #$0001 : STA !sram_fanfare 178 | LDA !sram_frame_counter_mode : BNE .upgrade_14to15 179 | LDA !FRAME_COUNTER_ADJUST_REALTIME : STA !sram_frame_counter_mode 180 | 181 | .upgrade_14to15 182 | TDC : STA !sram_bomb_torizo_door 183 | 184 | .upgrade_15to16 185 | TDC : STA !sram_door_display_mode 186 | STA !sram_cm_font : STA !sram_presetequiprando_beampref 187 | STA !sram_display_mode_reward 188 | LDA !CTRL_Y : STA !sram_cm_fast_scroll_button 189 | LDA !PRESET_EQUIP_RANDO_INIT : STA !sram_presetequiprando 190 | LDA #$000E : STA !sram_presetequiprando_max_etanks 191 | LDA #$0004 : STA !sram_presetequiprando_max_reserves 192 | LDA #$002E : STA !sram_presetequiprando_max_missiles 193 | LDA #$000A : STA !sram_presetequiprando_max_supers 194 | LDA #$000A : STA !sram_presetequiprando_max_pbs 195 | 196 | .upgrade_16to17 197 | TDC : STA !sram_spin_lock : STA !sram_ctrl_toggle_spin_lock 198 | DEC : STA !sram_map_grid_alignment 199 | 200 | .upgrade_17to18 201 | TDC : STA !sram_number_gfx_choice 202 | STA !sram_superhud_bottom 203 | STA !sram_superhud_middle 204 | STA !sram_superhud_top 205 | STA !sram_infidoppler_enabled 206 | 207 | .upgrade_18to19 208 | TDC : STA !sram_ctrl_randomize_rng 209 | STA !sram_ctrl_reveal_damage 210 | STA !sram_ctrl_force_stand 211 | STA !sram_random_bubble_sfx 212 | STA !sram_loadstate_rando_energy 213 | STA !sram_loadstate_rando_reserves 214 | STA !sram_loadstate_rando_missiles 215 | STA !sram_loadstate_rando_supers 216 | STA !sram_loadstate_rando_powerbombs 217 | LDA #$0384 : STA !sram_demo_timer 218 | LDA #$0100 : STA !sram_ceres_timer 219 | LDA #$0300 : STA !sram_zebes_timer 220 | 221 | LDA !SRAM_VERSION : STA !sram_initialized 222 | RTS 223 | } 224 | 225 | init_sram_controller_shortcuts: 226 | { 227 | ; branch called by ctrl_reset_defaults in mainmenu.asm 228 | LDA #$3000 : STA !sram_ctrl_menu ; Start + Select 229 | LDA #$6010 : STA !sram_ctrl_save_state ; Select + Y + R 230 | LDA #$6020 : STA !sram_ctrl_load_state ; Select + Y + L 231 | LDA #$5020 : STA !sram_ctrl_load_last_preset ; Start + Y + L 232 | TDC : STA !sram_ctrl_full_equipment 233 | STA !sram_ctrl_kill_enemies 234 | STA !sram_ctrl_reset_segment_timer 235 | STA !sram_ctrl_reset_segment_later 236 | STA !sram_ctrl_random_preset 237 | STA !sram_ctrl_save_custom_preset 238 | STA !sram_ctrl_load_custom_preset 239 | STA !sram_ctrl_inc_custom_preset 240 | STA !sram_ctrl_dec_custom_preset 241 | ; duplicates for reset defaults routine 242 | STA !sram_ctrl_toggle_tileviewer 243 | STA !sram_ctrl_update_timers 244 | STA !sram_ctrl_auto_save_state 245 | STA !sram_ctrl_toggle_spin_lock 246 | STA !sram_ctrl_randomize_rng 247 | STA !sram_ctrl_reveal_damage 248 | STA !sram_ctrl_force_stand 249 | RTL 250 | } 251 | 252 | init_menu_customization: 253 | { 254 | LDA #$0002 : STA !sram_custompalette_profile 255 | LDA #$7277 : STA !sram_palette_border 256 | LDA #$48F3 : STA !sram_palette_headeroutline 257 | LDA #$7FFF : STA !sram_palette_text 258 | LDA #$0000 : STA !sram_palette_background 259 | LDA #$0000 : STA !sram_palette_numoutline 260 | LDA #$7FFF : STA !sram_palette_numfill 261 | LDA #$4376 : STA !sram_palette_toggleon 262 | LDA #$761F : STA !sram_palette_seltext 263 | LDA #$0000 : STA !sram_palette_seltextbg 264 | LDA #$0000 : STA !sram_palette_numseloutline 265 | LDA #$761F : STA !sram_palette_numsel 266 | 267 | LDA #$0001 : STA !sram_menu_background 268 | LDA #$0002 : STA !sram_cm_scroll_delay 269 | 270 | ; chosen seeds will automatically change over time, and will never be zero 271 | LDA.w #init_wram_based_on_sram : STA !sram_seed_X 272 | LDA !SRAM_VERSION : STA !sram_seed_Y 273 | 274 | .soundFX 275 | ; branch called by sfx_reset in customizemenu.asm 276 | LDA #$0037 : STA !sram_customsfx_move 277 | LDA #$0038 : STA !sram_customsfx_toggle 278 | LDA #$002A : STA !sram_customsfx_number 279 | LDA #$0028 : STA !sram_customsfx_confirm 280 | LDA #$0007 : STA !sram_customsfx_goback 281 | RTL 282 | } 283 | 284 | init_post_boot: 285 | { 286 | ; Load the last selected file slot (so that the user's controller 287 | ; bindings will apply if they load a preset without loading a save file) 288 | LDA $701FEC : STA !CURRENT_SAVE_FILE 289 | CMP #$0003 : BCC .valid_index 290 | TDC 291 | .valid_index 292 | JSL $818085 ; Load save file 293 | BCC .check_quickboot 294 | 295 | ; No valid save, load a new file (for default controller bindings) 296 | JSR $B2CB 297 | 298 | .check_quickboot 299 | JSL init_suit_properties_ram 300 | ; Is quickboot enabled? 301 | LDA !sram_cutscenes : AND !CUTSCENE_QUICKBOOT : BEQ .done 302 | 303 | ; Boot to the infohud menu 304 | JML cm_boot 305 | 306 | .done 307 | JML $82893D ; hijacked code: start main game loop 308 | } 309 | 310 | %endfree(81) 311 | -------------------------------------------------------------------------------- /src/main.asm: -------------------------------------------------------------------------------- 1 | asar 1.81 2 | lorom 3 | 4 | !FEATURE_SD2SNES ?= 0 5 | !FEATURE_TINYSTATES ?= 0 6 | !FEATURE_MAPSTATES ?= 0 7 | !FEATURE_DEV ?= 0 8 | !FEATURE_PAL ?= 0 9 | !FEATURE_TALLMENU ?= 0 10 | !FEATURE_VANILLAHUD ?= 0 11 | !INFOHUD_ALWAYS_SHOW_X_Y ?= 0 12 | !ORIGINAL_MESSAGE_TEXT ?= 0 13 | !PRESERVE_WRAM_DURING_SPACETIME ?= 1 14 | !RAW_TILE_GRAPHICS ?= 1 15 | 16 | !VERSION_MAJOR = 2 17 | !VERSION_MINOR = 6 18 | !VERSION_BUILD = 7 19 | !VERSION_REV = 1 20 | 21 | table ../resources/normal.tbl 22 | print "" 23 | 24 | if !FEATURE_PAL 25 | print "PAL REGION" 26 | else 27 | print "NTSC REGION" 28 | endif 29 | 30 | if !FEATURE_TALLMENU 31 | print "TALL MENU" 32 | endif 33 | 34 | if !FEATURE_VANILLAHUD 35 | print "VANILLA HUD" 36 | if !INFOHUD_ALWAYS_SHOW_X_Y 37 | print "ALWAYS SHOW X/Y IGNORED" 38 | endif 39 | else 40 | if !INFOHUD_ALWAYS_SHOW_X_Y 41 | print "ALWAYS SHOW X/Y" 42 | endif 43 | endif 44 | 45 | if !ORIGINAL_MESSAGE_TEXT 46 | print "PRESERVE FANFARE MESSAGES" 47 | endif 48 | 49 | if !PRESERVE_WRAM_DURING_SPACETIME 50 | else 51 | print "WRAM NOT PRESERVED DURING SPACETIME" 52 | endif 53 | 54 | if !RAW_TILE_GRAPHICS 55 | else 56 | print "FAST PRESETS DISABLED" 57 | endif 58 | 59 | if !FEATURE_MAPSTATES 60 | print "MAPSTATES ENABLED" 61 | endif 62 | 63 | if !FEATURE_SD2SNES 64 | print "SD2SNES ENABLED" 65 | !FEATURE_TINYSTATES = 0 66 | incsrc macros.asm 67 | incsrc defines.asm 68 | incsrc freespace.asm 69 | incsrc save.asm 70 | else 71 | if !FEATURE_TINYSTATES 72 | print "TINYSTATES ENABLED" 73 | !FEATURE_SD2SNES = 1 ; Set this to enable savestate features 74 | incsrc macros.asm 75 | incsrc defines.asm 76 | incsrc freespace.asm 77 | incsrc tinystates.asm 78 | else 79 | print "SD2SNES AND TINYSTATES DISABLED" 80 | incsrc macros.asm 81 | incsrc defines.asm 82 | incsrc freespace.asm 83 | endif 84 | endif 85 | 86 | incsrc minimap.asm 87 | incsrc menu.asm 88 | incsrc gamemode.asm 89 | incsrc roomnames.asm 90 | incsrc clearenemies.asm 91 | incsrc demos.asm 92 | incsrc infohud.asm 93 | incsrc enemy_rng.asm 94 | incsrc custompresets.asm 95 | incsrc presets.asm 96 | incsrc damage.asm 97 | incsrc physics.asm 98 | incsrc misc.asm 99 | incsrc layout.asm 100 | incsrc cutscenes.asm 101 | incsrc init.asm 102 | incsrc fanfare.asm 103 | incsrc spriteprio.asm 104 | incsrc spritefeat.asm 105 | 106 | if !RAW_TILE_GRAPHICS 107 | incsrc tilegraphics.asm 108 | endif 109 | 110 | if !FEATURE_DEV 111 | incsrc symbols.asm 112 | endif 113 | 114 | ; Make sure the ROM expands to 4MB 115 | org $FFFFFF : db $FF 116 | 117 | %printfreespace() 118 | print "Assembly complete. Total bytes written: ", bytes 119 | 120 | -------------------------------------------------------------------------------- /src/minimap.asm: -------------------------------------------------------------------------------- 1 | ;======================================================= 2 | ;Patches to support the minimap 3 | ;======================================================= 4 | 5 | org $9AB200 ; graphics for HUD 6 | hudgfx_bin: 7 | if !FEATURE_VANILLAHUD 8 | ; Vanilla HUD build only needs this label 9 | else 10 | incbin ../resources/hudgfx.bin 11 | 12 | 13 | ; Place minimap graphics in bank DF 14 | %startfree(DF) 15 | mapgfx_bin: 16 | incbin ../resources/mapgfx.bin 17 | 18 | ; Next block needs to be all zeros to clear a tilemap 19 | fillbyte $00 20 | fill 4096 21 | %endfree(DF) 22 | 23 | 24 | ; The default HUD minimap should be cleared 25 | org $8098FF 26 | mm_default_HUD_row_1: 27 | dw #$2C0F, #$2C0F, #$2C0F, #$2C0F, #$2C0F 28 | 29 | org $80993F 30 | mm_default_HUD_row_2: 31 | dw #$2C0F, #$2C0F, #$2C0F, #$2C0F, #$2C0F 32 | 33 | org $80997F 34 | mm_default_HUD_row_3: 35 | dw #$2C0F, #$2C0F, #$2C0F, #$2C0F, #$2C0F 36 | 37 | ; The default energy 0 text should be cleared 38 | org $80994D 39 | mm_default_HUD_energy: 40 | dw #$2C0F, #$2C0F, #$2C0F, #$2C0F, #$2C0F, #$2C0F 41 | 42 | 43 | org $809AF3 44 | JSL mm_initialize_minimap 45 | 46 | org $809B51 47 | JMP $9BFB ; skip drawing auto reserve icon and normal energy numbers and tanks during HUD routine 48 | 49 | 50 | org $8282E5 ; write and clear tiles to VRAM 51 | JSL mm_write_and_clear_hud_tiles 52 | JSL overwrite_HUD_numbers 53 | BRA mm_write_next_tiles 54 | 55 | mm_refresh_reserves: 56 | LDA #$FFFF : STA !ram_reserves_last 57 | RTS 58 | warnpc $828305 59 | 60 | org $828305 61 | mm_write_next_tiles: 62 | 63 | org $828EB8 ; write and clear tiles to VRAM 64 | JSL mm_write_and_clear_hud_tiles 65 | PLP 66 | RTL 67 | 68 | 69 | org $829022 70 | JSR mm_setup_map_scrolling 71 | 72 | ; Move Ceres map data up four bytes 73 | ; (overlaps with Tourian map data but both have zeros) 74 | org $829723 75 | mm_map_data_table_ceres_entry: 76 | dw $9D23 77 | 78 | org $829D50 79 | mm_map_data_ceres_start_nonzero: 80 | db $08 81 | 82 | org $829D64 83 | mm_map_data_ceres_end_nonzero: 84 | db $0F,$00,$00,$00,$01,$F8,$00,$00,$00,$00 85 | 86 | ; Move start of setup map scrolling routine up four bytes 87 | org $829E23 88 | mm_setup_map_scrolling: 89 | ; Copy from $829E27 routine 90 | %ai16() 91 | STA $14 92 | LDA !MAP_MAX_X_SCROLL : SEC : SBC !MAP_MIN_X_SCROLL 93 | LSR : CLC : ADC !MAP_MIN_X_SCROLL 94 | SEC : SBC #$0080 95 | 96 | ; Insert new four-byte instruction 97 | ; #$FFFF maintains vanilla behavior and #$FFF8 always aligns the grid 98 | AND !sram_map_grid_alignment 99 | 100 | ; Continue with vanilla routine 101 | warnpc $829E3B 102 | 103 | 104 | org $82AEAF ; routine to remove auto reserve icon on HUD from equip screen 105 | JSR mm_refresh_reserves 106 | 107 | org $82AED9 ; routine to draw auto reserve icon on HUD from equip screen 108 | JSR mm_refresh_reserves 109 | 110 | org $82E488 ; write tiles to VRAM 111 | JMP mm_write_hud_tiles_during_door 112 | 113 | 114 | ; Placed in bank 82 so that the jumps work 115 | %startfree(82) 116 | 117 | mm_write_hud_tiles_during_door: 118 | { 119 | LDA !ram_minimap : BNE .minimap_vram 120 | 121 | ; Load in normal vram 122 | JSR $E039 123 | dl hudgfx_bin : dw $4000, $1000 124 | JSL overwrite_HUD_numbers 125 | JMP $E492 ; resume logic 126 | 127 | .minimap_vram 128 | JSR $E039 129 | dl mapgfx_bin : dw $4000, $1000 130 | JSL overwrite_HUD_numbers 131 | JMP $E492 ; resume logic 132 | } 133 | 134 | %endfree(82) 135 | 136 | 137 | org $90A7E8 ; optimize following code by exactly six bytes 138 | TDC : TAX : INC 139 | STA !DISABLE_MINIMAP 140 | ; only clear minimap if it is visible (this check requires six bytes) 141 | LDA !ram_minimap : BEQ mm_skip_clear_minimap 142 | LDA #$2C1F 143 | warnpc $90A7F7 144 | 145 | org $90A80A ; normally runs after minimap grid has been drawn 146 | mm_skip_clear_minimap: 147 | 148 | org $90A91B ; optimize for performance and to make room for extra minimap enabled check 149 | mm_update_minimap: 150 | { 151 | LDA !ram_minimap : BNE .start 152 | .skip 153 | RTL 154 | .start 155 | ; we can check for disable minimap before PHP (saves one byte) 156 | LDA !DISABLE_MINIMAP : BNE .skip 157 | PHP : %ai16() 158 | 159 | ; based on $90A925, sanity check X and Y position 160 | LDA !SAMUS_X : LSR #4 161 | CMP !ROOM_WIDTH_BLOCKS : BCC .checkY 162 | .checkFailed 163 | PLP : RTL 164 | .checkY 165 | LDA !SAMUS_Y : LSR #4 166 | CMP !ROOM_HEIGHT_BLOCKS : BCS .checkFailed 167 | 168 | ; based on $90A941, initialize local variables 169 | STZ $2E 170 | LDA !SAMUS_X+1 : AND #$00FF 171 | CLC : ADC !ROOM_MAP_X_COORDINATE 172 | PHA : AND #$0020 : STA $22 173 | PLA : AND #$001F : STA $12 174 | AND #$0007 : TAY 175 | LDA $12 : LSR #3 : STA $14 176 | LDA !SAMUS_Y+1 : AND #$00FF 177 | SEC : ADC !ROOM_MAP_Y_COORDINATE 178 | } 179 | warnpc $90A971 ; return to original code here 180 | 181 | org $90A97E 182 | JMP mm_inc_tile_count 183 | 184 | 185 | ; Placed in bank 90 so that the jumps work 186 | %startfree(90) 187 | 188 | mm_initialize_minimap: 189 | { 190 | ; If we just left Ceres, increment segment timer 191 | LDA !GAMEMODE : AND #$00FF : CMP #$0006 : BNE .init_minimap 192 | LDA #$0000 : STA $12 : STA $14 : STA !ram_room_has_set_rng 193 | STA !ram_realtime_room : STA !ram_last_realtime_room 194 | STA !ram_gametime_room : STA !ram_last_gametime_room 195 | STA !ram_last_room_lag : STA !ram_last_door_lag_frames : STA !ram_transition_counter 196 | 197 | ; adding 1:13 to seg timer to account for missed frames between Ceres and Zebes 198 | LDA !ram_seg_rt_frames : CLC : ADC #$000D : STA !ram_seg_rt_frames 199 | CMP #$003C : BMI .add_seconds 200 | SEC : SBC #$003C : STA !ram_seg_rt_frames : INC $12 201 | 202 | .add_seconds 203 | LDA !ram_seg_rt_seconds : CLC : ADC #$0001 : ADC $12 : STA !ram_seg_rt_seconds 204 | CMP #$003C : BMI .add_minutes 205 | SEC : SBC #$003C : STA !ram_seg_rt_seconds : INC $14 206 | 207 | .add_minutes 208 | LDA $14 : BEQ .init_minimap : CLC : ADC !ram_seg_rt_minutes : STA !ram_seg_rt_minutes 209 | 210 | .init_minimap 211 | LDA !ram_minimap : BEQ .skip_minimap 212 | JMP $A8EF ; resume original logic 213 | 214 | .skip_minimap 215 | RTL 216 | } 217 | 218 | mm_inc_tile_count: 219 | { 220 | ; Overwritten logic 221 | STY $20 222 | STX $1E 223 | 224 | ; Check if tile is already set 225 | LDA !MAP_TILES_EXPLORED,X : ORA $AC04,Y : CMP !MAP_TILES_EXPLORED,X : BEQ .done 226 | ; Set tile and increment counter 227 | STA !MAP_TILES_EXPLORED,X 228 | %ai16() 229 | LDA !MAP_COUNTER : INC : STA !MAP_COUNTER 230 | JMP $A98D ; resume original logic skipping past %ai16() 231 | 232 | .done 233 | JMP $A98B ; resume original logic including %ai16() 234 | } 235 | 236 | mm_write_and_clear_hud_tiles: 237 | { 238 | %i16() 239 | LDA !ram_minimap : BNE .minimap_vram 240 | 241 | ; Load in normal vram 242 | LDA #$80 : STA $2115 ; word-access, incr by 1 243 | LDX #$4000 : STX $2116 ; VRAM address (8000 in vram) 244 | LDX.w #hudgfx_bin : STX $4302 ; Source offset 245 | LDA.b #hudgfx_bin>>16 : STA $4304 ; Source bank 246 | LDX #$2000 : STX $4305 ; Size (0x10 = 1 tile) 247 | LDA #$01 : STA $4300 ; word, normal increment (DMA MODE) 248 | LDA #$18 : STA $4301 ; destination (VRAM write) 249 | LDA #$01 : STA $420B ; initiate DMA (channel 1) 250 | %i8() 251 | RTL 252 | 253 | .minimap_vram 254 | LDA #$80 : STA $2115 ; word-access, incr by 1 255 | LDX #$4000 : STX $2116 ; VRAM address (8000 in vram) 256 | LDX.w #mapgfx_bin : STX $4302 ; Source offset 257 | LDA.b #mapgfx_bin>>16 : STA $4304 ; Source bank 258 | LDX #$2000 : STX $4305 ; Size (0x10 = 1 tile) 259 | LDA #$01 : STA $4300 ; word, normal increment (DMA MODE) 260 | LDA #$18 : STA $4301 ; destination (VRAM write) 261 | LDA #$01 : STA $420B ; initiate DMA (channel 1) 262 | %i8() 263 | RTL 264 | } 265 | 266 | %endfree(90) 267 | endif ; !FEATURE_VANILLAHUD 268 | 269 | -------------------------------------------------------------------------------- /src/physics.asm: -------------------------------------------------------------------------------- 1 | 2 | ; Re-initialize water physics variables after Init Samus routine clears it 3 | ; Overwrites a JSL to an RTL in game state 6 4 | org $828067 5 | JSL init_physics_ram 6 | 7 | ; Replace gravity suit checks with the water physics check 8 | org $84B423 9 | LDA !SAMUS_WATER_PHYSICS 10 | 11 | org $84B4D1 12 | LDA !SAMUS_WATER_PHYSICS 13 | 14 | org $908096 15 | LDA !SAMUS_WATER_PHYSICS : BIT #$0020 16 | 17 | org $908198 18 | LDA !SAMUS_WATER_PHYSICS 19 | 20 | org $90841D 21 | LDA !SAMUS_WATER_PHYSICS 22 | 23 | org $909741 24 | LDA !SAMUS_WATER_PHYSICS 25 | 26 | org $9098C2 27 | LDA !SAMUS_WATER_PHYSICS 28 | 29 | org $90994F 30 | LDA !SAMUS_WATER_PHYSICS 31 | 32 | org $9099DC 33 | LDA !SAMUS_WATER_PHYSICS 34 | 35 | org $909A2F 36 | LDA !SAMUS_WATER_PHYSICS 37 | 38 | org $909BD4 39 | LDA !SAMUS_WATER_PHYSICS 40 | 41 | org $909C24 42 | LDA !SAMUS_WATER_PHYSICS 43 | 44 | org $909C5B 45 | LDA !SAMUS_WATER_PHYSICS 46 | 47 | org $90A439 48 | LDA !SAMUS_WATER_PHYSICS : BIT #$0020 49 | 50 | if !FEATURE_PAL 51 | org $91D917 52 | else 53 | org $91D9B2 54 | endif 55 | LDA !SAMUS_WATER_PHYSICS : BIT #$0020 56 | 57 | if !FEATURE_PAL 58 | org $91F5EF 59 | else 60 | org $91F68A 61 | endif 62 | LDA !SAMUS_WATER_PHYSICS 63 | 64 | if !FEATURE_PAL 65 | org $91F650 66 | else 67 | org $91F6EB 68 | endif 69 | LDA !SAMUS_WATER_PHYSICS 70 | 71 | if !FEATURE_PAL 72 | org $91FA73 73 | else 74 | org $91FB0E 75 | endif 76 | LDA !SAMUS_WATER_PHYSICS 77 | 78 | org $9BC4BE 79 | LDA !SAMUS_WATER_PHYSICS : BIT #$0020 80 | 81 | 82 | org $9092EA 83 | double_jump_check: 84 | { 85 | LDA !SAMUS_ITEMS_EQUIPPED : BIT #$0200 : BEQ .no_space_jump 86 | 87 | .jump_allowed 88 | JMP $98BC 89 | 90 | .no_space_jump 91 | ; Increment double jump counter 92 | INC !SAMUS_DOUBLE_JUMP 93 | 94 | ; This might end up being a walljump, 95 | ; in which case it shouldn't count as a double jump 96 | ; Increment a counter for that case as well 97 | INC !SAMUS_SUBTRACT_WALL_JUMP 98 | 99 | ; Allow double jump if counter is one 100 | LDA !SAMUS_DOUBLE_JUMP : CMP #$0001 : BNE .end 101 | JMP $98BC 102 | 103 | .end 104 | TDC 105 | RTL 106 | } 107 | warnpc $909348 108 | 109 | org $90A46E 110 | ; Water physics flag also used to determine if space jump or double jump equipped 111 | LDA !SAMUS_WATER_PHYSICS 112 | 113 | org $90A4C4 114 | ; Replace JSL $9098BC so we can check if double jump allowed 115 | JSL double_jump_check 116 | 117 | if !FEATURE_PAL 118 | org $91F006 119 | else 120 | org $91F0A1 121 | endif 122 | ; Replace a JSR to next routine with a STZ followed by the next routine 123 | STZ !SAMUS_DOUBLE_JUMP : NOP 124 | 125 | if !FEATURE_PAL 126 | org $91F3AB 127 | else 128 | org $91F446 129 | endif 130 | JSR double_jump_walljump_check 131 | 132 | if !FEATURE_PAL 133 | org $91FBA7 134 | else 135 | org $91FC42 136 | endif 137 | double_jump_walljump_check: 138 | { 139 | ; If transitioning to walljump 140 | AND #$FF00 : CMP #$1400 : BNE .done 141 | ; If flag to give back double jump set 142 | LDA !SAMUS_SUBTRACT_WALL_JUMP : BEQ .done 143 | DEC !SAMUS_DOUBLE_JUMP 144 | 145 | .done 146 | STZ !SAMUS_SUBTRACT_WALL_JUMP 147 | 148 | if !FEATURE_PAL 149 | JMP $F3CD 150 | else 151 | JMP $F468 152 | endif 153 | } 154 | %warnpc($91FC66, $91FBCB) 155 | 156 | ; The following three fix double jump when landing on spikes 157 | org $948EA9 158 | STA $0A4E : STZ !SAMUS_DOUBLE_JUMP 159 | 160 | org $948EE4 161 | STA $0A4E : STZ !SAMUS_DOUBLE_JUMP 162 | 163 | org $948F1F 164 | STA $0A4E : STZ !SAMUS_DOUBLE_JUMP 165 | 166 | 167 | ; Relocate $9182D9 routine 168 | org $9BC868 169 | JSL handle_transition_table_lookup_failure 170 | 171 | if !FEATURE_PAL 172 | org $91957A 173 | else 174 | org $9195BC 175 | endif 176 | handle_transition_table_lookup_failure: 177 | { 178 | PHP : PHB : PHK : PLB 179 | %ai16() 180 | JSR set_prospective_pose_change_command 181 | BCC $08 182 | LDA !SAMUS_POSE : STA !SAMUS_POTENTIAL_POSE_VALUES 183 | BRA $15 184 | LDA !SAMUS_POSE : ASL : ASL : ASL : TAX 185 | if !FEATURE_PAL 186 | LDA.w $91B583,X 187 | else 188 | LDA.w $91B62B,X 189 | endif 190 | AND #$00FF 191 | CMP #$00FF : BEQ $E6 192 | STA !SAMUS_POTENTIAL_POSE_VALUES 193 | PLB : PLP : RTL 194 | } 195 | 196 | ; Relocate $918332 table 197 | prospective_pose_change_table: 198 | { 199 | db $02 ; 0: Standing 200 | db $01 ; 1: Running 201 | db $01 ; 2: Normal jumping 202 | db $00 ; 3: Spin jumping 203 | db $06 ; 4: Morph ball - on ground 204 | db $02 ; 5: Crouching 205 | db $08 ; 6: Falling 206 | db $02 ; 7: Unused 207 | db $01 ; 8: Morph ball - falling 208 | db $06 ; 9: Unused 209 | db $02 ; Ah: Knockback / crystal flash ending 210 | db $02 ; Bh: Unused 211 | db $02 ; Ch: Unused 212 | db $06 ; Dh: Unused 213 | db $02 ; Eh: Turning around - on ground 214 | db $02 ; Fh: Crouching/standing/morphing/unmorphing transition 215 | db $02 ; 10h: Moonwalking 216 | db $06 ; 11h: Spring ball - on ground 217 | db $06 ; 12h: Spring ball - in air 218 | db $06 ; 13h: Spring ball - falling 219 | db $06 ; 14h: Wall jumping 220 | db $02 ; 15h: Ran into a wall 221 | db $06 ; 16h: Grappling 222 | db $02 ; 17h: Turning around - jumping 223 | db $02 ; 18h: Turning around - falling 224 | db $02 ; 19h: Damage boost 225 | db $02 ; 1Ah: Grabbed by Draygon 226 | db $02 ; 1Bh: Shinespark / crystal flash / drained by metroid / damaged by MB's attacks 227 | db $08 ; should not be used but keeping same as vanilla just in case 228 | } 229 | %warnpc($919658, $919616) 230 | 231 | ; Relocate most of $9181A9 routine to the end of $9181F4 routine, 232 | ; so instead of JSR $81F4 make it JMP $81F4 233 | org $9181A9 234 | determine_perspective_pose: 235 | { 236 | LDA !IH_CONTROLLER_PRI : BEQ .no_input 237 | JMP $81F4 238 | .no_input 239 | STZ $0A18 240 | JSL handle_transition_table_lookup_failure 241 | CLC : RTS 242 | } 243 | 244 | ; Relocate $918304 routine 245 | set_prospective_pose_change_command: 246 | { 247 | LDA !SAMUS_MOVEMENT_TYPE : AND #$00FF : TAX 248 | LDA.w prospective_pose_change_table,X 249 | AND #$00FF : CMP #$0001 : BEQ $05 250 | STA !SAMUS_POTENTIAL_POSE_FLAGS 251 | CLC : RTS 252 | LDA !SAMUS_X_MOMENTUM : BNE $0A 253 | LDA !SAMUS_X_SUBMOMENTUM : BNE $05 254 | LDA #$0002 : BRA $EC 255 | LDA #$0001 : STA !SAMUS_POTENTIAL_POSE_FLAGS 256 | SEC : RTS 257 | } 258 | warnpc $9181F4 259 | 260 | ; Overwrite $9182D9 routine and RTS at end of $9181F4 routine 261 | org $9182D8 262 | hook_spin_lock: 263 | { 264 | LDA !sram_spin_lock : BEQ .resume_vanilla 265 | LDA !SAMUS_MOVEMENT_TYPE : AND #$00FF 266 | CMP #$0003 ; spin-jumping movement type 267 | BEQ .disable_up_down 268 | CMP #$0014 ; wall-jumping movement type 269 | BNE .resume_vanilla 270 | 271 | .disable_up_down: 272 | ; Override up/down inputs to be treated as not held or newly pressed 273 | LDA $14 : ORA #$0C00 : STA $14 274 | LDA $12 : ORA #$0C00 : LDA $12 275 | 276 | .resume_vanilla: 277 | ; Finish $9181A9 routine starting from $9181B0 278 | LDA !SAMUS_POSE : ASL : TAX 279 | if !FEATURE_PAL 280 | LDA.w $919E3A,X 281 | else 282 | LDA.w $919EE2,X 283 | endif 284 | TAY : LDA $0000,Y : INC : BEQ $23 285 | DEC : BEQ $04 286 | AND $12 : BNE $09 287 | LDA $0002,Y : BEQ $19 288 | AND $14 : BEQ $15 289 | TYA : CLC : ADC #$0006 : TAY 290 | LDA $0000,Y : INC : BNE $E4 291 | STZ $0A18 292 | JSL handle_transition_table_lookup_failure 293 | CLC : RTS 294 | LDA $0004,Y : CMP !SAMUS_POSE : BEQ $F6 295 | STA !SAMUS_POTENTIAL_POSE_VALUES 296 | STZ !SAMUS_BOMB_JUMP_DIRECTION 297 | SEC : RTS 298 | } 299 | warnpc $91834E 300 | 301 | -------------------------------------------------------------------------------- /src/save.asm: -------------------------------------------------------------------------------- 1 | ; SD2SNES Savestate code 2 | ; by acmlm, total, Myria 3 | ; 4 | 5 | macro wram_to_sram(wram_addr, size, sram_addr) 6 | dw $0000|$4312, &$FFFF ; VRAM address >> 1. 7 | dw $0000|$4314, ((>>16)&$FF)|((&$FF)<<8) ; A addr = $70xxxx, size = $xx00 8 | dw $0000|$4316, (>>8)&$FF ; size = $80xx ($8000), unused bank reg = $00. 9 | dw $0000|$2181, &$FFFF ; WRAM addr = $xx0000 10 | dw $1000|$2183, ((>>16)&$FF)-$7E ; WRAM addr = $7Exxxx (bank is relative to $7E) 11 | dw $1000|$420B, $02 ; Trigger DMA on channel 1 12 | endmacro 13 | 14 | macro vram_to_sram(vram_addr, size, sram_addr) 15 | dw $0000|$2116, >>1 ; VRAM address >> 1. 16 | dw $9000|$213A, $0000 ; VRAM dummy read. 17 | dw $0000|$4312, &$FFFF ; A addr = $xx0000 18 | dw $0000|$4314, ((>>16)&$FF)|((&$FF)<<8) ; A addr = $70xxxx, size = $xx00 19 | dw $0000|$4316, (>>8)&$FF ; size = $80xx ($0000), unused bank reg = $00. 20 | dw $1000|$420B, $02 ; Trigger DMA on channel 1 21 | endmacro 22 | 23 | macro sram_to_wram(wram_addr, size, sram_addr) 24 | dw $0000|$4312, &$FFFF ; A addr = $xx0000 25 | dw $0000|$4314, ((>>16)&$FF)|((&$FF)<<8) ; A addr = $70xxxx, size = $xx00 26 | dw $0000|$4316, (>>8)&$FF ; size = $80xx ($8000), unused bank reg = $00. 27 | dw $0000|$2181, &$FFFF ; WRAM addr = $xx0000 28 | dw $1000|$2183, ((>>16)&$FF)-$7E ; WRAM addr = $7Exxxx (bank is relative to $7E) 29 | dw $1000|$420B, $02 ; Trigger DMA on channel 1 30 | endmacro 31 | 32 | macro sram_to_vram(vram_addr, size, sram_addr) 33 | dw $0000|$2116, >>1 ; VRAM address >> 1. 34 | dw $0000|$4312, &$FFFF ; A addr = $xx0000 35 | dw $0000|$4314, ((>>16)&$FF)|((&$FF)<<8) ; A addr = $70xxxx, size = $xx00 36 | dw $0000|$4316, (>>8)&$FF ; size = $80xx ($0000), unused bank reg = $00. 37 | dw $1000|$420B, $02 ; Trigger DMA on channel 1 38 | endmacro 39 | 40 | 41 | %startfree(80) 42 | 43 | ; These can be modified to do game-specific things before and after saving and loading 44 | ; Both A and X/Y are 16-bit here 45 | pre_load_state: 46 | { 47 | LDA !MUSIC_DATA : STA !SRAM_MUSIC_DATA 48 | LDA !MUSIC_TRACK : STA !SRAM_MUSIC_TRACK 49 | LDA !SOUND_TIMER : STA !SRAM_SOUND_TIMER 50 | 51 | LDA !ram_slowdown_mode : STA !SRAM_SLOWDOWN_MODE 52 | 53 | ; Rerandomize 54 | LDA !sram_save_has_set_rng : BNE .done 55 | LDA !sram_rerandomize : BEQ .done 56 | LDA !CACHED_RANDOM_NUMBER : STA !SRAM_SAVED_RNG 57 | LDA !FRAME_COUNTER : STA !SRAM_SAVED_FRAME_COUNTER 58 | LDA !ram_seed_X : STA !sram_seed_X 59 | LDA !ram_seed_Y : STA !sram_seed_Y 60 | 61 | .done 62 | RTS 63 | } 64 | 65 | post_load_state: 66 | { 67 | JSR post_load_music 68 | 69 | ; Reload OOB tile viewer if enabled 70 | LDA !ram_sprite_feature_flags : BIT !SPRITE_OOB_WATCH : BEQ .tileviewerDone 71 | JSL upload_sprite_oob_tiles 72 | .tileviewerDone 73 | 74 | ; Reload BG3 GFX if minimap setting changed 75 | LDA !ram_minimap : CMP !SRAM_SAVED_MINIMAP : BEQ .minimapDone 76 | JSL cm_transfer_original_tileset 77 | LDA !ram_minimap : BEQ .disableMinimap 78 | ; Enabled minimap, clear stale tiles 79 | LDA !IH_BLANK 80 | STA !HUD_TILEMAP+$3A : STA !HUD_TILEMAP+$7A : STA !HUD_TILEMAP+$BA 81 | LDA #$2C1E ; minimap border 82 | STA !HUD_TILEMAP+$46 : STA !HUD_TILEMAP+$86 : STA !HUD_TILEMAP+$C6 83 | BRA .minimapDone 84 | .disableMinimap 85 | LDA !IH_BLANK : STA !HUD_TILEMAP+$7C : STA !HUD_TILEMAP+$7E 86 | .minimapDone 87 | 88 | ; Reload custom HUD number GFX 89 | JSL overwrite_HUD_numbers 90 | 91 | LDA !SRAM_SLOWDOWN_MODE : CMP #$FFFF : BEQ .rng 92 | AND #$00FF : STA !ram_slowdown_mode 93 | 94 | .rng 95 | ; Rerandomize 96 | LDA !sram_save_has_set_rng : BNE .randomizeOnLoad 97 | LDA !sram_rerandomize : BEQ .randomizeOnLoad 98 | LDA !SRAM_SAVED_RNG : STA !CACHED_RANDOM_NUMBER 99 | LDA !SRAM_SAVED_FRAME_COUNTER : STA !FRAME_COUNTER 100 | LDA !sram_seed_X : STA !ram_seed_X 101 | LDA !sram_seed_Y : STA !ram_seed_Y 102 | JSL MenuRNG ; rerandomize hack RNG 103 | 104 | .randomizeOnLoad 105 | ; Randomize energy/ammo? 106 | LDA !ram_loadstate_rando_enable : BEQ .done 107 | JSL RandomizeOnLoad 108 | 109 | .done 110 | JSL init_wram_based_on_sram 111 | 112 | ; Freeze inputs if necessary 113 | LDA !ram_freeze_on_load : BEQ .return 114 | LDA !ram_slowdown_mode : BNE .return 115 | LDA #$FFFF : STA !ram_slowdown_mode 116 | INC : STA !ram_slowdown_controller_1 : STA !ram_slowdown_controller_2 117 | INC : STA !ram_slowdown_frames 118 | ; Preserve segment timer during freeze 119 | LDA !ram_seg_rt_frames : STA !SRAM_SEG_TIMER_F 120 | LDA !ram_seg_rt_seconds : STA !SRAM_SEG_TIMER_S 121 | LDA !ram_seg_rt_minutes : STA !SRAM_SEG_TIMER_M 122 | 123 | .return 124 | RTS 125 | } 126 | 127 | post_load_music: 128 | { 129 | JSL stop_all_sounds 130 | 131 | LDY !MUSIC_TRACK 132 | LDA !MUSIC_QUEUE_NEXT : CMP !MUSIC_QUEUE_START : BEQ .music_queue_empty 133 | 134 | DEC #2 : AND #$000E : TAX 135 | LDA !MUSIC_QUEUE_ENTRIES,X : BMI .queued_music_data 136 | TXA : TAY : CMP !MUSIC_QUEUE_START : BEQ .no_music_data 137 | 138 | .music_queue_data_search 139 | DEC #2 : AND #$000E : TAX 140 | LDA !MUSIC_QUEUE_ENTRIES,X : BMI .queued_music_data 141 | TXA : CMP !MUSIC_QUEUE_START : BNE .music_queue_data_search 142 | 143 | .no_music_data 144 | LDA !sram_music_toggle : CMP #$0002 : BPL .fast_off_preset_off 145 | 146 | ; No data found in queue, check if we need to insert it 147 | LDA !SRAM_MUSIC_DATA : CMP !MUSIC_DATA : BEQ .music_queue_increase_timer 148 | 149 | ; Insert queued music data 150 | DEX #2 : TXA : AND #$000E : TAX 151 | LDA #$FF00 : CLC : ADC !MUSIC_DATA : STA !MUSIC_QUEUE_ENTRIES,X 152 | LDA #$0008 : STA !MUSIC_QUEUE_TIMERS,X 153 | 154 | .queued_music_data 155 | LDA !sram_music_toggle : CMP #$0002 : BMI .queued_music_data_clear_track 156 | 157 | ; There is music data in the queue, assume it was loaded 158 | LDA !MUSIC_QUEUE_ENTRIES,X : STA !MUSIC_DATA 159 | BRA .fast_off_preset_off 160 | 161 | .music_queue_empty 162 | LDA !sram_music_toggle : CMP #$0002 : BPL .fast_off_preset_off 163 | LDA !SRAM_MUSIC_DATA : CMP !MUSIC_DATA : BNE .clear_track_load_data 164 | JMP .check_track 165 | 166 | .clear_track_load_data 167 | TDC : JSL !MUSIC_ROUTINE 168 | LDA #$FF00 : CLC : ADC !MUSIC_DATA : JSL !MUSIC_ROUTINE 169 | BRA .load_track 170 | 171 | .fast_off_preset_off 172 | ; Treat music as already loaded 173 | STZ !MUSIC_QUEUE_TIMERS : STZ !MUSIC_QUEUE_TIMERS+$2 174 | STZ !MUSIC_QUEUE_TIMERS+$4 : STZ !MUSIC_QUEUE_TIMERS+$6 175 | STZ !MUSIC_QUEUE_TIMERS+$8 : STZ !MUSIC_QUEUE_TIMERS+$A 176 | STZ !MUSIC_QUEUE_TIMERS+$C : STZ !MUSIC_QUEUE_TIMERS+$E 177 | STZ !MUSIC_QUEUE_NEXT : STZ !MUSIC_QUEUE_START 178 | STZ !MUSIC_ENTRY : STZ !MUSIC_TIMER 179 | STZ !SOUND_TIMER : STY !MUSIC_TRACK 180 | BRA .done 181 | 182 | .music_queue_increase_timer 183 | ; Data is correct, but we may need to increase our sound timer 184 | LDA !SRAM_SOUND_TIMER : CMP !MUSIC_TIMER : BMI .done 185 | STA !MUSIC_TIMER : STA !SOUND_TIMER 186 | BRA .done 187 | 188 | .queued_music_data_clear_track 189 | ; Insert clear track before queued music data and start queue there 190 | DEX #2 : TXA : AND #$000E : STA !MUSIC_QUEUE_START : TAX 191 | STZ !MUSIC_QUEUE_ENTRIES,X : STZ !MUSIC_ENTRY 192 | 193 | ; Clear all timers before this point 194 | .music_clear_timer_loop 195 | TXA : DEC #2 : AND #$000E : TAX 196 | STZ !MUSIC_QUEUE_TIMERS,X : CPX !MUSIC_QUEUE_NEXT : BNE .music_clear_timer_loop 197 | 198 | ; Set timer on the clear track command 199 | LDX !MUSIC_QUEUE_START 200 | 201 | .queued_music_prepare_set_timer 202 | LDA !SRAM_SOUND_TIMER : BNE .queued_music_set_timer 203 | INC 204 | 205 | .queued_music_set_timer 206 | STA !MUSIC_QUEUE_TIMERS,X : STA !SOUND_TIMER : STA !MUSIC_TIMER 207 | BRA .done 208 | 209 | .check_track 210 | LDA !SRAM_MUSIC_TRACK : CMP !MUSIC_TRACK : BEQ .done 211 | 212 | .load_track 213 | LDA !MUSIC_TRACK : JSL !MUSIC_ROUTINE 214 | 215 | .done 216 | RTS 217 | } 218 | 219 | ; These restored registers are game-specific and needs to be updated for different games 220 | register_restore_return: 221 | { 222 | %a8() 223 | LDA !REG_4200_NMI : STA $4200 224 | LDA #$0F : STA $13 : STA $2100 225 | RTL 226 | } 227 | 228 | save_state: 229 | { 230 | %ai8() 231 | TDC : PHA : PLB 232 | 233 | TAX : TXY 234 | .save_dma_regs 235 | ; Store DMA registers to SRAM 236 | LDA $4300,X : STA !SRAM_DMA_BANK,X 237 | INX 238 | INY : CPY #$0B : BNE .save_dma_regs 239 | CPX #$7B : BEQ .done 240 | TXA : CLC : ADC #$05 : TAX 241 | LDY #$00 242 | BRA .save_dma_regs 243 | 244 | .done 245 | %ai16() 246 | LDX #save_write_table 247 | ; fallthrough to run_vm 248 | } 249 | 250 | run_vm: 251 | { 252 | PHK : PLB 253 | JMP vm 254 | } 255 | 256 | save_write_table: 257 | ; Turn PPU off 258 | dw $1000|$2100, $80 259 | dw $1000|$4200, $00 260 | ; Single address, B bus -> A bus. B address = reflector to WRAM ($2180). 261 | dw $0000|$4310, $8080 ; direction = B->A, byte reg, B addr = $2180 262 | 263 | ; Copy WRAM segments, uses $710000-$747FFF 264 | %wram_to_sram($7E0000, $8000, $710000) 265 | %wram_to_sram($7E8000, $8000, $720000) 266 | %wram_to_sram($7F0000, $8000, $730000) 267 | %wram_to_sram($7F8000, $8000, $740000) 268 | 269 | ; Address pair, B bus -> A bus. B address = VRAM read ($2139). 270 | dw $0000|$4310, $3981 ; direction = B->A, word reg, B addr = $2139 271 | dw $1000|$2115, $0080 ; VRAM address increment mode. 272 | 273 | ; Copy VRAM segments, uses $750000-$767FFF 274 | %vram_to_sram($0000, $8000, $750000) 275 | %vram_to_sram($8000, $8000, $760000) 276 | 277 | ; Copy CGRAM, uses SRAM $772000-$7721FF 278 | dw $1000|$2121, $00 ; CGRAM address 279 | dw $0000|$4310, $3B80 ; direction = B->A, byte reg, B addr = $213B 280 | dw $0000|$4312, $2000 ; A addr = $xx2000 281 | dw $0000|$4314, $0077 ; A addr = $77xxxx, size = $xx00 282 | dw $0000|$4316, $0002 ; size = $02xx ($0200), unused bank reg = $00. 283 | dw $1000|$420B, $02 ; Trigger DMA on channel 1 284 | 285 | ; Done 286 | dw $0000, save_return 287 | 288 | save_return: 289 | { 290 | PEA $0000 : PLB : PLB 291 | 292 | %ai16() 293 | LDA !ram_room_has_set_rng : STA !sram_save_has_set_rng 294 | LDA !ram_minimap : STA !SRAM_SAVED_MINIMAP 295 | LDA !SAFEWORD : STA !SRAM_SAVED_STATE 296 | 297 | TSC : STA !SRAM_SAVED_SP 298 | JMP register_restore_return 299 | } 300 | 301 | load_state: 302 | { 303 | JSR pre_load_state 304 | 305 | %a8() 306 | TDC : PHA : PLB 307 | LDX #load_write_table 308 | JMP run_vm 309 | } 310 | 311 | load_write_table: 312 | ; Disable HDMA 313 | dw $1000|$420C, $00 314 | ; Turn PPU off 315 | dw $1000|$2100, $80 316 | dw $1000|$4200, $00 317 | ; Single address, A bus -> B bus. B address = reflector to WRAM ($2180). 318 | dw $0000|$4310, $8000 ; direction = A->B, B addr = $2180 319 | 320 | ; Copy WRAM segments, uses $710000-$747FFF 321 | %sram_to_wram($7E0000, $8000, $710000) 322 | %sram_to_wram($7E8000, $8000, $720000) 323 | %sram_to_wram($7F0000, $8000, $730000) 324 | %sram_to_wram($7F8000, $8000, $740000) 325 | 326 | ; Address pair, A bus -> B bus. B address = VRAM write ($2118). 327 | dw $0000|$4310, $1801 ; direction = A->B, B addr = $2118 328 | dw $1000|$2115, $0080 ; VRAM address increment mode. 329 | 330 | ; Copy VRAM segments, uses $750000-$767FFF 331 | %sram_to_vram($0000, $8000, $750000) 332 | %sram_to_vram($8000, $8000, $760000) 333 | 334 | ; Copy CGRAM, uses SRAM $772000-$7721FF 335 | dw $1000|$2121, $00 ; CGRAM address 336 | dw $0000|$4310, $2200 ; direction = A->B, byte reg, B addr = $2122 337 | dw $0000|$4312, $2000 ; A addr = $xx2000 338 | dw $0000|$4314, $0077 ; A addr = $77xxxx, size = $xx00 339 | dw $0000|$4316, $0002 ; size = $02xx ($0200), unused bank reg = $00. 340 | dw $1000|$420B, $02 ; Trigger DMA on channel 1 341 | 342 | ; Done 343 | dw $0000, load_return 344 | 345 | load_return: 346 | { 347 | %ai16() 348 | LDA !SRAM_SAVED_SP : TCS 349 | 350 | ; rewrite inputs so that holding load won't keep loading, as well as rewriting saving input to loading input 351 | LDA !IH_CONTROLLER_PRI : EOR !sram_ctrl_save_state : ORA !sram_ctrl_load_state 352 | STA !IH_CONTROLLER_PRI : STA !IH_CONTROLLER_PRI_NEW : STA !IH_CONTROLLER_PRI_PREV 353 | 354 | ; clear frame held counters 355 | TDC 356 | STA !WRAM_MENU_START+$B8 : STA !WRAM_MENU_START+$BA 357 | STA !WRAM_MENU_START+$BC : STA !WRAM_MENU_START+$BE 358 | STA !WRAM_MENU_START+$C0 : STA !WRAM_MENU_START+$C2 359 | STA !WRAM_MENU_START+$C4 : STA !WRAM_MENU_START+$C6 360 | STA !WRAM_MENU_START+$C8 : STA !WRAM_MENU_START+$CA 361 | STA !WRAM_MENU_START+$CC : STA !WRAM_MENU_START+$CE 362 | 363 | %ai8() 364 | PHA : PLB 365 | 366 | TAX : TXY 367 | .load_dma_regs 368 | ; Load DMA registers from SRAM 369 | LDA !SRAM_DMA_BANK,X : STA $4300,X 370 | INX 371 | INY : CPY #$0B : BNE .load_dma_regs 372 | CPX #$7B : BEQ .load_dma_regs_done 373 | TXA : CLC : ADC #$05 : TAX 374 | LDY #$00 375 | BRA .load_dma_regs 376 | 377 | .load_dma_regs_done 378 | ; Restore registers and return. 379 | %ai16() 380 | JSR post_load_state 381 | JMP register_restore_return 382 | } 383 | 384 | vm: 385 | { 386 | ; Data format: xx xx yy yy 387 | ; xxxx = little-endian address to write to .vm's bank 388 | ; yyyy = little-endian value to write 389 | ; If xxxx has high bit set, read and discard instead of write. 390 | ; If xxxx has bit 12 set ($1000), byte instead of word. 391 | ; If yyyy has $DD in the low half, it means that this operation is a byte 392 | ; write instead of a word write. If xxxx is $0000, end the VM. 393 | %ai16() 394 | ; Read address to write to 395 | LDA.w $0000,X : BEQ .vm_done 396 | TAY 397 | INX #2 398 | ; Check for byte mode 399 | BIT.w #$1000 : BEQ .vm_word_mode 400 | AND.w #$EFFF : TAY 401 | %a8() 402 | .vm_word_mode 403 | ; Read value 404 | LDA.w $0000,X 405 | INX #2 406 | .vm_write 407 | ; Check for read mode (high bit of address) 408 | CPY.w #$8000 : BCS .vm_read 409 | STA $0000,Y 410 | BRA vm 411 | .vm_read 412 | ; "Subtract" $8000 from Y by taking advantage of bank wrapping. 413 | LDA $8000,Y 414 | BRA vm 415 | .vm_done 416 | ; A, X and Y are 16-bit at exit. 417 | ; Return to caller. The word in the table after the terminator is the 418 | ; code address to return to. 419 | JMP ($0002,X) 420 | } 421 | 422 | %endfree(80) 423 | 424 | -------------------------------------------------------------------------------- /src/spriteprio.asm: -------------------------------------------------------------------------------- 1 | 2 | ; $90:861C 22 AE 89 81 JSL $8189AE[$81:89AE] ; Add Samus spritemap to OAM 3 | org $90861C 4 | JSL oam_add_samus_sprite_with_prio 5 | 6 | ; $90:8643 22 AE 89 81 JSL $8189AE[$81:89AE] ; Add Samus spritemap to OAM 7 | org $908643 8 | JSL oam_add_samus_sprite_with_prio 9 | 10 | 11 | %startfree(81) 12 | 13 | ; This is a copy of the code that adds samus spritemap to the OAM buffer 14 | ; ($8189AE) with a modification that it can adjust the sprite propery flags 15 | ; It's a copy so that it only affects Samus, and only adds a single instruction, 16 | ; although that instruction is in a loop so we still want to avoid it by default. 17 | ; If no sprite modification is needed then we'll jump back to the vanilla routine. 18 | ; It also turns out setting the bank is unnecessary so we can skip those instructions. 19 | oam_add_samus_sprite_with_prio: 20 | { 21 | STY $12 ; $12 = [Y] (Y position of spritemap center) 22 | STX $14 ; $14 = [X] (X position of spritemap center) 23 | ASL : TAX 24 | LDY $808D,X ; Y = [$92:808D + [A] * 2] (address of spritemap) 25 | 26 | LDA !sram_sprite_prio_flag : BNE .modify 27 | PHB : JMP $89BD ; Jump back to vanilla routine, which does a PLB at the end 28 | 29 | .modify 30 | LDA $0000,Y : BEQ .end ; terminator 31 | STA $18 ; size 32 | INY #2 33 | LDX !OAM_STACK_POINTER ; OAM data index 34 | CLC 35 | 36 | .loop 37 | ; OAM data entry X position = spritemap pointer + X position 38 | LDA $0000,Y : ADC $14 : STA $0370,X 39 | ; If [OAM data entry X position] & 100h: 40 | AND #$0100 : BEQ .l3 41 | ; If [Y] & 8000h (size bit) != 0: 42 | LDA $0000,Y : BPL .l2 43 | 44 | LDA $81859F,X : STA $16 45 | ; Set OAM entry high X position bit and size bit 46 | LDA ($16) : ORA $8185A1,X : STA ($16) 47 | JMP .l4 48 | 49 | .l2 50 | ; size bit = 0 51 | LDA $81859F,X : STA $16 52 | ; set OAM entry high X position bit 53 | LDA ($16) : ORA $81839F,X : STA ($16) 54 | JMP .l4 55 | 56 | .l3 57 | ; X position & 100h == 0 58 | ; If [Y] & 8000h (size bit) != 0: 59 | LDA $0000,Y : BPL .l4 60 | LDA $81859F,X : STA $16 61 | ; Set OAM entry size bit 62 | LDA ($16) : ORA $8183A1,X : STA ($16) 63 | 64 | .l4 65 | ; OAM entry Y position 66 | LDA $0002,Y : CLC : ADC $12 : STA $0371,X 67 | ; OAM entry tile number and attributes 68 | LDA $0003,Y : ORA !sram_sprite_prio_flag : STA $0372,X 69 | ; Next sprite map entry 70 | TYA : CLC : ADC #$0005 71 | ; Next OAM index 72 | TAY : TXA : ADC #$0004 : AND #$01FF : TAX 73 | 74 | ; If not processed all sprite map entries: next! 75 | DEC $18 : BNE .loop 76 | STX !OAM_STACK_POINTER ; OAM index 77 | 78 | .end 79 | RTL 80 | } 81 | 82 | %endfree(81) 83 | 84 | -------------------------------------------------------------------------------- /tools/pauselag.lua: -------------------------------------------------------------------------------- 1 | local F = 0 2 | 3 | 4 | memory.registerexecute(0x82E78C, function () 5 | F = emu.framecount() 6 | end) 7 | memory.registerexecute(0x82E7D2, function () 8 | print(emu.framecount() - F) 9 | end) 10 | 11 | while true do 12 | snes9x.frameadvance() 13 | end 14 | -------------------------------------------------------------------------------- /web/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next", "next/core-web-vitals"] 3 | } 4 | -------------------------------------------------------------------------------- /web/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # patches 37 | /public/patches/ 38 | -------------------------------------------------------------------------------- /web/components/layout.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head' 2 | import Link from 'next/link' 3 | import { useRouter } from 'next/router' 4 | 5 | const MenuLinks = [ 6 | { 7 | name: 'Patch', 8 | href: '/', 9 | }, 10 | { 11 | name: 'Help', 12 | href: '/help', 13 | }, 14 | { 15 | name: 'Infohud Modes', 16 | href: '/infohudmode', 17 | }, 18 | { 19 | name: 'Changelog', 20 | href: '/changelog', 21 | }, 22 | ] 23 | 24 | const MenuLink = ({ href, name }) => { 25 | let styles = 'hover:text-indigo-600 text-gray-700' 26 | const router = useRouter() 27 | const { asPath } = router 28 | const isActive = asPath === href 29 | if (isActive) { 30 | styles = `${styles} underline underline-offset-4` 31 | } 32 | return ( 33 | 34 | {name} 35 | 36 | ) 37 | } 38 | 39 | export default function Layout({children}) { 40 | return (<> 41 | 42 | 43 | Super Metroid Practice Hack 44 | 45 |
46 | 58 |
59 |
60 | {children} 61 |
62 |
63 |
64 | ) 65 | }; -------------------------------------------------------------------------------- /web/components/mdx.js: -------------------------------------------------------------------------------- 1 | import { MDXProvider } from '@mdx-js/react' 2 | import React from 'react' 3 | 4 | /* eslint-disable react/display-name, @next/next/no-img-element, jsx-a11y/alt-text */ 5 | const components = { 6 | h1: props =>
{props.children}
, 7 | h2: props =>

, 8 | p: props =>

, 9 | ol: props =>

    , 10 | ul: props =>
      , 11 | table: props => , 12 | th: props =>
      , 13 | td: props => , 14 | img: props => 15 | } 16 | /* eslint-enable */ 17 | 18 | export default function MDX({MarkdownComponent}) { 19 | return ( 20 |
      21 | 22 | 23 | 24 |
      25 | ) 26 | } -------------------------------------------------------------------------------- /web/components/patch.js: -------------------------------------------------------------------------------- 1 | import config from '../data/config.json' 2 | import { useState } from 'react' 3 | import { saveAs } from 'file-saver' 4 | 5 | export default function Patch({variant, romData}) { 6 | const [formData, setFormData] = useState( 7 | Object.fromEntries(config.configurations.map(c => ([c.id, c.options[0].id]))) 8 | ); 9 | 10 | const applyIps = (rom, patch) => { 11 | const big = false; 12 | let offset = 5; 13 | const footer = 3; 14 | const view = new DataView(patch.buffer); 15 | while (offset + footer < patch.length) { 16 | const dest = (patch[offset] << 16) + view.getUint16(offset + 1, big); 17 | const length = view.getUint16(offset + 3, big); 18 | offset += 5; 19 | if (length > 0) { 20 | rom.set(patch.slice(offset, offset + length), dest); 21 | offset += length; 22 | } else { 23 | const rle_length = view.getUint16(offset, big); 24 | const rle_byte = patch[offset + 2]; 25 | rom.set(Uint8Array.from(new Array(rle_length), () => rle_byte), dest); 26 | offset += 3; 27 | } 28 | } 29 | } 30 | 31 | const onDownload = async () => { 32 | try { 33 | const options = {...formData, "variant": variant}; 34 | const patch = config.patches.find((item) => { return Object.keys(options).every(field => item[field] == options[field]) }); 35 | const patchData = new Uint8Array(await (await fetch(patch.file, { cache: 'no-store' })).arrayBuffer()); 36 | const fileName = config.name + "-v" + config.version + Object.entries(patch).filter(([k,v]) => k !== 'file' && k !== 'size').reduce((o, [k, v]) => o + "-" + v.toString().toLowerCase(), "") + ".sfc"; 37 | let expandedRom = new Uint8Array(patch.size); 38 | expandedRom.set(romData); 39 | applyIps(expandedRom, patchData); 40 | 41 | if(expandedRom !== null) { 42 | saveAs(new Blob([expandedRom]), fileName); 43 | } else { 44 | alert("Could not create patched file."); 45 | } 46 | } catch (err) { 47 | console.log("Error patching rom", err); 48 | } 49 | } 50 | 51 | return ( 52 |
      53 |
      54 | {config.name} - Version {config.version} - {variant} 55 |
      56 |

      57 | {config.configurations.map(c => ( 58 |
      59 | 60 | 65 |
      66 | ))} 67 |
      68 | 69 |
      70 |
      71 | ) 72 | } -------------------------------------------------------------------------------- /web/components/upload.js: -------------------------------------------------------------------------------- 1 | import { useRef } from "react" 2 | import { crc32 } from '../lib/crc32' 3 | import config from '../data/config.json' 4 | 5 | export default function Upload({variant, onUploaded}) { 6 | const fileInput = useRef(null); 7 | const baseData = config.base[variant]; 8 | 9 | const readAsArrayBuffer = async (blob) => { 10 | const fileReader = new FileReader(); 11 | return new Promise((resolve, reject) => { 12 | fileReader.onerror = () => { 13 | fileReader.abort(); 14 | reject(new DOMException("Error parsing data")); 15 | }; 16 | 17 | fileReader.onload = (e) => { 18 | resolve(e.target.result); 19 | }; 20 | 21 | fileReader.readAsArrayBuffer(blob); 22 | }); 23 | }; 24 | 25 | const onSubmitRom = async (e) => { 26 | e.preventDefault(); 27 | 28 | const file = fileInput.current.files[0]; 29 | 30 | if (file == null) { 31 | alert("You must select a file first"); 32 | return; 33 | } 34 | 35 | let fileData = null; 36 | 37 | try { 38 | fileData = new Uint8Array(await readAsArrayBuffer(file)); 39 | } catch (err) { 40 | alert("Could not read uploaded file data", err); 41 | return; 42 | } 43 | 44 | /* Strip header if found */ 45 | if((fileData.length & 0x200) > 0) 46 | { 47 | fileData = fileData.slice(0x200, fileData.length); 48 | } 49 | 50 | const crc = crc32(fileData); 51 | if(crc.toString(16).toUpperCase() !== baseData.crc) { 52 | alert("The select file's checksum does not match the expected value, try with another file."); 53 | return; 54 | } 55 | 56 | onUploaded(fileData); 57 | }; 58 | 59 | return ( 60 |
      61 |
      62 | Select your ROM 63 |
      64 |

      To be able to create a practice hack ROM you must first select your regular Super Metroid ROM.

      65 |

      It's usually named {baseData.name} and it should have the CRC checksum: {baseData.crc}

      66 |
      67 |
      68 | 69 | 70 |
      71 |
      72 | 73 |
      74 |
      75 |
      76 | ) 77 | } -------------------------------------------------------------------------------- /web/data/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Super Metroid Practice Hack", 3 | "version": "2.6.7.1", 4 | "variants": ["NTSC", "PAL"], 5 | "base": { 6 | "NTSC": { 7 | "name": "Super Metroid (JU)", 8 | "crc": "D63ED5F8" 9 | }, 10 | "PAL": { 11 | "name": "Super Metroid (E)", 12 | "crc": "AD2CBF9C" 13 | } 14 | }, 15 | "configurations": [ 16 | { 17 | "id": "platform", 18 | "name": "Platform", 19 | "options": [ 20 | { "id": "sd2snes", "name": "SD2SNES, FXPAK, SuperNT, Snes9x 1.61+, BSNES v115"}, 21 | { "id": "tinystates", "name": "MiSTer, Everdrive, Modern Emulators" }, 22 | { "id": "emulator", "name": "SNES Classic, Virtual Console" } 23 | ] 24 | } 25 | ], 26 | "patches": [ 27 | { "platform": "emulator", "variant": "NTSC", "file": "/patches/emulator-ntsc.ips", "size": 4194304}, 28 | { "platform": "emulator", "variant": "PAL", "file": "/patches/emulator-pal.ips", "size": 4194304}, 29 | 30 | { "platform": "sd2snes", "variant": "NTSC", "file": "/patches/sd2snes-ntsc.ips", "size": 4194304}, 31 | { "platform": "sd2snes", "variant": "PAL", "file": "/patches/sd2snes-pal.ips", "size": 4194304}, 32 | 33 | { "platform": "tinystates", "variant": "NTSC", "file": "/patches/tinystates-ntsc.ips", "size": 4194304}, 34 | { "platform": "tinystates", "variant": "PAL", "file": "/patches/tinystates-pal.ips", "size": 4194304} 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /web/lib/crc32.js: -------------------------------------------------------------------------------- 1 | var makeCRCTable = function(){ 2 | var c; 3 | var crcTable = []; 4 | for(var n =0; n < 256; n++){ 5 | c = n; 6 | for(var k =0; k < 8; k++){ 7 | c = ((c&1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)); 8 | } 9 | crcTable[n] = c; 10 | } 11 | return crcTable; 12 | } 13 | 14 | export const crc32 = function(data) { 15 | var crcTable = window.crcTable || (window.crcTable = makeCRCTable()); 16 | var crc = 0 ^ (-1); 17 | 18 | for (var i = 0; i < data.length; i++ ) { 19 | crc = (crc >>> 8) ^ crcTable[(crc ^ data[i]) & 0xFF]; 20 | } 21 | 22 | return (crc ^ (-1)) >>> 0; 23 | }; 24 | 25 | -------------------------------------------------------------------------------- /web/next.config.js: -------------------------------------------------------------------------------- 1 | const withMDX = require('@next/mdx')() 2 | module.exports = withMDX({ 3 | reactStrictMode: true, 4 | }) 5 | -------------------------------------------------------------------------------- /web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web", 3 | "version": "0.1.0", 4 | "private": true, 5 | "engines": { 6 | "node": "18.x", 7 | "npm": "8.x" 8 | }, 9 | "scripts": { 10 | "dev": "next dev", 11 | "build": "next build", 12 | "start": "next start", 13 | "lint": "next lint" 14 | }, 15 | "dependencies": { 16 | "@mdx-js/loader": "^1.6.22", 17 | "@next/mdx": "^11.1.0", 18 | "autoprefixer": "^10.4.13", 19 | "file-saver": "^2.0.5", 20 | "localforage": "^1.9.0", 21 | "marked": "^4.0.10", 22 | "next": "12.1.0", 23 | "postcss": "^8.4.31", 24 | "react": "17.0.2", 25 | "react-dom": "17.0.2", 26 | "tailwindcss": "^3.2.4" 27 | }, 28 | "devDependencies": { 29 | "eslint": "7.32.0", 30 | "eslint-config-next": "11.0.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /web/pages/_app.js: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css' 2 | 3 | function MyApp({ Component, pageProps }) { 4 | return 5 | } 6 | 7 | export default MyApp 8 | -------------------------------------------------------------------------------- /web/pages/changelog.js: -------------------------------------------------------------------------------- 1 | import Layout from '../components/layout' 2 | import MDX from '../components/mdx' 3 | import ChangelogMarkdown from '../data/changelog.mdx' 4 | 5 | export default function Changelog() { 6 | return ( 7 | 8 | 9 | 10 | ) 11 | } -------------------------------------------------------------------------------- /web/pages/help.js: -------------------------------------------------------------------------------- 1 | import Layout from '../components/layout' 2 | import MDX from '../components/mdx' 3 | import HelpMarkdown from '../data/help.mdx' 4 | 5 | export default function Help() { 6 | return ( 7 | 8 | 9 | 10 | ) 11 | } -------------------------------------------------------------------------------- /web/pages/index.js: -------------------------------------------------------------------------------- 1 | import Layout from '../components/layout' 2 | import Patch from '../components/patch' 3 | import Upload from '../components/upload' 4 | import { useState, useEffect } from 'react' 5 | import localforage from 'localforage' 6 | import config from '../data/config.json' 7 | import Link from 'next/link' 8 | 9 | export default function Home() { 10 | const [romData, setRomData] = useState(); 11 | const [variant, setVariant] = useState(config.variants[0]); 12 | 13 | useEffect(() => { 14 | async function getRomData() { 15 | for(const v of config.variants) { 16 | try { 17 | let data = await localforage.getItem(`romData-${v}`); 18 | setRomData((d) => { return {...d, [v]: data}}); 19 | } catch (e) { 20 | console.log(e); 21 | } 22 | } 23 | }; 24 | 25 | getRomData(); 26 | }, []); 27 | 28 | const onRomUploaded = async (uploadedRomData) => { 29 | try { 30 | await localforage.setItem(`romData-${variant}`, uploadedRomData); 31 | setRomData((d) => { return {...d, [variant]: uploadedRomData}}); 32 | } catch (e) { 33 | console.log(e); 34 | } 35 | } 36 | 37 | return ( 38 | 39 |
      40 | {config.variants.map((v) => { 41 | const active = (v === variant); 42 | const color = active ? "red" : "gray"; 43 | return (
      setVariant(v)} className={`py-2 p-4 mt-4 mr-2 h-auto bg-${color}-100 text-${color}-500 rounded shadow-sm font-sans font-bold cursor-pointer`}>{v}
      ) 44 | })} 45 |
      46 | {romData !== undefined ? romData[variant] ? : : ""} 47 |
      48 |
      49 | Information 50 |
      51 |
      52 |

      For information on what's available in the practice hack and how to use it, you can visit the Help

      53 |

      If you're wondering what's changed since the last version, just head to the Changelog

      54 |

      Strong with SNES Assembly? Take a look at the source code at GitHub

      55 |
      56 |
      57 |
      58 | ) 59 | } 60 | -------------------------------------------------------------------------------- /web/pages/infohudmode.js: -------------------------------------------------------------------------------- 1 | import Layout from '../components/layout' 2 | import MDX from '../components/mdx' 3 | import HelpMarkdown from '../data/infohudmode.mdx' 4 | 5 | export default function InfohudMode() { 6 | return ( 7 | 8 | 9 | 10 | ) 11 | } -------------------------------------------------------------------------------- /web/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/web/public/favicon.ico -------------------------------------------------------------------------------- /web/public/smhud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewtal/sm_practice_hack/07397fec3a8dfb42f7fa35c9af37331f551ab521/web/public/smhud.png -------------------------------------------------------------------------------- /web/styles/globals.css: -------------------------------------------------------------------------------- 1 | /* ./styles/globals.css */ 2 | @tailwind base; 3 | @tailwind components; 4 | @tailwind utilities; 5 | 6 | @layer base { 7 | a { 8 | @apply text-indigo-400; 9 | } 10 | a:hover { 11 | @apply text-indigo-600; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /web/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | purge: { 3 | content: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], 4 | safelist: [ 5 | 'bg-red-100', 6 | 'bg-gray-100', 7 | 'text-red-500', 8 | 'text-gray-500', 9 | ] 10 | }, 11 | darkMode: false, // or 'media' or 'class' 12 | theme: { 13 | extend: {}, 14 | }, 15 | variants: { 16 | extend: {}, 17 | }, 18 | plugins: [], 19 | } 20 | --------------------------------------------------------------------------------