Compare commits

...

54 Commits

Author SHA1 Message Date
Alejandro Asenjo Nitti
42646e2cb1 Add discord banner embed (#668) 2025-10-04 20:39:51 -04:00
Wiseguy
54950a1040 1.2.2 (#652)
* Update RT64 for multimonitor frame pacing issue and bump version number

* Add community discord server link to readme

* Bump version number to 1.2.2
2025-08-26 20:28:41 -04:00
Wiseguy
24704d86f1 1.2.1 (#649) 2025-08-18 17:21:15 -04:00
Wiseguy
b5360b0546 1.2.1 Changes (#625)
* Update runtime for hook and callback sorting, update version number

* Actually update version number

* Automatically open mods menu when dragging a mod which also prevents issues when installing a mod from the launcher menu

* Update RT64 to add texture pack shift configuration and fix minimized memory leak

* Update runtime for return hook getter exports

* Actually update rt64

* Update RT64 to fix texture pack ordering

* Implement optional dependencies, fix memory slotmaps, bump version number to 1.2.1-dev

* Add command-line option to show console output on Windows. (#632)

* Add new raphnet adapter revision to controller DB

* Add another mayflash N64 adapter to the controller database file

* Update runtime after merge for optional dependencies

* Update runtime for optional dependency mod callback fix

* Add mayflash magic NS to controller database

* Update RT64 for extended address fix and x11 dependency removal

* Update RT64 to fix build issue caused by x11

* Update runtime to remove unnnecessary x11 includes

* Fix more x11 define compilation issues

* Fix the x86-64 CPU requirement listing in the readme (#634)

* Transform tagging for keaton grass tornado

* Interpolation for sword trails

* Switch RT64 to gEXVertex fix branch (temporary until merge)

* Add 8bitdo 64 bluetooth controller to database

* Add export to get bowstring transform ID

* Update RT64 to fix linux dev mode menu and texture streaming race condition

* Fix all the interpolation glitches in the Gibdo Mask cutscene (#641)

* Fix the actor extension API breaking when registering an extension for actor type 0 first

* Remove slotmap submodule and integrate header directly after submodule URL changed

* Transform tagging for ObjGrass

* Adding autosave events. (#611)

* Adding autosave events. Dead simple.

* Update autosaving.c to include @recomp_event comments

* Prevent autosaves during minigames and fix holding powder keg on autosave load (#640)

* Update runtime for more accurate VI and switch to improved pacing RT64 branch (#644)

* Update runtime for more accurate VI and switch to improved pacing RT64 branch

* Update N64ModernRuntime and RT64 after merge

* Update recompiler to match runtime symbol list

* Remove unused gibdo patch file

---------

Co-authored-by: Darío <dariosamo@gmail.com>
Co-authored-by: Reonu <15913880+Reonu@users.noreply.github.com>
2025-08-15 21:31:57 -04:00
DoctorDink
46d9e92dda Prevent autosaves during minigames and fix holding powder keg on autosave load (#640) 2025-08-11 21:46:38 -04:00
Wiseguy
a215103002 Remove slotmap submodule and integrate header directly after submodule URL changed (#645) 2025-08-10 18:33:09 -04:00
Manuel Alfayate Corchete
bb7030bcd1 Remove X11 usage from CMakeLists (#633) 2025-07-25 14:54:43 -04:00
LT_SCHMIDDY
0bc0fd2610 Adding autosave events. (#611)
* Adding autosave events. Dead simple.

* Update autosaving.c to include @recomp_event comments
2025-06-18 21:34:32 -04:00
Wiseguy
54997438de Update readme for randomizer link and portable mode 2025-05-04 13:25:16 -04:00
Wiseguy
01bccb9059 1.2.0 (#579) 2025-05-04 11:25:23 -04:00
Wiseguy
983d7f43f8 1.2 Release Candidate (#572)
* Remove dummy description for mod config options

* Tag release candidate version

* Apply min width to element triggering rmlui assert (#573)

* Restore 0th day (#574)

* Handle controller up events even while binding inputs to avoid spamming the bind button

* Add MouseButton UI event and use it to fix focus issue on radio, also fix sliders not moving until mouse is released

* Bump version string to 1.2.0-rc2

* mod configure menu description padding set to 16

* Added the ability for focus to set the current mod config option description (#576)

* Added the ability for focus to set the current mod config option description

* add focus to text input

* only clear description if element matches

* Fix race condition crash when setting element text, bump version to 1.2.0-rc3

* Revert "Fix race condition crash when setting element text, bump version to 1.2.0-rc3"

This reverts commit 4934a04d8a.

* Defer setting an element's text if it has children to fix race condition crash, bump version to 1.2.0-rc3

* Defer remaining set_text calls to prevent another race conditionresource

* Update runtime to fix some issues that could happen after mod conflicts
and bump version to 1.2.0-rc4

* Update runtime to fix regenerated functions using the wrong event index and bump version to 1.2.0-rc5

* Add support for suffixed .so. files. Also prevent dropping extracted dynamic libraries.

* Update RT64 commit to fix cstdint include for re-spirv.

* Bump version to rc6.

* Dummy commit to fix CI bot

* Use compile-time macro for Flatpak instead.

* Rename macro.

* Bump version to 1.2.0-rc7

* Fix define on flatpak, add cwd behavior.

* Temporarily disable current working dir code.

* Add the cmake option for flatpak.

* Bump version to 1.2.0-rc8

* Update MacPorts. (#578)

* Update MacPorts.

* Try GitHub runner.

* Deselect universal, return to blaze.

* pull universal libiconv first

* Fix controller nav issues in config menu, bump version to 1.2.0-rc9

---------

Co-authored-by: thecozies <79979276+thecozies@users.noreply.github.com>
Co-authored-by: LittleCube <littlecubehax@gmail.com>
Co-authored-by: Dario <dariosamo@gmail.com>
2025-05-04 10:33:10 -04:00
Darío
14f92c41ab Flatpak support. (#569)
* Add flatpak support.

* Add gamepad to supported controls list in flatpak metainfo

---------

Co-authored-by: Wiseguy <68165316+Mr-Wiseguy@users.noreply.github.com>
2025-04-28 23:36:25 -04:00
Wiseguy
c27300e6c8 Misc in-game HUD fixes (#571)
* Fix float value used to calculate clock angle to prevent overshooting every hour

* Change addressing mode on title card rect to prevent wrapping on the left edge when HD textures are in use

* Center the moon fall countdown timer
2025-04-28 22:44:46 -04:00
Wiseguy
d766cf328f Modding menu and UI and additional mod exports (#535)
* init config opt system w/ 3 types and description support

* Move config registry/option to librecomp + added Color conf opt type

* Updated color option type styling

* Added dropdown option type

* Added TextField option type

* Button config type + callback wip

* init mod menu + bem class + button presets

* WIP mod menu, fix some warnings

* Rewrite mod details under new UI system.

* Refactored mods menu entirely.

* Remove ModMenu.scss.

* Take ownership of created pointers on Element class.

* Add styles.

* Multi-style state and disabled state propagation.

* Switch to string views.

* Convert to spaces, hook up mod enabled to toggle.

* Mod menu progress.

* Layout for mod details panel, add gap property setters

* Update RmlUi for gap property in flexbox

* Add slot_map and begin ui context

* Implement context and resource storage slotmaps

* Config submenu.

* Refactored to account for context changes.

* Turn off tab searching when submenu is open.

* Revert accidental RmlUi downgrade

* Upgrade RmlUi to 6.0 release

* Text input.

* Radio option.

* Cleanup.

* Refactor Rml document handling to use new ContextId system (prompts currently unimplemented)

* Add support for config schema.

* Split config sub menu into separate context and fix configure button, prevent infinite loop when looking for autofocus element

* Reimplement mechanism to open the config menu to a specific tab

* Begin implementing mod UI API

* Link storage to mod menu.

* Proper enum parsing.

* Enable mod reordering.

* Draggable improvements to mod menu and runtime update.

* Adjust styling of submenu.

* Mods folder button.

* Linux build fixes.

* Hook up new manifest fields to mod UI

* Add basic thumbnail parsing functionality.

* More style changes.

* Implement update event for elements

* Use RT64's texture laoding instead.

* Restore spacer animations.

* Animation API begone.

* Auto-enabled mods.

* Update runtime submodule and N64Recomp commit in CI for mod config API, remove unnecessary extern C

* Sub menu display name, assert on text input.

* Clamp delta time to fix UI disappearing on OS with timestamps that don't always increase.

* Add a state for when no mods are installed.

* Unify API function naming scheme and export relevant API functions

* Add actor update/init events and save init event (#536)

* Expose remaining property setters to mod UI API

* Implemented mod UI callbacks

* Implement actor extension data and use it for transform tagging

* Zero the memory allocated to hold extended actor data

* Implement label and textinput in mod UI API

* Patch virtual address translation to support entire extended RAM address space (#533)

* Download full target build of llvm in CI Windows runners to fix missing MIPS support and update N64Recomp CI commit

* Enable triple buffering in RT64 (#546)

* Implement controlling input capturing for mod UI contexts

* Created mod UI API functions for setting visibility, setting text, and destroying elements

* Fix errant RML tag in mod menu and insert breaks for newlines when setting element text

* Fix compilation after rebase

* Fixes for macOS

* Set the blender description manually for the UI renderer

* Created mod UI API functions for imageview elements

* Switch to designated initializers to work around missing aggregate initialization compiler support

* Update RT64 for driver bug workarounds and misc fixes

* Update RT64 to fix native sampler issues with tile copies

* Update RT64 for depth clear optimization and more native sampler changes

* Update RT64 and allow it to choose the graphics API when set to Auto

* Update runtime to allow renderers to choose the graphics API

* Update RT64 to enable early Z test shader optimization

* Implement data structure mod APIs

* Update lunasvg to increase its minimum cmake version

* Switch to runtime concatenation of function name in data API error reporting to fix Linux compilation issue

* Add missing typename to fix compilation on some compilers

* Update RT64 to fix failed assert with MSAA off

* Reimplement prompts as a separate UI context and make it so the quit game prompt doesn't bring up the config menu

* DnD prototype.

* Fix to dynamic lib path and runtime commit.

* Finish drag and drop mod installation, disable mod refresh button and code mod toggle when game starts

* Remove std::format usage and add missing <list> includes to fix Linux/MacOS compilation

* Switch to aggregate initialization for Version to work around missing implicit constructor on some compilers

* Replace use of std::bind with lambdas

* Add mod install button, put mod description in scroll container, minor mod menu tweaks

* Update runtime to fix renderer shutdown race condition

* Implement texture pack reordering

* Add mod UI API exports for slider, password input, and label radio and expose RmlUi debugger on F8

* Update runtime for mod version export

* Update runtime for save swapping mod API

* Apply recomp.rcss to mod UI contexts (fixes scrolls)

* Updated mod list styling (#561)

* Updated mod list styling

* mod entry max height

* Update RT64 for v5 texture hash

* Update runtime for mod API to get save file path

* Add special config option id to control texture pack state for code mods

* Update runtime for mod default enabled state

* Add exports for stars' display lists (#563)

* Update runtime to fix default value of enabled_by_default

* Update runtime to allow NULL recomp_free

* Implement navigation and focus styling for new UI framework (no manual overrides yet)

* Fix the previous scissor state bleeding when drawing the RmlUi output onto the swapchain buffer

* Use a multiple file select dialog for mod install button

* Add mod export for loading UI image from bytes (png/dds)

* Manual navigation in UI framework and WIP mod menu navigation

* Repeat key events when holding down controller inputs for UI navigation

* Patch AnimationContext_SetLoadFrame to allow custom animations (#564)

* Close context when showing or hiding a context and reopen afterwards to prevent deadlocks

* Add quotes around xdg-open and open commands to support paths with spaces

* Update RT64 for high precision texture coordinates when using texture replacements

* Add support for built-in mods and convert D-Pad to a built-in mod (#567)

* Add embedded mod (using mm_recomp_draw_distance as an example).

* Update runtime after merge

* Experiment with removing the D-Pad.

* Add event needed for dpad as mod, revert remaining changes in built-in patches for dpad

* Add built-in dpad mod, add remaining event calls to input.c

* Add built-in mods readme

---------

Co-authored-by: Dario <dariosamo@gmail.com>

* Fixing navigation of mods menu.

* Focused state for mod entry.

* Prevent hover styling and focus on input elements when disabled

* Fix up/down navigation on text input elements

* Set mod tab to navigate down to first mod, fix redundant mod scanning

* Remove more redundant mod scanning and fix mods being scanned during gameplay

* Update runtime for mod folder export

* Improve radio navigation and setup mod config submenu navigation setup

* Restore fd anywhere export functionality (#570)

* fix fd

* add comment back in

* Make config tabset navigate down to first mod entry when mod menu is open, make mod configure screen focus on configure button after closing

* Add navigation exports to mod UI API

* Fix opening the config menu via keyboard/controller causing a double animation warning in RmlUi

---------

Co-authored-by: Dario <dariosamo@gmail.com>
Co-authored-by: thecozies <79979276+thecozies@users.noreply.github.com>
Co-authored-by: Garrett Cox <garrettjcox@gmail.com>
Co-authored-by: David Chavez <david@dcvz.io>
Co-authored-by: danielryb <59661841+danielryb@users.noreply.github.com>
Co-authored-by: Reonu <danileon95@gmail.com>
Co-authored-by: LittleCube <littlecubehax@gmail.com>
2025-04-28 03:01:36 -04:00
David Chavez
8ec7b282e3 Use custom controller db mappings (#425)
* Use custom controller db mappings

* Update controller database.

* Update lunasvg to increase its minimum cmake version

* Remove errant cmakelists change that was originally for a newer RmlUi version

* Fix apple bundle command referencing the old game controller db filename

* Readd cmakelists change for static lunasvg but with correct RmlUi library name

---------

Co-authored-by: Dario <dariosamo@gmail.com>
Co-authored-by: Mr-Wiseguy <mrwiseguyromhacking@gmail.com>
Co-authored-by: Wiseguy <68165316+Mr-Wiseguy@users.noreply.github.com>
2025-04-23 22:10:39 -04:00
danielryb
0e96cc3dc9 Add exports for stars' display lists (#563) 2025-04-09 16:48:34 -04:00
squidbus
fd16c379ff Use Application Support directory on macOS. (#553) 2025-03-25 17:22:48 +01:00
squidbus
3d3524ffe7 Fix portable mode on macOS (#552) 2025-03-23 20:23:44 +01:00
David Chavez
1c8668fb65 Update RT64 for MacOS Intel GPU support and region resolve fix (#550) 2025-03-15 19:25:29 +01:00
Hugo Locurcio
c6d77fe5ca Fix link to decompress_baserom.py in Building documentation (#548) 2025-03-14 18:12:09 -04:00
Wiseguy
21a6f4046f Download full target build of llvm in CI Windows runners to fix missing MIPS support and update N64Recomp CI commit (#549) 2025-03-14 18:11:29 -04:00
David Chavez
25e7b31228 Add macOS Support (#537) 2025-03-14 21:07:07 +01:00
Wiseguy
91db87632c Mod Support (#499)
Integrates the modding functionality in N64ModernRuntime and adds several exported functions for mods to use. Also adds a ROM decompressor so that the runtime has access to the uncompressed code in the ROM for hooking purposes.
2025-02-14 18:38:10 -05:00
lkoger
0d0f64e32f Fix ninja package name in Ubuntu build instructions (#482) 2024-10-06 13:07:26 -04:00
Mr-Wiseguy
d99a84f04f Fix strict mode validation failure in latest N64Recomp version and update N64Recomp commit in github workflow 2024-09-06 16:56:58 -04:00
Wiseguy
af1404b83d Fix commit of N64Recomp used by workflow (#468) 2024-08-16 10:15:20 -04:00
Wiseguy
83ecc68d18 Improve build times by using the recompiler to output multiple functions per file (#465)
* Update RT64 for many things, most notably re-spirv

* Improve build times by using the recompiler to output multiple functions per file
2024-08-16 09:50:01 -04:00
Wiseguy
7697a972a3 Update RT64 for many things, most notably re-spirv (#463) 2024-08-10 19:31:37 -04:00
Wiseguy
473b3d3d02 Add transform tagging for moths to fix their interpolation (#460)
Co-authored-by: Tharo <17233964+Thar0@users.noreply.github.com>
2024-08-09 23:43:43 -04:00
David Chavez
d782d3dcb9 chore(ci): finalize PR build artifacts (#455) 2024-08-01 21:00:38 +02:00
David Chavez
142b4d021b chore(ci): further tweaks to PR description updates (#454) 2024-08-01 14:39:40 +02:00
David Chavez
6598da434e chore(ci): link and update build artifacts for PRs (#453) 2024-08-01 13:02:35 +02:00
Mr-Wiseguy
3346400775 Fix message box being used by accident in preload_executable 2024-07-28 22:04:14 -04:00
Wiseguy
c90434962f Preload executable into memory on Windows to prevent stutters (#450) 2024-07-28 22:00:39 -04:00
Wiseguy
9981b922dc Fix .recomp_patch section functions not getting loaded (#449) 2024-07-28 21:43:14 -04:00
Wiseguy
528a22d86f Update RT64 to fix compilation error from missing includes on some compilers (#448) 2024-07-28 12:10:07 -04:00
Wiseguy
4c2cc2003a Opt out of constexpr mutex constructor on windows to prevent vcredist issues (#444) 2024-07-26 21:44:28 -04:00
Wiseguy
c21d55938b Update RT64 for odd-sized HD texture mipmap fix (#445) 2024-07-26 18:57:22 -04:00
Reonu
5aa650bffa Update RT64 for HD texture framework and DXIL linker (#262)
* WIP HD texture support

* Remove STB implementation as it's already defined in RT64

* Fix texcoords for seamless pause background patch

* Fix RT64 compilation error and temporarily disable shader cache

* Fix vertices for bottom strip in seamless pause background patch

* Update RT64 for mip preloading and alignment fixes

* Update RT64 for zipped texture pack support and fix CMake warning flags for clang-cl

* Update RT64 to have multiple pack loading and texture memory stats in the debugger

* Update RT64 to fix replace button crash

* Update to RT64 main as HD textures were merged and completely removed shader cache as it's not needed

---------

Co-authored-by: Wiseguy <68165316+Mr-Wiseguy@users.noreply.github.com>
2024-07-25 23:08:23 -04:00
Wiseguy
a8a5e216fe Tag all patches with the RECOMP_PATCH attribute in preparation for the recompiler's strict mode (#441) 2024-07-25 22:17:00 -04:00
Mr-Wiseguy
97912578e9 Fix patching error that resulted in the rewind button highlight being in the wrong position 2024-07-24 23:42:33 -04:00
Wiseguy
e35bb0700f Fix bug where ocarina inputs are dropped right after taking out the ocarina (#428) 2024-07-08 16:33:33 -04:00
David Chavez
c7baa7ef8f Fix ubuntu-18.04 workflow (#429) 2024-07-08 18:52:33 +02:00
briaguya
19d2e38499 Allow configuring menu accept/apply buttons (#385)
* feat: allow configuring menu accept/apply buttons

* Update assets/icons/Reset.svg

Co-authored-by: thecozies <79979276+thecozies@users.noreply.github.com>

---------

Co-authored-by: thecozies <79979276+thecozies@users.noreply.github.com>
2024-07-05 21:19:31 -04:00
Wiseguy
79fc56f1fd Remove decomp elf dependency and automate patch relocations (#419)
* Changed patch recompilation to use new reference symbol functionality and removed all manual relocations

* Moved symbol tomls to submodule, switched from objcopy to recompiler output binary mechanism for patch recompilation

* Update N64Recomp commit in CI to symbol_reference_file branch

* Remove option in patches toml that doesn't exist

* Update N64Recomp to fix issue with pause screen cursor, fix some issues caused by patches and overlay function-local statics

* Disable unpaired lo16 warnings and update N64Recomp in CI

* Update build instructions to reflect that the decomp elf is no longer needed
2024-07-05 16:33:34 -04:00
Wiseguy
5e3aac4b45 Update README.md to add multilang support in upcoming features 2024-07-02 15:10:24 -04:00
Reonu
6dd618e5a4 Update RT64 to fix Nvidia Wayland crash (#424) 2024-07-01 17:37:21 -04:00
briaguya
208e3044fc fix: use sdl userevent to set await_stick_return when scanning for input (#413) 2024-06-22 22:06:25 -04:00
danielryb
cbb5e8e7a4 Allow skipping Song of Soaring cutscene (#409) 2024-06-22 22:02:33 -04:00
Anghelo Carvajal
4abd0fe720 Update N64ModernRuntime and add debug game thread naming (#408) 2024-06-21 15:40:06 +02:00
David Chavez
07cfe51010 Update SDL2 version for remaining workflows (#401) 2024-06-17 13:10:34 +02:00
David Chavez
b086945b67 Add Blaze message to README (#397) 2024-06-17 00:51:21 +02:00
David Chavez
1a6a3b3082 CI: build on ubuntu-18.04 (#346) 2024-06-16 21:38:23 +02:00
David Chavez
19fcd9bf31 Update to latest N64ModernRuntime (#392)
- updates to new controller changes
- updates to new render context changes
- updates to new controller number changes
- fix for crash on save thread
2024-06-12 09:40:14 +02:00
190 changed files with 21908 additions and 73790 deletions

View File

@@ -7,9 +7,11 @@ assignees: ''
---
## Do not report issues with mods on this page. Please report them on the repo for the mods themselves.
## If you have a crash on startup, please make sure your graphics drivers are up to date before submitting a bug report.
**What is your GPU driver version? Old drivers, particularly on Nvidia, are known to cause crashes on boot on 1.1.0. If you are on Nvidia and the game is crashing on boot, please update to driver version 555.85 or newer before opening an issue.**
**What is your GPU driver version? Old drivers, particularly on Nvidia, are known to cause crashes on boot. If you are on Nvidia and the game is crashing on boot, please update to a recent driver version before opening an issue.**
**Have you checked whether this issue is vanilla behavior? In other words, does it occur on original hardware?**
@@ -36,5 +38,8 @@ Please attach a screenshot of the bug.
- GPU: [e.g. NVIDIA GeForce .../Radeon RX .../Intel UHD .../etc.]
- GPU driver: [e.g Nvidia driver 545.XX, AMD driver 24.X.X, etc]
**Mods Installed**
List the mods you had installed and enabled when you encountered the error.
**Additional context**
Add any other context about the problem here.

View File

@@ -1,10 +1,10 @@
ARCH=$(uname -m)
LINUX_DEPLOY_ARCH=$(uname -m)
if [ "$ARCH" == "x86_64" ]; then
if [ "$ARCH" = "x86_64" ]; then
ARCH="x86_64"
LINUX_DEPLOY_ARCH="x86_64"
elif [ "$ARCH" == "aarch64" ]; then
elif [ "$ARCH" = "aarch64" ]; then
ARCH="arm_aarch64"
LINUX_DEPLOY_ARCH="aarch64"
else
@@ -14,11 +14,13 @@ fi
curl -sSfLO "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-$LINUX_DEPLOY_ARCH.AppImage"
curl -sSfLO "https://github.com/linuxdeploy/linuxdeploy-plugin-gtk/raw/master/linuxdeploy-plugin-gtk.sh"
chmod a+x linuxdeploy*
mkdir -p AppDir/usr/bin
cp Zelda64Recompiled AppDir/usr/bin/
cp -r assets/ AppDir/usr/bin/
cp recompcontrollerdb.txt AppDir/usr/bin/
cp icons/512.png AppDir/Zelda64Recompiled.png
cp .github/linux/Zelda64Recompiled.desktop AppDir/
@@ -34,4 +36,10 @@ echo 'else' >> AppDir/AppRun
echo ' cd "$this_dir"/usr/bin/' >> AppDir/AppRun
echo ' ./Zelda64Recompiled' >> AppDir/AppRun
echo 'fi' >> AppDir/AppRun
# Remove conflicting libraries
rm -rf AppDir/usr/lib/libgmodule*
rm -rf AppDir/usr/lib/gio/modules/*.so
rm -rf AppDir/usr/lib/libwayland*
./deploy/usr/bin/linuxdeploy-plugin-appimage --appdir=AppDir

26
.github/macos/Info.plist.in vendored Normal file
View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>CFBundleExecutable</key>
<string>Zelda64Recompiled</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.games</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>LSMinimumSystemVersion</key>
<string>11</string>
<key>GCSupportsGameMode</key>
<true/>
</dict>
</plist>

87
.github/macos/apple_bundle.cmake vendored Normal file
View File

@@ -0,0 +1,87 @@
# Define the path to the entitlements file
set(ENTITLEMENTS_FILE ${CMAKE_SOURCE_DIR}/.github/macos/entitlements.plist)
# Set bundle properties
set_target_properties(Zelda64Recompiled PROPERTIES
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_BUNDLE_NAME "Zelda64Recompiled"
MACOSX_BUNDLE_GUI_IDENTIFIER "com.github.zelda64recompiled"
MACOSX_BUNDLE_BUNDLE_VERSION "1.0"
MACOSX_BUNDLE_SHORT_VERSION_STRING "1.0"
MACOSX_BUNDLE_ICON_FILE "AppIcon.icns"
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_BINARY_DIR}/Info.plist
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "-"
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS ${ENTITLEMENTS_FILE}
)
# Create icon files for macOS bundle
set(ICON_SOURCE ${CMAKE_SOURCE_DIR}/icons/512.png)
set(ICONSET_DIR ${CMAKE_BINARY_DIR}/AppIcon.iconset)
set(ICNS_FILE ${CMAKE_BINARY_DIR}/resources/AppIcon.icns)
# Create iconset directory and add PNG file
add_custom_command(
OUTPUT ${ICONSET_DIR}
COMMAND ${CMAKE_COMMAND} -E make_directory ${ICONSET_DIR}
COMMAND ${CMAKE_COMMAND} -E copy ${ICON_SOURCE} ${ICONSET_DIR}/icon_512x512.png
COMMAND ${CMAKE_COMMAND} -E copy ${ICON_SOURCE} ${ICONSET_DIR}/icon_512x512@2x.png
COMMAND touch ${ICONSET_DIR}
COMMENT "Creating iconset directory and copying PNG file"
)
# Convert iconset to icns
add_custom_command(
OUTPUT ${ICNS_FILE}
DEPENDS ${ICONSET_DIR}
COMMAND iconutil -c icns ${ICONSET_DIR} -o ${ICNS_FILE}
COMMENT "Converting iconset to icns"
)
# Custom target to ensure icns creation
add_custom_target(create_icns ALL DEPENDS ${ICNS_FILE})
# Set source file properties for the resulting icns file
set_source_files_properties(${ICNS_FILE} PROPERTIES
MACOSX_PACKAGE_LOCATION "Resources"
)
# Add the icns file to the executable target
target_sources(Zelda64Recompiled PRIVATE ${ICNS_FILE})
# Ensure Zelda64Recompiled depends on create_icns
add_dependencies(Zelda64Recompiled create_icns)
# Configure Info.plist
configure_file(${CMAKE_SOURCE_DIR}/.github/macos/Info.plist.in ${CMAKE_BINARY_DIR}/Info.plist @ONLY)
# Install the app bundle
install(TARGETS Zelda64Recompiled BUNDLE DESTINATION .)
# Ensure the entitlements file exists
if(NOT EXISTS ${ENTITLEMENTS_FILE})
message(FATAL_ERROR "Entitlements file not found at ${ENTITLEMENTS_FILE}")
endif()
# Post-build steps for macOS bundle
add_custom_command(TARGET Zelda64Recompiled POST_BUILD
# Copy and fix frameworks first
COMMAND ${CMAKE_COMMAND} -D CMAKE_BUILD_TYPE=$<CONFIG> -D CMAKE_GENERATOR=${CMAKE_GENERATOR} -P ${CMAKE_SOURCE_DIR}/.github/macos/fixup_bundle.cmake
# Copy all resources
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/assets ${CMAKE_BINARY_DIR}/temp_assets
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/temp_assets/scss
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_BINARY_DIR}/temp_assets $<TARGET_BUNDLE_DIR:Zelda64Recompiled>/Contents/Resources/assets
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/temp_assets
# Copy controller database
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/recompcontrollerdb.txt $<TARGET_BUNDLE_DIR:Zelda64Recompiled>/Contents/Resources/
# Set RPATH
COMMAND install_name_tool -add_rpath "@executable_path/../Frameworks/" $<TARGET_BUNDLE_DIR:Zelda64Recompiled>/Contents/MacOS/Zelda64Recompiled
# Sign the bundle
COMMAND codesign --verbose=4 --options=runtime --no-strict --sign - --entitlements ${ENTITLEMENTS_FILE} --deep --force $<TARGET_BUNDLE_DIR:Zelda64Recompiled>
COMMENT "Performing post-build steps for macOS bundle"
VERBATIM
)

14
.github/macos/entitlements.plist vendored Normal file
View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.disable-executable-page-protection</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>

44
.github/macos/fixup_bundle.cmake vendored Normal file
View File

@@ -0,0 +1,44 @@
include(BundleUtilities)
# Check for pkgx installation
find_program(PKGX_EXECUTABLE pkgx)
# Xcode generator puts the build type in the build directory
set(BUILD_PREFIX "")
if (CMAKE_GENERATOR STREQUAL "Xcode")
set(BUILD_PREFIX "${CMAKE_BUILD_TYPE}/")
endif()
# Use generator expressions to get the absolute path to the bundle
set(APPS "${BUILD_PREFIX}Zelda64Recompiled.app/Contents/MacOS/Zelda64Recompiled")
# Set up framework search paths
set(DIRS "${BUILD_PREFIX}Zelda64Recompiled.app/Contents/Frameworks")
# Detect if we're using pkgx
if(PKGX_EXECUTABLE)
message(STATUS "pkgx detected, adding pkgx directories to framework search path")
list(APPEND DIRS "$ENV{HOME}/.pkgx/")
endif()
# Convert all paths to absolute paths
file(REAL_PATH ${APPS} APPS)
set(RESOLVED_DIRS "")
foreach(DIR IN LISTS DIRS)
# Handle home directory expansion
string(REPLACE "~" "$ENV{HOME}" DIR "${DIR}")
# Convert to absolute path, but don't fail if directory doesn't exist
if(EXISTS "${DIR}")
file(REAL_PATH "${DIR}" RESOLVED_DIR)
list(APPEND RESOLVED_DIRS "${RESOLVED_DIR}")
endif()
endforeach()
# Debug output
message(STATUS "Bundle fixup paths:")
message(STATUS " App: ${APPS}")
message(STATUS " Search dirs: ${RESOLVED_DIRS}")
# Fix up the bundle
fixup_bundle("${APPS}" "" "${RESOLVED_DIRS}")

73
.github/macos/ld64 vendored Executable file
View File

@@ -0,0 +1,73 @@
#!/usr/bin/python3
"""
Custom ld64 wrapper for macOS
This script wraps the standard macOS linker (/usr/bin/ld) to modify executable memory
protection flags in the resulting Mach-O binary. It works in three stages:
1. First, it passes through all arguments to the regular macOS linker to create the binary
2. Then, it parses command line arguments to identify output file and segment protection flags
3. Finally, it modifies the output binary's Mach-O headers to ensure segments (particularly __TEXT)
have the maximum protection flags (rwx) we specify, even if the default macOS linker would restrict them
This is necessary because macOS restricts writable+executable memory by default,
but certain applications need this capability for dynamic code generation or JIT compilation.
Usage: Same as the standard ld64 linker, with the added benefit that -segprot options
will have their max_prot values properly preserved in the output binary.
"""
import sys
import subprocess
from itertools import takewhile
from macholib import MachO, ptypes
def parse_rwx(text):
return ('r' in text and 1) | ('w' in text and 2) | ('x' in text and 4)
def apply_maxprots(path, maxprots):
mach = MachO.MachO(path)
header = mach.headers[0]
offset = ptypes.sizeof(header.mach_header)
for cload, ccmd, cdata in header.commands:
if not hasattr(ccmd, 'segname'):
break
if hasattr(ccmd.segname, 'to_str'):
segname = ccmd.segname.to_str().decode('utf-8').strip('\0')
else:
segname = ccmd.segname.decode('utf-8').strip('\0')
if segname in maxprots and ccmd.maxprot != maxprots[segname]:
fields = list(takewhile(lambda field: field[0] != 'maxprot', cload._fields_ + ccmd._fields_))
index = offset + sum(ptypes.sizeof(typ) for _, typ in fields)
with open(path, 'r+b') as fh:
fh.seek(index)
fh.write(bytes([maxprots[segname]]))
offset += cload.cmdsize
try:
subprocess.check_call(['/usr/bin/ld'] + sys.argv[1:])
except subprocess.CalledProcessError as ex:
sys.exit(ex.returncode)
output_file = 'a.out'
segprots = {'__TEXT': parse_rwx('rwx')} # maxprot = rwx
i = 1
while i < len(sys.argv):
if sys.argv[i] == '-o' and i + 1 < len(sys.argv):
output_file = sys.argv[i + 1]
i += 2
elif sys.argv[i] == '-segprot' and i + 3 < len(sys.argv):
segment = sys.argv[i + 1]
maxprot = sys.argv[i + 2]
segprots[segment] = parse_rwx(maxprot)
i += 4
else:
i += 1
apply_maxprots(output_file, segprots)

16
.github/macos/macports.yaml vendored Normal file
View File

@@ -0,0 +1,16 @@
version: '2.10.6'
prefix: '/opt/local'
variants:
select:
- aqua
- metal
deselect: x11
ports:
- name: libiconv
select: universal
- name: libsdl2
select: universal
- name: freetype
select: universal
- name: clang-18
- name: llvm-18

View File

@@ -0,0 +1,64 @@
name: update-pr-artifacts
on:
workflow_run:
workflows: [validate-external, validate-internal]
types:
- completed
jobs:
update-pr-artifacts:
runs-on: ubuntu-latest
if: (github.event.workflow_run.event == 'pull_request' || github.event.workflow_run.event == 'pull_request_target') && github.event.workflow_run.conclusion == 'success'
name: Update PR Artifacts
steps:
- name: Get PR Number
id: pr-number
uses: actions/github-script@v6
with:
result-encoding: string
script: |
const { owner, repo } = context.repo;
const findPRNumber = async () => {
const pulls = await github.rest.pulls.list({ owner, repo });
for await (const { data } of github.paginate.iterator(pulls)) {
for (const pull of data) {
if (pull.head.sha === '${{ github.event.workflow_run.head_sha }}' && pull.user.id === ${{ github.event.sender.id }}) {
return pull.number;
}
}
}
return null;
};
const prNumber = await findPRNumber();
if (!prNumber) {
core.error(`No matching pull request found`);
} else {
return prNumber;
}
- name: Create Artifacts Content
id: artifacts-content
uses: actions/github-script@v6
with:
result-encoding: string
script: |
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
const nightlyLinks = artifacts.data.artifacts.reduce((acc, item) => {
acc += `- [${item.name}.zip](https://nightly.link/${context.repo.owner}/${context.repo.repo}/actions/artifacts/${item.id}.zip)\n`;
return acc;
}, '### Build Artifacts\n');
return nightlyLinks;
- name: Update PR Description
uses: garrettjoecox/pr-section@3.1.0
with:
section-name: 'artifacts'
repo-token: '${{ secrets.GITHUB_TOKEN }}'
pr-number: ${{ steps.pr-number.outputs.result }}
section-value: '${{ steps.artifacts-content.outputs.result }}'

View File

@@ -5,11 +5,15 @@ on:
SDL2_VERSION:
type: string
required: false
default: '2.28.5'
default: '2.30.3'
N64RECOMP_COMMIT:
type: string
required: false
default: '2a2df89349ff25a3afb3a09617deb3a166efe2f3'
default: 'a13e5cff96686776b0e03baf23923e3c1927b770'
DXC_CHECKSUM:
type: string
required: false
default: '4e6f4e52989aca69739880b40b9f988357f15d10ca03284377b81f1502463ff5'
secrets:
ZRE_REPO_WITH_PAT:
required: true
@@ -18,13 +22,98 @@ concurrency:
cancel-in-progress: true
jobs:
build-linux:
runs-on: ${{ matrix.arch == 'x64' && matrix.os || format('blaze/{0}', matrix.os) }}
runs-on: ${{ format('blaze/compute/{0}-amd64', matrix.os) }}
container:
image: dcvz/n64recomp:ubuntu-18.04
volumes:
# create a volume that we'll use as the new source for the node binary
# https://github.com/actions/checkout/issues/1809
- /nodeoverride:/nodeoverride:rw,rshared
- /nodeoverride:/__e/node20:ro,rshared
strategy:
matrix:
type: [ Debug, Release ]
os: [ ubuntu-22.04 ]
arch: [ x64, arm64 ]
name: ${{ matrix.os }} (${{ matrix.arch }}, ${{ matrix.type }})
name: ubuntu-18.04 (x64, ${{ matrix.type }}, Native, AppImage)
steps:
- name: Copy the Node20 binary
run: |
cp -r /node20217/* /nodeoverride/
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.ref }}
submodules: recursive
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
with:
key: ${{ matrix.os }}-z64re-ccache-${{ matrix.type }}-x64-${{ inputs.N64RECOMP_COMMIT }}
- name: Prepare Build
run: |-
git clone ${{ secrets.ZRE_REPO_WITH_PAT }}
unzip zre/files.zip > /dev/null 2>&1
- name: Build N64Recomp & RSPRecomp
run: |
git clone https://github.com/Mr-Wiseguy/N64Recomp.git --recurse-submodules N64RecompSource
cd N64RecompSource
git checkout ${{ inputs.N64RECOMP_COMMIT }}
git submodule update --init --recursive
# enable ccache
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
# Build N64Recomp & RSPRecomp
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER=g++-11 -DCMAKE_C_COMPILER=gcc-11 -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S . -B cmake-build
cmake --build cmake-build --config Release --target N64RecompCLI -j $(nproc)
cmake --build cmake-build --config Release --target RSPRecomp -j $(nproc)
# Copy N64Recomp & RSPRecomp to root directory
cp cmake-build/N64Recomp ..
cp cmake-build/RSPRecomp ..
- name: Run N64Recomp & RSPRecomp
run: |
./N64Recomp us.rev1.toml
./RSPRecomp aspMain.us.rev1.toml
./RSPRecomp njpgdspMain.us.rev1.toml
- name: Hotpatch DXC into RT64's contrib
run: |
# check if dxc was updated before we replace it, to detect changes
echo ${{ inputs.DXC_CHECKSUM }} ./lib/rt64/src/contrib/dxc/bin/x64/dxc-linux | sha256sum --status -c -
cp -v /usr/local/lib/libdxcompiler.so ./lib/rt64/src/contrib/dxc/lib/x64/libdxcompiler.so
cp -v /usr/local/bin/dxc ./lib/rt64/src/contrib/dxc/bin/x64/dxc-linux
- name: Build ZeldaRecomp
run: |-
# enable ccache
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
cmake -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER=clang++-17 -DCMAKE_C_COMPILER=clang-17 -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S . -B cmake-build -DPATCHES_C_COMPILER=clang-17 -DPATCHES_LD=ld.lld-17
cmake --build cmake-build --config ${{ matrix.type }} --target Zelda64Recompiled -j $(nproc)
- name: Prepare Archive
run: |
mv cmake-build/Zelda64Recompiled Zelda64Recompiled
rm -rf assets/scss
tar -czf Zelda64Recompiled.tar.gz Zelda64Recompiled assets/ recompcontrollerdb.txt
- name: Archive Zelda64Recomp
uses: actions/upload-artifact@v4
with:
name: Zelda64Recompiled-${{ runner.os }}-X64-${{ matrix.type }}
path: Zelda64Recompiled.tar.gz
- name: Build AppImage
run: |-
./.github/linux/appimage.sh
- name: Zelda64Recomp AppImage
uses: actions/upload-artifact@v4
with:
name: Zelda64Recompiled-AppImage-X64-${{ matrix.type }}
path: Zelda64Recompiled-*.AppImage
build-linux-arm64:
runs-on: ${{ format('blaze/compute/{0}', matrix.os) }}
strategy:
matrix:
type: [ Debug, Release ]
os: [ ubuntu-22.04 ]
name: ${{ matrix.os }} (arm64, ${{ matrix.type }}, Native, AppImage)
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -34,7 +123,7 @@ jobs:
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
with:
key: ${{ matrix.os }}-z64re-ccache-${{ matrix.type }}-${{ matrix.arch }}
key: ${{ matrix.os }}-z64re-ccache-${{ matrix.type }}-arm64-${{ inputs.N64RECOMP_COMMIT }}
- name: Install Linux Dependencies
run: |
sudo apt-get update
@@ -52,7 +141,7 @@ jobs:
./configure
make -j 10
sudo make install
sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/
sudo cp -av /usr/local/lib/libSDL* /lib/aarch64-linux-gnu/
echo ::endgroup::
- name: Prepare Build
run: |-
@@ -71,7 +160,7 @@ jobs:
# Build N64Recomp & RSPRecomp
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER=g++-11 -DCMAKE_C_COMPILER=gcc-11 -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S . -B cmake-build
cmake --build cmake-build --config Release --target N64Recomp -j $(nproc)
cmake --build cmake-build --config Release --target N64RecompCLI -j $(nproc)
cmake --build cmake-build --config Release --target RSPRecomp -j $(nproc)
# Copy N64Recomp & RSPRecomp to root directory
@@ -93,24 +182,74 @@ jobs:
run: |
mv cmake-build/Zelda64Recompiled Zelda64Recompiled
rm -rf assets/scss
tar -czf Zelda64Recompiled.tar.gz Zelda64Recompiled assets/
tar -czf Zelda64Recompiled.tar.gz Zelda64Recompiled assets/ recompcontrollerdb.txt
- name: Archive Zelda64Recomp
uses: actions/upload-artifact@v4
with:
name: Zelda64Recompiled-${{ runner.os }}-${{ runner.arch }}-${{ matrix.type }}
name: Zelda64Recompiled-${{ runner.os }}-ARM64-${{ matrix.type }}
path: Zelda64Recompiled.tar.gz
- name: Prepare AppImage
run: ./.github/linux/appimage.sh
- name: Zelda64Recomp AppImage
uses: actions/upload-artifact@v4
with:
name: Zelda64Recompiled-AppImage-${{ runner.arch }}-${{ matrix.type }}
name: Zelda64Recompiled-AppImage-ARM64-${{ matrix.type }}
path: Zelda64Recompiled-*.AppImage
build-linux-flatpak:
runs-on: ubuntu-latest
env:
FLATPAK_ID: io.github.zelda64recomp.zelda64recomp
FREEDESKTOP_VERSION: 23.08
LLVM_VERSION: 18
strategy:
matrix:
type: [ Debug, Release ]
os: [ ubuntu-latest ]
name: ${{ matrix.os }} (x64, ${{ matrix.type }}, Flatpak)
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.ref }}
submodules: recursive
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
with:
key: ${{ matrix.os }}-z64re-ccache-${{ matrix.type }}-x64-${{ inputs.N64RECOMP_COMMIT }}
- name: Install Linux Dependencies
run: |
sudo apt-get update
sudo apt-get install -y flatpak-builder ccache lld
- name: Prepare Build
run: |-
git clone ${{ secrets.ZRE_REPO_WITH_PAT }}
./zre/process.sh
- name: Prepare Flatpak
run: |
flatpak --user remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak --user install -y flathub org.freedesktop.Sdk//${{ env.FREEDESKTOP_VERSION }}
flatpak --user install -y flathub org.freedesktop.Sdk.Extension.llvm${{ env.LLVM_VERSION }}//${{ env.FREEDESKTOP_VERSION }}
- name: Build ZeldaRecomp
run: |-
export CCACHE_DIR=/tmp/ccache
git config --global protocol.file.allow always
make -C patches CC=clang LD=ld.lld
flatpak-builder --user --force-clean --install-deps-from=flathub --repo=repo --ccache builddir ./flatpak/${{ env.FLATPAK_ID }}.json
flatpak build-bundle repo ./${{ env.FLATPAK_ID }}.flatpak ${{ env.FLATPAK_ID }} --runtime-repo=https://flathub.org/repo/flathub.flatpakrepo
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: Zelda64Recompiled-Flatpak-X64-${{ matrix.type }}
path: ./${{ env.FLATPAK_ID }}.flatpak
build-windows:
runs-on: windows-latest
strategy:
matrix:
type: [ Debug, Release ]
type: [ Debug, RelWithDebInfo ]
name: windows (${{ matrix.type }})
steps:
- name: Checkout
@@ -126,6 +265,13 @@ jobs:
run: |
choco install ninja
Remove-Item -Path "C:\ProgramData\Chocolatey\bin\ccache.exe" -Force -ErrorAction SilentlyContinue
- name: Download portable LLVM
run: |
$ProgressPreference = 'SilentlyContinue'
Invoke-WebRequest -Uri "https://github.com/llvm/llvm-project/releases/download/llvmorg-19.1.3/LLVM-19.1.3-Windows-X64.tar.xz" -OutFile "LLVM.tar.xz"
New-Item -ItemType Directory -Path portable-llvm > $null
7z x LLVM.tar.xz
7z x LLVM.tar -oportable-llvm
- name: Configure Developer Command Prompt
uses: ilammy/msvc-dev-cmd@v1
- name: Prepare Build
@@ -145,7 +291,7 @@ jobs:
$cpuCores = (Get-CimInstance -ClassName Win32_Processor).NumberOfLogicalProcessors
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S . -B cmake-build
cmake --build cmake-build --config Release --target N64Recomp -j $cpuCores
cmake --build cmake-build --config Release --target N64RecompCLI -j $cpuCores
cmake --build cmake-build --config Release --target RSPRecomp -j $cpuCores
# Copy N64Recomp & RSPRecomp to root directory
@@ -165,7 +311,7 @@ jobs:
# remove LLVM from PATH so it doesn't overshadow the one provided by VS
$env:PATH = ($env:PATH -split ';' | Where-Object { $_ -ne 'C:\Program Files\LLVM\bin' }) -join ';'
cmake -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER=clang-cl -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S . -B cmake-build -DCMAKE_CXX_FLAGS="-Xclang -fexceptions -Xclang -fcxx-exceptions"
cmake -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER=clang-cl -DCMAKE_MAKE_PROGRAM=ninja -DPATCHES_C_COMPILER="..\portable-llvm\LLVM-19.1.3-Windows-X64\bin\clang" -DPATCHES_LD="..\portable-llvm\LLVM-19.1.3-Windows-X64\bin\ld.lld" -G Ninja -S . -B cmake-build -DCMAKE_CXX_FLAGS="-Xclang -fexceptions -Xclang -fcxx-exceptions"
cmake --build cmake-build --config ${{ matrix.type }} --target Zelda64Recompiled -j $cpuCores
env:
SDL2_VERSION: ${{ inputs.SDL2_VERSION }}
@@ -175,6 +321,7 @@ jobs:
Move-Item -Path "cmake-build/dxcompiler.dll" -Destination "dxcompiler.dll"
Move-Item -Path "cmake-build/dxil.dll" -Destination "dxil.dll"
Move-Item -Path "cmake-build/SDL2.dll" -Destination "SDL2.dll"
Move-Item -Path "cmake-build/Zelda64Recompiled.pdb" -Destination "Zelda64Recompiled.pdb"
Remove-Item -Path "assets/scss" -Recurse -Force
- name: Archive Zelda64Recomp
uses: actions/upload-artifact@v4
@@ -186,3 +333,81 @@ jobs:
dxil.dll
SDL2.dll
assets/
recompcontrollerdb.txt
- name: Archive Debug Files
uses: actions/upload-artifact@v4
with:
name: Zelda64Recompiled-PDB-${{ matrix.type }}
path: |
Zelda64Recompiled.pdb
build-macos:
runs-on: blaze/macos-14
strategy:
matrix:
type: [ Debug, Release ]
name: macos (x64, arm64, ${{ matrix.type }})
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.ref }}
submodules: recursive
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
with:
key: ${{ runner.os }}-z64re-ccache-${{ matrix.type }}
- name: Homebrew Setup
run: |
brew install ninja
brew uninstall --ignore-dependencies libpng freetype
- name: MacPorts Setup
uses: melusina-org/setup-macports@v1
id: 'macports'
with:
parameters: '.github/macos/macports.yaml'
- name: Prepare Build
run: |-
git clone ${{ secrets.ZRE_REPO_WITH_PAT }}
./zre/process.sh
cp ./zre/mm_shader_cache.bin ./shadercache/
- name: Build N64Recomp & RSPRecomp
run: |
git clone https://github.com/Mr-Wiseguy/N64Recomp.git --recurse-submodules N64RecompSource
cd N64RecompSource
git checkout ${{ inputs.N64RECOMP_COMMIT }}
git submodule update --init --recursive
# enable ccache
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
# Build N64Recomp & RSPRecomp
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S . -B cmake-build
cmake --build cmake-build --config Release --target N64Recomp -j $(sysctl -n hw.ncpu)
cmake --build cmake-build --config Release --target RSPRecomp -j $(sysctl -n hw.ncpu)
# Copy N64Recomp & RSPRecomp to root directory
cp cmake-build/N64Recomp ..
cp cmake-build/RSPRecomp ..
- name: Run N64Recomp & RSPRecomp
run: |
./N64Recomp us.rev1.toml
./RSPRecomp aspMain.us.rev1.toml
./RSPRecomp njpgdspMain.us.rev1.toml
- name: Build ZeldaRecomp
run: |-
# enable ccache
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
cmake -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S . -B cmake-build \
-DPATCHES_LD=/opt/local/bin/ld.lld-mp-18 -DCMAKE_AR=/opt/local/bin/llvm-ar-mp-18 -DPATCHES_C_COMPILER=/opt/local/bin/clang-mp-18 \
-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"
cmake --build cmake-build --config ${{ matrix.type }} --target Zelda64Recompiled -j $(sysctl -n hw.ncpu)
- name: Prepare Archive
run: |
mv cmake-build/Zelda64Recompiled.app Zelda64Recompiled.app
zip -r -y Zelda64Recompiled.zip Zelda64Recompiled.app
- name: Archive Zelda64Recomp
uses: actions/upload-artifact@v4
with:
name: Zelda64Recompiled-${{ runner.os }}-${{ matrix.type }}
path: Zelda64Recompiled.zip

10
.gitignore vendored
View File

@@ -2,6 +2,7 @@
.vscode/settings.json
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/tasks.json
# Input elf and rom files
*.elf
@@ -12,7 +13,7 @@ RecompiledFuncs/
RecompiledPatches/
# Linux build output
build/
build*/
*.o
# Windows build output
@@ -57,4 +58,11 @@ node_modules/
# Recompiler Linux binary
N64Recomp
RSPRecomp
.DS_Store
# Cmake build directory
.cache
.idea
build-*
cmake-build-*

7
.gitmodules vendored
View File

@@ -13,9 +13,10 @@
[submodule "lib/lunasvg"]
path = lib/lunasvg
url = https://github.com/sammycage/lunasvg
[submodule "lib/sse2neon"]
path = lib/sse2neon
url = https://github.com/DLTcollab/sse2neon.git
[submodule "lib/N64ModernRuntime"]
path = lib/N64ModernRuntime
url = https://github.com/N64Recomp/N64ModernRuntime.git
[submodule "Zelda64RecompSyms"]
path = Zelda64RecompSyms
url = https://github.com/Zelda64Recomp/Zelda64RecompSyms

View File

@@ -1,13 +1,8 @@
# Building Guide
> [!NOTE]
> The need for an elf file from the Majora's Mask decompilation is temporary and will be removed in the future.
This guide will help you build the project on your local machine. The process will require you to provide a decompressed ROM of the US version of the game.
This guide will help you build the project on your local machine. The process will require you to provide two items:
- A decompressed ROM of the US version of the game.
- An elf file created from [this commit](https://github.com/zeldaret/mm/tree/23beee0717364de43ca9a82957cc910cf818de90) of the Majora's Mask decompilation.
These steps cover: acquiring these, running the required processes and finally building the project.
These steps cover: decompressing the ROM, running the recompiler and finally building the project.
## 1. Clone the Zelda64Recomp Repository
This project makes use of submodules so you will need to clone the repository with the `--recurse-submodules` flag.
@@ -25,7 +20,7 @@ For Linux the instructions for Ubuntu are provided, but you can find the equival
```bash
# For Ubuntu, simply run:
sudo apt-get install cmake ninja libsdl2-dev libgtk-3-dev lld llvm clang-15
sudo apt-get install cmake ninja-build libsdl2-dev libgtk-3-dev lld llvm clang
```
### Windows
@@ -40,20 +35,14 @@ The other tool necessary will be `make` which can be installe via [Chocolatey](h
choco install make
```
## 3. Creating the ELF file & decompressed ROM
You will need to build [this commit](https://github.com/zeldaret/mm/tree/23beee0717364de43ca9a82957cc910cf818de90) of the Majora's Mask decompilation. Follow their build instructions to generate the ELF file and decompressed ROM. However, while building you may get the following build error:
```bash
RuntimeError: 'jr' instruction does not have an 'jump label' field
```
## 3. Decompressing the target ROM
You will need to decompress the NTSC-U N64 Majora's Mask ROM (sha1: d6133ace5afaa0882cf214cf88daba39e266c078) before running the recompiler.
To fix this you will have to modify the problematic file `tools/disasm/disasm.py` at line 1115. This issue is due to a bug in this specific commit of the decomp project and will be resolved once Zelda64Recomp is updated to a more recent commit. To fix it, replace the line:
```diff
- elif insn.isJump():
+ elif insn.isJumpWithAddress():
```
There are a few tools that can do it:
* This python script from the Majora's Mask decompilation project: https://github.com/zeldaret/mm/blob/main/tools/decompress_baserom.py
* https://github.com/z64tools/z64decompress
Upon successful build it will generate the two required files. Copy them to the root of the Zelda64Recomp repository:
- `mm.us.rev1.rom_uncompressed.elf`
Regardless of which method you use, copy the decompressed ROM to the root of the Zelda64Recomp repository with this filename:
- `mm.us.rev1.rom_uncompressed.z64`
## 4. Generating the C code
@@ -71,7 +60,9 @@ After that, go back to the repository root, and run the following commands:
Finally, you can build the project! :rocket:
On Windows, you can open the repository folder with Visual Studio, and you'll be able to `[build / run / debug]` the project from there. If you prefer the commandline or you're on a Unix platform you can build the project using CMake:
On Windows, you can open the repository folder with Visual Studio, and you'll be able to `[build / run / debug]` the project from there.
If you prefer the command line or you're on a Unix platform you can build the project using CMake:
```bash
cmake -S . -B build-cmake -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -G Ninja -DCMAKE_BUILD_TYPE=Release # or Debug if you want to debug
@@ -80,7 +71,7 @@ cmake --build build-cmake --target Zelda64Recompiled -j$(nproc) --config Release
## 6. Success
Voilà! You should now have a `Zelda64Recompiled` file in the `build-cmake` directory!
Voilà! You should now have a `Zelda64Recompiled` executable in the build directory! If you used Visual Studio this will be `out/build/x64-[Configuration]` and if you used the provided CMake commands then this will be `build-cmake`. You will need to run the executable out of the root folder of this project or copy the assets folder to the build folder to run it.
> [!IMPORTANT]
> [!IMPORTANT]
> In the game itself, you should be using a standard ROM, not the decompressed one.

View File

@@ -1,12 +1,33 @@
cmake_minimum_required(VERSION 3.20)
project(Zelda64Recompiled)
if (APPLE)
set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0" CACHE STRING "Minimum OS X deployment version")
endif()
project(Zelda64Recompiled LANGUAGES C CXX)
set(CMAKE_C_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-everything -Wall -Wextra")
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-everything /W4")
endif()
# Opt out of constexpr mutex constructor on windows to prevent vcredist issues
if (WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR")
endif()
if (APPLE)
enable_language(OBJC OBJCXX)
endif()
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
option(RECOMP_FLATPAK "Configure the build for Flatpak compatibility." OFF)
endif()
# Avoid warning about DOWNLOAD_EXTRACT_TIMESTAMP in CMake 3.24:
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
@@ -21,6 +42,13 @@ if (WIN32)
endif()
set(RT64_STATIC TRUE)
set(RT64_SDL_WINDOW_VULKAN TRUE)
add_compile_definitions(HLSL_CPU)
if (RECOMP_FLATPAK)
add_compile_definitions(RECOMP_FLATPAK)
endif()
add_subdirectory(${CMAKE_SOURCE_DIR}/lib/rt64 ${CMAKE_BINARY_DIR}/rt64)
# set(BUILD_SHARED_LIBS_SAVED "${BUILD_SHARED_LIBS}")
@@ -28,8 +56,10 @@ set(BUILD_SHARED_LIBS OFF)
SET(LUNASVG_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
add_subdirectory(${CMAKE_SOURCE_DIR}/lib/lunasvg)
# set(BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS_SAVED}")
SET(ENABLE_SVG_PLUGIN ON CACHE BOOL "" FORCE)
SET(RMLUI_SVG_PLUGIN ON CACHE BOOL "" FORCE)
SET(RMLUI_TESTS_ENABLED OFF CACHE BOOL "" FORCE)
add_subdirectory(${CMAKE_SOURCE_DIR}/lib/RmlUi)
target_compile_definitions(rmlui_core PRIVATE LUNASVG_BUILD_STATIC)
add_subdirectory(${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime)
@@ -49,6 +79,7 @@ target_include_directories(RecompiledFuncs PRIVATE
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/ultramodern/include
${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/librecomp/include
${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/N64Recomp/include
)
file(GLOB FUNC_C_SOURCES ${CMAKE_SOURCE_DIR}/RecompiledFuncs/*.c)
@@ -70,6 +101,7 @@ target_include_directories(PatchesLib PRIVATE
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/ultramodern/include
${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/librecomp/include
${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/N64Recomp/include
)
target_sources(PatchesLib PRIVATE
@@ -80,46 +112,47 @@ target_sources(PatchesLib PRIVATE
set_source_files_properties(${CMAKE_SOURCE_DIR}/RecompiledPatches/patches.c PROPERTIES COMPILE_FLAGS -fno-strict-aliasing)
# Build patches elf
if(NOT DEFINED PATCHES_C_COMPILER)
set(PATCHES_C_COMPILER clang)
endif()
if(NOT DEFINED PATCHES_LD)
set(PATCHES_LD ld.lld)
endif()
add_custom_target(PatchesBin
COMMAND make
COMMAND ${CMAKE_COMMAND} -E env CC=${PATCHES_C_COMPILER} LD=${PATCHES_LD} make
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/patches
BYPRODUCTS ${CMAKE_SOURCE_DIR}/patches/patches.bin
BYPRODUCTS ${CMAKE_SOURCE_DIR}/patches/patches.elf
)
# Generate patches_bin.c from patches.bin
add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/RecompiledPatches/patches_bin.c
COMMAND file_to_c ${CMAKE_SOURCE_DIR}/patches/patches.bin mm_patches_bin ${CMAKE_SOURCE_DIR}/RecompiledPatches/patches_bin.c ${CMAKE_SOURCE_DIR}/RecompiledPatches/patches_bin.h
COMMAND file_to_c ${CMAKE_SOURCE_DIR}/patches/patches.bin mm_patches_bin ${CMAKE_SOURCE_DIR}/RecompiledPatches/patches_bin.c ${CMAKE_SOURCE_DIR}/RecompiledPatches/patches_bin.h
DEPENDS ${CMAKE_SOURCE_DIR}/patches/patches.bin
)
# Recompile patches elf into patches.c
# Recompile patches elf into patches.c and patches.bin
add_custom_command(OUTPUT
${CMAKE_SOURCE_DIR}/patches/patches.bin
${CMAKE_SOURCE_DIR}/RecompiledPatches/patches.c
${CMAKE_SOURCE_DIR}/RecompiledPatches/recomp_overlays.inl
${CMAKE_SOURCE_DIR}/RecompiledPatches/funcs.h
# TODO: Look into why modifying patches requires two builds to take
COMMAND ./N64Recomp patches.toml
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
DEPENDS ${CMAKE_SOURCE_DIR}/patches/patches.bin
DEPENDS ${CMAKE_SOURCE_DIR}/patches/patches.elf
)
# Main executable
add_executable(Zelda64Recompiled)
# Generate mm_shader_cache.c from the MM shader cache if it exists
if (EXISTS ${CMAKE_SOURCE_DIR}/shadercache/mm_shader_cache.bin)
set(HAS_MM_SHADER_CACHE TRUE)
target_compile_definitions(Zelda64Recompiled PRIVATE HAS_MM_SHADER_CACHE)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/mm_shader_cache.c ${CMAKE_CURRENT_BINARY_DIR}/mm_shader_cache.h
COMMAND file_to_c ${CMAKE_SOURCE_DIR}/shadercache/mm_shader_cache.bin mm_shader_cache_bytes ${CMAKE_CURRENT_BINARY_DIR}/mm_shader_cache.c ${CMAKE_CURRENT_BINARY_DIR}/mm_shader_cache.h
DEPENDS ${CMAKE_SOURCE_DIR}/shadercache/mm_shader_cache.bin
)
endif()
set (SOURCES
${CMAKE_SOURCE_DIR}/src/main/main.cpp
${CMAKE_SOURCE_DIR}/src/main/support.cpp
${CMAKE_SOURCE_DIR}/src/main/register_overlays.cpp
${CMAKE_SOURCE_DIR}/src/main/register_patches.cpp
${CMAKE_SOURCE_DIR}/src/main/rt64_render_context.cpp
${CMAKE_SOURCE_DIR}/src/game/input.cpp
${CMAKE_SOURCE_DIR}/src/game/controls.cpp
@@ -128,12 +161,41 @@ set (SOURCES
${CMAKE_SOURCE_DIR}/src/game/debug.cpp
${CMAKE_SOURCE_DIR}/src/game/quicksaving.cpp
${CMAKE_SOURCE_DIR}/src/game/recomp_api.cpp
${CMAKE_SOURCE_DIR}/src/game/recomp_actor_api.cpp
${CMAKE_SOURCE_DIR}/src/game/recomp_data_api.cpp
${CMAKE_SOURCE_DIR}/src/game/rom_decompression.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_renderer.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_state.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_launcher.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_config.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_prompt.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_config_sub_menu.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_color_hack.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_rml_hacks.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_elements.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_mod_details_panel.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_mod_installer.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_mod_menu.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_api.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_api_events.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_api_images.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_utils.cpp
${CMAKE_SOURCE_DIR}/src/ui/util/hsv.cpp
${CMAKE_SOURCE_DIR}/src/ui/core/ui_context.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_button.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_clickable.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_container.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_element.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_image.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_label.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_radio.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_scroll_container.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_slider.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_span.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_style.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_text_input.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_toggle.cpp
${CMAKE_SOURCE_DIR}/rsp/aspMain.cpp
${CMAKE_SOURCE_DIR}/rsp/njpgdspMain.cpp
@@ -141,12 +203,13 @@ set (SOURCES
${CMAKE_SOURCE_DIR}/lib/RmlUi/Backends/RmlUi_Platform_SDL.cpp
)
if (HAS_MM_SHADER_CACHE)
list(APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/mm_shader_cache.c)
if (APPLE)
list(APPEND SOURCES ${CMAKE_SOURCE_DIR}/src/main/support_apple.mm)
endif()
target_include_directories(Zelda64Recompiled PRIVATE
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/N64Recomp/include
${CMAKE_SOURCE_DIR}/lib/concurrentqueue
${CMAKE_SOURCE_DIR}/lib/GamepadMotionHelpers
${CMAKE_SOURCE_DIR}/lib/RmlUi/Include
@@ -157,9 +220,9 @@ target_include_directories(Zelda64Recompiled PRIVATE
${CMAKE_SOURCE_DIR}/lib/rt64/src
${CMAKE_SOURCE_DIR}/lib/rt64/src/rhi
${CMAKE_SOURCE_DIR}/lib/rt64/src/render
${CMAKE_SOURCE_DIR}/lib/sse2neon
${CMAKE_SOURCE_DIR}/lib/freetype-windows-binaries/include
${CMAKE_SOURCE_DIR}/lib/rt64/src/contrib/nativefiledialog-extended/src/include
${CMAKE_SOURCE_DIR}/lib/SlotMap
${CMAKE_BINARY_DIR}/shaders
${CMAKE_CURRENT_BINARY_DIR}
)
@@ -177,13 +240,24 @@ else()
)
endif()
if (MSVC)
# Disable identical code folding, since this breaks mod function patching as multiple functions can get merged into one.
target_link_options(Zelda64Recompiled PRIVATE /OPT:NOICF)
elseif (APPLE)
# Use a wrapper around ld64 that respects segprot's `max_prot` value in order
# to make our executable memory writable (required for mod function patching)
target_link_options(Zelda64Recompiled PRIVATE
"-fuse-ld=${CMAKE_SOURCE_DIR}/.github/macos/ld64"
)
endif()
if (WIN32)
include(FetchContent)
if (DEFINED ENV{SDL2_VERSION})
set(SDL2_VERSION $ENV{SDL2_VERSION})
else()
set(SDL2_VERSION "2.28.5")
set(SDL2_VERSION "2.30.3")
endif()
# Fetch SDL2 on windows
@@ -217,15 +291,29 @@ if (WIN32)
)
target_sources(Zelda64Recompiled PRIVATE ${CMAKE_SOURCE_DIR}/icons/app.rc)
target_link_libraries(Zelda64Recompiled PRIVATE SDL2 Winmm.lib)
endif()
if (APPLE)
find_package(SDL2 REQUIRED)
target_include_directories(Zelda64Recompiled PRIVATE ${SDL2_INCLUDE_DIRS})
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package(Threads REQUIRED)
target_link_libraries(Zelda64Recompiled PRIVATE ${CMAKE_DL_LIBS} Threads::Threads SDL2::SDL2)
include(${CMAKE_SOURCE_DIR}/.github/macos/apple_bundle.cmake)
endif()
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
find_package(SDL2 REQUIRED)
find_package(X11 REQUIRED)
add_compile_definitions("RT64_SDL_WINDOW_VULKAN")
# Generate icon_bytes.c from the app icon PNG.
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.c ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.h
COMMAND file_to_c ${CMAKE_SOURCE_DIR}/icons/512.png icon_bytes ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.c ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.h
COMMAND file_to_c ${CMAKE_SOURCE_DIR}/icons/512.png icon_bytes ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.c ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.h
DEPENDS ${CMAKE_SOURCE_DIR}/icons/512.png
)
target_sources(Zelda64Recompiled PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.c)
@@ -235,14 +323,6 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux")
target_include_directories(Zelda64Recompiled PRIVATE ${SDL2_INCLUDE_DIRS})
message(STATUS "X11_FOUND = ${X11_FOUND}")
message(STATUS "X11_Xrandr_FOUND = ${X11_Xrandr_FOUND}")
message(STATUS "X11_INCLUDE_DIR = ${X11_INCLUDE_DIR}")
message(STATUS "X11_LIBRARIES = ${X11_LIBRARIES}")
target_include_directories(Zelda64Recompiled PRIVATE ${X11_INCLUDE_DIR} ${X11_Xrandr_INCLUDE_PATH})
target_link_libraries(Zelda64Recompiled PRIVATE ${X11_LIBRARIES} ${X11_Xrandr_LIB})
find_package(Freetype REQUIRED)
message(STATUS "FREETYPE_FOUND = ${FREETYPE_FOUND}")
@@ -250,7 +330,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux")
message(STATUS "FREETYPE_LIBRARIES = ${FREETYPE_LIBRARIES}")
include_directories(${FREETYPE_LIBRARIES})
target_link_libraries(Zelda64Recompiled PRIVATE ${FREETYPE_LIBRARIES})
target_link_libraries(Zelda64Recompiled PRIVATE ${FREETYPE_LIBRARIES} SDL2::SDL2)
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
@@ -262,12 +342,11 @@ endif()
target_link_libraries(Zelda64Recompiled PRIVATE
PatchesLib
RecompiledFuncs
SDL2
librecomp
ultramodern
rt64
RmlCore
RmlDebugger
RmlUi::Core
RmlUi::Debugger
nfd
lunasvg
)
@@ -287,15 +366,17 @@ if (${WIN32})
set (DXC "${PROJECT_SOURCE_DIR}/lib/rt64/src/contrib/dxc/bin/x64/dxc.exe")
add_compile_definitions(NOMINMAX)
else()
if(CMAKE_SIZEOF_VOID_P EQUAL 8 AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64")
if (APPLE)
set (DXC "DYLD_LIBRARY_PATH=${PROJECT_SOURCE_DIR}/lib/rt64/src/contrib/dxc/lib/x64" "${PROJECT_SOURCE_DIR}/lib/rt64/src/contrib/dxc/bin/x64/dxc")
if (APPLE)
# Apple's binary is universal, so it'll work on both x86_64 and arm64
set (DXC "DYLD_LIBRARY_PATH=${PROJECT_SOURCE_DIR}/lib/rt64/src/contrib/dxc/lib/arm64" "${PROJECT_SOURCE_DIR}/lib/rt64/src/contrib/dxc/bin/arm64/dxc-macos")
if(CMAKE_SIZEOF_VOID_P EQUAL 8 AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64")
set(SPIRVCROSS "DYLD_LIBRARY_PATH=${PROJECT_SOURCE_DIR}/lib/rt64/src/contrib/spirv-cross/lib/x64" "${PROJECT_SOURCE_DIR}/lib/rt64//src/contrib/spirv-cross/bin/x64/spirv-cross")
else()
set (DXC "LD_LIBRARY_PATH=${PROJECT_SOURCE_DIR}/lib/rt64/src/contrib/dxc/lib/x64" "${PROJECT_SOURCE_DIR}/lib/rt64/src/contrib/dxc/bin/x64/dxc")
set(SPIRVCROSS "DYLD_LIBRARY_PATH=${PROJECT_SOURCE_DIR}/lib/rt64/src/contrib/spirv-cross/lib/arm64" "${PROJECT_SOURCE_DIR}/lib/rt64//src/contrib/spirv-cross/bin/x64/spirv-cross")
endif()
else()
if (APPLE)
set (DXC "DYLD_LIBRARY_PATH=${PROJECT_SOURCE_DIR}/lib/rt64/src/contrib/dxc/lib/arm64" "${PROJECT_SOURCE_DIR}/lib/rt64/src/contrib/dxc/bin/arm64/dxc-macos")
if(CMAKE_SIZEOF_VOID_P EQUAL 8 AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64")
set (DXC "LD_LIBRARY_PATH=${PROJECT_SOURCE_DIR}/lib/rt64/src/contrib/dxc/lib/x64" "${PROJECT_SOURCE_DIR}/lib/rt64/src/contrib/dxc/bin/x64/dxc-linux")
else()
set (DXC "LD_LIBRARY_PATH=${PROJECT_SOURCE_DIR}/lib/rt64/src/contrib/dxc/lib/arm64" "${PROJECT_SOURCE_DIR}/lib/rt64/src/contrib/dxc/bin/arm64/dxc-linux")
endif()
@@ -303,9 +384,29 @@ else()
endif()
build_vertex_shader(Zelda64Recompiled "shaders/InterfaceVS.hlsl" "shaders/InterfaceVS.hlsl")
build_pixel_shader (Zelda64Recompiled "shaders/InterfacePS.hlsl" "shaders/InterfacePS.hlsl")
build_pixel_shader(Zelda64Recompiled "shaders/InterfacePS.hlsl" "shaders/InterfacePS.hlsl")
# Embed all .nrm files in the "mods" directory
file(GLOB NRM_FILES "${CMAKE_SOURCE_DIR}/mods/*.nrm")
set(GENERATED_NRM_SOURCES "")
foreach(NRM_FILE ${NRM_FILES})
get_filename_component(NRM_NAME ${NRM_FILE} NAME_WE)
set(OUT_C "${CMAKE_CURRENT_BINARY_DIR}/mods/${NRM_NAME}.c")
set(OUT_H "${CMAKE_CURRENT_BINARY_DIR}/mods/${NRM_NAME}.h")
add_custom_command(
OUTPUT ${OUT_C} ${OUT_H}
COMMAND file_to_c ${NRM_FILE} ${NRM_NAME} ${OUT_C} ${OUT_H}
DEPENDS ${NRM_FILE}
)
list(APPEND GENERATED_NRM_SOURCES ${OUT_C})
endforeach()
target_sources(Zelda64Recompiled PRIVATE ${GENERATED_NRM_SOURCES})
target_sources(Zelda64Recompiled PRIVATE ${SOURCES})
set_property(TARGET Zelda64Recompiled PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")

View File

@@ -1,10 +1,28 @@
# Zelda 64: Recompiled
Zelda 64: Recompiled is a project that uses [N64: Recompiled](https://github.com/Mr-Wiseguy/N64Recomp) to **statically recompile** Majora's Mask (and soon Ocarina of Time) into a native port with many new features and enhancements. This project uses [RT64](https://github.com/rt64/rt64) as the rendering engine to provide some of these enhancements.
Zelda 64: Recompiled is a project that uses [N64: Recompiled](https://github.com/Mr-Wiseguy/N64Recomp) to **statically recompile** Majora's Mask (and soon Ocarina of Time) into a native port with many new features, enhancements, and extensive mod support. This project uses [RT64](https://github.com/rt64/rt64) as the rendering engine to provide graphical enhancements.
### [Check out the latest release here](https://github.com/Mr-Wiseguy/Zelda64Recomp/releases/latest).
Join the [N64: Recompiled Community Discord](https://discord.gg/AWZThJ4dPf) to discuss this and other N64: Recompiled projects!
[![Discord Invitation](https://discordapp.com/api/guilds/1374083583739826328/widget.png?style=banner2 'N64 Recomp')](https://discord.gg/AWZThJ4dPf)
### **This repository and its releases do not contain game assets. The original game is required to build or run this project.**
<div align="left" valign="middle">
<a href="https://runblaze.dev">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://www.runblaze.dev/logo_dark.png">
<img align="right" src="https://www.runblaze.dev/logo_light.png" height="102px"/>
</picture>
</a>
<br style="display: none;"/>
_Special thanks to [Blaze](https://runblaze.dev) for their support of this project. They provide high-performance Linux (AMD64 & ARM64) and Apple Silicon macOS runners for GitHub Actions, greatly reducing our automated build times._
</div>
## Table of Contents
* [System Requirements](#system-requirements)
* [Features](#features)
@@ -13,6 +31,7 @@ Zelda 64: Recompiled is a project that uses [N64: Recompiled](https://github.com
* [Easy-to-Use Menus](#easy-to-use-menus)
* [High Framerate Support](#high-framerate-support)
* [Widescreen and Ultrawide Support](#widescreen-and-ultrawide-support)
* [Mod Support](#mod-support)
* [Dual Analog Camera](#dual-analog-camera)
* [Gyro Aim](#gyro-aim)
* [Additional Control Options](#additional-control-options)
@@ -27,12 +46,13 @@ Zelda 64: Recompiled is a project that uses [N64: Recompiled](https://github.com
* [Libraries Used and Projects Referenced](#libraries-used-and-projects-referenced)
## System Requirements
A GPU supporting Direct3D 12.0 (Shader Model 6) or Vulkan 1.2 is required to run this project. The oldest GPUs that should be supported for each vendor are:
A GPU supporting Direct3D 12.0 (Shader Model 6), Vulkan 1.2, or Metal Argument Buffers Tier 2 support is required to run this project. The oldest GPUs that should be supported for each vendor are:
* GeForce GT 630
* Radeon HD 7750 (the one from 2012, not to be confused with the RX 7000 series) and newer
* Intel HD 510 (Skylake)
* A Mac with Apple Silicon or an Intel 7th Gen CPU with MacOS 13.0+
A CPU supporting the AVX instruction set is also required (Intel Core 2000 series or AMD Bulldozer and newer).
On x86-64 PCs, a CPU supporting the SSE4.1 instruction set is also required (Intel Core 2 Penryn series or AMD Bulldozer and newer). ARM64 builds will work on any ARM64 CPU.
If you have issues with crashes on startup, make sure your graphics drivers are fully up to date.
@@ -57,6 +77,13 @@ Any aspect ratio is supported, with most effects modded to work correctly in wid
**Note**: Some animation quirks can be seen at the edges of the screen in certain cutscenes when using very wide aspect ratios.
#### Mod Support
Install community made mods and texture packs! Mods can change any part of the game, including adding completely new features and content. You can install mods by simply dragging the mod files onto the game window before starting the game or by clicking the **Install Mods** button in the mod menu. Mods can be toggled in the mod menu, and some mods can be configured there as well.
Many mods are available on the project's Thunderstore page: https://thunderstore.io/c/zelda-64-recompiled/. The Thunderstore mod manager/r2modman is not required or supported, so be sure to click the "Manual Download" button when downloading a mod instead of the "Install with Mod Manager" button.
If you're interested in making mods for this project, check out [the mod template](https://github.com/Zelda64Recomp/MMRecompModTemplate) and [the modding documentation](https://hackmd.io/fMDiGEJ9TBSjomuZZOgzNg). If you're interested in making texture packs, check out [the RT64 documentation](https://github.com/rt64/rt64/blob/main/TEXTURE-PACKS.md).
#### Dual Analog Camera
Play with a dual analog control layout like later entries in the series! When this option is enabled, the right stick will control the camera. You can still have the C-Buttons mapped to the right stick if you so wish, so long as you also map them to other buttons on the controller. The right stick C-button inputs will be "silenced", except when you take out the ocarina, so you can still play the ocarina with the right stick.
@@ -78,16 +105,14 @@ This project has been optimized to have as little input lag as possible, making
Saving and loading files, going from place to place, and pausing all happen in the blink of an eye thanks to the game running natively on modern hardware.
#### Linux and Steam Deck Support
A Linux binary is available for playing on most up-to-date distros, including on the Steam Deck.
A Linux binary as well as a Flatpak is available for playing on most up-to-date distros, including on the Steam Deck.
To play on Steam Deck, extract the Linux build onto your deck. Then, in desktop mode, right click the Zelda64Recompiled executable file and select "Add to Steam". From there, you can return to Gaming mode and configure the controls as needed. See the [Steam Deck gyro aim FAQ section](#how-do-i-set-up-gyro-aiming-on-steam-deck) for more detailed instructions.
## Planned Features
* Ocarina of Time support
* Mod support and Randomizer
* Texture Packs
* Model Replacements
* Ray Tracing (via RT64)
* Ray Tracing and Higher Quality Model Replacements (via RT64)
* Multi-language support with support for loading custom translations
## FAQ
@@ -107,15 +132,20 @@ You'll probably also want to change the default behavior so that you don't need
#### Where is the savefile stored?
- Windows: `%LOCALAPPDATA%\Zelda64Recompiled\saves`
- Linux: `~/.config/Zelda64Recompiled/saves`
- macOS: `~/Library/Application Support/Zelda64Recompiled/saves`
#### How do I choose a different ROM?
**You don't.** This project is **only** a port of Majora's Mask (and Ocarina of Time in the future), and it will only accept one specific ROM: the US version of the N64 release of Majora's Mask. ROMs in formats other than .z64 will be automatically converted, as long as it is the correct ROM. **It is not an emulator and it cannot run any arbitrary ROM.**
**You don't.** This project is **only** a port of Majora's Mask (and Ocarina of Time in the future), and it will only accept one specific ROM: the US version of the N64 release of Majora's Mask. ROMs in formats other than .z64 will be automatically converted, as long as it is the correct ROM. **It is not an emulator and it cannot run any arbitrary ROM.**
If you want to play a modded ROM or in another language, note that support for modding and other languages will be added to the project itself in the future and will not rely on you supplying a different ROM.
Instead, you can change the game by installing mods. See the [mod support](#mod-support) section for details.
#### Does this project have a randomizer?
Yes, there is a randomizer available as a mod for this project which can be found at https://github.com/RecompRando/MMRecompRando/releases/latest. Simply download MMRecompRando.zip from the releases and install it like any other mod.
#### Can you run this project as a portable application?
Yes, if you place a file named `portable.txt` in the same folder as the executable then this project will run in portable mode. In portable mode, the save files, config files, and mods are placed in the same folder as the executable.
## Known Issues
* Intel GPUs on Linux may not currently work. If you have experience with Vulkan development on Linux, help here would be greatly appreciated!
* The prebuilt Linux binary may not work correctly on some distributions of Linux. If you encounter such an issue, building the project locally yourself is recommended. A Flatpak or AppImage may be provided in the future to solve this issue. Adding the Linux version to Steam and setting "Steam Linux Runtime" as the compatibility tool or launching it via Gamescope may work around the issue. Alternatively, running the Windows version with Proton is known to work well and may also work around this issue.
* Overlays such as MSI Afterburner and other software such as Wallpaper Engine can cause performance issues with this project that prevent the game from rendering correctly. Disabling such software is recommended.
## Building
@@ -130,5 +160,3 @@ Building is not required to play this project, as prebuilt binaries (which do no
* [Gamepad Motion Helpers](https://github.com/JibbSmart/GamepadMotionHelpers) for sensor fusion and calibration algorithms to implement gyro aiming
* [Majora's Mask Decompilation](https://github.com/zeldaret/mm) for headers and some function definitions, used for making patches or some enhancements
* [Ares emulator](https://github.com/ares-emulator/ares) for RSP vector instruction reference implementations, used in RSP recompilation
Special thanks to [thecozies](https://github.com/thecozies) for designing and helping implement the launcher and config menus!

1
Zelda64RecompSyms Submodule

Submodule Zelda64RecompSyms added at a325f8fa5c

View File

@@ -1,30 +0,0 @@
<template name="prompt">
<head>
</head>
<body class="prompt">
<div class="prompt__overlay" />
<div class="prompt__content-wrapper" data-if="promptOpen">
<div class="prompt__content">
<h3>{{ promptHeader }}</h3>
<p>{{ promptContent }}</p>
<div class="prompt__controls">
<button
autofocus="true"
id="prompt__confirm-button"
class="button button--success"
style="nav-left: none; nav-right: #prompt__cancel-button"
>
<div class="button__label" id="prompt__confirm-button-label">{{ promptConfirmLabel }}</div>
</button>
<button
id="prompt__cancel-button"
class="button button--error"
style="nav-right: none; nav-left: #prompt__confirm-button"
>
<div class="button__label" id="prompt__cancel-button-label">{{ promptCancelLabel }}</div>
</button>
</div>
</div>
</div>
</body>
</template>

View File

@@ -20,19 +20,19 @@
}
.col {
flex: 1;
text-align: center;
text-align: center;
}
</style>
<link type="text/template" href="config_menu/general.rml" />
<link type="text/template" href="config_menu/controls.rml" />
<link type="text/template" href="config_menu/graphics.rml" />
<link type="text/template" href="config_menu/sound.rml" />
<link type="text/template" href="config_menu/mods.rml" />
<link type="text/template" href="config_menu/debug.rml" />
<link type="text/template" href="components/prompt.rml" />
</head>
<body class="window">
<!-- <handle move_target="#document"> -->
<div id="window" class="rmlui-window rmlui-window--hidden" style="display:flex; flex-flow: column; background-color:rgba(0,0,0,0)" onkeydown="config_keydown">
<div id="window" class="rmlui-window" style="display:flex; flex-flow: column; background-color:rgba(0,0,0,0)" onkeydown="config_keydown">
<div class="centered-page" onclick="close_config_menu_backdrop">
<div class="centered-page__modal">
<tabset class="tabs" id="config_tabset">
@@ -64,6 +64,13 @@
<panel class="config" data-model="sound_options_model">
<template src="config-menu__sound" />
</panel>
<tab class="tab" id="tab_mods">
<div>Mods</div>
<div class="tab__indicator"></div>
</tab>
<panel class="config">
<template src="config-menu__mods" />
</panel>
<tab class="tab" data-model="debug_model" data-if="debug_enabled" id="tab_debug">
<div>Debug</div>
<div class="tab__indicator"></div>
@@ -109,19 +116,6 @@
<label><span style="font-family:promptfont;">&#x21A7;</span> Accept</label> -->
</div>
</div>
<div
id="prompt-root"
data-model="prompt_model"
data-if="prompt__open"
data-alias-promptOpen="prompt__open"
data-alias-promptHeader="prompt__header"
data-alias-promptContent="prompt__content"
data-alias-promptConfirmLabel="prompt__confirmLabel"
data-alias-promptCancelLabel="prompt__cancelLabel"
data-event-click="prompt__on_click"
>
<template src="prompt"/>
</div>
</div>
<!-- </handle> -->
<!-- <handle size_target="#document" style="position: absolute; width: 16dp; height: 16dp; bottom: 0px; right: 0px; cursor: resize;"></handle> -->

View File

@@ -41,7 +41,7 @@
data-for="input_bindings, i : inputs.array"
data-event-mouseover="set_input_row_focus(i)"
data-class-control-option--active="get_input_enum_name(i)==cur_input_row"
data-if="!input_device_is_keyboard || get_input_enum_name(i) != 'TOGGLE_MENU'"
data-if="!input_device_is_keyboard || (get_input_enum_name(i) != 'TOGGLE_MENU' && get_input_enum_name(i) != 'ACCEPT_MENU' && get_input_enum_name(i) != 'APPLY_MENU')"
>
<label
class="control-option__label"
@@ -67,14 +67,25 @@
</button>
</div>
<button
data-if="get_input_enum_name(i) != 'TOGGLE_MENU' && get_input_enum_name(i) != 'ACCEPT_MENU'"
data-event-blur="set_input_row_focus(-1)"
data-event-focus="set_input_row_focus(i)"
data-event-click="clear_input_bindings(i)"
class="icon-button icon-button--danger"
class="icon-button icon-button--error"
data-attr-style="i == 0 ? 'nav-up:#cont_kb_toggle' : 'nav-up:auto'"
>
<svg src="icons/Trash.svg" />
</button>
<button
data-if="get_input_enum_name(i) == 'TOGGLE_MENU' || get_input_enum_name(i) == 'ACCEPT_MENU'"
data-event-blur="set_input_row_focus(-1)"
data-event-focus="set_input_row_focus(i)"
data-event-click="reset_single_input_binding_to_default(i)"
class="icon-button icon-button--error"
data-attr-style="i == 0 ? 'nav-up:#cont_kb_toggle' : 'nav-up:auto'"
>
<svg src="icons/Reset.svg" />
</button>
</div>
</div>
</div>

View File

@@ -316,7 +316,7 @@
id="apply_button"
style="nav-up:#hr_original"
>
<div class="button__label">Apply <span class="prompt-font-sm">{{gfx_help__apply}}</span></div>
<div class="button__label">Apply<span class="prompt-font-sm">{{gfx_help__apply}}</span></div>
</button>
</div>
</div>

View File

@@ -0,0 +1,9 @@
<template name="config-menu__mods">
<head>
</head>
<body>
<form class="config__form">
<recomp-mod-menu id="menu_mods" />
</form>
</body>
</template>

View File

@@ -0,0 +1,70 @@
<rml>
<head>
<link type="text/rcss" href="rml.rcss"/>
<link type="text/rcss" href="recomp.rcss"/>
<title>Inventory</title>
<style>
body
{
width: 100%;
height: 100%;
}
/* Hide the window icon. */
div#title_bar div#icon
{
display: none;
}
.flex-grid {
display: flex;
}
.col {
flex: 1;
text-align: center;
}
</style>
</head>
<body class="window">
<!-- <handle move_target="#document"> -->
<div id="window" class="rmlui-window" style="display:flex; flex-flow: column; background-color:rgba(0,0,0,0)" onkeydown="config_keydown">
<div class="centered-page" onclick="close_config_menu_backdrop">
<div class="centered-page__modal">
<div class="config__icon-buttons">
<button
class="icon-button"
onclick="open_quit_game_prompt"
id="config__quit-game-button"
>
<svg src="icons/Quit.svg" />
</button>
<button
class="icon-button"
onclick="close_config_menu"
id="config__close-menu-button"
>
<svg src="icons/X.svg" />
</button>
</div>
<recomp-config-sub-menu id="config_sub_menu" />
</div>
<div
class="centered-page__controls"
data-model="nav_help_model"
>
<label>
<span>Navigate</span>
<span class="prompt-font-sm">{{nav_help__navigate}}</span>
</label>
<label>
<span>Accept</span>
<span class="prompt-font-sm">{{nav_help__accept}}</span>
</label>
<label>
<span>Exit</span>
<span class="prompt-font-sm">{{nav_help__exit}}</span>
</label>
</div>
</div>
</div>
</body>
</rml>

5
assets/icons/Reset.svg Normal file
View File

@@ -0,0 +1,5 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M27.6949 10.3162C25.5859 5.98481 21.1415 3 16 3C8.8203 3 3 8.8203 3 16C3 23.1797 8.8203 29 16 29C21.1415 29 25.5859 26.0152 27.6949 21.6838" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round"/>
<path d="M29.052 11.0519V5.0519" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M29.052 11.0519H23.052" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 555 B

View File

@@ -51,6 +51,10 @@
<div class="menu-list-item__bullet">•</div>
<div class="menu-list-item__label">Settings</div>
</button>
<button onclick="open_mods" class="menu-list-item menu-list-item--right">
<div class="menu-list-item__bullet">•</div>
<div class="menu-list-item__label">Mods</div>
</button>
<button onclick="exit_game" class="menu-list-item menu-list-item--right">
<div class="menu-list-item__bullet">•</div>
<div class="menu-list-item__label">Exit</div>
@@ -58,7 +62,7 @@
</div>
</div>
<div class="bottom-left">
<label>{{version_number}}</label>
<label>v{{version_number}}</label>
</div>
</div>
</div>

File diff suppressed because one or more lines are too long

View File

@@ -307,11 +307,11 @@
"dev": true
},
"node_modules/braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dependencies": {
"fill-range": "^7.0.1"
"fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
@@ -638,9 +638,9 @@
}
},
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dependencies": {
"to-regex-range": "^5.0.1"
},
@@ -1192,12 +1192,12 @@
}
},
"node_modules/micromatch": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
"dependencies": {
"braces": "^3.0.2",
"braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"engines": {

View File

@@ -99,159 +99,3 @@
flex-direction: row;
}
.config-option {
display: flex;
flex: 1;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
margin: space(16) space(0) space(24);
&:last-child {
margin-top: space(16);
}
}
.config-option__title {
@extend %label-md;
padding: 0 space(12);
}
.config-option__list {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
width: 100%;
height: auto;
padding: 0;
input:first-of-type {
nav-left: none;
}
input:last-of-type {
nav-right: none;
}
.config-option__tab-label {
@extend %label-sm;
@include trans-colors-opa;
display: block;
position: relative;
height: auto;
margin: space(4) space(12) 0;
padding: space(8) 0;
color: $color-text-inactive;
tab-index: none;
&:hover {
color: $color-text;
cursor: pointer;
}
}
input.radio {
@extend %nav-all;
@include trans-colors-opa;
visibility: visible;
width: 0;
height: 0;
&:not(:disabled) {
&:checked + .config-option__tab-label {
border-bottom: 1dp;
border-color: $color-text;
color: $color-text;
&:hover {
cursor: default;
}
}
.rmlui-window:not([mouse-active]) &:focus + .config-option__tab-label {
transition: none;
animation: $focus-anim-border;
border-color: $color-secondary;
color: $color-secondary;
}
&:focus + .config-option__tab-label,
&:hover + .config-option__tab-label {
color: $color-text;
}
}
&:disabled + .config-option__tab-label {
opacity: 0.5;
&:hover {
cursor: default;
}
}
}
input.range slidertrack {
@include trans-colors;
height: 2dp;
margin-top: space(8);
background-color: $color-border;
}
input.range sliderbar {
@include trans-colors;
width: space(16);
height: space(16);
margin-top: space(1);
margin-right: space(-8);
margin-left: space(-8);
transition: background-color $transition-quick;
border-radius: 8dp;
background-color: $color-text-dim;
.rmlui-window:not([mouse-active]) &:focus {
@include border($color-a);
animation: $focus-anim-bg;
}
&:hover {
background-color: $color-text;
cursor: pointer;
}
}
input.range sliderbar:active,
input.range slidertrack:active + sliderbar {
background-color: $color-secondary;
}
input.range sliderarrowdec,
input.range sliderarrowinc {
display: none;
}
}
.config-option__details {
@extend %label-xs;
height: space(18);
margin: space(14) space(12) 0;
color: $color-primary;
}
.config-option__range-wrapper {
max-width: space(360);
margin-top: space(4);
}
.config-option__range-label {
@extend %label-sm;
display: block;
// flex: 0 0 space(32);
width: space(56);
margin: 0 12dp;
margin-right: space(16);
padding: 0;
color: $color-text;
tab-index: none;
}

View File

@@ -0,0 +1,27 @@
.config-description {
flex: 1 1 100%;
width: auto;
height: auto;
padding: space(16);
border-radius: 0dp;
border-bottom-right-radius: $border-radius-modal;
border-bottom-left-radius: $border-radius-modal;
background-color: $color-bg-shadow;
text-align: left;
&__contents {
@extend %body;
padding: space(16);
line-height: space(28);
white-space: pre-line;
b {
color: $color-primary;
}
i {
color: $color-warning;
font-style: normal;
}
}
}

View File

@@ -0,0 +1,29 @@
.config-group {
position: relative;
&--scrollable {
flex: 1 1 100%;
width: auto;
height: auto;
padding: 0 0 0 space(16);
.config-group__wrapper {
max-height: 100%;
overflow-y: auto;
}
}
&__title {
@extend %label-md;
color: $color-primary;
&--hidden {
display: none;
}
}
&__wrapper {
padding: space(16) 0;
}
}

View File

@@ -0,0 +1,413 @@
.config-option {
display: flex;
flex: 1;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
margin: space(16) space(0) space(24);
&--hz {
flex-direction: row-reverse;
align-items: center;
margin-top: space(4);
margin-bottom: space(4);
.config-option__title {
@extend %label-md;
flex: 1 1 100%;
}
.config-option__list {
flex: 1 1 auto;
width: auto;
}
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
}
}
.config-option__title {
@extend %label-md;
padding: 0 space(12);
}
.config-option__radio-tabs,
.config-option__list {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
width: 100%;
height: auto;
padding: 0;
input:first-of-type {
nav-left: none;
}
input:last-of-type {
nav-right: none;
}
.config-option__tab-label {
@extend %label-sm;
@include trans-colors-opa;
display: block;
position: relative;
height: auto;
margin: space(4) space(12) 0;
padding: space(8) 0;
color: $color-text-inactive;
tab-index: none;
&:hover {
color: $color-text;
cursor: pointer;
}
}
.config-option__checkbox-wrapper {
@include trans-colors-opa;
width: space(32);
height: space(32);
margin: space(4) space(12) 0;
border-radius: $border-radius-sm;
opacity: 0.5;
background-color: $color-bg-overlay;
cursor: pointer;
&:hover {
opacity: 1;
}
&[checked] {
background-color: $color-a;
}
}
.config-option__checkbox {
@extend %nav-all;
@include trans-colors-opa;
visibility: visible;
width: 0;
height: 0;
}
// TODO: Remove & Replace old stylings
input.radio {
@extend %nav-all;
@include trans-colors-opa;
visibility: visible;
width: 0;
height: 0;
&:not(:disabled) {
&:checked + .config-option__tab-label {
border-bottom: 1dp;
border-color: $color-text;
color: $color-text;
&:hover {
cursor: default;
}
}
.rmlui-window:not([mouse-active]) &:focus + .config-option__tab-label {
transition: none;
animation: $focus-anim-border;
border-color: $color-secondary;
color: $color-secondary;
}
&:focus + .config-option__tab-label,
&:hover + .config-option__tab-label {
color: $color-text;
}
}
&:disabled + .config-option__tab-label {
opacity: 0.5;
&:hover {
cursor: default;
}
}
}
input.range slidertrack {
@include trans-colors;
height: 2dp;
margin-top: space(8);
background-color: $color-border;
}
input.range sliderbar {
@include trans-colors;
width: space(16);
height: space(16);
margin-top: space(1);
margin-right: space(-8);
margin-left: space(-8);
transition: background-color $transition-quick;
border-radius: 8dp;
background-color: $color-text-dim;
.rmlui-window:not([mouse-active]) &:focus {
@include border($color-a);
animation: $focus-anim-bg;
}
&:hover {
background-color: $color-text;
cursor: pointer;
}
}
input.range sliderbar:active,
input.range slidertrack:active + sliderbar {
background-color: $color-secondary;
}
input.range sliderarrowdec,
input.range sliderarrowinc {
display: none;
}
}
.config-option__details {
@extend %label-xs;
height: space(18);
margin: space(14) space(12) 0;
color: $color-primary;
}
.config-option-color {
width: 100%;
max-width: space(360);
height: auto;
margin-top: space(4);
margin-left: space(12);
padding: 0;
&__preview-wrapper {
display: flex;
flex-direction: row;
width: 100%;
height: space(8 * 9);
}
&__preview-block {
display: block;
width: space(8 * 11);
height: 100%;
border-width: $border-width-thickness;
border-radius: $border-radius-lg;
border-color: $color-border;
}
&__hsv-wrapper {
display: flex;
flex: 1 1 100%;
flex-direction: column;
width: auto;
height: auto;
padding-left: space(8);
.config-option-range {
flex: 1 1 auto;
label {
min-width: space(72);
}
input {
flex: 1 1 auto;
}
}
}
}
.config-option-range {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
width: 100%;
max-width: space(360);
height: auto;
margin-top: space(4);
padding: 0;
&__label {
@extend %label-sm;
display: block;
width: space(56);
margin: 0 12dp;
margin-right: space(16);
padding: 0;
color: $color-text;
tab-index: none;
}
&__range-input {
flex: 1;
slidertrack {
@include trans-colors;
height: 2dp;
margin-top: space(8);
background-color: $color-border;
}
sliderbar {
@include trans-colors;
width: space(16);
height: space(16);
margin-top: space(1);
margin-right: space(-8);
margin-left: space(-8);
transition: background-color $transition-quick;
border-radius: 8dp;
background-color: $color-text-dim;
.rmlui-window:not([mouse-active]) &:focus {
@include border($color-a);
animation: $focus-anim-bg;
}
&:hover {
background-color: $color-text;
cursor: pointer;
}
}
sliderbar:active,
slidertrack:active + sliderbar {
background-color: $color-secondary;
}
sliderarrowdec,
sliderarrowinc {
display: none;
}
}
}
.config-option__range-wrapper {
max-width: space(360);
margin-top: space(4);
}
.config-option__range-label {
@extend %label-sm;
display: block;
// flex: 0 0 space(32);
width: space(56);
margin: 0 12dp;
margin-right: space(16);
padding: 0;
color: $color-text;
tab-index: none;
}
.config-option-dropdown, .config-option-textfield {
display: flex;
position: relative;
flex: 1 1 100%;
flex-direction: row;
align-items: center;
justify-content: flex-start;
width: auto;
height: auto;
padding: space(8) space(24) space(8) space(12);
&__select {
display: block;
height: space(48);
padding: space(14);
cursor: pointer;
}
&__wrapper {
// Cursed guess & check so that this appears to be the same height as the select
$extra-space: 2;
display: flex;
align-items: center;
justify-content: flex-start;
width: 100%;
height: auto;
padding: space(0 + $extra-space) 0 space(10 + $extra-space);
cursor: text;
input {
width: 100%;
height: auto;
vertical-align: middle;
}
}
&__select, &__wrapper {
@extend %body;
@extend %nav-all;
@include trans-colors-border;
@include border($color-white-a50);
position: relative;
box-sizing: border-box;
flex: 1 1 100%;
width: auto;
border-radius: $border-radius-md;
background-color: $color-white-a5;
&:hover, &:focus {
@include border($color-white-a80);
background-color: $color-white-a20;
}
selectvalue {
display: inline;
height: auto;
margin: auto 0;
}
selectbox {
@include border($color-border);
margin-top: space(2);
padding: space(4) 0;
border-radius: $border-radius-md;
background-color: $color-background-3;
option {
@extend %nav-all;
@include trans-colors;
padding: space(8) space(12);
background-color: $color-transparent;
color: $color-text-dim;
font-weight: 400;
&:hover, &:focus {
background-color: $color-white-a20;
}
&:hover:not(:checked) {
cursor: pointer;
}
&:checked {
background-color: $color-white-a5;
color: $color-white;
}
}
}
}
}

View File

@@ -2,7 +2,7 @@
/*
<button
class="icon-button icon-button--danger"
class="icon-button icon-button--error"
>
<svg src="icons/Trash.svg" />
</button>
@@ -82,7 +82,7 @@ $icon-button-size: 56 - ($border-width-thickness-num * 2);
@include create-icon-button-variation($color-success);
}
&--danger {
&--error {
@include create-icon-button-variation($color-error);
}

View File

@@ -342,6 +342,8 @@ $stick-size: 200;
flex-direction: row;
justify-content: space-between;
width: space(268);
// WORKAROUND FIX: prevents RMLui assert error
min-width: 1dp;
height: space(128);
margin-right: space(10);
}

View File

@@ -20,7 +20,6 @@
position: relative;
margin: 0;
padding: space(20) space(24);
transition: color $transition-quick;
opacity: 0.9;
background-color: rgba(0,0,0,0);
color: $color-text-inactive;

View File

@@ -2,6 +2,9 @@
@import "./ControlOption";
@import "./Tabs";
@import "./Config";
@import "./ConfigGroup";
@import "./ConfigOption";
@import "./ConfigDescription";
@import "./InputConfig";
@import "./Button";
@import "./IconButton";

View File

@@ -189,8 +189,7 @@ select {
// background: rgb(150,150,150)
// }
input.radio,
input.checkbox {
input.radio {
flex: 0;
width:0dp;
nav-up:auto;
@@ -200,3 +199,14 @@ input.checkbox {
tab-index:auto;
focus:auto;
}
input.checkbox {
width: space(20);
height: space(20);
nav-up:auto;
nav-right:auto;
nav-down:auto;
nav-left:auto;
tab-index:auto;
focus:auto;
}

View File

@@ -0,0 +1,38 @@
{
"consumables": "Consumables",
"consumables/infinite_magic": "Infinite Magic",
"consumables/infinite_magic:description": "You get sooo much magic lol!!",
"consumables/infinite_rupees": "Infinite Rupees",
"consumables/infinite_arrows": "Infinite Arrows",
"consumables/infinite_bombs": "Infinite Bombs",
"consumables/infinite_health": "Infinite Health",
"consumable_actions": "Consumable Actions",
"consumable_actions/refill_all": "primary",
"consumable_actions/refill_all:description": "Refills anything that can be refilled, like magic, rupees, arrows, bombs, health, etc.",
"gameplay": "Gameplay",
"gameplay/movement": "Movement",
"gameplay/movement/L_for_fast": "Hold L to move fast",
"gameplay/movement/L_for_fast/values/off": "Off",
"gameplay/movement/L_for_fast/values/x2": "X2",
"gameplay/movement/L_for_fast/values/x4": "X4",
"gameplay/movement/L_for_fast/values/x6": "X6",
"gameplay/movement/L_for_fast2": "Hold L to move fast the sequel",
"gameplay/movement/L_for_fast2/values/off": "Off",
"gameplay/movement/L_for_fast2/values/x2": "X2",
"gameplay/movement/L_for_fast2/values/x4": "X4",
"gameplay/movement/L_for_fast2/values/x6": "X6",
"gameplay/movement/L_to_levitate": "Hold L to levitate",
"gameplay/movement/always_quickspin": "Always quickspin",
"gameplay/movement/always_quickspin:description": "Always <b>quickspin</b> whenever using your <i>sword</i> and in a <i>state</i> where <i>you</i> can <b>quickspin.</b><br /><br />yeah...",
"gameplay/movement/heart_color": "Hearts color",
"gameplay/movement/link_size": "Link's Size",
"gameplay/movement/link_name": "Link's Name",
"gameplay/movement/link_name:description": "Change Link's name to something silly!",
"gameplay/abilities": "Abilities",
"gameplay/abilities/fd_anywhere": "Fierce Deity Anywhere",
"gameplay/abilities/permanent_razor_sword": "Permanent Razor Sword",
"gameplay/abilities/permanent_razor_sword2": "MORE Permanent Razor Sword"
}

135
config_example.cheats.json Normal file
View File

@@ -0,0 +1,135 @@
[
{
"type": "CheckboxGroup",
"key": "consumables",
"toggle": true,
"toggleDefault": false,
"options": [
{
"type": "Checkbox",
"key": "infinite_magic",
"default": false
},
{
"type": "Checkbox",
"key": "infinite_rupees",
"default": false
},
{
"type": "Checkbox",
"key": "infinite_arrows",
"default": false
},
{
"type": "Checkbox",
"key": "infinite_bombs",
"default": false
},
{
"type": "Checkbox",
"key": "infinite_health",
"default": false,
"callback": "on_update_health"
}
]
},
{
"type": "Group",
"key": "consumable_actions",
"options": [
{
"type": "Button",
"key": "refill_all",
"variant": "primary",
"callback": "on_refill_all"
}
]
},
{
"type": "Group",
"key": "gameplay",
"toggle": true,
"toggleDefault": true,
"options": [
{
"type": "Group",
"key": "movement",
"options": [
{
"type": "Dropdown",
"key": "L_for_fast",
"default": "x2",
"values": [
"off",
"x2",
"x4",
"x6"
]
},
{
"type": "RadioTabs",
"key": "L_for_fast2",
"default": "x2",
"values": [
"off",
"x2",
"x4",
"x6"
]
},
{
"type": "Checkbox",
"key": "L_to_levitate",
"default": false
},
{
"type": "Checkbox",
"key": "always_quickspin",
"default": false
},
{
"type": "Color",
"key": "heart_color",
"default": [255, 50, 50]
},
{
"type": "Range",
"key": "link_size",
"default": 100,
"suffix": "%",
"min": 20,
"max": 400,
"step": 20
},
{
"type": "TextField",
"key": "link_name",
"default": "George",
"maxlength": 10
}
]
},
{
"type": "Group",
"key": "abilities",
"options": [
{
"type": "Checkbox",
"key": "fd_anywhere",
"default": true
},
{
"type": "Checkbox",
"key": "permanent_razor_sword",
"default": true
},
{
"type": "Checkbox",
"key": "permanent_razor_sword2",
"default": true
}
]
}
]
}
]

4
flatpak/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
.flatpak-builder
builddir
repo
*.flatpak

15
flatpak/README.md Normal file
View File

@@ -0,0 +1,15 @@
Before building the Flatpak, you must build the patches on the root directory first. **The LLVM Extension for freedesktop does not include the MIPS compiler and will fail to build the patches inside the flatpak**.
```sh
make -C patches CC=clang LD=ld.lld
```
Build
```sh
flatpak-builder --force-clean --user --install-deps-from=flathub --repo=repo --install builddir io.github.zelda64recomp.zelda64recomp.json
```
Bundle
```sh
flatpak build-bundle repo io.github.zelda64recomp.zelda64recomp.flatpak io.github.zelda64recomp.zelda64recomp --runtime-repo=https://flathub.org/repo/flathub.flatpakrepo
```

View File

@@ -0,0 +1,8 @@
[Desktop Entry]
Name=Zelda 64: Recompiled
Exec=/app/bin/Zelda64Recompiled
Type=Application
Icon=io.github.zelda64recomp.zelda64recomp
Categories=Game;
Comment=Static recompilation of Majora's Mask (and soon Ocarina of Time) for PC.
MimeType=x-scheme-handler/zelda64recomp

View File

@@ -0,0 +1,63 @@
{
"id": "io.github.zelda64recomp.zelda64recomp",
"runtime": "org.freedesktop.Platform",
"runtime-version": "23.08",
"sdk": "org.freedesktop.Sdk",
"sdk-extensions" : [ "org.freedesktop.Sdk.Extension.llvm18" ],
"finish-args": [
"--share=network",
"--socket=wayland",
"--socket=fallback-x11",
"--socket=pulseaudio",
"--device=all",
"--filesystem=host",
"--filesystem=/media",
"--filesystem=/run/media",
"--filesystem=/mnt"
],
"modules": [
{
"name": "Zelda64Recompiled",
"buildsystem": "simple",
"build-commands": [
"cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S lib/N64Recomp -B lib/N64Recomp/cmake-build",
"cmake --build lib/N64Recomp/cmake-build --config Release --target N64Recomp --parallel",
"cmake --build lib/N64Recomp/cmake-build --config Release --target RSPRecomp --parallel",
"cp lib/N64Recomp/cmake-build/N64Recomp N64Recomp",
"cp lib/N64Recomp/cmake-build/RSPRecomp RSPRecomp",
"./N64Recomp us.rev1.toml",
"./RSPRecomp aspMain.us.rev1.toml",
"./RSPRecomp njpgdspMain.us.rev1.toml",
"cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -DCMAKE_MAKE_PROGRAM=ninja -DPATCHES_C_COMPILER=clang -DPATCHES_LD=ld.lld -DRECOMP_FLATPAK=ON -G Ninja -S . -B cmake-build",
"cmake --build cmake-build --config Release --target Zelda64Recompiled --parallel",
"rm -rf assets/scss",
"mkdir -p /app/bin",
"cp cmake-build/Zelda64Recompiled /app/bin/Zelda64Recompiled",
"cp recompcontrollerdb.txt /app/bin/recompcontrollerdb.txt",
"cp -R assets /app/bin/assets",
"install -Dm644 icons/512.png /app/share/icons/hicolor/512x512/apps/${FLATPAK_ID}.png",
"install -Dm644 flatpak/io.github.zelda64recomp.zelda64recomp.metainfo.xml /app/share/metainfo/${FLATPAK_ID}.metainfo.xml",
"install -Dm644 flatpak/io.github.zelda64recomp.zelda64recomp.desktop /app/share/applications/${FLATPAK_ID}.desktop"
],
"sources": [
{
"type": "git",
"url": "https://github.com/N64Recomp/N64Recomp.git",
"commit": "989a86b36912403cd323de884bf834f2605ea770",
"dest": "lib/N64Recomp"
},
{
"type": "dir",
"path": "../"
}
],
"build-options": {
"append-path": "/usr/lib/sdk/llvm18/bin",
"prepend-ld-library-path": "/usr/lib/sdk/llvm18/lib",
"build-args": [
"--share=network"
]
}
}
]
}

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>io.github.zelda64recomp.zelda64recomp</id>
<name>Zelda 64: Recompiled</name>
<summary>Static recompilation of Majora's Mask (and soon Ocarina of Time) for PC.</summary>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0+</project_license>
<supports>
<control>pointing</control>
<control>keyboard</control>
<control>touch</control>
<control>gamepad</control>
</supports>
<description>
<p>
Zelda 64: Recompiled is a project that uses N64: Recompiled to statically recompile Majora's Mask (and soon Ocarina of Time) into a native port with many new features and enhancements. This project uses RT64 as the rendering engine to provide some of these enhancements.
The original game is required to run this project.
https://github.com/Zelda64Recomp/Zelda64Recomp
</p>
</description>
<launchable type="desktop-id">io.github.zelda64recomp.zelda64recomp.desktop</launchable>
</component>

10
include/overloaded.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef __OVERLOADED_H__
#define __OVERLOADED_H__
// Helper for std::visit
template<class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
#endif

11
include/recomp_data.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef __RECOMP_DATA_H__
#define __RECOMP_DATA_H__
namespace recomputil {
void init_extended_actor_data();
void reset_actor_data();
void register_data_api_exports();
}
#endif

View File

@@ -9,6 +9,8 @@
#include <string>
#include <string_view>
#include "ultramodern/input.hpp"
#include "json/json.hpp"
namespace recomp {
@@ -38,7 +40,9 @@ namespace recomp {
DEFINE_INPUT(X_AXIS_POS, 0, "Right") \
#define DEFINE_RECOMP_UI_INPUTS() \
DEFINE_INPUT(TOGGLE_MENU, 0, "Toggle Menu")
DEFINE_INPUT(TOGGLE_MENU, 0, "Toggle Menu") \
DEFINE_INPUT(ACCEPT_MENU, 0, "Accept (Menu)") \
DEFINE_INPUT(APPLY_MENU, 0, "Apply (Menu)")
#define DEFINE_ALL_INPUTS() \
DEFINE_N64_BUTTON_INPUTS() \
@@ -86,6 +90,7 @@ namespace recomp {
void cancel_scanning_input();
void config_menu_set_cont_or_kb(bool cont_interacted);
InputField get_scanned_input();
int get_scanned_input_index();
struct DefaultN64Mappings {
std::vector<InputField> a;
@@ -111,9 +116,12 @@ namespace recomp {
std::vector<InputField> analog_down;
std::vector<InputField> toggle_menu;
std::vector<InputField> accept_menu;
std::vector<InputField> apply_menu;
};
constexpr const std::vector<InputField>& get_default_mapping_for_input(const DefaultN64Mappings& defaults, const GameInput input) {
inline const std::vector<InputField>& get_default_mapping_for_input(const DefaultN64Mappings& defaults, const GameInput input) {
static const std::vector<InputField> empty_input_field{};
switch (input) {
case GameInput::A: return defaults.a;
case GameInput::B: return defaults.b;
@@ -134,7 +142,9 @@ namespace recomp {
case GameInput::Y_AXIS_POS: return defaults.analog_up;
case GameInput::Y_AXIS_NEG: return defaults.analog_down;
case GameInput::TOGGLE_MENU: return defaults.toggle_menu;
default: return std::vector<InputField>();
case GameInput::ACCEPT_MENU: return defaults.accept_menu;
case GameInput::APPLY_MENU: return defaults.apply_menu;
default: return empty_input_field;
}
}
@@ -150,10 +160,12 @@ namespace recomp {
InputField& get_input_binding(GameInput input, size_t binding_index, InputDevice device);
void set_input_binding(GameInput input, size_t binding_index, InputDevice device, InputField value);
void get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out);
void set_rumble(bool);
bool get_n64_input(int controller_num, uint16_t* buttons_out, float* x_out, float* y_out);
void set_rumble(int controller_num, bool);
void update_rumble();
void handle_events();
ultramodern::input::connected_device_info_t get_connected_device_info(int controller_num);
// Rumble strength ranges from 0 to 100.
int get_rumble_strength();

View File

@@ -3,123 +3,144 @@
#include <memory>
#include <string>
#include <string_view>
#include <list>
// TODO move this file into src/ui
#include "SDL.h"
#include "RmlUi/Core.h"
#include "../src/ui/util/hsv.h"
#include "../src/ui/util/bem.h"
#include "../src/ui/core/ui_context.h"
namespace Rml {
class ElementDocument;
class EventListenerInstancer;
class Context;
class Event;
class ElementDocument;
class EventListenerInstancer;
class Context;
class Event;
}
namespace recompui {
class UiEventListenerInstancer;
class UiEventListenerInstancer;
class MenuController {
public:
virtual ~MenuController() {}
virtual Rml::ElementDocument* load_document(Rml::Context* context) = 0;
virtual void register_events(UiEventListenerInstancer& listener) = 0;
virtual void make_bindings(Rml::Context* context) = 0;
};
// TODO remove this once the UI has been ported over to the new system.
class MenuController {
public:
virtual ~MenuController() {}
virtual void load_document() = 0;
virtual void register_events(UiEventListenerInstancer& listener) = 0;
virtual void make_bindings(Rml::Context* context) = 0;
};
std::unique_ptr<MenuController> create_launcher_menu();
std::unique_ptr<MenuController> create_config_menu();
std::unique_ptr<MenuController> create_launcher_menu();
std::unique_ptr<MenuController> create_config_menu();
using event_handler_t = void(const std::string& param, Rml::Event&);
using event_handler_t = void(const std::string& param, Rml::Event&);
void queue_event(const SDL_Event& event);
bool try_deque_event(SDL_Event& out);
void queue_event(const SDL_Event& event);
bool try_deque_event(SDL_Event& out);
std::unique_ptr<UiEventListenerInstancer> make_event_listener_instancer();
void register_event(UiEventListenerInstancer& listener, const std::string& name, event_handler_t* handler);
std::unique_ptr<UiEventListenerInstancer> make_event_listener_instancer();
void register_event(UiEventListenerInstancer& listener, const std::string& name, event_handler_t* handler);
enum class Menu {
Launcher,
Config,
None
};
void show_context(ContextId context, std::string_view param);
void hide_context(ContextId context);
void hide_all_contexts();
bool is_context_shown(ContextId context);
bool is_context_capturing_input();
bool is_context_capturing_mouse();
bool is_any_context_shown();
ContextId try_close_current_context();
void set_current_menu(Menu menu);
Menu get_current_menu();
ContextId get_launcher_context_id();
ContextId get_config_context_id();
ContextId get_config_sub_menu_context_id();
enum class ConfigSubmenu {
General,
Controls,
Graphics,
Audio,
Debug,
Count
};
enum class ConfigTab {
General,
Controls,
Graphics,
Sound,
Mods,
Debug,
};
enum class ButtonVariant {
Primary,
Secondary,
Tertiary,
Success,
Error,
Warning,
NumVariants,
};
void set_config_tab(ConfigTab tab);
int config_tab_to_index(ConfigTab tab);
Rml::ElementTabSet* get_config_tabset();
Rml::Element* get_mod_tab();
void set_config_tabset_mod_nav();
void focus_mod_configure_button();
void set_config_submenu(ConfigSubmenu submenu);
enum class ButtonVariant {
Primary,
Secondary,
Tertiary,
Success,
Error,
Warning,
NumVariants,
};
void destroy_ui();
void apply_color_hack();
void get_window_size(int& width, int& height);
void set_cursor_visible(bool visible);
void update_supported_options();
void toggle_fullscreen();
void update_rml_display_refresh_rate();
void init_styling(const std::filesystem::path& rcss_file);
void init_prompt_context();
void open_choice_prompt(
const std::string& header_text,
const std::string& content_text,
const std::string& confirm_label_text,
const std::string& cancel_label_text,
std::function<void()> confirm_action,
std::function<void()> cancel_action,
ButtonVariant confirm_variant = ButtonVariant::Success,
ButtonVariant cancel_variant = ButtonVariant::Error,
bool focus_on_cancel = true,
const std::string& return_element_id = ""
);
void open_info_prompt(
const std::string& header_text,
const std::string& content_text,
const std::string& okay_label_text,
std::function<void()> okay_action,
ButtonVariant okay_variant = ButtonVariant::Error,
const std::string& return_element_id = ""
);
void open_notification(
const std::string& header_text,
const std::string& content_text,
const std::string& return_element_id = ""
);
void close_prompt();
bool is_prompt_open();
void update_mod_list(bool scan_mods = true);
void process_game_started();
extern const std::unordered_map<ButtonVariant, std::string> button_variants;
void apply_color_hack();
void get_window_size(int& width, int& height);
void set_cursor_visible(bool visible);
void update_supported_options();
void toggle_fullscreen();
struct PromptContext {
Rml::DataModelHandle model_handle;
std::string header = "";
std::string content = "";
std::string confirmLabel = "Confirm";
std::string cancelLabel = "Cancel";
ButtonVariant confirmVariant = ButtonVariant::Success;
ButtonVariant cancelVariant = ButtonVariant::Error;
std::function<void()> onConfirm;
std::function<void()> onCancel;
std::string returnElementId = "";
bool open = false;
bool shouldFocus = false;
bool focusOnCancel = true;
PromptContext() = default;
void close_prompt();
void open_prompt(
const std::string& headerText,
const std::string& contentText,
const std::string& confirmLabelText,
const std::string& cancelLabelText,
std::function<void()> confirmCb,
std::function<void()> cancelCb,
ButtonVariant _confirmVariant = ButtonVariant::Success,
ButtonVariant _cancelVariant = ButtonVariant::Error,
bool _focusOnCancel = true,
const std::string& _returnElementId = ""
);
void on_confirm(void);
void on_cancel(void);
void on_click(Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs);
};
PromptContext *get_prompt_context(void);
bool get_cont_active(void);
void set_cont_active(bool active);
void activate_mouse();
bool get_cont_active(void);
void set_cont_active(bool active);
void activate_mouse();
void message_box(const char* msg);
void set_render_hooks();
Rml::ElementPtr create_custom_element(Rml::Element* parent, std::string tag);
Rml::ElementDocument* load_document(const std::filesystem::path& path);
Rml::ElementDocument* create_empty_document();
Rml::Element* get_child_by_tag(Rml::Element* parent, const std::string& tag);
void queue_image_from_bytes_rgba32(const std::string &src, const std::vector<char> &bytes, uint32_t width, uint32_t height);
void queue_image_from_bytes_file(const std::string &src, const std::vector<char> &bytes);
void release_image(const std::string &src);
void drop_files(const std::list<std::filesystem::path> &file_list);
}
#endif

View File

@@ -4,6 +4,7 @@
#include <filesystem>
#include <string_view>
#include "ultramodern/config.hpp"
#include "recomp_input.h"
namespace zelda64 {
constexpr std::u8string_view program_id = u8"Zelda64Recompiled";
@@ -16,6 +17,7 @@ namespace zelda64 {
void reset_input_bindings();
void reset_cont_input_bindings();
void reset_kb_input_bindings();
void reset_single_input_binding(recomp::InputDevice device, recomp::GameInput input);
std::filesystem::path get_app_folder_path();

View File

@@ -1,9 +1,14 @@
#ifndef __ZELDA_GAME_H__
#define __ZELDA_GAME_H__
#include <cstdint>
#include <span>
#include <vector>
namespace zelda64 {
void quicksave_save();
void quicksave_load();
std::vector<uint8_t> decompress_mm(std::span<const uint8_t> compressed_rom);
};
#endif

61
include/zelda_render.h Normal file
View File

@@ -0,0 +1,61 @@
#ifndef __ZELDA_RENDER_H__
#define __ZELDA_RENDER_H__
#include <unordered_set>
#include <filesystem>
#include "common/rt64_user_configuration.h"
#include "ultramodern/renderer_context.hpp"
#include "librecomp/mods.hpp"
namespace RT64 {
struct Application;
}
namespace zelda64 {
namespace renderer {
inline const std::string special_option_texture_pack_enabled = "_recomp_texture_pack_enabled";
class RT64Context final : public ultramodern::renderer::RendererContext {
public:
~RT64Context() override;
RT64Context(uint8_t *rdram, ultramodern::renderer::WindowHandle window_handle, bool developer_mode);
bool valid() override { return static_cast<bool>(app); }
bool update_config(const ultramodern::renderer::GraphicsConfig &old_config, const ultramodern::renderer::GraphicsConfig &new_config) override;
void enable_instant_present() override;
void send_dl(const OSTask *task) override;
void update_screen() override;
void shutdown() override;
uint32_t get_display_framerate() const override;
float get_resolution_scale() const override;
private:
std::unique_ptr<RT64::Application> app;
std::unordered_set<std::string> enabled_texture_packs;
std::unordered_set<std::string> secondary_disabled_texture_packs;
void check_texture_pack_actions();
};
std::unique_ptr<ultramodern::renderer::RendererContext> create_render_context(uint8_t *rdram, ultramodern::renderer::WindowHandle window_handle, bool developer_mode);
RT64::UserConfiguration::Antialiasing RT64MaxMSAA();
bool RT64SamplePositionsSupported();
bool RT64HighPrecisionFBEnabled();
void trigger_texture_pack_update();
void enable_texture_pack(const recomp::mods::ModContext& context, const recomp::mods::ModHandle& mod);
void disable_texture_pack(const recomp::mods::ModHandle& mod);
void secondary_enable_texture_pack(const std::string& mod_id);
void secondary_disable_texture_pack(const std::string& mod_id);
// Texture pack enable option. Must be an enum with two options.
// The first option is treated as disabled and the second option is treated as enabled.
bool is_texture_pack_enable_config_option(const recomp::mods::ConfigOption& option, bool show_errors);
}
}
#endif

26
include/zelda_support.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef __ZELDA_SUPPORT_H__
#define __ZELDA_SUPPORT_H__
#include <functional>
#include <filesystem>
#include <vector>
#include <optional>
#include <list>
namespace zelda64 {
std::filesystem::path get_program_path();
std::filesystem::path get_asset_path(const char* asset);
void open_file_dialog(std::function<void(bool success, const std::filesystem::path& path)> callback);
void open_file_dialog_multiple(std::function<void(bool success, const std::list<std::filesystem::path>& paths)> callback);
void show_error_message_box(const char *title, const char *message);
// Apple specific methods that usually require Objective-C. Implemented in support_apple.mm.
#ifdef __APPLE__
void dispatch_on_ui_thread(std::function<void()> func);
std::optional<std::filesystem::path> get_application_support_directory();
std::filesystem::path get_bundle_resource_directory();
std::filesystem::path get_bundle_directory();
#endif
}
#endif

View File

@@ -7,7 +7,8 @@
"project": "CMakeLists.txt",
"projectTarget": "Zelda64Recompiled.exe",
"name": "Zelda64Recompiled.exe",
"currentDir": "${workspaceRoot}"
"currentDir": "${workspaceRoot}",
"args": ["--show-console"]
}
]
}

View File

@@ -1,2 +1,9 @@
set(FREETYPE_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/lib/freetype-windows-binaries/include)
set(FREETYPE_LIBRARIES "${CMAKE_SOURCE_DIR}/lib/freetype-windows-binaries/release static/vs2015-2022/win64/freetype.lib")
add_library(Freetype::Freetype STATIC IMPORTED)
set_target_properties(Freetype::Freetype PROPERTIES
IMPORTED_LOCATION ${FREETYPE_LIBRARIES}
)
target_include_directories(Freetype::Freetype INTERFACE
${FREETYPE_INCLUDE_DIRS}
)

257
lib/SlotMap/README.md Normal file
View File

@@ -0,0 +1,257 @@
This file is originally from the repo: https://github.com/SergeyMakeev/SlotMap
The original license and README are as follows:
```
MIT License
Copyright (c) 2022 Sergey Makeev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
# Slot Map
[![Actions Status](https://github.com/SergeyMakeev/slot_map/workflows/build/badge.svg)](https://github.com/SergeyMakeev/slot_map/actions)
[![Build status](https://ci.appveyor.com/api/projects/status/i00kv17e3ia5jr7q?svg=true)](https://ci.appveyor.com/project/SergeyMakeev/slot-map)
[![codecov](https://codecov.io/gh/SergeyMakeev/slot_map/branch/main/graph/badge.svg?token=3GRAFTRYQU)](https://codecov.io/gh/SergeyMakeev/slot_map)
![MIT](https://img.shields.io/badge/license-MIT-blue.svg)
A Slot Map is a high-performance associative container with persistent unique keys to access stored values. Upon insertion, a key is returned that can be used to later access or remove the values. Insertion, removal, and access are all guaranteed to take `O(1)` time (best, worst, and average case)
Great for storing collections of objects that need stable, safe references but have no clear ownership.
The difference between a `std::unordered_map` and a `dod::slot_map` is that the slot map generates and returns the key when inserting a value. A key is always unique and will only refer to the value that was inserted.
Usage example:
```cpp
slot_map<std::string> strings;
auto red = strings.emplace("Red");
auto green = strings.emplace("Green");
auto blue = strings.emplace("Blue");
const std::string* val1 = strings.get(red);
if (val1)
{
printf("red = '%s'\n", val1->c_str());
}
strings.erase(green);
printf("%d\n", strings.has(green));
printf("%d\n", strings.has(blue));
```
Output:
```
red = 'Red'
0
1
```
# Implementation details
The slot map container will allocate memory in pages (default page size = 4096 elements) to avoid memory spikes during growth and be able to deallocate pages that are no longer needed.
Also, the page-based memory allocator is very important since it guarantees "pointers stability"; hence, we never move values in memory.
Keys are always uses `uint64_t/uint32_t` (configurable) and technically typless, but we "artificially" make them typed to get a few extra compile-time checks.
i.e., the following code will produce a compiler error
```cpp
slot_map<std::string> strings;
slot_map<int> numbers;
slot_map<int>::key numKey = numbers.emplace(3);
const std::string* value = strings.get(numKey); // <---- can not use slot_map<int>::key to index slot_map<std::string> !
```
The keys can be converted to/from their numeric types if you do not need additional type checks.
```cpp
slot_map<int> numbers;
slot_map<int>::key numKey = numbers.emplace(3);
uint64_t rawKey = numKey; // convert to numeric type (like cast pointer to void*)
...
slot_map<int>::key numKey2{rawKey}; // create key from numeric type
```
When a slot is reused, its version is automatically incremented (to invalidate all existing keys that refers to the same slot).
But since we only use 20-bits *(10-bits for 32 bit keys)* for version counter, there is a possibility that the version counter will wrap around,
and a new item will get the same key as a removed item.
To mitigate this potential issue, once the version counter overflows, we disable that slot so that no new keys are returned for this slot
(this gives us a guarantee that there are no key collisions)
To prevent version overflow from happening too often, we need to ensure that we don't reuse the same slot too often.
So we do not reuse recently freed slot-indices as long as their number is below a certain threshold (`kMinFreeIndices = 64`).
Keys also can carry a few extra bits of information provided by a user that we called `tag`.
That might be handy to add application-specific data to keys.
For example:
```cpp
slot_map<std::string> strings;
auto red = strings.emplace("Red");
red.set_tag(13);
auto tag = red.get_tag();
assert(tag == 13);
```
Here is how a key structure looks like internally
64-bit key type
| Component | Number of bits |
| ---------------|------------------------|
| tag | 12 |
| version | 20 (0..1,048,575 |
| index | 32 (0..4,294,967,295) |
32-bit key type
| Component | Number of bits |
| ---------------|---------------------|
| tag | 2 |
| version | 10 (0..1023) |
| index | 20 (0..1,048,575) |
Note: To use your custom memory allocator define `SLOT_MAP_ALLOC`/`SLOT_MAP_FREE` before including `"slot_map.h"`
```cpp
#define SLOT_MAP_ALLOC(sizeInBytes, alignment) aligned_alloc(alignment, sizeInBytes)
#define SLOT_MAP_FREE(ptr) free(ptr)
#include "slot_map.h"
```
# API
`bool has_key(key k) const noexcept`
Returns true if the slot map contains a specific key
`void reset()`
Clears the slot map and releases any allocated memory.
Note: By calling this function, you must guarantee that no handles are in use!
Otherwise calling this function might be dangerous and lead to key "collisions".
You might consider using "clear()" instead.
`void clear()`
Clears the slot map but keeps the allocated memory for reuse.
Automatically increases version for all the removed elements (the same as calling "erase()" for all existing elements)
`const T* get(key k) const noexcept`
If key exists returns a const pointer to the value corresponding to the given key or returns null elsewere.
`T* get(key k)`
If key exists returns a pointer to the value corresponding to the given key or returns null elsewere.
`key emplace(Args&&... args)`
Constructs element in-place and returns a unique key that can be used to access this value.
`void erase(key k)`
Removes element (if such key exists) from the slot map.
`std::optional<T> pop(key k)`
Removes element (if such key exists) from the slot map, returning the value at the key if the key was not previously removed.
`bool empty() const noexcept`
Returns true if the slot map is empty.
`size_type size() const noexcept`
Returns the number of elements in the slot map.
`void swap(slot_map& other) noexcept`
Exchanges the content of the slot map by the content of another slot map object of the same type.
`slot_map(const slot_map& other)`
Copy constructor
`slot_map& operator=(const slot_map& other)`
Copy assignment
`slot_map(slot_map&& other) noexcept`
Move constructor
`slot_map& operator=(slot_map&& other) noexcept`
Move asignment
`const_values_iterator begin() const noexcept`
`const_values_iterator end() const noexcept`
Const values iterator
```cpp
for (const auto& value : slotMap)
{
...
}
```
`Items items() const noexcept`
Const key/value iterator
```cpp
for (const auto& [key, value] : slotMap.items())
{
...
}
```
# References
Sean Middleditch
Data Structures for Game Developers: The Slot Map, 2013
https://web.archive.org/web/20180121142549/http://seanmiddleditch.com/data-structures-for-game-developers-the-slot-map/
Niklas Gray
Building a Data-Oriented Entity System (part 1), 2014
http://bitsquid.blogspot.com/2014/08/building-data-oriented-entity-system.html
Noel Llopis
Managing Data Relationships, 2010
https://gamesfromwithin.com/managing-data-relationships
Stefan Reinalter
Adventures in data-oriented design - Part 3c: External References, 2013
https://blog.molecular-matters.com/2013/07/24/adventures-in-data-oriented-design-part-3c-external-references/
Niklas Gray
Managing Decoupling Part 4 - The ID Lookup Table, 2011
https://bitsquid.blogspot.com/2011/09/managing-decoupling-part-4-id-lookup.html
Sander Mertens
Making the most of ECS identifiers, 2020
https://ajmmertens.medium.com/doing-a-lot-with-a-little-ecs-identifiers-25a72bd2647
Michele Caini
ECS back and forth. Part 9 - Sparse sets and EnTT, 2020
https://skypjack.github.io/2020-08-02-ecs-baf-part-9/
Andre Weissflog
Handles are the better pointers, 2018
https://floooh.github.io/2018/06/17/handles-vs-pointers.html
Allan Deutsch
C++Now 2017: "The Slot Map Data Structure", 2017
https://www.youtube.com/watch?v=SHaAR7XPtNU
Jeff Gates
Init, Update, Draw - Data Arrays, 2012
https://greysphere.tumblr.com/post/31601463396/data-arrays
Niklas Gray
Data Structures Part 1: Bulk Data, 2019
https://ourmachinery.com/post/data-structures-part-1-bulk-data/

1366
lib/SlotMap/slot_map.h Normal file

File diff suppressed because it is too large Load Diff

Submodule lib/sse2neon deleted from 42c704755d

6
mods/BUILTIN_MODS.md Normal file
View File

@@ -0,0 +1,6 @@
# Built-in Mods
This folder contains mods that are built into the Zelda 64: Recompiled project. Built-in mods behave like normal mods but are present in a clean installation of the project. They can be updated or downgraded by placing the .nrm file for the mod in the appdata mods folder in the same way a normal mod would be, which overrides the built-in version with the manually installed version.
The list of built-in mods is as follows:
* Majora's Mask: Recompiled D-Pad Mod - https://github.com/Zelda64Recomp/MMRecompDpadMod

Binary file not shown.

View File

@@ -5,5 +5,16 @@
elf_path = "patches/patches.elf"
output_func_path = "RecompiledPatches"
single_file_output = true
# Allow absolute symbols to be used as jump targets
# Allow absolute symbols to be used as jump targets.
use_absolute_symbols = true
# Point the recompiler at the symbol files so that it can resolve relocations during recompilation.
func_reference_syms_file = "Zelda64RecompSyms/mm.us.rev1.syms.toml"
data_reference_syms_files = [ "Zelda64RecompSyms/mm.us.rev1.datasyms.toml", "Zelda64RecompSyms/mm.us.rev1.datasyms_static.toml", "patches/custom_syms.toml" ]
# Tell the recompiler to write the output binary. Doing this instead of using objcopy allows the recompiler to patch MIPS32 relocs.
output_binary_path = "patches/patches.bin"
# Do not emit warnings for unpaired LO16 values, as clang produces many of them.
unpaired_lo16_warnings = false
# Allow exporting functions and events for mods to use.
allow_exports = true
# # Enable strict patch mode, validates that patched symbols exist and that non-patch functions aren't symbols.
strict_patch_mode = true

View File

@@ -1,25 +1,18 @@
TARGET = patches.elf
CC := clang
LD := ld.lld
OBJCOPY := llvm-objcopy
CC ?= clang
LD ?= ld.lld
CFLAGS := -target mips -mips2 -mabi=32 -O2 -G0 -mno-abicalls -mno-odd-spreg -mno-check-zero-division \
-fomit-frame-pointer -ffast-math -fno-unsafe-math-optimizations -fno-builtin-memset \
-Wall -Wextra -Wno-incompatible-library-redeclaration -Wno-unused-parameter -Wno-unknown-pragmas -Wno-unused-variable -Wno-missing-braces -Wno-unsupported-floating-point-opt
CPPFLAGS := -nostdinc -D_LANGUAGE_C -DMIPS -I dummy_headers -I ../lib/mm-decomp/include -I ../lib/mm-decomp/src -I ../lib/mm-decomp/assets -I../lib/rt64/include
LDFLAGS := -nostdlib -T patches.ld -T syms.ld --just-symbols=../mm.us.rev1.rom_uncompressed.elf --allow-multiple-definition -Map patches.map
BINFLAGS := -O binary --remove-section=.bss --remove-section=.pad --remove-section=.text
LDFLAGS := -nostdlib -T patches.ld -T syms.ld -Map patches.map --unresolved-symbols=ignore-all --emit-relocs
C_SRCS := $(wildcard *.c)
C_OBJS := $(C_SRCS:.c=.o)
C_DEPS := $(C_SRCS:.c=.d)
DATABIN := $(TARGET:.elf=.bin)
$(DATABIN): $(TARGET)
$(OBJCOPY) $(BINFLAGS) $(TARGET) $@
$(TARGET): $(C_OBJS) patches.ld syms.ld
$(LD) $(C_OBJS) $(LDFLAGS) -o $@
@@ -27,7 +20,7 @@ $(C_OBJS): %.o : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) $< -MMD -MF $(@:.o=.d) -c -o $@
clean:
rm -rf $(C_OBJS) $(TARGET) $(DATABIN) $(C_DEPS)
rm -rf $(C_OBJS) $(TARGET) $(C_DEPS)
-include $(C_DEPS)

91
patches/actor_data.c Normal file
View File

@@ -0,0 +1,91 @@
#include "patches.h"
#include "extended_actors.h"
#include "transform_ids.h"
#include "actor_funcs.h"
// Use 32 bits of compiler-inserted padding to hold the actor's slot.
// 0x22 between halfDaysBits and world
#define actorIdByte0(actor) ((u8*)(actor))[0x22]
// 0x23 between halfDaysBits and world
#define actorIdByte1(actor) ((u8*)(actor))[0x23]
// 0x3A between audioFlags and focus
#define actorIdByte2(actor) ((u8*)(actor))[0x3A]
// 0x3B between audioFlags and focus
#define actorIdByte3(actor) ((u8*)(actor))[0x3B]
u32 actor_get_slot(Actor* actor) {
return (actorIdByte0(actor) << 24) | (actorIdByte1(actor) << 16) | (actorIdByte2(actor) << 8) | (actorIdByte3(actor) << 0);
}
void actor_set_slot(Actor* actor, ActorExtensionId slot) {
u32 b0 = (slot >> 24) & 0xFF;
u32 b1 = (slot >> 16) & 0xFF;
u32 b2 = (slot >> 8) & 0xFF;
u32 b3 = (slot >> 0) & 0xFF;
actorIdByte0(actor) = b0;
actorIdByte1(actor) = b1;
actorIdByte2(actor) = b2;
actorIdByte3(actor) = b3;
}
RECOMP_EXPORT ActorExtensionId z64recomp_extend_actor(s16 actor_id, u32 size) {
return recomp_register_actor_extension(actor_id, size);
}
RECOMP_EXPORT ActorExtensionId z64recomp_extend_actor_all(u32 size) {
return recomp_register_actor_extension_generic(size);
}
RECOMP_EXPORT void* z64recomp_get_extended_actor_data(Actor* actor, ActorExtensionId extension) {
return recomp_get_actor_data(actor_get_slot(actor), extension, actor->id);
}
RECOMP_EXPORT u32 z64recomp_get_actor_spawn_index(Actor* actor) {
return recomp_get_actor_spawn_index(actor_get_slot(actor));
}
RECOMP_EXPORT u32 actor_transform_id(Actor* actor) {
u32 spawn_index = z64recomp_get_actor_spawn_index(actor);
return (spawn_index * ACTOR_TRANSFORM_ID_COUNT) + ACTOR_TRANSFORM_ID_START;
}
typedef enum {
ACTOR_TRANSFORM_FLAG_INTERPOLATION_SKIPPED = 1 << 0,
ACTOR_CUSTOM_FLAG_1 = 1 << 1,
} CustomActorFlags;
typedef struct {
CustomActorFlags flags;
} BaseActorExtensionData;
ActorExtensionId base_actor_extension_handle;
void register_base_actor_extensions() {
base_actor_extension_handle = z64recomp_extend_actor_all(sizeof(BaseActorExtensionData));
}
BaseActorExtensionData* get_base_extension_data(Actor* actor) {
return (BaseActorExtensionData*)z64recomp_get_extended_actor_data(actor, base_actor_extension_handle);
}
RECOMP_EXPORT u32 actor_get_interpolation_skipped(Actor* actor) {
return (get_base_extension_data(actor)->flags & ACTOR_TRANSFORM_FLAG_INTERPOLATION_SKIPPED) != 0;
}
RECOMP_EXPORT void actor_set_interpolation_skipped(Actor* actor) {
get_base_extension_data(actor)->flags |= ACTOR_TRANSFORM_FLAG_INTERPOLATION_SKIPPED;
}
RECOMP_EXPORT void actor_clear_interpolation_skipped(Actor* actor) {
get_base_extension_data(actor)->flags &= ~ACTOR_TRANSFORM_FLAG_INTERPOLATION_SKIPPED;
}
void actor_set_custom_flag_1(Actor* actor) {
get_base_extension_data(actor)->flags |= ACTOR_CUSTOM_FLAG_1;
}
bool actor_get_custom_flag_1(Actor* actor) {
return (get_base_extension_data(actor)->flags & ACTOR_CUSTOM_FLAG_1) != 0;
}

14
patches/actor_funcs.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef __MEM_FUNCS_H__
#define __MEM_FUNCS_H__
#include "patch_helpers.h"
DECLARE_FUNC(u32, recomp_register_actor_extension, u32 actor_type, u32 size);
DECLARE_FUNC(u32, recomp_register_actor_extension_generic, u32 size);
DECLARE_FUNC(void, recomp_clear_all_actor_data);
DECLARE_FUNC(u32, recomp_create_actor_data, u32 actor_type);
DECLARE_FUNC(void, recomp_destroy_actor_data, u32 actor_handle);
DECLARE_FUNC(void*, recomp_get_actor_data, u32 actor_handle, u32 extension_handle, u32 actor_type);
DECLARE_FUNC(u32, recomp_get_actor_spawn_index, u32 actor_handle);
#endif

View File

@@ -1,14 +1,18 @@
#include "patches.h"
#include "fault.h"
#include "transform_ids.h"
u16 next_actor_transform = 0;
#include "extended_actors.h"
#include "z64actor.h"
#include "actor_funcs.h"
extern FaultClient sActorFaultClient;
void Actor_Destroy(Actor* actor, PlayState* play);
Actor* Actor_Delete(ActorContext* actorCtx, Actor* actor, PlayState* play);
void ZeldaArena_Free(void* ptr);
Actor* Actor_RemoveFromCategory(PlayState* play, ActorContext* actorCtx, Actor* actorToRemove);
void Actor_FreeOverlay(ActorOverlay* entry);
void Actor_CleanupContext(ActorContext* actorCtx, PlayState* play) {
RECOMP_PATCH void Actor_CleanupContext(ActorContext* actorCtx, PlayState* play) {
s32 i;
Fault_RemoveClient(&sActorFaultClient);
@@ -33,21 +37,22 @@ void Actor_CleanupContext(ActorContext* actorCtx, PlayState* play) {
actorCtx->absoluteSpace = NULL;
}
// @recomp Reset the actor transform IDs as all actors have been deleted.
next_actor_transform = 0;
// @recomp Reset the actor extension data.
recomp_clear_all_actor_data();
Play_SaveCycleSceneFlags(&play->state);
ActorOverlayTable_Cleanup();
}
u32 create_actor_transform_id() {
u32 ret = next_actor_transform;
next_actor_transform++;
RECOMP_DECLARE_EVENT(recomp_should_actor_init(PlayState* play, Actor* actor, bool* should));
RECOMP_DECLARE_EVENT(recomp_after_actor_init(PlayState* play, Actor* actor));
RECOMP_DECLARE_EVENT(recomp_should_actor_update(PlayState* play, Actor* actor, bool* should));
RECOMP_DECLARE_EVENT(recomp_after_actor_update(PlayState* play, Actor* actor));
return ret;
}
RECOMP_PATCH void Actor_Init(Actor* actor, PlayState* play) {
// @recomp Allocate the actor's extension data.
actor_set_slot(actor, recomp_create_actor_data(actor->id));
void Actor_Init(Actor* actor, PlayState* play) {
Actor_SetWorldToHome(actor);
Actor_SetShapeRotToWorld(actor);
Actor_SetFocus(actor, 0.0f);
@@ -69,14 +74,160 @@ void Actor_Init(Actor* actor, PlayState* play) {
ActorShape_Init(&actor->shape, 0.0f, NULL, 0.0f);
if (Object_IsLoaded(&play->objectCtx, actor->objectSlot)) {
Actor_SetObjectDependency(play, actor);
actor->init(actor, play);
actor->init = NULL;
// @recomp Augmented, allowing us to prevent actor init and hook into after init
bool shouldInit = true;
recomp_should_actor_init(play, actor, &shouldInit);
if (shouldInit) {
actor->init(actor, play);
actor->init = NULL;
recomp_after_actor_init(play, actor);
} else {
actor->init = NULL;
Actor_Kill(actor);
}
}
}
RECOMP_PATCH Actor* Actor_Delete(ActorContext* actorCtx, Actor* actor, PlayState* play) {
s32 pad;
Player* player = GET_PLAYER(play);
Actor* newHead;
ActorOverlay* overlayEntry = actor->overlayEntry;
if ((player != NULL) && (actor == player->lockOnActor)) {
Player_Untarget(player);
Camera_ChangeMode(Play_GetCamera(play, Play_GetActiveCamId(play)), CAM_MODE_NORMAL);
}
if (actor == actorCtx->targetCtx.fairyActor) {
actorCtx->targetCtx.fairyActor = NULL;
}
if (actor == actorCtx->targetCtx.forcedTargetActor) {
actorCtx->targetCtx.forcedTargetActor = NULL;
}
if (actor == actorCtx->targetCtx.bgmEnemy) {
actorCtx->targetCtx.bgmEnemy = NULL;
}
AudioSfx_StopByPos(&actor->projectedPos);
Actor_Destroy(actor, play);
newHead = Actor_RemoveFromCategory(play, actorCtx, actor);
// @recomp Destroy the actor's extension data.
recomp_destroy_actor_data(actor_get_slot(actor));
// @recomp Pick a transform ID for this actor and encode it into struct padding
u32 cur_transform_id = create_actor_transform_id();
actorIdByte0(actor) = (cur_transform_id >> 0) & 0xFF;
actorIdByte1(actor) = (cur_transform_id >> 8) & 0xFF;;
ZeldaArena_Free(actor);
if (overlayEntry->vramStart != NULL) {
overlayEntry->numLoaded--;
Actor_FreeOverlay(overlayEntry);
}
return newHead;
}
// @recomp Copied from z_actor.c
typedef struct {
/* 0x00 */ PlayState* play;
/* 0x04 */ Actor* actor;
/* 0x08 */ u32 freezeExceptionFlag;
/* 0x0C */ u32 canFreezeCategory;
/* 0x10 */ Actor* talkActor;
/* 0x14 */ Player* player;
/* 0x18 */ u32 updateActorFlagsMask; // Actor will update only if at least 1 actor flag is set in this bitmask
} UpdateActor_Params; // size = 0x1C
RECOMP_PATCH Actor* Actor_UpdateActor(UpdateActor_Params* params) {
PlayState* play = params->play;
Actor* actor = params->actor;
Actor* nextActor;
if (actor->world.pos.y < -25000.0f) {
actor->world.pos.y = -25000.0f;
}
actor->sfxId = 0;
actor->audioFlags &= ~(((1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | (1 << 0)) | ((1 << 6) | (1 << 5))); // ACTOR_AUDIO_FLAG_ALL
if (actor->init != NULL) {
if (Object_IsLoaded(&play->objectCtx, actor->objectSlot)) {
Actor_SetObjectDependency(play, actor);
// @recomp Augmented, allowing us to prevent actor init and hook into after init
bool shouldInit = true;
recomp_should_actor_init(play, actor, &shouldInit);
if (shouldInit) {
actor->init(actor, play);
actor->init = NULL;
recomp_after_actor_init(play, actor);
} else {
actor->init = NULL;
Actor_Kill(actor);
}
}
nextActor = actor->next;
} else if (actor->update == NULL) {
if (!actor->isDrawn) {
nextActor = Actor_Delete(&play->actorCtx, actor, play);
} else {
Actor_Destroy(actor, play);
nextActor = actor->next;
}
} else {
if (!Object_IsLoaded(&play->objectCtx, actor->objectSlot)) {
Actor_Kill(actor);
} else if (((params->freezeExceptionFlag != 0) && !(actor->flags & params->freezeExceptionFlag)) ||
(((!params->freezeExceptionFlag) != 0) &&
(!(actor->flags & (1 << 20)) ||
((actor->category == ACTORCAT_EXPLOSIVES) && (params->player->stateFlags1 & PLAYER_STATE1_200))) &&
params->canFreezeCategory && (actor != params->talkActor) && (actor != params->player->heldActor) &&
(actor->parent != &params->player->actor))) {
CollisionCheck_ResetDamage(&actor->colChkInfo);
} else {
Math_Vec3f_Copy(&actor->prevPos, &actor->world.pos);
actor->xzDistToPlayer = Actor_WorldDistXZToActor(actor, &params->player->actor);
actor->playerHeightRel = Actor_HeightDiff(actor, &params->player->actor);
actor->xyzDistToPlayerSq = SQ(actor->xzDistToPlayer) + SQ(actor->playerHeightRel);
actor->yawTowardsPlayer = Actor_WorldYawTowardActor(actor, &params->player->actor);
actor->flags &= ~(1 << 24);
if ((DECR(actor->freezeTimer) == 0) && (actor->flags & params->updateActorFlagsMask)) {
if (actor == params->player->lockOnActor) {
actor->isLockedOn = true;
} else {
actor->isLockedOn = false;
}
if ((actor->targetPriority != 0) && (params->player->lockOnActor == NULL)) {
actor->targetPriority = 0;
}
Actor_SetObjectDependency(play, actor);
if (actor->colorFilterTimer != 0) {
actor->colorFilterTimer--;
}
// @recomp Augmented, allowing us to prevent actor update and hook into after update
bool shouldUpdate = true;
recomp_should_actor_update(play, actor, &shouldUpdate);
if (shouldUpdate) {
actor->update(actor, play);
recomp_after_actor_update(play, actor);
}
DynaPoly_UnsetAllInteractFlags(play, &play->colCtx.dyna, actor);
}
CollisionCheck_ResetDamage(&actor->colChkInfo);
}
nextActor = actor->next;
}
return nextActor;
}
// Extract the transform ID for this actor, add the limb index and write that as the matrix group to POLY_OPA_DISP.
@@ -135,7 +286,7 @@ Gfx* pop_post_limb_matrix_group(Gfx* dlist, Actor* actor) {
/*
* Draws the limb at `limbIndex` with a level of detail display lists index by `dListIndex`
*/
void SkelAnime_DrawLimbLod(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
RECOMP_PATCH void SkelAnime_DrawLimbLod(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, Actor* actor, s32 lod) {
LodLimb* limb;
Gfx* dList;
@@ -197,7 +348,7 @@ void SkelAnime_DrawLimbLod(PlayState* play, s32 limbIndex, void** skeleton, Vec3
* Draw all limbs of type `LodLimb` in a given skeleton
* Near or far display list is specified via `lod`
*/
void SkelAnime_DrawLod(PlayState* play, void** skeleton, Vec3s* jointTable, OverrideLimbDrawOpa overrideLimbDraw,
RECOMP_PATCH void SkelAnime_DrawLod(PlayState* play, void** skeleton, Vec3s* jointTable, OverrideLimbDrawOpa overrideLimbDraw,
PostLimbDrawOpa postLimbDraw, Actor* actor, s32 lod) {
LodLimb* rootLimb;
s32 pad;
@@ -261,7 +412,7 @@ void SkelAnime_DrawLod(PlayState* play, void** skeleton, Vec3s* jointTable, Over
* Draw a limb of type `LodLimb` contained within a flexible skeleton
* Near or far display list is specified via `lod`
*/
void SkelAnime_DrawFlexLimbLod(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
RECOMP_PATCH void SkelAnime_DrawFlexLimbLod(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
OverrideLimbDrawFlex overrideLimbDraw, PostLimbDrawFlex postLimbDraw, Actor* actor,
s32 lod, Mtx** mtx) {
LodLimb* limb;
@@ -332,7 +483,7 @@ void SkelAnime_DrawFlexLimbLod(PlayState* play, s32 limbIndex, void** skeleton,
* Limbs in a flexible skeleton have meshes that can stretch to line up with other limbs.
* An array of matrices is dynamically allocated so each limb can access any transform to ensure its meshes line up.
*/
void SkelAnime_DrawFlexLod(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dListCount,
RECOMP_PATCH void SkelAnime_DrawFlexLod(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dListCount,
OverrideLimbDrawFlex overrideLimbDraw, PostLimbDrawFlex postLimbDraw, Actor* actor,
s32 lod) {
LodLimb* rootLimb;
@@ -403,7 +554,7 @@ void SkelAnime_DrawFlexLod(PlayState* play, void** skeleton, Vec3s* jointTable,
/*
* Draws the limb of the Skeleton `skeleton` at `limbIndex`
*/
void SkelAnime_DrawLimbOpa(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
RECOMP_PATCH void SkelAnime_DrawLimbOpa(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, Actor* actor) {
StandardLimb* limb;
Gfx* dList;
@@ -463,7 +614,7 @@ void SkelAnime_DrawLimbOpa(PlayState* play, s32 limbIndex, void** skeleton, Vec3
/**
* Draw all limbs of type `StandardLimb` in a given skeleton to the polyOpa buffer
*/
void SkelAnime_DrawOpa(PlayState* play, void** skeleton, Vec3s* jointTable, OverrideLimbDrawOpa overrideLimbDraw,
RECOMP_PATCH void SkelAnime_DrawOpa(PlayState* play, void** skeleton, Vec3s* jointTable, OverrideLimbDrawOpa overrideLimbDraw,
PostLimbDrawOpa postLimbDraw, Actor* actor) {
StandardLimb* rootLimb;
s32 pad;
@@ -521,7 +672,7 @@ void SkelAnime_DrawOpa(PlayState* play, void** skeleton, Vec3s* jointTable, Over
CLOSE_DISPS(play->state.gfxCtx);
}
void SkelAnime_DrawFlexLimbOpa(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
RECOMP_PATCH void SkelAnime_DrawFlexLimbOpa(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, Actor* actor,
Mtx** limbMatricies) {
StandardLimb* limb;
@@ -591,7 +742,7 @@ void SkelAnime_DrawFlexLimbOpa(PlayState* play, s32 limbIndex, void** skeleton,
* Limbs in a flexible skeleton have meshes that can stretch to line up with other limbs.
* An array of matrices is dynamically allocated so each limb can access any transform to ensure its meshes line up.
*/
void SkelAnime_DrawFlexOpa(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dListCount,
RECOMP_PATCH void SkelAnime_DrawFlexOpa(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dListCount,
OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, Actor* actor) {
StandardLimb* rootLimb;
s32 pad;
@@ -661,7 +812,7 @@ void SkelAnime_DrawFlexOpa(PlayState* play, void** skeleton, Vec3s* jointTable,
CLOSE_DISPS(play->state.gfxCtx);
}
void SkelAnime_DrawTransformFlexLimbOpa(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
RECOMP_PATCH void SkelAnime_DrawTransformFlexLimbOpa(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw,
TransformLimbDrawOpa transformLimbDraw, Actor* actor, Mtx** mtx) {
StandardLimb* limb;
@@ -744,7 +895,7 @@ void SkelAnime_DrawTransformFlexLimbOpa(PlayState* play, s32 limbIndex, void** s
* coordinates.
* Note that the `TransformLimbDraw` does not have a NULL check, so must be provided even if empty.
*/
void SkelAnime_DrawTransformFlexOpa(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dListCount,
RECOMP_PATCH void SkelAnime_DrawTransformFlexOpa(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dListCount,
OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw,
TransformLimbDrawOpa transformLimbDraw, Actor* actor) {
StandardLimb* rootLimb;
@@ -825,7 +976,7 @@ void SkelAnime_DrawTransformFlexOpa(PlayState* play, void** skeleton, Vec3s* joi
* Draws the Skeleton `skeleton`'s limb at index `limbIndex`. Appends all generated graphics commands to
* `gfx`. Returns a pointer to the next gfx to be appended to.
*/
Gfx* SkelAnime_DrawLimb(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
RECOMP_PATCH Gfx* SkelAnime_DrawLimb(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
OverrideLimbDraw overrideLimbDraw, PostLimbDraw postLimbDraw, Actor* actor, Gfx* gfx) {
StandardLimb* limb;
Gfx* dList;
@@ -884,7 +1035,7 @@ Gfx* SkelAnime_DrawLimb(PlayState* play, s32 limbIndex, void** skeleton, Vec3s*
* Draws the Skeleton `skeleton` Appends all generated graphics to `gfx`, and returns a pointer to the
* next gfx to be appended to.
*/
Gfx* SkelAnime_Draw(PlayState* play, void** skeleton, Vec3s* jointTable, OverrideLimbDraw overrideLimbDraw,
RECOMP_PATCH Gfx* SkelAnime_Draw(PlayState* play, void** skeleton, Vec3s* jointTable, OverrideLimbDraw overrideLimbDraw,
PostLimbDraw postLimbDraw, Actor* actor, Gfx* gfx) {
StandardLimb* rootLimb;
s32 pad;
@@ -944,7 +1095,7 @@ Gfx* SkelAnime_Draw(PlayState* play, void** skeleton, Vec3s* jointTable, Overrid
/**
* Draw a limb of type `StandardLimb` contained within a flexible skeleton to the specified display buffer
*/
Gfx* SkelAnime_DrawFlexLimb(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
RECOMP_PATCH Gfx* SkelAnime_DrawFlexLimb(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
OverrideLimbDraw overrideLimbDraw, PostLimbDraw postLimbDraw, Actor* actor, Mtx** mtx,
Gfx* gfx) {
StandardLimb* limb;
@@ -1014,7 +1165,7 @@ Gfx* SkelAnime_DrawFlexLimb(PlayState* play, s32 limbIndex, void** skeleton, Vec
* Limbs in a flexible skeleton have meshes that can stretch to line up with other limbs.
* An array of matrices is dynamically allocated so each limb can access any transform to ensure its meshes line up.
*/
Gfx* SkelAnime_DrawFlex(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dListCount,
RECOMP_PATCH Gfx* SkelAnime_DrawFlex(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dListCount,
OverrideLimbDraw overrideLimbDraw, PostLimbDraw postLimbDraw, Actor* actor, Gfx* gfx) {
StandardLimb* rootLimb;
s32 pad;
@@ -1085,7 +1236,7 @@ Gfx* SkelAnime_DrawFlex(PlayState* play, void** skeleton, Vec3s* jointTable, s32
extern MtxF gSkinLimbMatrices[];
void Skin_DrawImpl(Actor* actor, PlayState* play, Skin* skin, SkinPostDraw postDraw,
RECOMP_PATCH void Skin_DrawImpl(Actor* actor, PlayState* play, Skin* skin, SkinPostDraw postDraw,
SkinOverrideLimbDraw overrideLimbDraw, s32 setTranslation, s32 arg6, s32 drawFlags) {
s32 i;
SkinLimb** skeleton;
@@ -1145,7 +1296,7 @@ close_disps:;
CLOSE_DISPS(gfxCtx);
}
__attribute__((noinline)) s32 scan_for_matrices(Gfx* start, Gfx* end) {
s32 scan_for_matrices(Gfx* start, Gfx* end) {
s32 matrix_count = 0;
Gfx* cur = start;
// Count any G_MTX commands between the start and end commands.
@@ -1201,7 +1352,7 @@ void tag_actor_displaylists(Actor* actor, PlayState* play, Gfx* opa_start, Gfx*
}
// @recomp Patched to automatically add transform tagging to actor matrices based on what DL commands they write in their draw function
void Actor_Draw(PlayState* play, Actor* actor) {
RECOMP_PATCH void Actor_Draw(PlayState* play, Actor* actor) {
Lights* light;
OPEN_DISPS(play->state.gfxCtx);
@@ -1285,3 +1436,10 @@ void Actor_Draw(PlayState* play, Actor* actor) {
CLOSE_DISPS(play->state.gfxCtx);
}
ActorExtensionId z64recomp_extend_actor(s16 actor_id, u32 size);
ActorExtensionId z64recomp_extend_actor_all(u32 size);
void* z64recomp_get_extended_actor_data(Actor* actor, ActorExtensionId extension);
u32 z64recomp_get_actor_spawn_index(Actor* actor);

View File

@@ -14,7 +14,7 @@ s32 ShrinkWindow_Letterbox_GetSizeTarget(void);
void ShrinkWindow_Letterbox_SetSizeTarget(s32 target);
// @recomp Patched function to set a global variable if the player can pause
void KaleidoSetup_Update(PlayState* play) {
RECOMP_PATCH void KaleidoSetup_Update(PlayState* play) {
Input* input = CONTROLLER1(&play->state);
MessageContext* msgCtx = &play->msgCtx;
Player* player = GET_PLAYER(play);
@@ -67,10 +67,16 @@ void KaleidoSetup_Update(PlayState* play) {
void Sram_SyncWriteToFlash(SramContext* sramCtx, s32 curPage, s32 numPages);
void autosave_reset_timer();
void autosave_reset_timer_slow();
void recomp_reset_autosave_timer();
void recomp_reset_autosave_timer_slow();
void do_autosave(PlayState* play) {
RECOMP_DECLARE_EVENT(recomp_on_autosave(PlayState* play));
RECOMP_DECLARE_EVENT(recomp_after_autosave(PlayState* play));
RECOMP_EXPORT void recomp_do_autosave(PlayState* play) {
// @recomp_event recomp_on_autosave(PlayState* play): Autosave triggered.
recomp_on_autosave(play);
// Transfer the scene flags into the cycle flags.
Play_SaveCycleSceneFlags(&play->state);
// Transfer the cycle flags into the save buffer. Logic copied from func_8014546C.
@@ -97,15 +103,27 @@ void do_autosave(PlayState* play) {
Sram_SyncWriteToFlash(sramCtx, gFlashOwlSaveStartPages[fileNum * 2 + 1], gFlashOwlSaveNumPages[fileNum * 2 + 1]);
gSaveContext.save.isOwlSave = false;
// @recomp_event recomp_on_autosave(PlayState* play): Autosave finished.
recomp_after_autosave(play);
}
// @recomp Do not clear the save if the save was an autosave.
void func_80147314(SramContext* sramCtx, s32 fileNum) {
bool loading_deletes_owl_save = true;
// @recomp_export void recomp_set_loading_deletes_owl_save(bool new_val): Set whether loading an owl save should also delete it.
RECOMP_EXPORT void recomp_set_loading_deletes_owl_save(bool new_val)
{
loading_deletes_owl_save = new_val;
}
// @recomp Do not clear the save if the save was an autosave, or if mods have disabled save deletion.
RECOMP_PATCH void func_80147314(SramContext* sramCtx, s32 fileNum) {
s32 save_type = gSaveContext.save.isOwlSave;
gSaveContext.save.isOwlSave = false;
// @recomp Prevent owl save/autosave deletion if autosaving is enabled.
if (!recomp_autosave_enabled()) {
// @recomp Prevent owl save/autosave deletion if autosaving is enabled, and...
// @recomp_use_export_var loading_deletes_owl_save: Prevent owl save deletion if mods disable it.
if (!recomp_get_autosave_enabled() && loading_deletes_owl_save) {
gSaveContext.save.saveInfo.playerData.newf[0] = '\0';
gSaveContext.save.saveInfo.playerData.newf[1] = '\0';
gSaveContext.save.saveInfo.playerData.newf[2] = '\0';
@@ -156,7 +174,7 @@ void delete_owl_save(SramContext* sramCtx, s32 fileNum) {
}
// @recomp Patched to delete owl saves when making regular saves.
void func_8014546C(SramContext* sramCtx) {
RECOMP_PATCH void func_8014546C(SramContext* sramCtx) {
s32 i;
if (gSaveContext.save.isOwlSave) {
@@ -176,7 +194,7 @@ void func_8014546C(SramContext* sramCtx) {
// @recomp Delete the owl save.
delete_owl_save(sramCtx, gSaveContext.fileNum);
// @recomp Reset the autosave timer.
autosave_reset_timer();
recomp_reset_autosave_timer();
for (i = 0; i < ARRAY_COUNT(gSaveContext.cycleSceneFlags); i++) {
gSaveContext.save.saveInfo.permanentSceneFlags[i].chest = gSaveContext.cycleSceneFlags[i].chest;
gSaveContext.save.saveInfo.permanentSceneFlags[i].switch0 = gSaveContext.cycleSceneFlags[i].switch0;
@@ -199,7 +217,7 @@ extern u16 D_801F6AF0;
extern u8 D_801F6AF2;
// @recomp Patched to call the new owl save deletion function.
void Sram_EraseSave(FileSelectState* fileSelect2, SramContext* sramCtx, s32 fileNum) {
RECOMP_PATCH void Sram_EraseSave(FileSelectState* fileSelect2, SramContext* sramCtx, s32 fileNum) {
FileSelectState* fileSelect = fileSelect2;
s32 pad;
@@ -344,11 +362,11 @@ void draw_autosave_icon(PlayState* play) {
CLOSE_DISPS(play->state.gfxCtx);
}
void show_autosave_icon() {
RECOMP_EXPORT void recomp_show_autosave_icon() {
autosave_icon_counter = AUTOSAVE_ICON_TOTAL_FRAMES;
}
u32 recomp_autosave_interval() {
RECOMP_EXPORT u32 recomp_autosave_interval() {
return 2 * 60 * 1000;
}
@@ -367,12 +385,12 @@ bool reached_final_three_hours() {
return false;
}
void autosave_reset_timer() {
RECOMP_EXPORT void recomp_reset_autosave_timer() {
last_autosave_time = osGetTime();
extra_autosave_delay_milliseconds = 0;
}
void autosave_reset_timer_slow() {
RECOMP_EXPORT void recomp_reset_autosave_timer_slow() {
// Set the most recent autosave time in the future to give extra time before an autosave triggers.
last_autosave_time = osGetTime();
extra_autosave_delay_milliseconds = 2 * 60 * 1000;
@@ -381,7 +399,7 @@ void autosave_reset_timer_slow() {
void autosave_post_play_update(PlayState* play) {
static int frames_since_save_changed = 0;
static int frames_since_autosave_ready = 0;
if (recomp_autosave_enabled()) {
if (recomp_get_autosave_enabled()) {
if (autosave_compare_saves(&gSaveContext, &prev_save_ctx)) {
frames_since_save_changed = 0;
Lib_MemCpy(&prev_save_ctx, &gSaveContext, offsetof(SaveContext, fileNum));
@@ -392,7 +410,7 @@ void autosave_post_play_update(PlayState* play) {
OSTime time_now = osGetTime();
// Check the following conditions:
// Check the following conditions for autosave safety:
// * The UI is in a normal state.
// * Time is passing.
// * No message is on screen.
@@ -400,6 +418,10 @@ void autosave_post_play_update(PlayState* play) {
// * No cutscene is running.
// * The game is not in cutscene mode.
// * The clock has not reached the final 3 hours.
// * The player is not in an active/inactive minigame (not all minigames use this flag, default is STATUS_END)
// * The player is not in a timed minigame in the first set (Shooting Gallery, Butler Race, Spirit House, etc)
// * The player is not in a timed minigame in the second set (Goron Race, Treasure Game, Beaver Bros, etc)
// * The player is not taking a boat cruise
// * The player is allowed to pause.
if (gSaveContext.hudVisibility == HUD_VISIBILITY_ALL &&
R_TIME_SPEED != 0 &&
@@ -409,6 +431,10 @@ void autosave_post_play_update(PlayState* play) {
gSaveContext.save.cutsceneIndex < 0xFFF0 &&
!Play_InCsMode(play) &&
!reached_final_three_hours() &&
gSaveContext.minigameStatus == MINIGAME_STATUS_END &&
gSaveContext.timerStates[TIMER_ID_MINIGAME_1] == TIMER_STATE_OFF &&
gSaveContext.timerStates[TIMER_ID_MINIGAME_2] == TIMER_STATE_OFF &&
!(CHECK_EVENTINF(EVENTINF_41)) &&
gCanPause
) {
frames_since_autosave_ready++;
@@ -425,20 +451,20 @@ void autosave_post_play_update(PlayState* play) {
frames_since_autosave_ready >= MIN_FRAMES_SINCE_READY &&
time_now - last_autosave_time > (OS_USEC_TO_CYCLES(1000 * (recomp_autosave_interval() + extra_autosave_delay_milliseconds)))
) {
do_autosave(play);
show_autosave_icon();
autosave_reset_timer();
recomp_do_autosave(play);
recomp_show_autosave_icon();
recomp_reset_autosave_timer();
}
}
else {
// Update the last autosave time to the current time to prevent autosaving immediately if autosaves are turned back on.
autosave_reset_timer();
recomp_reset_autosave_timer();
}
gCanPause = false;
}
void autosave_init() {
autosave_reset_timer_slow();
recomp_reset_autosave_timer_slow();
Lib_MemCpy(&prev_save_ctx, &gSaveContext, offsetof(SaveContext, fileNum));
}
@@ -468,7 +494,7 @@ extern s16 sSceneCutsceneCount;
bool skip_entry_cutscene = false;
// @recomp Patched to skip the entrance cutscene if the flag is enabled.
s16 CutsceneManager_FindEntranceCsId(void) {
RECOMP_PATCH s16 CutsceneManager_FindEntranceCsId(void) {
PlayState* play;
s32 csId;
@@ -522,14 +548,20 @@ s32 spawn_entrance_from_autosave_entrance(s16 autosave_entrance) {
}
}
RECOMP_DECLARE_EVENT(recomp_on_load_save(FileSelectState* fileSelect, SramContext* sramCtx));
RECOMP_DECLARE_EVENT(recomp_after_load_save(FileSelectState* fileSelect, SramContext* sramCtx));
// @recomp Patched to change the entrance for autosaves and initialize autosaves.
void Sram_OpenSave(FileSelectState* fileSelect, SramContext* sramCtx) {
RECOMP_PATCH void Sram_OpenSave(FileSelectState* fileSelect, SramContext* sramCtx) {
s32 i;
s32 pad;
s32 phi_t1 = 0;
s32 pad1;
s32 fileNum;
// @recomp_event recomp_on_load_save(FileSelectState* fileSelect, SramContext* sramCtx): A save-file was just chosen.
recomp_on_load_save(fileSelect, sramCtx);
if (gSaveContext.flashSaveAvailable) {
bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE);
@@ -597,6 +629,9 @@ void Sram_OpenSave(FileSelectState* fileSelect, SramContext* sramCtx) {
}
// @recomp Handle autosaves.
else if (gSaveContext.save.isOwlSave == SAVE_TYPE_AUTOSAVE) {
// Clear Rock Sirloin from being held, due to MM hardcoding its behavior
gSaveContext.unk_1014 = 0;
gSaveContext.save.entrance = spawn_entrance_from_autosave_entrance(gSaveContext.save.entrance);
// Skip the turtle cutscene that happens when entering Great Bay Temple.
@@ -653,29 +688,49 @@ void Sram_OpenSave(FileSelectState* fileSelect, SramContext* sramCtx) {
// @recomp Initialize the autosave state tracking.
autosave_init();
// @recomp_event recomp_after_load_save(FileSelectState* fileSelect, SramContext* sramCtx): The save has finished loading.
recomp_after_load_save(fileSelect, sramCtx);
}
bool moon_crash_resets_save = true;
// @recomp_export void recomp_set_moon_crash_resets_save(bool new_val): Set whether a moon crash should revert the player's save data.
RECOMP_EXPORT void recomp_set_moon_crash_resets_save(bool new_val)
{
moon_crash_resets_save = new_val;
}
extern s32 Actor_ProcessTalkRequest(Actor* actor, GameState* gameState);
RECOMP_DECLARE_EVENT(recomp_on_moon_crash(SramContext* sramCtx));
RECOMP_DECLARE_EVENT(recomp_after_moon_crash(SramContext* sramCtx));
// @recomp Reset the autosave timer when the moon crashes.
void Sram_ResetSaveFromMoonCrash(SramContext* sramCtx) {
RECOMP_PATCH void Sram_ResetSaveFromMoonCrash(SramContext* sramCtx) {
s32 i;
s32 cutsceneIndex = gSaveContext.save.cutsceneIndex;
bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE);
// @recomp_event recomp_on_moon_crash(SramContext* sramCtx): A moon crash has just been triggered.
recomp_on_moon_crash(sramCtx);
if (SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2],
gFlashSaveNumPages[gSaveContext.fileNum * 2]) != 0) {
SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2 + 1],
gFlashSaveNumPages[gSaveContext.fileNum * 2 + 1]);
if (moon_crash_resets_save)
{
bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE);
if (SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2],
gFlashSaveNumPages[gSaveContext.fileNum * 2]) != 0) {
SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2 + 1],
gFlashSaveNumPages[gSaveContext.fileNum * 2 + 1]);
}
Lib_MemCpy(&gSaveContext.save, sramCtx->saveBuf, sizeof(Save));
if (CHECK_NEWF(gSaveContext.save.saveInfo.playerData.newf)) {
SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2 + 1],
gFlashSaveNumPages[gSaveContext.fileNum * 2 + 1]);
Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, sizeof(Save));
}
gSaveContext.save.cutsceneIndex = cutsceneIndex;
}
Lib_MemCpy(&gSaveContext.save, sramCtx->saveBuf, sizeof(Save));
if (CHECK_NEWF(gSaveContext.save.saveInfo.playerData.newf)) {
SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2 + 1],
gFlashSaveNumPages[gSaveContext.fileNum * 2 + 1]);
Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, sizeof(Save));
}
gSaveContext.save.cutsceneIndex = cutsceneIndex;
for (i = 0; i < ARRAY_COUNT(gSaveContext.eventInf); i++) {
gSaveContext.eventInf[i] = 0;
@@ -705,25 +760,56 @@ void Sram_ResetSaveFromMoonCrash(SramContext* sramCtx) {
gSaveContext.jinxTimer = 0;
// @recomp Use the slow autosave timer to give the player extra time to respond to the moon crashing to decide if they want to reload their autosave.
autosave_reset_timer_slow();
recomp_reset_autosave_timer_slow();
// @recomp_event recomp_after_moon_crash(SramContext* sramCtx): The effects of moon crash have been written.
recomp_after_moon_crash(sramCtx);
}
// @recomp If autosave is enabled, skip the part of the owl statue dialog that talks about the file being deleted on load, since it's not true.
void ObjWarpstone_Update(Actor* thisx, PlayState* play) {
bool owls_save_and_quit = true;
// @recomp_export void recomp_set_owls_save_and_quit(bool new_val): Set if owls should use their code to save and quit. If false is passed, owl saves now do nothing.
RECOMP_EXPORT void recomp_set_owls_save_and_quit(bool new_val)
{
owls_save_and_quit = new_val;
}
RECOMP_DECLARE_EVENT(recomp_on_owl_update(ObjWarpstone* this, PlayState* play));
RECOMP_DECLARE_EVENT(recomp_on_owl_save(ObjWarpstone* this, PlayState* play));
RECOMP_DECLARE_EVENT(recomp_after_owl_save(ObjWarpstone* this, PlayState* play));
// @recomp If autosave is enabled or owl save deletion is disabled, skip the part of the owl statue dialog that talks about the file being deleted on load, since it's not true.
RECOMP_PATCH void ObjWarpstone_Update(Actor* thisx, PlayState* play) {
ObjWarpstone* this = (ObjWarpstone*)thisx;
s32 pad;
// @recomp_event recomp_on_owl_update(ObjWarpstone* this, PlayState* play): Allow mods to handle owl update frames.
recomp_on_owl_update(this, play);
if (this->isTalking) {
if (Actor_TextboxIsClosing(&this->dyna.actor, play)) {
this->isTalking = false;
} else if ((Message_GetState(&play->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(play)) {
if (play->msgCtx.choiceIndex != 0) {
// @recomp_event recomp_on_owl_save(ObjWarpstone* this, PlayState* play): The player chose to save from an owl statue.
recomp_on_owl_save(this, play);
Audio_PlaySfx_MessageDecide();
play->msgCtx.msgMode = MSGMODE_OWL_SAVE_0;
// @recomp_use_export_var owls_save_and_quit: Only use normal owl save if quit flag is set.
if (owls_save_and_quit) {
play->msgCtx.msgMode = MSGMODE_OWL_SAVE_0;
} else {
Message_CloseTextbox(play);
}
play->msgCtx.unk120D6 = 0;
play->msgCtx.unk120D4 = 0;
gSaveContext.save.owlWarpId = OBJ_WARPSTONE_GET_OWL_WARP_ID(&this->dyna.actor);
// @recomp_event recomp_after_owl_save(ObjWarpstone* this, PlayState* play): Owl save is finished.
recomp_after_owl_save(this, play);
} else {
Message_CloseTextbox(play);
}
@@ -734,8 +820,8 @@ void ObjWarpstone_Update(Actor* thisx, PlayState* play) {
Actor_OfferTalkNearColChkInfoCylinder(&this->dyna.actor, play);
}
// @recomp Skip the text talking about the save being deleted on load, if autosave is enabled.
if (recomp_autosave_enabled()) {
// @recomp_use_export_var loading_deletes_owl_save: Skip the text talking about the save being deleted on load, if autosave is enabled or if owl save deletion is disabled.
if (recomp_get_autosave_enabled() || !loading_deletes_owl_save) {
if (this->isTalking && play->msgCtx.currentTextId == 0xC01 && play->msgCtx.msgBufPos == 269) {
play->msgCtx.msgBufPos = 530;
}

View File

@@ -61,7 +61,7 @@ void edit_billboard_groups(PlayState* play) {
CLOSE_DISPS(play->state.gfxCtx);
}
Mtx* Matrix_NewMtx(GraphicsContext* gfxCtx) {
RECOMP_PATCH Mtx* Matrix_NewMtx(GraphicsContext* gfxCtx) {
Mtx* ret = Matrix_ToMtx(GRAPH_ALLOC(gfxCtx, sizeof(Mtx)));
if (*current_billboard_state) {
@@ -77,7 +77,7 @@ Mtx* Matrix_NewMtx(GraphicsContext* gfxCtx) {
return ret;
}
void Matrix_Init(GameState* gameState) {
RECOMP_PATCH void Matrix_Init(GameState* gameState) {
sMatrixStack = THA_AllocTailAlign16(&gameState->tha, MATRIX_STACK_SIZE * sizeof(MtxF));
sCurrentMatrix = sMatrixStack;
@@ -94,7 +94,7 @@ void matrix_play_update(PlayState* play) {
play_billboard_matrix = &play->billboardMtxF;
}
void Matrix_Push(void) {
RECOMP_PATCH void Matrix_Push(void) {
MtxF* prev = sCurrentMatrix;
sCurrentMatrix++;
@@ -106,20 +106,20 @@ void Matrix_Push(void) {
*current_billboard_state = *prev_billboard;
}
void Matrix_Pop(void) {
RECOMP_PATCH void Matrix_Pop(void) {
sCurrentMatrix--;
// @recomp Pop the matrix stack billboard state.
current_billboard_state--;
}
void Matrix_Put(MtxF* src) {
RECOMP_PATCH void Matrix_Put(MtxF* src) {
Matrix_MtxFCopy(sCurrentMatrix, src);
// @recomp Update the current billboard state.
*current_billboard_state = (src == play_billboard_matrix);
}
void Matrix_ReplaceRotation(MtxF* mf) {
RECOMP_PATCH void Matrix_ReplaceRotation(MtxF* mf) {
MtxF* cmf = sCurrentMatrix;
f32 acc;
f32 component;
@@ -168,7 +168,7 @@ void Matrix_ReplaceRotation(MtxF* mf) {
*current_billboard_state = (mf == play_billboard_matrix);
}
void Matrix_Mult(MtxF* mf, MatrixMode mode) {
RECOMP_PATCH void Matrix_Mult(MtxF* mf, MatrixMode mode) {
MtxF* cmf = Matrix_GetCurrent();
if (mode == MTXMODE_APPLY) {
@@ -183,7 +183,7 @@ void Matrix_Mult(MtxF* mf, MatrixMode mode) {
}
}
void Matrix_Translate(f32 x, f32 y, f32 z, MatrixMode mode) {
RECOMP_PATCH void Matrix_Translate(f32 x, f32 y, f32 z, MatrixMode mode) {
MtxF* cmf = sCurrentMatrix;
f32 tempX;
f32 tempY;
@@ -209,7 +209,7 @@ void Matrix_Translate(f32 x, f32 y, f32 z, MatrixMode mode) {
}
}
void Matrix_Scale(f32 x, f32 y, f32 z, MatrixMode mode) {
RECOMP_PATCH void Matrix_Scale(f32 x, f32 y, f32 z, MatrixMode mode) {
MtxF* cmf = sCurrentMatrix;
if (mode == MTXMODE_APPLY) {
@@ -233,7 +233,7 @@ void Matrix_Scale(f32 x, f32 y, f32 z, MatrixMode mode) {
}
}
void Matrix_RotateXS(s16 x, MatrixMode mode) {
RECOMP_PATCH void Matrix_RotateXS(s16 x, MatrixMode mode) {
MtxF* cmf;
f32 sin;
f32 cos;
@@ -300,7 +300,7 @@ void Matrix_RotateXS(s16 x, MatrixMode mode) {
}
}
void Matrix_RotateXF(f32 x, MatrixMode mode) {
RECOMP_PATCH void Matrix_RotateXF(f32 x, MatrixMode mode) {
MtxF* cmf;
f32 sin;
f32 cos;
@@ -369,7 +369,7 @@ void Matrix_RotateXF(f32 x, MatrixMode mode) {
}
}
void Matrix_RotateXFNew(f32 x) {
RECOMP_PATCH void Matrix_RotateXFNew(f32 x) {
MtxF* cmf = sCurrentMatrix;
s32 pad[2];
f32 sin;
@@ -406,7 +406,7 @@ void Matrix_RotateXFNew(f32 x) {
// @recomp Clear the current billboard state.
*current_billboard_state = false;
}
void Matrix_RotateYS(s16 y, MatrixMode mode) {
RECOMP_PATCH void Matrix_RotateYS(s16 y, MatrixMode mode) {
MtxF* cmf;
f32 sin;
f32 cos;
@@ -473,7 +473,7 @@ void Matrix_RotateYS(s16 y, MatrixMode mode) {
}
}
void Matrix_RotateYF(f32 y, MatrixMode mode) {
RECOMP_PATCH void Matrix_RotateYF(f32 y, MatrixMode mode) {
MtxF* cmf;
f32 sin;
f32 cos;
@@ -542,7 +542,7 @@ void Matrix_RotateYF(f32 y, MatrixMode mode) {
}
}
void Matrix_RotateZS(s16 z, MatrixMode mode) {
RECOMP_PATCH void Matrix_RotateZS(s16 z, MatrixMode mode) {
MtxF* cmf;
f32 sin;
f32 cos;
@@ -611,7 +611,7 @@ void Matrix_RotateZS(s16 z, MatrixMode mode) {
}
}
void Matrix_RotateZF(f32 z, MatrixMode mode) {
RECOMP_PATCH void Matrix_RotateZF(f32 z, MatrixMode mode) {
MtxF* cmf;
f32 sin;
f32 cos;
@@ -678,7 +678,7 @@ void Matrix_RotateZF(f32 z, MatrixMode mode) {
}
}
void Matrix_RotateZYX(s16 x, s16 y, s16 z, MatrixMode mode) {
RECOMP_PATCH void Matrix_RotateZYX(s16 x, s16 y, s16 z, MatrixMode mode) {
MtxF* cmf = sCurrentMatrix;
f32 temp1;
f32 temp2;
@@ -768,7 +768,7 @@ void Matrix_RotateZYX(s16 x, s16 y, s16 z, MatrixMode mode) {
}
}
void Matrix_SetTranslateRotateYXZ(f32 x, f32 y, f32 z, Vec3s* rot) {
RECOMP_PATCH void Matrix_SetTranslateRotateYXZ(f32 x, f32 y, f32 z, Vec3s* rot) {
MtxF* cmf = sCurrentMatrix;
f32 sinY = Math_SinS(rot->y);
f32 cosY = Math_CosS(rot->y);
@@ -829,7 +829,7 @@ void Matrix_SetTranslateRotateYXZ(f32 x, f32 y, f32 z, Vec3s* rot) {
*current_billboard_state = false;
}
void Matrix_RotateAxisF(f32 angle, Vec3f* axis, MatrixMode mode) {
RECOMP_PATCH void Matrix_RotateAxisF(f32 angle, Vec3f* axis, MatrixMode mode) {
MtxF* cmf;
f32 sin;
f32 cos;
@@ -926,7 +926,7 @@ void Matrix_RotateAxisF(f32 angle, Vec3f* axis, MatrixMode mode) {
}
}
void Matrix_RotateAxisS(s16 angle, Vec3f* axis, MatrixMode mode) {
RECOMP_PATCH void Matrix_RotateAxisS(s16 angle, Vec3f* axis, MatrixMode mode) {
MtxF* cmf;
f32 cos;
f32 sin;

View File

@@ -9,6 +9,8 @@
#include "z64shrink_window.h"
#include "z64player.h"
static bool camera_fixes = false;
static bool prev_analog_cam_active = false;
static bool can_use_analog_cam = false;
static bool analog_cam_active = false;
@@ -26,6 +28,10 @@ float analog_camera_y_sensitivity = 500.0f;
static const float analog_cam_threshold = 0.1f;
RECOMP_EXPORT void recomp_set_camera_fixes(bool new_val) {
camera_fixes = new_val;
}
void update_analog_camera_params(Camera* camera) {
// recomp_printf("Camera at: %.2f %.2f %.2f\n"
// " eye: %.2f %.2f %.2f\n"
@@ -48,17 +54,17 @@ void update_analog_camera_params(Camera* camera) {
void update_analog_cam(Camera* c) {
can_use_analog_cam = true;
Player* player = GET_PLAYER(c->play);
// recomp_printf(" val: %d\n", func_80123434(player));
// Check if the player just started Z targeting and reset to auto cam if so.
static bool prev_targeting_held = false;
bool targeting_held = func_80123434(player) || player->lockOnActor != NULL;
if (targeting_held && !prev_targeting_held) {
analog_cam_active = false;
}
// Enable analog cam if the right stick is held.
float input_x, input_y;
recomp_get_camera_inputs(&input_x, &input_y);
@@ -66,12 +72,12 @@ void update_analog_cam(Camera* c) {
if (fabsf(input_x) >= analog_cam_threshold || fabsf(input_y) >= analog_cam_threshold) {
analog_cam_active = true;
}
if (analog_cam_skip_once) {
analog_cam_active = false;
analog_cam_skip_once = false;
}
// Record the Z targeting state.
prev_targeting_held = targeting_held;
@@ -92,7 +98,7 @@ void update_analog_cam(Camera* c) {
analog_camera_pos.pitch += analog_camera_pitch_vel;
analog_camera_pos.yaw += analog_camera_yaw_vel;
if (analog_camera_pos.pitch > 0x36B0) {
analog_camera_pos.pitch = 0x36B0;
}
@@ -176,7 +182,7 @@ void Camera_UpdateInterface(s32 interfaceFlags);
s32 func_800CB7CC(Camera* camera);
s32 func_800CB854(Camera* camera);
Vec3s Camera_Update(Camera* camera) {
RECOMP_PATCH Vec3s Camera_Update(Camera* camera) {
Vec3f viewAt;
Vec3f viewEye;
Vec3f viewUp;
@@ -461,7 +467,7 @@ s32 func_800CBA7C(Camera* camera);
#define RELOAD_PARAMS(camera) ((camera->animState == 0) || (camera->animState == 10) || (camera->animState == 20))
// @recomp Patched for analog cam.
s32 Camera_Normal1(Camera* camera) {
RECOMP_PATCH s32 Camera_Normal1(Camera* camera) {
Vec3f* eye = &camera->eye;
Vec3f* at = &camera->at;
Vec3f* eyeNext = &camera->eyeNext;
@@ -782,9 +788,9 @@ s32 Camera_Normal1(Camera* camera) {
}
// @recomp Update the analog camera.
if (recomp_analog_cam_enabled()) {
if (recomp_get_analog_cam_enabled()) {
update_analog_cam(camera);
if (analog_cam_active) {
spB4.pitch = analog_camera_pos.pitch;
// spB4.r = analog_camera_pos.r;
@@ -838,7 +844,7 @@ s32 Camera_Normal1(Camera* camera) {
phi_f2 = (gSaveContext.save.saveInfo.playerData.health <= 0x10) ? 0.8f : 1.0f;
// @recomp Don't zoom in on low health when dual analog is used
if (recomp_analog_cam_enabled()) {
if (recomp_get_analog_cam_enabled()) {
phi_f2 = 1.0f;
}
@@ -870,7 +876,7 @@ s32 Camera_Normal1(Camera* camera) {
* Camera for climbing structures
*/
// @recomp Patched for analog cam.
s32 Camera_Jump2(Camera* camera) {
RECOMP_PATCH s32 Camera_Jump2(Camera* camera) {
Vec3f* eye = &camera->eye;
Vec3f* at = &camera->at;
Vec3f* eyeNext = &camera->eyeNext;
@@ -1017,14 +1023,14 @@ s32 Camera_Jump2(Camera* camera) {
camera->pitchUpdateRateInv = 100.0f;
camera->rUpdateRateInv = 100.0f;
}
spB4.pitch = CLAMP_MAX(spB4.pitch, DEG_TO_BINANG(60.43f));
spB4.pitch = CLAMP_MIN(spB4.pitch, -DEG_TO_BINANG(60.43f));
// @recomp Update the analog camera.
if (recomp_analog_cam_enabled()) {
if (recomp_get_analog_cam_enabled()) {
update_analog_cam(camera);
if (analog_cam_active) {
spB4.pitch = analog_camera_pos.pitch;
// spB4.r = analog_camera_pos.r;
@@ -1063,7 +1069,11 @@ s32 Camera_Jump2(Camera* camera) {
* Used for targeting
*/
// @recomp Patched for analog cam.
s32 Camera_Parallel1(Camera* camera) {
RECOMP_PATCH s32 Camera_Parallel1(Camera* camera) {
// @recomp
static bool prev_targeting_held = false;
bool isChargingDekuFlowerDive = !!(((Player*)camera->focalActor)->stateFlags3 & PLAYER_STATE3_100);
Vec3f* eye = &camera->eye;
Vec3f* at = &camera->at;
Vec3f* eyeNext = &camera->eyeNext;
@@ -1089,6 +1099,10 @@ s32 Camera_Parallel1(Camera* camera) {
CameraModeValue* values;
f32 yNormal;
// @recomp Read timer4 from timer2.
s16 timer4 = rwData->timer2 >> 5; // @recomp Used to check if z-target can be quit. Mirrors timer2 values during z-target in unmodified function.
rwData->timer2 &= 0x001F;
if (!RELOAD_PARAMS(camera)) {
} else {
values = sCameraSettings[camera->setting].cameraModes[camera->mode].values;
@@ -1151,6 +1165,8 @@ s32 Camera_Parallel1(Camera* camera) {
rwData->timer2 = 20;
} else {
rwData->timer2 = 6;
// @recomp Initiate timer4 for z-target.
timer4 = 6;
}
if ((camera->focalActor == &GET_PLAYER(camera->play)->actor) && (camera->mode == CAM_MODE_CHARGE)) {
@@ -1187,12 +1203,55 @@ s32 Camera_Parallel1(Camera* camera) {
rwData->unk_26 = 1;
camera->animState = 1;
sCameraInterfaceFlags = roData->interfaceFlags;
// @recomp Reset prev_targeting_held after transition.
prev_targeting_held = false;
break;
}
// @recomp Change behavior for z-target only.
if (camera_fixes) {
if ((roData->interfaceFlags & (PARALLEL1_FLAG_3 | PARALLEL1_FLAG_2 | PARALLEL1_FLAG_1)) == PARALLEL1_FLAG_1) {
Player* player = GET_PLAYER(camera->play);
bool targeting_held = func_80123434(player) || player->lockOnActor != NULL;
// @recomp Fix camera rotating with player if z-target gets released too fast after transition.
if ((targeting_held) && (!prev_targeting_held)) {
// @recomp Reset timer2 to avoid immediate rotation, if player presses, releases and presses z-target in a very short time-window.
rwData->timer2 = 6;
rwData->unk_1E = BINANG_ROT180(camera->focalActorPosRot.rot.y) + roData->unk_22;
}
// @recomp Maintain vanilla behavior for quitting z-target.
if ((timer4 == 0) && (!targeting_held)) {
rwData->timer2 = 0;
}
// @recomp Decrease timer4 only in cases where timer2 would be decreased.
if ((timer4 > 0)
&& (rwData->timer3 <= 0)) {
timer4--;
}
prev_targeting_held = targeting_held;
}
}
if (rwData->timer2 != 0) {
switch (roData->interfaceFlags & (PARALLEL1_FLAG_3 | PARALLEL1_FLAG_2 | PARALLEL1_FLAG_1)) {
case PARALLEL1_FLAG_1:
if (camera_fixes) {
// @recomp Fix camera rotating with player if z-target gets released too fast after transition.
if (isChargingDekuFlowerDive) {
// @recomp Fix camera wiggle during dive into deku flower.
rwData->unk_1E = BINANG_ROT180(camera->focalActorPosRot.rot.y) + roData->unk_22;
}
rwData->unk_20 = roData->unk_20;
break;
}
// @recomp fallthrough
case (PARALLEL1_FLAG_3 | PARALLEL1_FLAG_2 | PARALLEL1_FLAG_1):
rwData->unk_1E = BINANG_ROT180(camera->focalActorPosRot.rot.y) + roData->unk_22;
rwData->unk_20 = roData->unk_20;
@@ -1372,9 +1431,9 @@ s32 Camera_Parallel1(Camera* camera) {
}
// @recomp Update the analog camera.
if (recomp_analog_cam_enabled()) {
if (recomp_get_analog_cam_enabled()) {
update_analog_cam(camera);
if (analog_cam_active) {
sp90.pitch = analog_camera_pos.pitch;
// sp90.r = analog_camera_pos.r;
@@ -1400,10 +1459,20 @@ s32 Camera_Parallel1(Camera* camera) {
func_800CBFA4(camera, at, eye, 3);
}
if (rwData->timer2 != 0) {
sUpdateCameraDirection = true;
if (camera_fixes) {
// @recomp Fix camera not updating input dir for the first few frames after transition.
if ((isChargingDekuFlowerDive) && (rwData->timer2 != 0)) {
// @recomp Fix camera wiggle during dive into deku flower.
sUpdateCameraDirection = true;
} else {
sUpdateCameraDirection = false;
}
} else {
sUpdateCameraDirection = false;
if (rwData->timer2 != 0) {
sUpdateCameraDirection = true;
} else {
sUpdateCameraDirection = false;
}
}
}
@@ -1412,6 +1481,9 @@ s32 Camera_Parallel1(Camera* camera) {
camera->atLerpStepScale = Camera_ClampLerpScale(camera, sp72 ? roData->unk_1C : roData->unk_18);
rwData->unk_26 &= ~1;
// @recomp Save timer4 in unused bits of timer2.
rwData->timer2 |= timer4 << 5;
return 1;
}
@@ -1421,7 +1493,7 @@ s32 Camera_Parallel1(Camera* camera) {
* Riding Epona and Zora
*/
// @recomp Patched for analog cam.
s32 Camera_Normal3(Camera* camera) {
RECOMP_PATCH s32 Camera_Normal3(Camera* camera) {
Normal3ReadOnlyData* roData = &camera->paramData.norm3.roData;
Normal3ReadWriteData* rwData = &camera->paramData.norm3.rwData;
f32 sp8C;
@@ -1580,11 +1652,11 @@ s32 Camera_Normal3(Camera* camera) {
Camera_UnsetStateFlag(camera, CAM_STATE_DISABLE_MODE_CHANGE);
}
}
// @recomp Update the analog camera.
if (recomp_analog_cam_enabled()) {
if (recomp_get_analog_cam_enabled()) {
update_analog_cam(camera);
if (analog_cam_active) {
sp80.pitch = analog_camera_pos.pitch;
// sp80.r = analog_camera_pos.r;
@@ -1620,7 +1692,7 @@ s32 Camera_Normal3(Camera* camera) {
* e.g. Gyorg, Pinnacle Rock, whirlpool, water
*/
// @recomp Patched for analog cam.
s32 Camera_Jump3(Camera* camera) {
RECOMP_PATCH s32 Camera_Jump3(Camera* camera) {
Vec3f* sp48 = &camera->eye;
Vec3f* sp44 = &camera->at;
Vec3f* sp40 = &camera->eyeNext;
@@ -1820,9 +1892,9 @@ s32 Camera_Jump3(Camera* camera) {
}
// @recomp Update the analog camera.
if (recomp_analog_cam_enabled()) {
if (recomp_get_analog_cam_enabled()) {
update_analog_cam(camera);
if (analog_cam_active) {
spAC.pitch = analog_camera_pos.pitch;
// spAC.r = analog_camera_pos.r;
@@ -1871,7 +1943,7 @@ void analog_cam_post_play_update(PlayState* play) {
// recomp_printf("prev_analog_cam_active: %d can_use_analog_cam: %d\n", prev_analog_cam_active, can_use_analog_cam);
// recomp_printf("setting: %d mode: %d func: %d\n", active_cam->setting, active_cam->mode, sCameraSettings[active_cam->setting].cameraModes[active_cam->mode].funcId);
// recomp_printf("active cam yaw %d\n", Camera_GetInputDirYaw(GET_ACTIVE_CAM(play)));
// Update parameters for the analog cam if the game is unpaused.
if (play->pauseCtx.state == PAUSE_STATE_OFF && R_PAUSE_BG_PRERENDER_STATE == PAUSE_BG_PRERENDER_OFF) {
update_analog_camera_params(active_cam);
@@ -1905,14 +1977,12 @@ extern u8 D_809EE4D0;
// @recomp Patch the Wart boss fight in the Great Bay temple so that the fight starts if you look at it with the right stick analog camera,
// instead of requiring entering first person mode mode.
void func_809EC568(Boss04* this, PlayState* play) {
RECOMP_PATCH void func_809EC568(Boss04* this, PlayState* play) {
Player* player = GET_PLAYER(play);
f32 x;
f32 y;
f32 z;
s32 pad;
// @recomp Manual relocation, TODO remove when automated.
u8* D_809EE4D0_relocated = (u8*)actor_relocate(&this->actor, &D_809EE4D0);
u16 maxProjectedPosToStartFight;
// @recomp Change the maximun projected position to start the fight depending on whether analog camera is enabled or not.
@@ -1936,7 +2006,7 @@ void func_809EC568(Boss04* this, PlayState* play) {
this->unk_2D0 = 2000.0f;
// @recomp do not require being in c-up mode if analog cam is enabled
// also, use the new variable instead of the vanilla value to check if the player is looking at the boss.
if (((player->stateFlags1 & PLAYER_STATE1_100000) || (recomp_analog_cam_enabled())) && (this->actor.projectedPos.z > 0.0f) &&
if (((player->stateFlags1 & PLAYER_STATE1_100000) || (recomp_get_analog_cam_enabled())) && (this->actor.projectedPos.z > 0.0f) &&
(fabsf(this->actor.projectedPos.x) < maxProjectedPosToStartFight) && (fabsf(this->actor.projectedPos.y) < maxProjectedPosToStartFight)) {
if ((this->unk_704 >= 15) && (CutsceneManager_GetCurrentCsId() == CS_ID_NONE)) {
Actor* boss;
@@ -2058,8 +2128,8 @@ void func_809EC568(Boss04* this, PlayState* play) {
if (this->unk_704 == 70) {
this->unk_2C8 = 300;
this->unk_2D0 = 0.0f;
*D_809EE4D0_relocated = 1;
D_809EE4D0 = 1;
this->unk_2E2 = 60;
this->unk_2E0 = 93;
}

View File

@@ -76,6 +76,17 @@ void camera_post_play_update(PlayState* play) {
if (force_interpolation) {
force_camera_interpolation();
}
// Dedicated section for workarounds where the heuristic fails to detect small camera teleports.
bool force_no_interpolation = false;
// Music Box House. The camera gets teleported by a very small amount when Link gets the Gibdo mask.
if (play->sceneId == SCENE_MUSICHOUSE && play->csCtx.scriptIndex == 2 && play->csCtx.curFrame == 525 && active_cam->setting == CAM_SET_FREE0) {
force_no_interpolation = true;
}
if (force_no_interpolation) {
force_camera_skip_interpolation();
}
}
}
}
@@ -95,7 +106,7 @@ void force_camera_ignore_tracking() {
camera_ignore_tracking = true;
}
void KaleidoScope_SetView(PauseContext* pauseCtx, f32 eyeX, f32 eyeY, f32 eyeZ) {
RECOMP_PATCH void KaleidoScope_SetView(PauseContext* pauseCtx, f32 eyeX, f32 eyeY, f32 eyeZ) {
Vec3f eye;
Vec3f at;
Vec3f up;
@@ -117,7 +128,7 @@ void KaleidoScope_SetView(PauseContext* pauseCtx, f32 eyeX, f32 eyeY, f32 eyeZ)
}
void FileSelect_SetView(FileSelectState* this, f32 eyeX, f32 eyeY, f32 eyeZ) {
RECOMP_PATCH void FileSelect_SetView(FileSelectState* this, f32 eyeX, f32 eyeY, f32 eyeZ) {
Vec3f eye;
Vec3f lookAt;
Vec3f up;
@@ -197,7 +208,7 @@ bool should_interpolate_perspective(Vec3f* eye, Vec3f* at) {
/**
* Apply view to POLY_OPA_DISP, POLY_XLU_DISP (and OVERLAY_DISP if ortho)
*/
void View_Apply(View* view, s32 mask) {
RECOMP_PATCH void View_Apply(View* view, s32 mask) {
mask = (view->flags & mask) | (mask >> 4);
// @recomp Determine if the camera should be interpolated this frame.
@@ -292,7 +303,7 @@ Vec3f Camera_Vec3sToVec3f(Vec3s* src);
* Used for many fixed-based camera settings i.e. camera is fixed in rotation, and often position (but not always)
*/
// @recomp Modified to not force interpolation while panning.
s32 Camera_Fixed1(Camera* camera) {
RECOMP_PATCH s32 Camera_Fixed1(Camera* camera) {
s32 pad[2];
s32 yawDiff;
VecGeo eyeOffset;

View File

@@ -1,7 +1,7 @@
#include "patches.h"
// Disable frustum culling for actors, but leave distance culling intact
s32 func_800BA2FC(PlayState* play, Actor* actor, Vec3f* projectedPos, f32 projectedW) {
RECOMP_PATCH s32 func_800BA2FC(PlayState* play, Actor* actor, Vec3f* projectedPos, f32 projectedW) {
if ((-actor->uncullZoneScale < projectedPos->z) &&
(projectedPos->z < (actor->uncullZoneForward + actor->uncullZoneScale))) {
// f32 phi_f12;
@@ -30,7 +30,7 @@ s32 func_800BA2FC(PlayState* play, Actor* actor, Vec3f* projectedPos, f32 projec
}
// Disable frustum culling for bush spawning
// s32 EnWood02_SpawnZoneCheck(EnWood02* this, PlayState* play, Vec3f* arg2) {
// RECOMP_PATCH s32 EnWood02_SpawnZoneCheck(EnWood02* this, PlayState* play, Vec3f* arg2) {
// f32 phi_f12;
// SkinMatrix_Vec3fMtxFMultXYZW(&play->viewProjectionMtxF, arg2, &this->actor.projectedPos, &this->actor.projectedW);
@@ -52,7 +52,7 @@ s32 func_800BA2FC(PlayState* play, Actor* actor, Vec3f* projectedPos, f32 projec
// }
// Disable frustum culling for grass
s32 func_809A9110(PlayState* play, Vec3f* pos) {
RECOMP_PATCH s32 func_809A9110(PlayState* play, Vec3f* pos) {
f32 w;
Vec3f projectedPos;
@@ -73,7 +73,7 @@ s32 func_809A9110(PlayState* play, Vec3f* pos) {
// Replace point light glow effect with RT64 point Z test so it works in widescreen
void Lights_GlowCheck(PlayState* play) {
RECOMP_PATCH void Lights_GlowCheck(PlayState* play) {
LightNode* light = play->lightCtx.listHead;
while (light != NULL) {
@@ -107,7 +107,7 @@ extern Gfx gameplay_keep_DL_029CF0[];
Vtx light_test_vert = VTX(0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF);
void Lights_DrawGlow(PlayState* play) {
RECOMP_PATCH void Lights_DrawGlow(PlayState* play) {
Gfx* dl;
LightPoint* params;
LightNode* light = play->lightCtx.listHead;

46
patches/custom_syms.toml Normal file
View File

@@ -0,0 +1,46 @@
# Custom symbols used for patches.
[[section]]
name = "ABSOLUTE_SYMS"
vram = 0x00000000
size = 0x0
symbols = [
# Alternate references to symbols that would otherwise be relocated
{ name = "Setup_Init_NORELOCATE", vram = 0x80173338 }, # Not relocatable, but here for uniformity with the other gamestate funcs
{ name = "MapSelect_Init_NORELOCATE", vram = 0x80801B4C },
{ name = "ConsoleLogo_Init_NORELOCATE", vram = 0x8080074C },
{ name = "Play_Init_NORELOCATE", vram = 0x8016A2C8 },
{ name = "TitleSetup_Init_NORELOCATE", vram = 0x80803F30 },
{ name = "FileSelect_Init_NORELOCATE", vram = 0x80813C98 },
{ name = "DayTelop_Init_NORELOCATE", vram = 0x80815820 },
]
[[section]]
name = "..ovl_En_Hg"
rom = 0x01034170
vram = 0x80BCF1D0
size = 0x10E0
symbols = [
{ name = "sPamelasFatherGibdoAnimationInfo", vram = 0x80bd0008 },
]
[[section]]
name = "..ovl_En_Hgo"
rom = 0x01035250
vram = 0x80BD02B0
size = 0xF30
symbols = [
{ name = "sPamelasFatherHumanAnimationInfo", vram = 0x80BD0EA0 },
]
[[section]]
name = "..ovl_En_Pamera"
rom = 0x0103D250
vram = 0x80BD82B0
size = 0x2780
symbols = [
{ name = "sPamelaAnimationInfo", vram = 0x80BDA4B8 },
]

View File

@@ -6,7 +6,7 @@
CsCmdActorCue* prev_cues_checked[ARRAY_COUNT(((CutsceneContext*)0)->actorCues)] = {0};
void Cutscene_ActorTranslate(Actor* actor, PlayState* play, s32 cueChannel) {
RECOMP_PATCH void Cutscene_ActorTranslate(Actor* actor, PlayState* play, s32 cueChannel) {
Vec3f startPos;
Vec3f endPos;
CsCmdActorCue* cue = play->csCtx.actorCues[cueChannel];
@@ -39,7 +39,7 @@ extern EnHorseCsFunc D_808890F0[];
extern EnHorseCsFunc D_8088911C[];
// @recomp Patched to skip interpolation on Epona when she's teleported by a cutscene.
void func_80884718(EnHorse* this, PlayState* play) {
RECOMP_PATCH void func_80884718(EnHorse* this, PlayState* play) {
CsCmdActorCue* cue;
if (Cutscene_IsCueInChannel(play, CS_CMD_ACTOR_CUE_112)) {
@@ -63,23 +63,19 @@ void func_80884718(EnHorse* this, PlayState* play) {
}
this->cueId = cue->id;
// @recomp Manual relocation, TODO remove when automated.
EnHorseCsFunc* D_808890F0_reloc = actor_relocate(&this->actor, D_808890F0);
if (D_808890F0_reloc[this->cueId] != NULL) {
D_808890F0_reloc[this->cueId](this, play, cue);
if (D_808890F0[this->cueId] != NULL) {
D_808890F0[this->cueId](this, play, cue);
}
}
// @recomp Manual relocation, TODO remove when automated.
EnHorseCsFunc* D_8088911C_reloc = actor_relocate(&this->actor, D_8088911C);
if (D_8088911C_reloc[this->cueId] != NULL) {
D_8088911C_reloc[this->cueId](this, play, cue);
if (D_8088911C[this->cueId] != NULL) {
D_8088911C[this->cueId](this, play, cue);
}
}
}
// @recomp Patched to skip interpolation on Epona when she's teleported by a cutscene.
void func_80883B70(EnHorse* this, CsCmdActorCue* cue) {
RECOMP_PATCH void func_80883B70(EnHorse* this, CsCmdActorCue* cue) {
// @recomp Being teleported by a new cue, so skip interpolation.
actor_set_interpolation_skipped(&this->actor);
@@ -94,7 +90,7 @@ void func_80883B70(EnHorse* this, CsCmdActorCue* cue) {
}
// @recomp Patched to skip interpolation on Link when he's teleported to a new cue.
void Player_Cutscene_SetPosAndYawToStart(Player* this, CsCmdActorCue* cue) {
RECOMP_PATCH void Player_Cutscene_SetPosAndYawToStart(Player* this, CsCmdActorCue* cue) {
// @recomp Being teleported by a new cue, so skip interpolation.
actor_set_interpolation_skipped(&this->actor);
@@ -108,7 +104,7 @@ void Player_Cutscene_SetPosAndYawToStart(Player* this, CsCmdActorCue* cue) {
CsCmdActorCue* prev_link_cue = NULL;
// @recomp Patched to skip interpolation on Link when he's teleported to a new cue.
void Player_Cutscene_Translate(PlayState* play, Player* this, CsCmdActorCue* cue) {
RECOMP_PATCH void Player_Cutscene_Translate(PlayState* play, Player* this, CsCmdActorCue* cue) {
f32 startX = cue->startPos.x;
f32 startY = cue->startPos.y;
f32 startZ = cue->startPos.z;

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 955 B

View File

@@ -0,0 +1,45 @@
typedef enum PamelasFatherGibdoLimb {
/* 0x00 */ PAMELAS_FATHER_GIBDO_LIMB_NONE,
/* 0x01 */ PAMELAS_FATHER_GIBDO_LIMB_ROOT,
/* 0x02 */ PAMELAS_FATHER_GIBDO_LIMB_ABDOMEN,
/* 0x03 */ PAMELAS_FATHER_GIBDO_LIMB_CHEST,
/* 0x04 */ PAMELAS_FATHER_GIBDO_LIMB_LEFT_UPPER_ARM,
/* 0x05 */ PAMELAS_FATHER_GIBDO_LIMB_LEFT_FOREARM,
/* 0x06 */ PAMELAS_FATHER_GIBDO_LIMB_LEFT_HAND,
/* 0x07 */ PAMELAS_FATHER_GIBDO_LIMB_RIGHT_UPPER_ARM,
/* 0x08 */ PAMELAS_FATHER_GIBDO_LIMB_RIGHT_FOREARM,
/* 0x09 */ PAMELAS_FATHER_GIBDO_LIMB_RIGHT_HAND,
/* 0x0A */ PAMELAS_FATHER_GIBDO_LIMB_EYEBROWS,
/* 0x0B */ PAMELAS_FATHER_GIBDO_LIMB_HEAD,
/* 0x0C */ PAMELAS_FATHER_GIBDO_LIMB_PELVIS,
/* 0x0D */ PAMELAS_FATHER_GIBDO_LIMB_LEFT_THIGH,
/* 0x0E */ PAMELAS_FATHER_GIBDO_LIMB_LEFT_SHIN,
/* 0x0F */ PAMELAS_FATHER_GIBDO_LIMB_LEFT_FOOT,
/* 0x10 */ PAMELAS_FATHER_GIBDO_LIMB_RIGHT_THIGH,
/* 0x11 */ PAMELAS_FATHER_GIBDO_LIMB_RIGHT_SHIN,
/* 0x12 */ PAMELAS_FATHER_GIBDO_LIMB_RIGHT_FOOT,
/* 0x13 */ PAMELAS_FATHER_GIBDO_LIMB_MAX
} PamelasFatherGibdoLimb;
typedef enum PamelasFatherHumanLimb {
/* 0x00 */ PAMELAS_FATHER_HUMAN_LIMB_NONE,
/* 0x01 */ PAMELAS_FATHER_HUMAN_LIMB_ROOT,
/* 0x02 */ PAMELAS_FATHER_HUMAN_LIMB_ABDOMEN,
/* 0x03 */ PAMELAS_FATHER_HUMAN_LIMB_CHEST,
/* 0x04 */ PAMELAS_FATHER_HUMAN_LIMB_LEFT_UPPER_ARM,
/* 0x05 */ PAMELAS_FATHER_HUMAN_LIMB_LEFT_FOREARM,
/* 0x06 */ PAMELAS_FATHER_HUMAN_LIMB_LEFT_HAND,
/* 0x07 */ PAMELAS_FATHER_HUMAN_LIMB_RIGHT_UPPER_ARM,
/* 0x08 */ PAMELAS_FATHER_HUMAN_LIMB_RIGHT_FOREARM,
/* 0x09 */ PAMELAS_FATHER_HUMAN_LIMB_RIGHT_HAND,
/* 0x0A */ PAMELAS_FATHER_HUMAN_LIMB_EYEBROWS,
/* 0x0B */ PAMELAS_FATHER_HUMAN_LIMB_HEAD,
/* 0x0C */ PAMELAS_FATHER_HUMAN_LIMB_PELVIS,
/* 0x0D */ PAMELAS_FATHER_HUMAN_LIMB_LEFT_THIGH,
/* 0x0E */ PAMELAS_FATHER_HUMAN_LIMB_LEFT_SHIN,
/* 0x0F */ PAMELAS_FATHER_HUMAN_LIMB_LEFT_FOOT,
/* 0x10 */ PAMELAS_FATHER_HUMAN_LIMB_RIGHT_THIGH,
/* 0x11 */ PAMELAS_FATHER_HUMAN_LIMB_RIGHT_SHIN,
/* 0x12 */ PAMELAS_FATHER_HUMAN_LIMB_RIGHT_FOOT,
/* 0x13 */ PAMELAS_FATHER_HUMAN_LIMB_MAX
} PamelasFatherHumanLimb;

View File

@@ -0,0 +1,26 @@
typedef enum PamelaLimb {
/* 0x00 */ PAMELA_LIMB_NONE,
/* 0x01 */ PAMELA_LIMB_ROOT,
/* 0x02 */ PAMELA_LIMB_UPPER_BODY_ROOT,
/* 0x03 */ PAMELA_LIMB_LEFT_UPPER_ARM,
/* 0x04 */ PAMELA_LIMB_LEFT_FOREARM,
/* 0x05 */ PAMELA_LIMB_LEFT_HAND,
/* 0x06 */ PAMELA_LIMB_RIGHT_UPPER_ARM,
/* 0x07 */ PAMELA_LIMB_RIGHT_FOREARM,
/* 0x08 */ PAMELA_LIMB_RIGHT_HAND,
/* 0x09 */ PAMELA_LIMB_HEAD,
/* 0x0A */ PAMELA_LIMB_HAIR_END,
/* 0x0B */ PAMELA_LIMB_CHEST,
/* 0x0C */ PAMELA_LIMB_NECK,
/* 0x0D */ PAMELA_LIMB_LEFT_THIGH,
/* 0x0E */ PAMELA_LIMB_LEFT_LEG,
/* 0x0F */ PAMELA_LIMB_LEFT_FOOT,
/* 0x10 */ PAMELA_LIMB_RIGHT_THIGH,
/* 0x11 */ PAMELA_LIMB_RIGHT_LEG,
/* 0x12 */ PAMELA_LIMB_RIGHT_FOOT,
/* 0x13 */ PAMELA_LIMB_FRONT_DRESS,
/* 0x14 */ PAMELA_LIMB_BACK_DRESS,
/* 0x15 */ PAMELA_LIMB_ABDOMEN,
/* 0x16 */ PAMELA_LIMB_PELVIS,
/* 0x17 */ PAMELA_LIMB_MAX
} PamelaLimb;

View File

@@ -9,7 +9,7 @@ extern Gfx sTransWipe3DL[];
#define THIS ((TransitionWipe3*)thisx)
// @recomp patched to scale the transition based on aspect ratio
void TransitionWipe3_Draw(void* thisx, Gfx** gfxP) {
RECOMP_PATCH void TransitionWipe3_Draw(void* thisx, Gfx** gfxP) {
Gfx* gfx = *gfxP;
Mtx* modelView = &THIS->modelView[THIS->frame];
f32 scale = 14.8f;
@@ -17,7 +17,7 @@ void TransitionWipe3_Draw(void* thisx, Gfx** gfxP) {
// @recomp Modify the scale based on the aspect ratio to make sure the transition circle covers the whole screen
float original_aspect_ratio = ((float)SCREEN_WIDTH) / ((float)SCREEN_HEIGHT);
scale *= recomp_get_aspect_ratio(original_aspect_ratio) / original_aspect_ratio;
scale *= recomp_get_target_aspect_ratio(original_aspect_ratio) / original_aspect_ratio;
THIS->frame ^= 1;
gDPPipeSync(gfx++);
@@ -34,12 +34,7 @@ void TransitionWipe3_Draw(void* thisx, Gfx** gfxP) {
guScale(modelView, scale, scale, 1.0f);
gSPMatrix(gfx++, modelView, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
}
// sTransWipe3DL is an overlay symbol, so its addresses need to be offset to get the actual loaded vram address.
// TODO remove this once the recompiler is able to handle overlay symbols automatically for patch functions.
ptrdiff_t reloc_offset;
TransitionOverlay* overlay_entry = &gTransitionOverlayTable[FBDEMO_WIPE3];
reloc_offset = (uintptr_t)Lib_PhysicalToVirtual(overlay_entry->loadInfo.addr) - (uintptr_t)overlay_entry->vramStart;
gSPDisplayList(gfx++, (Gfx*)((u8*)sTransWipe3DL + reloc_offset));
gSPDisplayList(gfx++, sTransWipe3DL);
gDPPipeSync(gfx++);
*gfxP = gfx;
}
@@ -58,7 +53,7 @@ extern s32 gFramerateDivisor;
// @recomp Motion blur works fine normally, but when running at a higher framerate the effect is much less pronounced
// as the previous frames decay quicker due to there being more frames drawn in the same period of time.
void Play_DrawMotionBlur(PlayState* this) {
RECOMP_PATCH void Play_DrawMotionBlur(PlayState* this) {
GraphicsContext* gfxCtx = this->state.gfxCtx;
s32 alpha;
Gfx* gfx;
@@ -131,10 +126,10 @@ void Play_DrawMotionBlur(PlayState* this) {
}
// @recomp Patched to increase the scale based on the aspect ratio.
void Actor_DrawLensOverlay(Gfx** gfxP, s32 lensMaskSize) {
RECOMP_PATCH void Actor_DrawLensOverlay(Gfx** gfxP, s32 lensMaskSize) {
// @recomp Calculate the increase in aspect ratio.
f32 original_aspect_ratio = (float)SCREEN_WIDTH / SCREEN_HEIGHT;
f32 aspect_ratio_scale = recomp_get_aspect_ratio(original_aspect_ratio) / original_aspect_ratio;
f32 aspect_ratio_scale = recomp_get_target_aspect_ratio(original_aspect_ratio) / original_aspect_ratio;
// @recomp Increase the circle's scale based on the aspect ratio scale. Also increase the base scaling
// from 0.003f to 0.004f to account for overscan removal.
@@ -144,7 +139,7 @@ void Actor_DrawLensOverlay(Gfx** gfxP, s32 lensMaskSize) {
// @recomp Patched to use ortho tris for interpolation and to prevent the telescope and lens effects from getting stretched wide.
void TransitionCircle_LoadAndSetTexture(Gfx** gfxp, TexturePtr texture, s32 fmt, s32 arg3, s32 masks, s32 maskt,
RECOMP_PATCH void TransitionCircle_LoadAndSetTexture(Gfx** gfxp, TexturePtr texture, s32 fmt, s32 arg3, s32 masks, s32 maskt,
f32 arg6) {
Gfx* gfx = *gfxp;
s32 xh = gCfbWidth;

View File

@@ -13,7 +13,7 @@ extern Gfx gEffDustDL[];
((&(particle).unk_1C)[1])
// @recomp Patched to record when a particle is moved to skip interpolation.
void func_808DC454(ObjectKankyo* this, PlayState* play) {
RECOMP_PATCH void func_808DC454(ObjectKankyo* this, PlayState* play) {
s16 i;
s32 pad1;
f32 phi_f20;
@@ -155,7 +155,7 @@ void func_808DC454(ObjectKankyo* this, PlayState* play) {
}
}
void func_808DD3C8(Actor* thisx, PlayState* play2) {
RECOMP_PATCH void func_808DD3C8(Actor* thisx, PlayState* play2) {
PlayState* play = play2;
ObjectKankyo* this = (ObjectKankyo*)thisx;
Vec3f worldPos;
@@ -178,15 +178,12 @@ void func_808DD3C8(Actor* thisx, PlayState* play2) {
spB4 = false;
if (this->actor.params == 3) {
// @recomp Manual relocation, TODO remove when the recompiler handles this automatically.
f32* D_808DE5B0_ptr = actor_relocate(thisx, &D_808DE5B0);
temp_f0 = func_80173B48(&play->state) / 1.4e7f;
temp_f0 = CLAMP(temp_f0, 0.0f, 1.0f);
Math_SmoothStepToF(D_808DE5B0_ptr, temp_f0, 0.2f, 0.1f, 0.001f);
Math_SmoothStepToF(&D_808DE5B0, temp_f0, 0.2f, 0.1f, 0.001f);
sp68 = play->envCtx.precipitation[PRECIP_SNOW_CUR];
sp68 *= *D_808DE5B0_ptr;
sp68 *= D_808DE5B0;
if ((play->envCtx.precipitation[PRECIP_SNOW_CUR] >= 32) && (sp68 < 32)) {
sp68 = 32;
@@ -334,7 +331,7 @@ static inline void pop_effect_tag(GraphicsContext* gfxCtx) {
}
// @recomp Patched to tag effects.
void Effect_DrawAll(GraphicsContext* gfxCtx) {
RECOMP_PATCH void Effect_DrawAll(GraphicsContext* gfxCtx) {
s32 i;
@@ -445,7 +442,7 @@ static TexturePtr sWaterSplashTextures[] = {
* applies to all effects of that type while drawing the first effect of that type.
*/
// @recomp Patched to tag matrices.
void EnClearTag_DrawEffects(Actor* thisx, PlayState* play) {
RECOMP_PATCH void EnClearTag_DrawEffects(Actor* thisx, PlayState* play) {
u8 isMaterialApplied = false;
s16 i;
s16 j;
@@ -469,8 +466,7 @@ void EnClearTag_DrawEffects(Actor* thisx, PlayState* play) {
// Apply the debris effect material if it has not already been applied.
if (!isMaterialApplied) {
isMaterialApplied++;
// @recomp Manual relocation, TODO remove when automated.
gSPDisplayList(POLY_OPA_DISP++, actor_relocate(thisx, gClearTagDebrisEffectMaterialDL));
gSPDisplayList(POLY_OPA_DISP++, gClearTagDebrisEffectMaterialDL);
}
// Draw the debris effect.
@@ -481,8 +477,7 @@ void EnClearTag_DrawEffects(Actor* thisx, PlayState* play) {
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
// @recomp Tag the matrix.
gEXMatrixGroupDecomposedNormal(POLY_OPA_DISP++, actor_transform_id(thisx) + i, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE);
// @recomp Manual relocation, TODO remove when automated.
gSPDisplayList(POLY_OPA_DISP++, actor_relocate(thisx, gClearTagDebrisEffectDL));
gSPDisplayList(POLY_OPA_DISP++, gClearTagDebrisEffectDL);
// @recomp Pop the matrix tag.
gEXPopMatrixGroup(POLY_OPA_DISP++, G_MTX_MODELVIEW);
}
@@ -531,8 +526,7 @@ void EnClearTag_DrawEffects(Actor* thisx, PlayState* play) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
// @recomp Tag the matrix.
gEXMatrixGroupDecomposedNormal(POLY_XLU_DISP++, actor_transform_id(thisx) + i, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE);
// @recomp Manual relocation, TODO remove when automated.
gSPDisplayList(POLY_XLU_DISP++, actor_relocate(thisx, gClearTagFlashEffectGroundDL));
gSPDisplayList(POLY_XLU_DISP++, gClearTagFlashEffectGroundDL);
// @recomp Pop the matrix tag.
gEXPopMatrixGroup(POLY_XLU_DISP++, G_MTX_MODELVIEW);
}
@@ -546,8 +540,7 @@ void EnClearTag_DrawEffects(Actor* thisx, PlayState* play) {
if ((effect->type == CLEAR_TAG_EFFECT_SMOKE) || (effect->type == CLEAR_TAG_EFFECT_ISOLATED_SMOKE)) {
// Apply the smoke effect material if it has not already been applied.
if (!isMaterialApplied) {
// @recomp Manual relocation, TODO remove when automated.
gSPDisplayList(POLY_XLU_DISP++, actor_relocate(thisx, gClearTagFireEffectMaterialDL));
gSPDisplayList(POLY_XLU_DISP++, gClearTagFireEffectMaterialDL);
isMaterialApplied++;
}
@@ -566,8 +559,7 @@ void EnClearTag_DrawEffects(Actor* thisx, PlayState* play) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
// @recomp Tag the matrix.
gEXMatrixGroupDecomposedNormal(POLY_XLU_DISP++, actor_transform_id(thisx) + i, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE);
// @recomp Manual relocation, TODO remove when automated.
gSPDisplayList(POLY_XLU_DISP++, actor_relocate(thisx, gClearTagFireEffectDL));
gSPDisplayList(POLY_XLU_DISP++, gClearTagFireEffectDL);
// @recomp Pop the matrix tag.
gEXPopMatrixGroup(POLY_XLU_DISP++, G_MTX_MODELVIEW);
}
@@ -580,8 +572,7 @@ void EnClearTag_DrawEffects(Actor* thisx, PlayState* play) {
if (effect->type == CLEAR_TAG_EFFECT_FIRE) {
// Apply the fire effect material if it has not already been applied.
if (!isMaterialApplied) {
// @recomp Manual relocation, TODO remove when automated.
gSPDisplayList(POLY_XLU_DISP++, actor_relocate(thisx, gClearTagFireEffectMaterialDL));
gSPDisplayList(POLY_XLU_DISP++, gClearTagFireEffectMaterialDL);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 215, 255, 128);
isMaterialApplied++;
}
@@ -596,8 +587,7 @@ void EnClearTag_DrawEffects(Actor* thisx, PlayState* play) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
// @recomp Tag the matrix.
gEXMatrixGroupDecomposedNormal(POLY_XLU_DISP++, actor_transform_id(thisx) + i, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE);
// @recomp Manual relocation, TODO remove when automated.
gSPDisplayList(POLY_XLU_DISP++, actor_relocate(thisx, gClearTagFireEffectDL));
gSPDisplayList(POLY_XLU_DISP++, gClearTagFireEffectDL);
// @recomp Pop the matrix tag.
gEXPopMatrixGroup(POLY_XLU_DISP++, G_MTX_MODELVIEW);
}
@@ -623,8 +613,7 @@ void EnClearTag_DrawEffects(Actor* thisx, PlayState* play) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
// @recomp Tag the matrix.
gEXMatrixGroupDecomposedNormal(POLY_XLU_DISP++, actor_transform_id(thisx) + i, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE);
// @recomp Manual relocation, TODO remove when automated.
gSPDisplayList(POLY_XLU_DISP++, actor_relocate(thisx, gClearTagFlashEffectDL));
gSPDisplayList(POLY_XLU_DISP++, gClearTagFlashEffectDL);
// @recomp Pop the matrix tag.
gEXPopMatrixGroup(POLY_XLU_DISP++, G_MTX_MODELVIEW);
}
@@ -640,8 +629,7 @@ void EnClearTag_DrawEffects(Actor* thisx, PlayState* play) {
gDPPipeSync(POLY_XLU_DISP++);
gDPSetEnvColor(POLY_XLU_DISP++, (u8)effect->envColor.r, (u8)effect->envColor.g, (u8)effect->envColor.b,
0);
// @recomp Manual relocation, TODO remove when automated.
gSPDisplayList(POLY_XLU_DISP++, actor_relocate(thisx, gClearTagLightRayEffectMaterialDL));
gSPDisplayList(POLY_XLU_DISP++, gClearTagLightRayEffectMaterialDL);
isMaterialApplied++;
}
@@ -657,8 +645,7 @@ void EnClearTag_DrawEffects(Actor* thisx, PlayState* play) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
// @recomp Tag the matrix.
gEXMatrixGroupDecomposedNormal(POLY_XLU_DISP++, actor_transform_id(thisx) + i, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE);
// @recomp Manual relocation, TODO remove when automated.
gSPDisplayList(POLY_XLU_DISP++, actor_relocate(thisx, gClearTagLightRayEffectDL));
gSPDisplayList(POLY_XLU_DISP++, gClearTagLightRayEffectDL);
// @recomp Pop the matrix tag.
gEXPopMatrixGroup(POLY_XLU_DISP++, G_MTX_MODELVIEW);
}
@@ -707,7 +694,7 @@ void EnClearTag_DrawEffects(Actor* thisx, PlayState* play) {
}
// @recomp Patched to tag the two custom lens flares (used by the Igos du Ikana curtains).
void Environment_DrawCustomLensFlare(PlayState* play) {
RECOMP_PATCH void Environment_DrawCustomLensFlare(PlayState* play) {
Vec3f pos;
// @recomp Set up the graphics context.
@@ -752,7 +739,7 @@ void Environment_DrawCustomLensFlare(PlayState* play) {
}
// @recomp Patched to tag the sun lens flare.
void Environment_DrawSunLensFlare(PlayState* play, EnvironmentContext* envCtx, View* view, GraphicsContext* gfxCtx,
RECOMP_PATCH void Environment_DrawSunLensFlare(PlayState* play, EnvironmentContext* envCtx, View* view, GraphicsContext* gfxCtx,
Vec3f vec) {
if ((play->envCtx.precipitation[PRECIP_RAIN_CUR] == 0) &&
!(GET_ACTIVE_CAM(play)->stateFlags & CAM_STATE_UNDERWATER) && (play->skyboxId == SKYBOX_NORMAL_SKY)) {

14
patches/extended_actors.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef __EXTENDED_ACTORS_H__
#define __EXTENDED_ACTORS_H__
#include "global.h"
typedef u32 ActorExtensionId;
ActorExtensionId z64recomp_extend_actor(s16 actor_id, u32 size);
ActorExtensionId z64recomp_extend_actor_all(u32 size);
void* z64recomp_get_extended_actor_data(Actor* actor, ActorExtensionId extension);
u32 z64recomp_get_actor_spawn_index(Actor* actor);
#endif

View File

@@ -8,7 +8,7 @@
#define PAGE_BG_WIDTH (PAGE_BG_COLS * PAGE_BG_QUAD_WIDTH)
#define PAGE_BG_HEIGHT (PAGE_BG_ROWS * PAGE_BG_QUAD_HEIGHT)
#define RECOMP_PAGE_ROW_HEIGHT 14
#define RECOMP_PAGE_ROW_HEIGHT 15
#define RECOMP_PAGE_ROW_COUNT ((PAGE_BG_HEIGHT + RECOMP_PAGE_ROW_HEIGHT - 1) / RECOMP_PAGE_ROW_HEIGHT)
extern s16* sVtxPageQuadsX[VTX_PAGE_MAX];
@@ -25,7 +25,7 @@ s16 sVtxPageGameOverSaveQuadsY[VTX_PAGE_SAVE_QUADS] = {
};
// @recomp patched to draw as strips with bilerp compensation instead of tiles.
s16 KaleidoScope_SetPageVertices(PlayState* play, Vtx* vtx, s16 vtxPage, s16 numQuads) {
RECOMP_PATCH s16 KaleidoScope_SetPageVertices(PlayState* play, Vtx* vtx, s16 vtxPage, s16 numQuads) {
PauseContext* pauseCtx = &play->pauseCtx;
GameOverContext* gameOverCtx = &play->gameOverCtx;
s16* quadsX;
@@ -35,11 +35,11 @@ s16 KaleidoScope_SetPageVertices(PlayState* play, Vtx* vtx, s16 vtxPage, s16 num
s32 cur_y;
u32 row;
cur_y = PAGE_BG_HEIGHT / 2;
cur_y = (PAGE_BG_HEIGHT + 2) / 2;
// 2 verts per row plus 2 extra verts at the start and the end.
for (row = 0; row < RECOMP_PAGE_ROW_COUNT + 2; row++) {
s32 next_y = MAX(cur_y - RECOMP_PAGE_ROW_HEIGHT, -PAGE_BG_HEIGHT / 2);
s32 next_y = MAX(cur_y - RECOMP_PAGE_ROW_HEIGHT, -(PAGE_BG_HEIGHT + 2) / 2);
vtx[4 * row + 0].v.ob[0] = -PAGE_BG_WIDTH / 2;
vtx[4 * row + 1].v.ob[0] = PAGE_BG_WIDTH / 2;
@@ -55,16 +55,16 @@ s16 KaleidoScope_SetPageVertices(PlayState* play, Vtx* vtx, s16 vtxPage, s16 num
vtx[4 * row + 0].v.flag = vtx[4 * row + 1].v.flag = vtx[4 * row + 2].v.flag = vtx[4 * row + 3].v.flag = 0;
#define PIXEL_OFFSET ((1 << 4))
#define PIXEL_OFFSET 0
vtx[4 * row + 0].v.tc[0] = PIXEL_OFFSET;
vtx[4 * row + 0].v.tc[1] = (1 << 5) + PIXEL_OFFSET;
vtx[4 * row + 1].v.tc[0] = PAGE_BG_WIDTH * (1 << 5) + PIXEL_OFFSET;
vtx[4 * row + 1].v.tc[1] = (1 << 5) + PIXEL_OFFSET;
vtx[4 * row + 0].v.tc[1] = PIXEL_OFFSET;
vtx[4 * row + 1].v.tc[0] = (PAGE_BG_WIDTH + 2 - 1) * (1 << 5) + PIXEL_OFFSET;
vtx[4 * row + 1].v.tc[1] = PIXEL_OFFSET;
vtx[4 * row + 2].v.tc[0] = PIXEL_OFFSET;
vtx[4 * row + 2].v.tc[1] = (cur_y - next_y + 1) * (1 << 5) + PIXEL_OFFSET;
vtx[4 * row + 3].v.tc[0] = PAGE_BG_WIDTH * (1 << 5) + PIXEL_OFFSET;
vtx[4 * row + 3].v.tc[1] = (cur_y - next_y + 1) * (1 << 5) + PIXEL_OFFSET;
vtx[4 * row + 2].v.tc[1] = (cur_y - next_y + 1 - 1) * (1 << 5) + PIXEL_OFFSET;
vtx[4 * row + 3].v.tc[0] = (PAGE_BG_WIDTH + 2 - 1) * (1 << 5) + PIXEL_OFFSET;
vtx[4 * row + 3].v.tc[1] = (cur_y - next_y + 1 - 1) * (1 << 5) + PIXEL_OFFSET;
vtx[4 * row + 0].v.cn[0] = vtx[4 * row + 1].v.cn[0] = vtx[4 * row + 2].v.cn[0] = vtx[4 * row + 3].v.cn[0] = 0;
vtx[4 * row + 0].v.cn[1] = vtx[4 * row + 1].v.cn[1] = vtx[4 * row + 2].v.cn[1] = vtx[4 * row + 3].v.cn[1] = 0;
@@ -73,21 +73,14 @@ s16 KaleidoScope_SetPageVertices(PlayState* play, Vtx* vtx, s16 vtxPage, s16 num
cur_y = next_y;
}
// These are overlay symbols, so their addresses need to be offset to get their actual loaded vram address.
// TODO remove this once the recompiler is able to handle overlay symbols automatically for patch functions.
s16** sVtxPageQuadsXRelocated = (s16**)KaleidoManager_GetRamAddr(sVtxPageQuadsX);
s16** sVtxPageQuadsWidthRelocated = (s16**)KaleidoManager_GetRamAddr(sVtxPageQuadsWidth);
s16** sVtxPageQuadsYRelocated = (s16**)KaleidoManager_GetRamAddr(sVtxPageQuadsY);
s16** sVtxPageQuadsHeightRelocated = (s16**)KaleidoManager_GetRamAddr(sVtxPageQuadsHeight);
s16 k = 60;
if (numQuads != 0) {
quadsX = sVtxPageQuadsXRelocated[vtxPage];
quadsWidth = sVtxPageQuadsWidthRelocated[vtxPage];
quadsY = sVtxPageQuadsYRelocated[vtxPage];
quadsHeight = sVtxPageQuadsHeightRelocated[vtxPage];
quadsX = sVtxPageQuadsX[vtxPage];
quadsWidth = sVtxPageQuadsWidth[vtxPage];
quadsY = sVtxPageQuadsY[vtxPage];
quadsHeight = sVtxPageQuadsHeight[vtxPage];
s16 i;
for (i = 0; i < numQuads; i++, k += 4) {
@@ -185,10 +178,10 @@ void KaleidoUpdateWrapper(PlayState* play) {
void KaleidoDrawWrapper(PlayState* play) {
// @recomp Update the background image pointers to reflect the overlay's load address.
bg_pointers[0] = KaleidoManager_GetRamAddr(sMaskPageBgTextures);
bg_pointers[1] = KaleidoManager_GetRamAddr(sItemPageBgTextures);
bg_pointers[2] = KaleidoManager_GetRamAddr(sMapPageBgTextures);
bg_pointers[3] = KaleidoManager_GetRamAddr(sQuestPageBgTextures);
bg_pointers[0] = sMaskPageBgTextures;
bg_pointers[1] = sItemPageBgTextures;
bg_pointers[2] = sMapPageBgTextures;
bg_pointers[3] = sQuestPageBgTextures;
KaleidoScope_Draw(play);
@@ -202,16 +195,16 @@ void KaleidoDrawWrapper(PlayState* play) {
uintptr_t old_segment_0D = gSegments[0x0D];
gSegments[0x08] = OS_K0_TO_PHYSICAL(play->pauseCtx.iconItemSegment);
gSegments[0x0D] = OS_K0_TO_PHYSICAL(play->pauseCtx.iconItemLangSegment);
assemble_image(KaleidoManager_GetRamAddr(sMaskPageBgTextures), &bg_images[0]);
assemble_image(KaleidoManager_GetRamAddr(sItemPageBgTextures), &bg_images[1]);
assemble_image(KaleidoManager_GetRamAddr(sMapPageBgTextures), &bg_images[2]);
assemble_image(KaleidoManager_GetRamAddr(sQuestPageBgTextures), &bg_images[3]);
assemble_image(sMaskPageBgTextures, &bg_images[0]);
assemble_image(sItemPageBgTextures, &bg_images[1]);
assemble_image(sMapPageBgTextures, &bg_images[2]);
assemble_image(sQuestPageBgTextures, &bg_images[3]);
gSegments[0x08] = old_segment_08;
gSegments[0x0D] = old_segment_0D;
}
}
void KaleidoScopeCall_Init(PlayState* play) {
RECOMP_PATCH void KaleidoScopeCall_Init(PlayState* play) {
// @recomp Set the update and draw func pointers to the wrappers instead of the actual functions.
sKaleidoScopeUpdateFunc = KaleidoUpdateWrapper;
sKaleidoScopeDrawFunc = KaleidoDrawWrapper;
@@ -219,7 +212,7 @@ void KaleidoScopeCall_Init(PlayState* play) {
}
// @recomp patched to fix bilerp seams.
Gfx* KaleidoScope_DrawPageSections(Gfx* gfx, Vtx* vertices, TexturePtr* textures) {
RECOMP_PATCH Gfx* KaleidoScope_DrawPageSections(Gfx* gfx, Vtx* vertices, TexturePtr* textures) {
s32 i;
s32 j;
@@ -241,20 +234,21 @@ Gfx* KaleidoScope_DrawPageSections(Gfx* gfx, Vtx* vertices, TexturePtr* textures
// Draw the rows.
for (u32 bg_row = 0; bg_row < RECOMP_PAGE_ROW_COUNT; bg_row++) {
u32 cur_row_height = MIN(RECOMP_PAGE_ROW_HEIGHT, PAGE_BG_HEIGHT + 1 - bg_row * RECOMP_PAGE_ROW_HEIGHT);
gDPLoadTextureTile(gfx++, *cur_image,
G_IM_FMT_IA, G_IM_SIZ_8b, // fmt, siz
PAGE_BG_WIDTH + 2, PAGE_BG_HEIGHT + 2, // width, height
0, (bg_row + 0) * RECOMP_PAGE_ROW_HEIGHT, // uls, ult
PAGE_BG_WIDTH + 2, (bg_row + 1) * RECOMP_PAGE_ROW_HEIGHT + 2, // lrs, lrt
0, bg_row * RECOMP_PAGE_ROW_HEIGHT, // uls, ult
PAGE_BG_WIDTH + 2 - 1, bg_row * RECOMP_PAGE_ROW_HEIGHT + cur_row_height + 1 - 1, // lrs, lrt
0, // pal
G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP,
G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP,
G_TX_NOMASK, G_TX_NOMASK,
G_TX_NOLOD, G_TX_NOLOD);
gDPSetTileSize(gfx++, G_TX_RENDERTILE,
0 << G_TEXTURE_IMAGE_FRAC,
0 << G_TEXTURE_IMAGE_FRAC,
(PAGE_BG_WIDTH + 2) <<G_TEXTURE_IMAGE_FRAC,
(RECOMP_PAGE_ROW_HEIGHT + 2) << G_TEXTURE_IMAGE_FRAC);
(PAGE_BG_WIDTH + 2 - 1) <<G_TEXTURE_IMAGE_FRAC,
(cur_row_height + 1 - 1) << G_TEXTURE_IMAGE_FRAC);
gSPVertex(gfx++, vertices + 4 * bg_row, 4, 0);
gSP2Triangles(gfx++, 0, 3, 1, 0x0, 3, 0, 2, 0x0);
}
@@ -270,7 +264,7 @@ int extra_vis = 0;
// @recomp Patch the giants cutscene to make certain frames take longer to mimic performance on console.
// This prevents the music from desyncing from the cutscene as it was designed around the console's frame times.
void Cutscene_UpdateScripted(PlayState* play, CutsceneContext* csCtx) {
RECOMP_PATCH void Cutscene_UpdateScripted(PlayState* play, CutsceneContext* csCtx) {
if ((gSaveContext.cutsceneTrigger != 0) && (play->transitionTrigger == TRANS_TRIGGER_START)) {
gSaveContext.cutsceneTrigger = 0;
}
@@ -303,7 +297,7 @@ void Cutscene_UpdateScripted(PlayState* play, CutsceneContext* csCtx) {
}
// @recomp Fix a texture scroll using an incorrect tile size, which resulted in the scroll jumping during the animation.
s32 DemoEffect_OverrideLimbDrawTimewarp(PlayState* play, SkelCurve* skelCurve, s32 limbIndex, Actor* thisx) {
RECOMP_PATCH s32 DemoEffect_OverrideLimbDrawTimewarp(PlayState* play, SkelCurve* skelCurve, s32 limbIndex, Actor* thisx) {
s32 pad;
DemoEffect* this = (DemoEffect*)thisx;
u32 frames = play->gameplayFrames;
@@ -333,34 +327,21 @@ s32 DemoEffect_OverrideLimbDrawTimewarp(PlayState* play, SkelCurve* skelCurve, s
return true;
}
void* gamestate_relocate(void* addr, GameStateId id) {
GameStateOverlay* ovl = &gGameStateOverlayTable[id];
if ((uintptr_t)addr >= 0x80800000) {
return (void*)((uintptr_t)addr -
(intptr_t)((uintptr_t)ovl->vramStart - (uintptr_t)ovl->loadedRamAddr));
}
else {
recomp_printf("Not an overlay address!: 0x%08X 0x%08X 0x%08X\n", (u32)addr, (u32)ovl->vramStart, (u32)ovl->loadedRamAddr);
return addr;
}
}
void DayTelop_Main(GameState* thisx);
void DayTelop_Destroy(GameState* thisx);
void DayTelop_Noop(DayTelopState* this);
void DayTelop_LoadGraphics(DayTelopState* this);
// @recomp Increase the length of the "Dawn of the X Day" screen to account for faster loading.
void DayTelop_Init(GameState* thisx) {
RECOMP_PATCH void DayTelop_Init(GameState* thisx) {
DayTelopState* this = (DayTelopState*)thisx;
GameState_SetFramerateDivisor(&this->state, 1);
Matrix_Init(&this->state);
ShrinkWindow_Destroy();
View_Init(&this->view, this->state.gfxCtx);
// @recomp Manual relocation, TODO remove when automated.
this->state.main = (GameStateFunc)gamestate_relocate(DayTelop_Main, GAMESTATE_DAYTELOP);
this->state.destroy = (GameStateFunc)gamestate_relocate(DayTelop_Destroy, GAMESTATE_DAYTELOP);
this->state.main = DayTelop_Main;
this->state.destroy = DayTelop_Destroy;
// @recomp Add 120 extra frames (2 seconds with a frame divisor of 1) to account for faster loading.
this->transitionCountdown = 260;
this->fadeInState = DAYTELOP_HOURSTEXT_OFF;
@@ -376,3 +357,111 @@ void DayTelop_Init(GameState* thisx) {
DayTelop_LoadGraphics(this);
Audio_PlaySfx(NA_SE_OC_TELOP_IMPACT);
}
extern PlayerAnimationHeader* D_8085D17C[PLAYER_FORM_MAX];
void Player_TalkWithPlayer(PlayState* play, Actor* actor);
void Player_Action_88(Player* this, PlayState* play);
void Player_SetAction_PreserveItemAction(PlayState* play, Player* this, PlayerActionFunc actionFunc, s32 arg3);
void Player_AnimationPlayOnceReverse(PlayState* play, Player* this, PlayerAnimationHeader* anim);
s32 Player_ActionChange_13(Player* this, PlayState* play);
s32 func_8085B28C(PlayState* play, Player* this, PlayerCsAction csAction);
void func_808525C4(PlayState* play, Player* this);
void func_8085255C(PlayState* play, Player* this);
void func_80836A5C(Player* this, PlayState* play);
s32 func_8082DA90(PlayState* play);
// @recomp Patched to fix the issue where ocarina inputs are discarded for the first 3 frames (150ms).
RECOMP_PATCH void Player_Action_63(Player* this, PlayState* play) {
if ((this->unk_AA5 != PLAYER_UNKAA5_4) && ((PlayerAnimation_Update(play, &this->skelAnime) &&
(this->skelAnime.animation == D_8085D17C[this->transformation])) ||
((this->skelAnime.mode == 0) && (this->av2.actionVar2 == 0)))) {
func_808525C4(play, this);
// @recomp Fix the bug where ocarina inputs are discarded for 3 frames by only running this on the first frame of this state.
if (this->av2.actionVar2 == 1) {
if (!(this->actor.flags & ACTOR_FLAG_20000000) || (this->unk_A90->id == ACTOR_EN_ZOT)) {
Message_DisplayOcarinaStaff(play, OCARINA_ACTION_FREE_PLAY);
}
}
} else if (this->av2.actionVar2 != 0) {
if (play->msgCtx.ocarinaMode == OCARINA_MODE_END) {
play->interfaceCtx.unk_222 = 0;
CutsceneManager_Stop(play->playerCsIds[PLAYER_CS_ID_ITEM_OCARINA]);
this->actor.flags &= ~ACTOR_FLAG_20000000;
if ((this->talkActor != NULL) && (this->talkActor == this->unk_A90) && (this->unk_A94 >= 0.0f)) {
Player_TalkWithPlayer(play, this->talkActor);
} else if (this->tatlTextId < 0) {
this->talkActor = this->tatlActor;
this->tatlActor->textId = -this->tatlTextId;
Player_TalkWithPlayer(play, this->talkActor);
} else if (!Player_ActionChange_13(this, play)) {
func_80836A5C(this, play);
Player_AnimationPlayOnceReverse(play, this, D_8085D17C[this->transformation]);
}
} else {
s32 var_v1 = (play->msgCtx.ocarinaMode >= OCARINA_MODE_WARP_TO_GREAT_BAY_COAST) &&
(play->msgCtx.ocarinaMode <= OCARINA_MODE_WARP_TO_ENTRANCE);
s32 pad[2];
if (var_v1 || (play->msgCtx.ocarinaMode == OCARINA_MODE_APPLY_SOT) ||
(play->msgCtx.ocarinaMode == OCARINA_MODE_APPLY_DOUBLE_SOT) ||
(play->msgCtx.ocarinaMode == OCARINA_MODE_APPLY_INV_SOT_FAST) ||
(play->msgCtx.ocarinaMode == OCARINA_MODE_APPLY_INV_SOT_SLOW)) {
if (play->msgCtx.ocarinaMode == OCARINA_MODE_APPLY_SOT) {
if (!func_8082DA90(play)) {
if (gSaveContext.save.saveInfo.playerData.threeDayResetCount == 1) {
play->nextEntrance = ENTRANCE(CUTSCENE, 1);
} else {
play->nextEntrance = ENTRANCE(CUTSCENE, 0);
}
gSaveContext.nextCutsceneIndex = 0xFFF7;
play->transitionTrigger = TRANS_TRIGGER_START;
}
} else {
Actor* actor;
play->interfaceCtx.unk_222 = 0;
CutsceneManager_Stop(play->playerCsIds[PLAYER_CS_ID_ITEM_OCARINA]);
this->actor.flags &= ~ACTOR_FLAG_20000000;
actor = Actor_Spawn(&play->actorCtx, play, var_v1 ? ACTOR_EN_TEST7 : ACTOR_EN_TEST6,
this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0,
0, play->msgCtx.ocarinaMode);
if (actor != NULL) {
this->stateFlags1 &= ~PLAYER_STATE1_20000000;
this->csAction = PLAYER_CSACTION_NONE;
func_8085B28C(play, NULL, PLAYER_CSACTION_19);
this->stateFlags1 |= PLAYER_STATE1_10000000 | PLAYER_STATE1_20000000;
} else {
func_80836A5C(this, play);
Player_AnimationPlayOnceReverse(play, this, D_8085D17C[this->transformation]);
}
}
} else if ((play->msgCtx.ocarinaMode == OCARINA_MODE_EVENT) &&
(play->msgCtx.lastPlayedSong == OCARINA_SONG_ELEGY)) {
play->interfaceCtx.unk_222 = 0;
CutsceneManager_Stop(play->playerCsIds[PLAYER_CS_ID_ITEM_OCARINA]);
this->actor.flags &= ~ACTOR_FLAG_20000000;
Player_SetAction_PreserveItemAction(play, this, Player_Action_88, 0);
this->stateFlags1 |= PLAYER_STATE1_10000000 | PLAYER_STATE1_20000000;
} else if (this->unk_AA5 == PLAYER_UNKAA5_4) {
f32 temp_fa0 = this->skelAnime.jointTable[PLAYER_LIMB_ROOT - 1].x;
f32 temp_fa1 = this->skelAnime.jointTable[PLAYER_LIMB_ROOT - 1].z;
f32 var_fv1;
var_fv1 = sqrtf(SQ(temp_fa0) + SQ(temp_fa1));
if (var_fv1 != 0.0f) {
var_fv1 = (var_fv1 - 100.0f) / var_fv1;
var_fv1 = CLAMP_MIN(var_fv1, 0.0f);
}
this->skelAnime.jointTable[PLAYER_LIMB_ROOT - 1].x = temp_fa0 * var_fv1;
this->skelAnime.jointTable[PLAYER_LIMB_ROOT - 1].z = temp_fa1 * var_fv1;
} else {
func_8085255C(play, this);
}
}
}
}

View File

@@ -0,0 +1,14 @@
#include "patches.h"
#include "ui_funcs.h"
// @recomp Patched to run UI callbacks.
RECOMP_PATCH void Graph_UpdateGame(GameState* gameState) {
recomp_run_ui_callbacks();
GameState_GetInput(gameState);
GameState_IncrementFrameCount(gameState);
if (SREG(20) < 3) {
Audio_Update();
}
}

View File

@@ -0,0 +1,107 @@
#include "patches.h"
#include "transform_ids.h"
#include "overlays/actors/ovl_En_Hgo/z_en_hgo.h"
typedef enum {
/* 0 */ HGO_ANIM_ARMS_FOLDED,
/* 1 */ HGO_ANIM_ASTONISHED,
/* 2 */ HGO_ANIM_KNEEL_DOWN_AND_HUG,
/* 3 */ HGO_ANIM_CONSOLE,
/* 4 */ HGO_ANIM_CONSOLE_HEAD_UP,
/* 5 */ HGO_ANIM_REACH_DOWN_TO_LIFT,
/* 6 */ HGO_ANIM_TOSS,
/* 7 */ HGO_ANIM_MAX
} HgoAnimation;
extern AnimationInfo sPamelasFatherHumanAnimationInfo[];
extern void EnHgo_Draw(Actor* thisx, PlayState* play);
extern void EnHgo_DoNothing(EnHgo* this, PlayState* play);
extern void EnHgo_SetupInitCollision(EnHgo* this);
// @recomp Skip interpolation when the animations change during the cutscene, as the
// animation changes are meant to happen at the same time as the camera cuts.
RECOMP_PATCH s32 EnHgo_HandleCsAction(EnHgo* this, PlayState* play) {
s32 cueChannel;
if (Cutscene_IsCueInChannel(play, CS_CMD_ACTOR_CUE_486)) {
cueChannel = Cutscene_GetCueChannel(play, CS_CMD_ACTOR_CUE_486);
if (this->cueId != play->csCtx.actorCues[cueChannel]->id) {
this->cueId = play->csCtx.actorCues[cueChannel]->id;
switch (play->csCtx.actorCues[cueChannel]->id) {
case 1:
this->animIndex = HGO_ANIM_ARMS_FOLDED;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_ARMS_FOLDED);
break;
case 2:
this->actor.draw = EnHgo_Draw;
this->animIndex = HGO_ANIM_ASTONISHED;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_ASTONISHED);
break;
case 3:
this->animIndex = HGO_ANIM_KNEEL_DOWN_AND_HUG;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_KNEEL_DOWN_AND_HUG);
break;
case 4:
this->animIndex = HGO_ANIM_CONSOLE;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_CONSOLE);
break;
case 5:
this->animIndex = HGO_ANIM_CONSOLE_HEAD_UP;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_CONSOLE_HEAD_UP);
break;
case 6:
this->animIndex = HGO_ANIM_REACH_DOWN_TO_LIFT;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_REACH_DOWN_TO_LIFT);
break;
default:
break;
}
actor_set_interpolation_skipped(&this->actor);
} else if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) {
switch (this->animIndex) {
case HGO_ANIM_ASTONISHED:
if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame) && !this->isInCutscene) {
this->isInCutscene = true;
if ((gSaveContext.sceneLayer == 0) &&
((play->csCtx.scriptIndex == 2) || (play->csCtx.scriptIndex == 4))) {
Actor_PlaySfx(&this->actor, NA_SE_VO_GBVO02);
}
}
break;
case HGO_ANIM_KNEEL_DOWN_AND_HUG:
this->animIndex = HGO_ANIM_CONSOLE;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_CONSOLE);
break;
case HGO_ANIM_REACH_DOWN_TO_LIFT:
this->animIndex = HGO_ANIM_TOSS;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_TOSS);
default:
break;
}
}
Cutscene_ActorTranslateAndYaw(&this->actor, play, cueChannel);
return true;
}
if ((play->csCtx.state == CS_STATE_IDLE) && CHECK_WEEKEVENTREG(WEEKEVENTREG_75_20) &&
(this->actionFunc == EnHgo_DoNothing)) {
this->actor.shape.rot.y = this->actor.world.rot.y;
Actor_Spawn(&play->actorCtx, play, ACTOR_ELF_MSG2, this->actor.focus.pos.x, this->actor.focus.pos.y,
this->actor.focus.pos.z, 7, 0, 0, 0x7F5A);
EnHgo_SetupInitCollision(this);
}
this->cueId = 99;
return false;
}

View File

@@ -0,0 +1,26 @@
#include "patches.h"
#include "transform_ids.h"
#include "overlays/actors/ovl_Dm_Char05/z_dm_char05.h"
extern void func_80AADF54(PlayState* play, DmChar05* this);
// @recomp Patched to avoid an interpolation glitch in Pamela's dad's cutscene
// that happens when the mask is meant to teleport offscreen.
RECOMP_PATCH void func_80AADB4C(Actor* thisx, PlayState* play) {
DmChar05* this = (DmChar05*)thisx;
if (this->unk_18E == 0) {
if (Cutscene_IsCueInChannel(play, CS_CMD_ACTOR_CUE_518) &&
(play->csCtx.actorCues[Cutscene_GetCueChannel(play, CS_CMD_ACTOR_CUE_518)]->id != 1)) {
// @recomp During this cue the mask does nothing other than teleport offscreen and stay still,
// so we can just skip interpolation the entire time.
if (play->csCtx.actorCues[Cutscene_GetCueChannel(play, CS_CMD_ACTOR_CUE_518)]->id == 3) {
actor_set_interpolation_skipped(thisx);
}
Gfx_SetupDL25_Opa(play->state.gfxCtx);
SkelAnime_DrawFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable,
this->skelAnime.dListCount, NULL, NULL, &this->actor);
}
} else if (this->unk_18E == 1) {
func_80AADF54(play, this);
}
}

View File

@@ -0,0 +1,74 @@
#include "patches.h"
#include "transform_ids.h"
#include "overlays/actors/ovl_En_Pamera/z_en_pamera.h"
extern void EnPamera_Draw(Actor* thisx, PlayState* play);
extern void func_80BD9E88(EnPamera* this);
extern void func_80BD9EE0(EnPamera* this);
extern void func_80BDA038(EnPamera* this);
extern void func_80BDA0A0(EnPamera* this);
extern void func_80BDA170(EnPamera* this);
extern void func_80BDA288(EnPamera* this);
extern void func_80BD994C(EnPamera* this, PlayState* play);
extern void EnPamera_HandleDialogue(EnPamera* this, PlayState* play);
extern void func_80BD9904(EnPamera* this);
extern void func_80BD9E60(EnPamera* this);
// @recomp Skip interpolation when the animations change during the cutscene, as the
// animation changes are meant to happen at the same time as the camera cuts.
RECOMP_PATCH s32 func_80BD9CB8(EnPamera* this, PlayState* play) {
s32 cueChannel;
if (Cutscene_IsCueInChannel(play, CS_CMD_ACTOR_CUE_485)) {
cueChannel = Cutscene_GetCueChannel(play, CS_CMD_ACTOR_CUE_485);
if (this->cueId != play->csCtx.actorCues[cueChannel]->id) {
this->cueId = play->csCtx.actorCues[cueChannel]->id;
switch (play->csCtx.actorCues[cueChannel]->id) {
case 1:
func_80BD9E88(this);
break;
case 2:
if (this->actor.draw == NULL) {
this->actor.draw = EnPamera_Draw;
this->actor.flags |= ACTOR_FLAG_TARGETABLE;
}
func_80BD9EE0(this);
break;
case 3:
func_80BDA038(this);
break;
case 4:
func_80BDA0A0(this);
break;
case 5:
func_80BDA170(this);
break;
case 6:
func_80BDA288(this);
break;
default:
break;
}
actor_set_interpolation_skipped(&this->actor);
}
Cutscene_ActorTranslateAndYaw(&this->actor, play, cueChannel);
this->setupFunc(this, play);
return true;
}
if ((play->csCtx.state == CS_STATE_IDLE) && CHECK_WEEKEVENTREG(WEEKEVENTREG_75_20)) {
if ((this->actionFunc != func_80BD994C) && (this->actionFunc != EnPamera_HandleDialogue)) {
this->actor.shape.rot.y = this->actor.world.rot.y;
func_80BD9904(this);
func_80BD9E60(this);
}
}
this->cueId = 99;
return false;
}

View File

@@ -3,7 +3,8 @@
#include "patch_helpers.h"
DECLARE_FUNC(float, recomp_get_aspect_ratio, float);
DECLARE_FUNC(void, recomp_get_window_resolution, u32*, u32*);
DECLARE_FUNC(float, recomp_get_target_aspect_ratio, float);
DECLARE_FUNC(s32, recomp_get_target_framerate, s32);
DECLARE_FUNC(s32, recomp_high_precision_fb_enabled);
DECLARE_FUNC(float, recomp_get_resolution_scale);

File diff suppressed because it is too large Load Diff

View File

@@ -8,13 +8,20 @@ typedef enum {
RECOMP_CAMERA_DUALANALOG,
} RecompCameraMode;
typedef enum {
RECOMP_AIMING_OVERRIDE_OFF = 0,
RECOMP_AIMING_OVERRIDE_DISABLE_LEFT_STICK = 1,
RECOMP_AIMING_OVERRIDE_FORCE_RIGHT_STICK = 2
} RecompAimingOverideMode;
extern RecompCameraMode recomp_camera_mode;
extern RecompAimingOverideMode recomp_aiming_override_mode;
DECLARE_FUNC(void, recomp_get_gyro_deltas, float* x, float* y);
DECLARE_FUNC(void, recomp_get_mouse_deltas, float* x, float* y);
DECLARE_FUNC(s32, recomp_get_targeting_mode);
DECLARE_FUNC(void, recomp_get_inverted_axes, s32* x, s32* y);
DECLARE_FUNC(s32, recomp_analog_cam_enabled);
DECLARE_FUNC(s32, recomp_get_analog_cam_enabled);
DECLARE_FUNC(void, recomp_get_analog_inverted_axes, s32* x, s32* y);
DECLARE_FUNC(void, recomp_get_camera_inputs, float* x, float* y);
DECLARE_FUNC(void, recomp_set_right_analog_suppressed, s32 suppressed);

View File

@@ -34,7 +34,7 @@ typedef enum {
/* 2 */ VOICE_INIT_SUCCESS // voice initialized
} VoiceInitStatus;
void PadMgr_HandleRetrace(void) {
RECOMP_PATCH void PadMgr_HandleRetrace(void) {
// Execute rumble callback
if (sPadMgrInstance->rumbleRetraceCallback != NULL) {
sPadMgrInstance->rumbleRetraceCallback(sPadMgrInstance->rumbleRetraceArg);
@@ -70,8 +70,11 @@ void poll_inputs(void) {
// Begin reading controller data
osContStartReadData(serialEventQueue);
bool needs_right_stick = recomp_get_analog_cam_enabled() || recomp_aiming_override_mode == RECOMP_AIMING_OVERRIDE_FORCE_RIGHT_STICK;
// Suppress the right analog stick if analog camera is active unless the ocarina is in use.
recomp_set_right_analog_suppressed(recomp_analog_cam_enabled() && sOcarinaInstrumentId == OCARINA_INSTRUMENT_OFF);
recomp_set_right_analog_suppressed(needs_right_stick && sOcarinaInstrumentId == OCARINA_INSTRUMENT_OFF);
// Resets this flag for the next frame;
recomp_aiming_override_mode = RECOMP_AIMING_OVERRIDE_OFF;
// Wait for controller data
osRecvMesg(serialEventQueue, NULL, OS_MESG_BLOCK);
@@ -113,7 +116,7 @@ void poll_inputs(void) {
}
// @recomp Patched to do the actual input polling.
void PadMgr_GetInput(Input* inputs, s32 gameRequest) {
RECOMP_PATCH void PadMgr_GetInput(Input* inputs, s32 gameRequest) {
// @recomp Do an actual poll if gameRequest is true.
if (gameRequest) {
poll_inputs();
@@ -126,7 +129,7 @@ void PadMgr_GetInput(Input* inputs, s32 gameRequest) {
}
// @recomp Just call PadMgr_GetInput.
void PadMgr_GetInput2(Input* inputs, s32 gameRequest) {
RECOMP_PATCH void PadMgr_GetInput2(Input* inputs, s32 gameRequest) {
PadMgr_GetInput(inputs, gameRequest);
}
@@ -138,7 +141,7 @@ void* osViGetCurrentFramebuffer_recomp();
OSMesgQueue *rdp_queue_ptr = NULL;
// @recomp Immediately sends the graphics task instead of queueing it in the scheduler.
void Graph_TaskSet00(GraphicsContext* gfxCtx, GameState* gameState) {
RECOMP_PATCH void Graph_TaskSet00(GraphicsContext* gfxCtx, GameState* gameState) {
static s32 retryCount = 10;
static s32 cfbIdx = 0;
OSTask_t* task = &gfxCtx->task.list.t;
@@ -271,7 +274,7 @@ extern VisZbuf sGameVisZbuf;
extern VisMono sGameVisMono;
extern ViMode sGameViMode;
void GameState_Destroy(GameState* gameState) {
RECOMP_PATCH void GameState_Destroy(GameState* gameState) {
AudioMgr_StopAllSfxExceptSystem();
Audio_Update();

View File

@@ -2,13 +2,12 @@
#include "transform_ids.h"
#include "overlays/actors/ovl_Arms_Hook/z_arms_hook.h"
// TODO replace these with externs when the recompiler can handle relocations in patches automatically.
Vec3f D_808C1C10 = { 0.0f, 0.0f, 0.0f };
Vec3f D_808C1C1C = { 0.0f, 0.0f, 900.0f };
Vec3f D_808C1C28 = { 0.0f, 500.0f, -3000.0f };
Vec3f D_808C1C34 = { 0.0f, -500.0f, -3000.0f };
Vec3f D_808C1C40 = { 0.0f, 500.0f, 0.0f };
Vec3f D_808C1C4C = { 0.0f, -500.0f, 0.0f };
extern Vec3f D_808C1C10;
extern Vec3f D_808C1C1C;
extern Vec3f D_808C1C28;
extern Vec3f D_808C1C34;
extern Vec3f D_808C1C40;
extern Vec3f D_808C1C4C;
extern Gfx object_link_child_DL_01D960[];
extern Gfx gHookshotChainDL[];
@@ -17,7 +16,7 @@ extern Gfx gHookshotChainDL[];
void ArmsHook_Shoot(ArmsHook* this, PlayState* play);
void ArmsHook_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void ArmsHook_Draw(Actor* thisx, PlayState* play) {
ArmsHook* this = THIS;
f32 f0;
Player* player = GET_PLAYER(play);
@@ -31,10 +30,7 @@ void ArmsHook_Draw(Actor* thisx, PlayState* play) {
OPEN_DISPS(play->state.gfxCtx);
// @recomp Manually relocate ArmsHook_Shoot because it's an overlay symbol.
// TODO remove this when the recompiler handles relocations in patches automatically.
if (((ArmsHookActionFunc)actor_relocate(thisx, ArmsHook_Shoot) != this->actionFunc) || (this->timer <= 0)) {
if ((ArmsHook_Shoot != this->actionFunc) || (this->timer <= 0)) {
Matrix_MultVec3f(&D_808C1C10, &this->unk1E0);
Matrix_MultVec3f(&D_808C1C28, &sp5C);
Matrix_MultVec3f(&D_808C1C34, &sp50);
@@ -77,7 +73,7 @@ void ArmsHook_Draw(Actor* thisx, PlayState* play) {
#undef THIS
extern Gfx gHookshotReticleDL[];
void Player_DrawHookshotReticle(PlayState* play, Player* player, f32 hookshotDistance) {
RECOMP_PATCH void Player_DrawHookshotReticle(PlayState* play, Player* player, f32 hookshotDistance) {
static Vec3f D_801C094C = { -500.0f, -100.0f, 0.0f };
CollisionPoly* poly;
s32 bgId;
@@ -147,7 +143,7 @@ Gfx bowstring_end_hook_dl[] = {
gsSPEndDisplayList(),
};
void Player_DrawGameplay(PlayState* play, Player* this, s32 lod, Gfx* cullDList,
RECOMP_PATCH void Player_DrawGameplay(PlayState* play, Player* this, s32 lod, Gfx* cullDList,
OverrideLimbDrawFlex overrideLimbDraw) {
OPEN_DISPS(play->state.gfxCtx);
@@ -189,3 +185,6 @@ void Player_DrawGameplay(PlayState* play, Player* this, s32 lod, Gfx* cullDList,
CLOSE_DISPS(play->state.gfxCtx);
}
RECOMP_EXPORT u32 z64recomp_get_bowstring_transform_id() {
return BOWSTRING_TRANSFORM_ID;
}

42
patches/memory_patches.c Normal file
View File

@@ -0,0 +1,42 @@
#include "patches.h"
// @recomp Leave the entire KSEG0 range unmodified when translating to a virtual address. This will allow
// using the entirety of the extended RAM address space for custom assets.
RECOMP_PATCH void* Lib_SegmentedToVirtual(void* ptr) {
if (IS_KSEG0(ptr)) {
return ptr;
}
else {
return SEGMENTED_TO_K0(ptr);
}
}
AnimationEntry* AnimationContext_AddEntry(AnimationContext* animationCtx, AnimationType type);
#define LINK_ANIMETION_OFFSET(addr, offset) \
(SEGMENT_ROM_START(link_animetion) + ((uintptr_t)addr & 0xFFFFFF) + ((u32)offset))
// @recomp Skip the DMA if the animation is already in RAM. Allows mods to play custom animations.
RECOMP_PATCH void AnimationContext_SetLoadFrame(PlayState* play, PlayerAnimationHeader* animation, s32 frame, s32 limbCount,
Vec3s* frameTable) {
AnimationEntry* task = AnimationContext_AddEntry(&play->animationCtx, ANIMATION_LINKANIMETION);
if (task != NULL) {
PlayerAnimationHeader* playerAnimHeader = Lib_SegmentedToVirtual(animation);
s32 pad;
osCreateMesgQueue(&task->data.load.msgQueue, task->data.load.msg,
ARRAY_COUNT(task->data.load.msg));
if (IS_KSEG0(playerAnimHeader->linkAnimSegment)) {
osSendMesg(&task->data.load.msgQueue, NULL, OS_MESG_NOBLOCK);
Lib_MemCpy(frameTable, ((u8*)playerAnimHeader->segmentVoid) + (sizeof(Vec3s) * limbCount + sizeof(s16)) * frame,
sizeof(Vec3s) * limbCount + sizeof(s16));
}
else {
DmaMgr_SendRequestImpl(
&task->data.load.req, frameTable,
LINK_ANIMETION_OFFSET(playerAnimHeader->linkAnimSegment, (sizeof(Vec3s) * limbCount + sizeof(s16)) * frame),
sizeof(Vec3s) * limbCount + sizeof(s16), 0, &task->data.load.msgQueue, NULL);
}
}
}

View File

@@ -10,6 +10,6 @@ DECLARE_FUNC(void, recomp_handle_quicksave_actions, OSMesgQueue* enter_mq, OSMes
DECLARE_FUNC(void, recomp_handle_quicksave_actions_main, OSMesgQueue* enter_mq, OSMesgQueue* exit_mq);
DECLARE_FUNC(u16, recomp_get_pending_warp);
DECLARE_FUNC(u32, recomp_get_pending_set_time);
DECLARE_FUNC(s32, recomp_autosave_enabled);
DECLARE_FUNC(s32, recomp_get_autosave_enabled);
#endif

View File

@@ -1010,14 +1010,12 @@ void set_all_vertex_flags() {
// Patches the given DL to replace a vertex command at the given position with a branch to the new DL.
void patch_ocarina_effect(Actor* actor, Gfx* original_dl, u32 dl_offset, Gfx* override_dl) {
Gfx* reloc_dl = (Gfx*)actor_relocate(actor, original_dl);
set_all_vertex_flags();
// Check if the DL hasn't been patched yet.
if (reloc_dl[dl_offset].words.w0 >> 24 == G_VTX) {
if (original_dl[dl_offset].words.w0 >> 24 == G_VTX) {
// Redirect the DL away from the original vertex command and to the override DL.
gSPBranchList(&reloc_dl[dl_offset], override_dl);
gSPBranchList(&original_dl[dl_offset], override_dl);
}
}
@@ -1032,7 +1030,7 @@ void set_ocarina_vertex_alphas(Vtx* verts, s32 count, u8 alpha) {
extern Gfx sSongOfTimeFrustumMaterialDL[];
void OceffWipe_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void OceffWipe_Draw(Actor* thisx, PlayState* play) {
u32 scroll = play->state.frames & 0xFF;
OceffWipe* this = (OceffWipe*)thisx;
f32 z;
@@ -1089,8 +1087,7 @@ void OceffWipe_Draw(Actor* thisx, PlayState* play) {
gDPSetEnvColor(POLY_XLU_DISP++, 100, 0, 255, 128);
}
// @recomp Manual relocation, TODO remove when automated.
gSPDisplayList(POLY_XLU_DISP++, (Gfx*)actor_relocate(thisx, &sSongOfTimeFrustumMaterialDL));
gSPDisplayList(POLY_XLU_DISP++, sSongOfTimeFrustumMaterialDL);
gSPDisplayList(POLY_XLU_DISP++, Gfx_TwoTexScroll(play->state.gfxCtx, G_TX_RENDERTILE, 0 - scroll, scroll * -2, 32,
32, 1, 0 - scroll, scroll * -2, 32, 32));
// @recomp Use the new DL instead of the original.
@@ -1101,7 +1098,7 @@ void OceffWipe_Draw(Actor* thisx, PlayState* play) {
extern Gfx sEponaSongFrustumMaterialDL[];
void OceffWipe2_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void OceffWipe2_Draw(Actor* thisx, PlayState* play) {
u32 scroll = play->state.frames & 0xFF;
OceffWipe2* this = (OceffWipe2*)thisx;
f32 z;
@@ -1144,8 +1141,7 @@ void OceffWipe2_Draw(Actor* thisx, PlayState* play) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 170, 255);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 100, 0, 128);
// @recomp Manual relocation, TODO remove when automated.
gSPDisplayList(POLY_XLU_DISP++, (Gfx*)actor_relocate(thisx, sEponaSongFrustumMaterialDL));
gSPDisplayList(POLY_XLU_DISP++, sEponaSongFrustumMaterialDL);
gSPDisplayList(POLY_XLU_DISP++, Gfx_TwoTexScroll(play->state.gfxCtx, G_TX_RENDERTILE, scroll * 6, scroll * -6, 64,
64, 1, scroll * -6, 0, 64, 64));
// @recomp Use the new DL instead of the original.
@@ -1156,7 +1152,7 @@ void OceffWipe2_Draw(Actor* thisx, PlayState* play) {
extern Gfx sSariaSongFrustrumMaterialDL[];
void OceffWipe3_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void OceffWipe3_Draw(Actor* thisx, PlayState* play) {
u32 scroll = play->state.frames & 0xFFF;
OceffWipe3* this = (OceffWipe3*)thisx;
f32 z;
@@ -1198,8 +1194,7 @@ void OceffWipe3_Draw(Actor* thisx, PlayState* play) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 170, 255);
gDPSetEnvColor(POLY_XLU_DISP++, 100, 200, 0, 128);
// @recomp Manual relocation, TODO remove when automated.
gSPDisplayList(POLY_XLU_DISP++, (Gfx*)actor_relocate(thisx, &sSariaSongFrustrumMaterialDL));
gSPDisplayList(POLY_XLU_DISP++, sSariaSongFrustrumMaterialDL);
gSPDisplayList(POLY_XLU_DISP++, Gfx_TwoTexScroll(play->state.gfxCtx, 0, scroll * 12, scroll * -12, 64, 64, 1,
scroll * 8, scroll * -8, 64, 64));
// @recomp Use the new DL instead of the original.
@@ -1212,7 +1207,7 @@ extern Gfx sScarecrowSongUnusedMaterialDL[];
extern Gfx sScarecrowSongMaterialDL[];
extern Gfx sScarecrowSongModelDL[];
void OceffWipe4_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void OceffWipe4_Draw(Actor* thisx, PlayState* play) {
u32 scroll = play->state.frames & 0xFFF;
OceffWipe4* this = (OceffWipe4*)thisx;
f32 z;
@@ -1253,17 +1248,16 @@ void OceffWipe4_Draw(Actor* thisx, PlayState* play) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
// @recomp Manual relocations, TODO remove when automated.
if (this->actor.params == OCEFF_WIPE4_UNUSED) {
gSPDisplayList(POLY_XLU_DISP++, (Gfx*)actor_relocate(thisx, sScarecrowSongUnusedMaterialDL));
gSPDisplayList(POLY_XLU_DISP++, sScarecrowSongUnusedMaterialDL);
} else {
gSPDisplayList(POLY_XLU_DISP++, (Gfx*)actor_relocate(thisx, sScarecrowSongMaterialDL));
gSPDisplayList(POLY_XLU_DISP++, sScarecrowSongMaterialDL);
}
gSPDisplayList(POLY_XLU_DISP++, (Gfx*)actor_relocate(thisx, sScarecrowSongModelDL));
gSPDisplayList(POLY_XLU_DISP++, sScarecrowSongModelDL);
gSPDisplayList(POLY_XLU_DISP++, Gfx_TwoTexScroll(play->state.gfxCtx, G_TX_RENDERTILE, scroll * 2, scroll * -2, 32,
64, 1, scroll * -1, scroll, 32, 32));
gSPDisplayList(POLY_XLU_DISP++, (Gfx*)actor_relocate(thisx, &sScarecrowSongModelDL[11]));
gSPDisplayList(POLY_XLU_DISP++, &sScarecrowSongModelDL[11]);
CLOSE_DISPS(play->state.gfxCtx);
}
@@ -1279,7 +1273,7 @@ static u8 sEnvColors[] = {
extern Gfx gOceff5DL[];
extern AnimatedMaterial gOceff5TexAnim[];
void OceffWipe5_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void OceffWipe5_Draw(Actor* thisx, PlayState* play) {
OceffWipe5* this = (OceffWipe5*)thisx;
f32 z;
s32 pad;
@@ -1339,9 +1333,8 @@ void OceffWipe5_Draw(Actor* thisx, PlayState* play) {
gDPSetEnvColor(POLY_XLU_DISP++, sEnvColors[colorIndex], sEnvColors[colorIndex + 1], sEnvColors[colorIndex + 2],
255);
// @recomp Manual relocations, TODO remove when automated.
AnimatedMat_Draw(play, (AnimatedMaterial*)actor_relocate(thisx, gOceff5TexAnim));
gSPDisplayList(POLY_XLU_DISP++, (Gfx*)actor_relocate(thisx, gOceff5DL));
AnimatedMat_Draw(play, gOceff5TexAnim);
gSPDisplayList(POLY_XLU_DISP++, gOceff5DL);
CLOSE_DISPS(play->state.gfxCtx);
}
@@ -1349,7 +1342,7 @@ void OceffWipe5_Draw(Actor* thisx, PlayState* play) {
extern Gfx gOceff6DL[];
extern AnimatedMaterial ovl_Oceff_Wipe6_Matanimheader_000338[];
void OceffWipe6_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void OceffWipe6_Draw(Actor* thisx, PlayState* play) {
OceffWipe6* this = (OceffWipe6*)thisx;
f32 z;
u8 alpha;
@@ -1392,9 +1385,8 @@ void OceffWipe6_Draw(Actor* thisx, PlayState* play) {
Matrix_RotateXS(0x708, MTXMODE_APPLY);
Matrix_Translate(0.0f, 0.0f, -z, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
// @recomp Manual relocations, TODO remove these when automatic.
AnimatedMat_Draw(play, (AnimatedMaterial*)actor_relocate(thisx, ovl_Oceff_Wipe6_Matanimheader_000338));
gSPDisplayList(POLY_XLU_DISP++, (Gfx*)actor_relocate(thisx, gOceff6DL));
AnimatedMat_Draw(play, ovl_Oceff_Wipe6_Matanimheader_000338);
gSPDisplayList(POLY_XLU_DISP++, gOceff6DL);
CLOSE_DISPS(play->state.gfxCtx);
}
@@ -1402,7 +1394,7 @@ void OceffWipe6_Draw(Actor* thisx, PlayState* play) {
extern Gfx sSongOfHealingEffectFrustumDL[];
extern AnimatedMaterial sSongofHealingEffectTexAnim[];
void OceffWipe7_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void OceffWipe7_Draw(Actor* thisx, PlayState* play) {
OceffWipe7* this = (OceffWipe7*)thisx;
f32 z;
u8 alpha;
@@ -1443,9 +1435,8 @@ void OceffWipe7_Draw(Actor* thisx, PlayState* play) {
Matrix_RotateXS(0x708, MTXMODE_APPLY);
Matrix_Translate(0.0f, 0.0f, -z, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
// @recomp Manual relocations, TODO remove when automated.
AnimatedMat_Draw(play, (AnimatedMaterial*)actor_relocate(thisx, sSongofHealingEffectTexAnim));
gSPDisplayList(POLY_XLU_DISP++, (Gfx*)actor_relocate(thisx, &sSongOfHealingEffectFrustumDL));
AnimatedMat_Draw(play, sSongofHealingEffectTexAnim);
gSPDisplayList(POLY_XLU_DISP++, sSongOfHealingEffectFrustumDL);
CLOSE_DISPS(play->state.gfxCtx);
}
@@ -1454,7 +1445,7 @@ extern AnimatedMaterial object_stk2_Matanimheader_009F60[];
// @recomp Patch the Skull Kid curse effect as well, which works similarly to the ocarina effects.
// In this case, the patch also includes effect transform tagging patches.
void EffStk_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void EffStk_Draw(Actor* thisx, PlayState* play) {
EffStk* this = (EffStk*)thisx;
s32 pad;
Camera* activeCam = GET_ACTIVE_CAM(play);

View File

@@ -40,63 +40,24 @@ extern u64 gFileSelCopyButtonENGTex[];
extern u64 gFileSelEraseButtonENGTex[];
extern u64 gFileSelYesButtonENGTex[];
extern u64 gFileSelQuitButtonENGTex[];
extern s16 D_80814280[];
extern s16 sWindowContentColors[];
extern TexturePtr sFileInfoBoxTextures[];
extern TexturePtr sTitleLabels[];
extern TexturePtr sWarningLabels[];
extern TexturePtr sFileButtonTextures[];
extern TexturePtr sActionButtonTextures[];
extern s16 sFileInfoBoxPartWidths[];
extern s16 sWalletFirstDigit[];
extern s16 D_80814620[];
extern s16 D_80814628[];
extern s16 D_80814630[];
extern s16 D_80814638[];
extern s16 D_80814644[];
extern s16 D_8081464C[];
// TODO extern these when the recompiler handles relocations automatically.
s16 D_80814280[] = {
2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 0, 1, 1, 2, 1, 1, 4, 2, 2, 2, 1, 1, 0, 2, 0, 1, 1, 1, 1, 1, 0,
1, 1, 1, 2, 2, 2, 2, 2, 3, 2, 2, 4, 3, 2, 4, 1, 2, 2, 1, 1, 2, 2, 3, 2, 2, 0, 2, 2, 2, 0, 3, 1, 0,
};
s16 sWindowContentColors[] = { 100, 150, 255 };
TexturePtr sFileInfoBoxTextures[] = {
gFileSelFileInfoBox0Tex, gFileSelFileInfoBox1Tex, gFileSelFileInfoBox2Tex, gFileSelFileInfoBox3Tex,
gFileSelFileInfoBox4Tex, gFileSelFileExtraInfoBox0Tex, gFileSelFileExtraInfoBox1Tex,
};
TexturePtr sTitleLabels[] = {
gFileSelPleaseSelectAFileENGTex, gFileSelOpenThisFileENGTex, gFileSelCopyWhichFileENGTex,
gFileSelCopyToWhichFileENGTex, gFileSelAreYouSureCopyENGTex, gFileSelFileCopiedENGTex,
gFileSelEraseWhichFileENGTex, gFileSelAreYouSureEraseENGTex, gFileSelFileErasedENGTex,
};
TexturePtr sWarningLabels[] = {
gFileSelNoFileToCopyENGTex, gFileSelNoFileToEraseENGTex, gFileSelNoEmptyFileENGTex,
gFileSelFileEmptyENGTex, gFileSelFileInUseENGTex,
};
TexturePtr sFileButtonTextures[] = {
gFileSelFile1ButtonENGTex,
gFileSelFile2ButtonENGTex,
gFileSelFile3ButtonENGTex,
};
TexturePtr sActionButtonTextures[] = {
gFileSelCopyButtonENGTex,
gFileSelEraseButtonENGTex,
gFileSelYesButtonENGTex,
gFileSelQuitButtonENGTex,
};
s16 sFileInfoBoxPartWidths[] = {
36, 36, 36, 36, 24, 28, 28,
};
s16 sWalletFirstDigit[] = {
1, // tens (Default Wallet)
0, // hundreds (Adult Wallet)
0, // hundreds (Giant Wallet)
};
s16 D_80814620[] = { 8, 8, 8, 0 };
s16 D_80814628[] = { 12, 12, 12, 0 };
s16 D_80814630[] = { 12, 12, 12, 0 };
s16 D_80814638[] = {
88, 104, 120, 940, 944, 948,
};
s16 D_80814644[] = { 88, 104, 120, 944 };
s16 D_8081464C[] = { 940, 944 };
s16 D_80814650[] = { 940, 944, 948 };
// @recomp Added a third position for the rewind button.
s16 D_80814650_patched[] = { 940, 944, 948 };
void FileSelect_Main(GameState* thisx);
void FileSelect_InitContext(GameState* thisx);
@@ -104,11 +65,11 @@ void FileSelect_DrawFileInfo(GameState *thisx, s16 fileIndex);
void FileSelect_SplitNumber(u16 value, u16 *hundreds, u16 *tens, u16 *ones);
// @recomp The options button is now the quit button, so close recomp instead of opening the options.
void FileSelect_RotateToOptions(GameState* thisx) {
RECOMP_PATCH void FileSelect_RotateToOptions(GameState* thisx) {
recomp_exit();
}
void FileSelect_Init(GameState* thisx) {
RECOMP_PATCH void FileSelect_Init(GameState* thisx) {
s32 pad;
FileSelectState* this = (FileSelectState*)thisx;
size_t size;
@@ -118,10 +79,8 @@ void FileSelect_Init(GameState* thisx) {
ShrinkWindow_Init();
View_Init(&this->view, this->state.gfxCtx);
// @recomp manually relocate these symbols as the recompiler doesn't do this automatically for patches yet.
GameStateOverlay* ovl = &gGameStateOverlayTable[GAMESTATE_FILE_SELECT];
this->state.main = (void*)((u32)FileSelect_Main - (u32)ovl->vramStart + (u32)ovl->loadedRamAddr);
this->state.destroy = (void*)((u32)FileSelect_Destroy - (u32)ovl->vramStart + (u32)ovl->loadedRamAddr);
this->state.main = FileSelect_Main;
this->state.destroy = FileSelect_Destroy;
FileSelect_InitContext(&this->state);
Font_LoadOrderedFont(&this->font);
@@ -150,7 +109,7 @@ void FileSelect_Init(GameState* thisx) {
}
void FileSelect_SetWindowContentVtx(GameState *thisx) {
RECOMP_PATCH void FileSelect_SetWindowContentVtx(GameState *thisx) {
FileSelectState *this = (FileSelectState *)thisx;
u16 vtxId;
s16 j;
@@ -782,7 +741,7 @@ void FileSelect_SetWindowContentVtx(GameState *thisx) {
}
}
else {
j = D_80814650[this->confirmButtonIndex];
j = D_80814650_patched[this->confirmButtonIndex];
}
this->windowContentVtx[vtxId + 0].v.ob[0] = this->windowContentVtx[vtxId + 2].v.ob[0] = this->windowPosX - 0xA;
@@ -827,7 +786,7 @@ void FileSelect_SetWindowContentVtx(GameState *thisx) {
* Draw most window contents including buttons, labels, and icons.
* Does not include anything from the keyboard and settings windows.
*/
void FileSelect_DrawWindowContents(GameState *thisx) {
RECOMP_PATCH void FileSelect_DrawWindowContents(GameState *thisx) {
FileSelectState *this = (FileSelectState *)thisx;
s16 fileIndex;
s16 temp;
@@ -1032,7 +991,7 @@ void FileSelect_DrawWindowContents(GameState *thisx) {
CLOSE_DISPS(this->state.gfxCtx);
}
void FileSelect_ConfirmFile(GameState *thisx) {
RECOMP_PATCH void FileSelect_ConfirmFile(GameState *thisx) {
FileSelectState *this = (FileSelectState *)thisx;
Input *input = CONTROLLER1(&this->state);
@@ -1077,11 +1036,14 @@ void FileSelect_ConfirmFile(GameState *thisx) {
}
}
// Non-relocatable reference to the original address of Play_Init.
void Play_Init_NORELOCATE(GameState*);
/**
* Load the save for the appropriate file and start the game.
* Update function for `SM_LOAD_GAME`
*/
void FileSelect_LoadGame(GameState* thisx) {
RECOMP_PATCH void FileSelect_LoadGame(GameState* thisx) {
FileSelectState* this = (FileSelectState*)thisx;
u16 i;
@@ -1101,7 +1063,7 @@ void FileSelect_LoadGame(GameState* thisx) {
gSaveContext.gameMode = GAMEMODE_NORMAL;
STOP_GAMESTATE(&this->state);
SET_NEXT_GAMESTATE(&this->state, Play_Init, sizeof(PlayState));
SET_NEXT_GAMESTATE(&this->state, Play_Init_NORELOCATE, sizeof(PlayState));
gSaveContext.respawnFlag = 0;
gSaveContext.respawn[RESPAWN_MODE_DOWN].entrance = ENTR_LOAD_OPENING;

View File

@@ -10,7 +10,7 @@ extern EffectSsInfo sEffectSsInfo;
u8 particle_reset_list[MAX_PARTICLES];
// @recomp Patched to track that the particle has been reset.
void EffectSS_ResetEntry(EffectSs* particle) {
RECOMP_PATCH void EffectSS_ResetEntry(EffectSs* particle) {
u32 i;
particle->type = EFFECT_SS_MAX;
@@ -39,7 +39,7 @@ void EffectSS_ResetEntry(EffectSs* particle) {
}
// @recomp Check numEntries to be sure enough space has been allocated for tracking particle statuses.
void EffectSS_Init(PlayState* play, s32 numEntries) {
RECOMP_PATCH void EffectSS_Init(PlayState* play, s32 numEntries) {
u32 i;
EffectSs* effectsSs;
EffectSsOverlay* overlay;
@@ -66,7 +66,7 @@ void EffectSS_Init(PlayState* play, s32 numEntries) {
}
// @recomp Add transform tags to particles
void EffectSS_DrawParticle(PlayState* play, s32 index) {
RECOMP_PATCH void EffectSS_DrawParticle(PlayState* play, s32 index) {
EffectSs* entry = &sEffectSsInfo.dataTable[index];
OPEN_DISPS(play->state.gfxCtx);
@@ -101,7 +101,7 @@ extern Gfx gSunSparkleModelDL[];
extern u8 D_80B23C40[];
extern u8 D_80B23C2C[];
// @recomp Modified to take the actor as an argument for relocation and to tag firework transforms.
// @recomp Modified to tag firework transforms.
void func_80B22FA8_patched(Actor* thisx, EnHanabiStruct* arg0, PlayState* play2) {
PlayState* play = play2;
GraphicsContext* gfxCtx = play->state.gfxCtx;
@@ -119,10 +119,6 @@ void func_80B22FA8_patched(Actor* thisx, EnHanabiStruct* arg0, PlayState* play2)
sp53 = 0xFF;
// @recomp Manually relocate, TODO remove when automated by recompiler.
u8* D_80B23C40_relocated = (u8*)actor_relocate(thisx, D_80B23C40);
u8* D_80B23C2C_relocated = (u8*)actor_relocate(thisx, D_80B23C2C);
for (i = 0; i < 400; i++, arg0++) {
if (arg0->unk_00 != 1) {
continue;
@@ -145,18 +141,18 @@ void func_80B22FA8_patched(Actor* thisx, EnHanabiStruct* arg0, PlayState* play2)
if (sp53 != arg0->unk_02) {
gDPPipeSync(POLY_XLU_DISP++);
gDPSetEnvColor(POLY_XLU_DISP++, D_80B23C40_relocated[arg0->unk_02], D_80B23C40_relocated[arg0->unk_02 + 1],
D_80B23C40_relocated[arg0->unk_02 + 2], 255);
gDPSetEnvColor(POLY_XLU_DISP++, D_80B23C40[arg0->unk_02], D_80B23C40[arg0->unk_02 + 1],
D_80B23C40[arg0->unk_02 + 2], 255);
sp53 = arg0->unk_02;
}
if (arg0->unk_01 < 6) {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, D_80B23C2C_relocated[arg0->unk_02], D_80B23C2C_relocated[arg0->unk_02 + 1],
D_80B23C2C_relocated[arg0->unk_02 + 2], arg0->unk_01 * 50);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, D_80B23C2C[arg0->unk_02], D_80B23C2C[arg0->unk_02 + 1],
D_80B23C2C[arg0->unk_02 + 2], arg0->unk_01 * 50);
} else {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, D_80B23C2C_relocated[arg0->unk_02], D_80B23C2C_relocated[arg0->unk_02 + 1],
D_80B23C2C_relocated[arg0->unk_02 + 2], 255);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, D_80B23C2C[arg0->unk_02], D_80B23C2C[arg0->unk_02 + 1],
D_80B23C2C[arg0->unk_02 + 2], 255);
}
gSPDisplayList(POLY_XLU_DISP++, gSunSparkleModelDL);
@@ -168,11 +164,11 @@ void func_80B22FA8_patched(Actor* thisx, EnHanabiStruct* arg0, PlayState* play2)
}
// @recomp Patched to call a custom version of a vanilla function.
void EnHanabi_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void EnHanabi_Draw(Actor* thisx, PlayState* play) {
EnHanabi* this = (EnHanabi*)thisx;
Matrix_Push();
// @recomp Call a modified version of the function that takes the actor for relocation purposes.
// @recomp Call a modified version of the function that takes the actor for tagging purposes.
func_80B22FA8_patched(thisx, this->unk_148, play);
Matrix_Pop();
}
@@ -180,7 +176,7 @@ void EnHanabi_Draw(Actor* thisx, PlayState* play) {
Vec3f kankyo_prev_pos_base[DEMOKANKYO_EFFECT_COUNT] = {0};
// @recomp Patched to draw the lost woods particles outside the 4:3 region and tag their transforms.
void DemoKakyo_DrawLostWoodsSparkle(Actor* thisx, PlayState* play2) {
RECOMP_PATCH void DemoKakyo_DrawLostWoodsSparkle(Actor* thisx, PlayState* play2) {
PlayState* play = play2;
DemoKankyo* this = (DemoKankyo*)thisx;
s16 i;
@@ -290,7 +286,7 @@ extern Gfx gBubbleDL[];
extern Gfx gLightOrbModelDL[];
// @recomp Patched to draw the lost woods particles outside the 4:3 region and tag their transforms.
void DemoKankyo_DrawMoonAndGiant(Actor* thisx, PlayState* play2) {
RECOMP_PATCH void DemoKankyo_DrawMoonAndGiant(Actor* thisx, PlayState* play2) {
PlayState* play = play2;
DemoKankyo* this = (DemoKankyo*)thisx;
s16 i;
@@ -385,13 +381,12 @@ void DemoKankyo_DrawMoonAndGiant(Actor* thisx, PlayState* play2) {
}
static Vec3f D_80A5AFB0 = { 0.0f, 0.0f, 0.0f };
static Vec3f D_80A5AFBC = { 0.0f, -1.0f, 0.0f };
// The byte after unk_01 in EnWaterEffectStruct is unused, so we'll use it as a respawn flag.
#define WATER_EFFECT_RESPAWNED(ptr) (&(ptr)->unk_01)[1]
// @recomp Mark respawned water effect particles so they can be skipped for the first frame.
void func_80A599E8(EnWaterEffect* this, Vec3f* arg1, u8 arg2) {
RECOMP_PATCH void func_80A599E8(EnWaterEffect* this, Vec3f* arg1, u8 arg2) {
s16 i;
EnWaterEffectStruct* ptr = &this->unk_144[0];
@@ -438,7 +433,7 @@ extern Gfx object_water_effect_DL_0043E8[];
extern Gfx gameplay_keep_DL_06AB30[];
// @recomp Tag the transforms for falling fire rocks.
void func_80A5A184(Actor* thisx, PlayState* play2) {
RECOMP_PATCH void func_80A5A184(Actor* thisx, PlayState* play2) {
PlayState* play = play2;
EnWaterEffect* this = (EnWaterEffect*)thisx;
GraphicsContext* gfxCtx = play->state.gfxCtx;

View File

@@ -4,7 +4,7 @@
#ifdef MIPS
#include "ultra64.h"
#else
#include "librecomp/recomp.h"
#include "recomp.h"
#endif
#ifdef __cplusplus

View File

@@ -1,6 +1,15 @@
#ifndef __PATCHES_H__
#define __PATCHES_H__
#define RECOMP_EXPORT __attribute__((section(".recomp_export")))
#define RECOMP_PATCH __attribute__((section(".recomp_patch")))
#define RECOMP_FORCE_PATCH __attribute__((section(".recomp_force_patch")))
#define RECOMP_DECLARE_EVENT(func) \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wunused-parameter\"") \
__attribute__((noinline, weak, used, section(".recomp_event"))) void func {} \
_Pragma("GCC diagnostic pop")
// TODO fix renaming symbols in patch recompilation
#define osCreateMesgQueue osCreateMesgQueue_recomp
#define osRecvMesg osRecvMesg_recomp
@@ -63,17 +72,6 @@
int recomp_printf(const char* fmt, ...);
float recomp_powf(float, float);
static inline void* actor_relocate(Actor* actor, void* addr) {
if ((uintptr_t)addr >= 0x80800000) {
return (void*)((uintptr_t)addr -
(intptr_t)((uintptr_t)actor->overlayEntry->vramStart - (uintptr_t)actor->overlayEntry->loadedRamAddr));
}
else {
recomp_printf("Not an overlay address!: 0x%08X 0x%08X 0x%08X\n", (u32)addr, (u32)actor->overlayEntry->vramStart, (u32)actor->overlayEntry->loadedRamAddr);
return addr;
}
}
typedef enum {
/* 0 */ PICTO_BOX_STATE_OFF, // Not using the pictograph
/* 1 */ PICTO_BOX_STATE_LENS, // Looking through the lens of the pictograph
@@ -94,9 +92,6 @@ typedef enum {
"\t.popsection\n"); \
extern u8 identifier[]
void draw_dpad(PlayState* play);
void draw_dpad_icons(PlayState* play);
void View_ApplyInterpolate(View* view, s32 mask, bool reset_interpolation_state);
void set_camera_skipped(bool skipped);

View File

@@ -1,5 +1,5 @@
RAMBASE = 0x80801000; /* Used to hold any new symbols */
EXTRA_RAM_SIZE = 0x01000000; /* Amount of extra ram allocated by recomp */
PATCH_RAM_END = 0x81000000; /* Amount of extra ram allocated by recomp */
MEMORY {
extram : ORIGIN = RAMBASE, LENGTH = 64M
@@ -7,19 +7,18 @@ MEMORY {
}
SECTIONS {
.ctors : { *(.ctors*) *(.init_array*) } >extram AT >rom
.dtors : { *(.dtors*) } >extram AT >rom
.rodata : { *(.rodata*) } >extram AT >rom
.data : { *(.data*) } >extram AT >rom
/* The following sections will be removed from the objcopy */
/* bss isn't noload to make .text rom addresses valid for the recompiler */
.bss : { *(.bss*) *(COMMON) } >extram AT >rom
ASSERT(. < RAMBASE + EXTRA_RAM_SIZE, "Maxed out recomp extra ram")
/* Padding to push .text to avoid conflicts with original function addresses */
.pad : { . += 0x1000000; } >extram AT >rom
.text : { *(.text*) } >extram AT >rom
.ctors : { *(.ctors*) *(.init_array*) } >extram AT >rom
.dtors : { *(.dtors*) } >extram AT >rom
.text : { *(.text*) } >extram AT >rom
.recomp_patch : { *(.recomp_patch*) *(.recomp_force_patch*) } >extram AT >rom
.recomp_export : { *(.recomp_export*) } >extram AT >rom
.recomp_event : { *(.recomp_event*) } >extram AT >rom
.rodata : { *(.rodata*) } >extram AT >rom
.data : { *(.data*) } >extram AT >rom
.bss (NOLOAD) : { *(.bss*) *(COMMON) } >extram
ASSERT(. <= PATCH_RAM_END, "Maxed out recomp extra ram")
.reloc 0 : { *(.reloc*) }
.symtab 0 : { *(.symtab) }
.strtab 0 : { *(.strtab) }
.shstrtab 0 : { *(.shstrtab) }

Some files were not shown because too many files have changed in this diff Show More