mirror of
https://github.com/Zelda64Recomp/Zelda64Recomp
synced 2025-10-06 00:22:39 +02:00
Compare commits
54 Commits
exploratio
...
dev
Author | SHA1 | Date | |
---|---|---|---|
|
42646e2cb1 | ||
|
54950a1040 | ||
|
24704d86f1 | ||
|
b5360b0546 | ||
|
46d9e92dda | ||
|
a215103002 | ||
|
bb7030bcd1 | ||
|
0bc0fd2610 | ||
|
54997438de | ||
|
01bccb9059 | ||
|
983d7f43f8 | ||
|
14f92c41ab | ||
|
c27300e6c8 | ||
|
d766cf328f | ||
|
8ec7b282e3 | ||
|
0e96cc3dc9 | ||
|
fd16c379ff | ||
|
3d3524ffe7 | ||
|
1c8668fb65 | ||
|
c6d77fe5ca | ||
|
21a6f4046f | ||
|
25e7b31228 | ||
|
91db87632c | ||
|
0d0f64e32f | ||
|
d99a84f04f | ||
|
af1404b83d | ||
|
83ecc68d18 | ||
|
7697a972a3 | ||
|
473b3d3d02 | ||
|
d782d3dcb9 | ||
|
142b4d021b | ||
|
6598da434e | ||
|
3346400775 | ||
|
c90434962f | ||
|
9981b922dc | ||
|
528a22d86f | ||
|
4c2cc2003a | ||
|
c21d55938b | ||
|
5aa650bffa | ||
|
a8a5e216fe | ||
|
97912578e9 | ||
|
e35bb0700f | ||
|
c7baa7ef8f | ||
|
19d2e38499 | ||
|
79fc56f1fd | ||
|
5e3aac4b45 | ||
|
6dd618e5a4 | ||
|
208e3044fc | ||
|
cbb5e8e7a4 | ||
|
4abd0fe720 | ||
|
07cfe51010 | ||
|
b086945b67 | ||
|
1a6a3b3082 | ||
|
19fcd9bf31 |
7
.github/ISSUE_TEMPLATE/bug_report.md
vendored
7
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -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.
|
||||
|
12
.github/linux/appimage.sh
vendored
12
.github/linux/appimage.sh
vendored
@@ -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
26
.github/macos/Info.plist.in
vendored
Normal 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
87
.github/macos/apple_bundle.cmake
vendored
Normal 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
14
.github/macos/entitlements.plist
vendored
Normal 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
44
.github/macos/fixup_bundle.cmake
vendored
Normal 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
73
.github/macos/ld64
vendored
Executable 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
16
.github/macos/macports.yaml
vendored
Normal 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
|
64
.github/workflows/update-pr-artifacts.yml
vendored
Normal file
64
.github/workflows/update-pr-artifacts.yml
vendored
Normal 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 }}'
|
253
.github/workflows/validate.yml
vendored
253
.github/workflows/validate.yml
vendored
@@ -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
10
.gitignore
vendored
@@ -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
7
.gitmodules
vendored
@@ -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
|
||||
|
||||
|
37
BUILDING.md
37
BUILDING.md
@@ -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.
|
||||
|
189
CMakeLists.txt
189
CMakeLists.txt
@@ -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}")
|
||||
|
||||
|
56
README.md
56
README.md
@@ -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!
|
||||
|
||||
[](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
1
Zelda64RecompSyms
Submodule
Submodule Zelda64RecompSyms added at a325f8fa5c
@@ -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>
|
@@ -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;">↧</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> -->
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
9
assets/config_menu/mods.rml
Normal file
9
assets/config_menu/mods.rml
Normal 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>
|
70
assets/config_sub_menu.rml
Normal file
70
assets/config_sub_menu.rml
Normal 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
5
assets/icons/Reset.svg
Normal 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 |
@@ -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>
|
||||
|
2513
assets/recomp.rcss
2513
assets/recomp.rcss
File diff suppressed because one or more lines are too long
22
assets/scss/package-lock.json
generated
22
assets/scss/package-lock.json
generated
@@ -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": {
|
||||
|
@@ -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;
|
||||
}
|
||||
|
27
assets/scss/styles/components/ConfigDescription.scss
Normal file
27
assets/scss/styles/components/ConfigDescription.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
29
assets/scss/styles/components/ConfigGroup.scss
Normal file
29
assets/scss/styles/components/ConfigGroup.scss
Normal 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;
|
||||
}
|
||||
}
|
413
assets/scss/styles/components/ConfigOption.scss
Normal file
413
assets/scss/styles/components/ConfigOption.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -2,6 +2,9 @@
|
||||
@import "./ControlOption";
|
||||
@import "./Tabs";
|
||||
@import "./Config";
|
||||
@import "./ConfigGroup";
|
||||
@import "./ConfigOption";
|
||||
@import "./ConfigDescription";
|
||||
@import "./InputConfig";
|
||||
@import "./Button";
|
||||
@import "./IconButton";
|
||||
|
@@ -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;
|
||||
}
|
||||
|
38
config_example.cheats.en_us.json
Normal file
38
config_example.cheats.en_us.json
Normal 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
135
config_example.cheats.json
Normal 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
4
flatpak/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
.flatpak-builder
|
||||
builddir
|
||||
repo
|
||||
*.flatpak
|
15
flatpak/README.md
Normal file
15
flatpak/README.md
Normal 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
|
||||
```
|
||||
|
8
flatpak/io.github.zelda64recomp.zelda64recomp.desktop
Normal file
8
flatpak/io.github.zelda64recomp.zelda64recomp.desktop
Normal 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
|
63
flatpak/io.github.zelda64recomp.zelda64recomp.json
Normal file
63
flatpak/io.github.zelda64recomp.zelda64recomp.json
Normal 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"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
29
flatpak/io.github.zelda64recomp.zelda64recomp.metainfo.xml
Normal file
29
flatpak/io.github.zelda64recomp.zelda64recomp.metainfo.xml
Normal 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
10
include/overloaded.h
Normal 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
11
include/recomp_data.h
Normal 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
|
@@ -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();
|
||||
|
@@ -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
|
||||
|
@@ -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();
|
||||
|
||||
|
@@ -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
61
include/zelda_render.h
Normal 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
26
include/zelda_support.h
Normal 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
|
@@ -7,7 +7,8 @@
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "Zelda64Recompiled.exe",
|
||||
"name": "Zelda64Recompiled.exe",
|
||||
"currentDir": "${workspaceRoot}"
|
||||
"currentDir": "${workspaceRoot}",
|
||||
"args": ["--show-console"]
|
||||
}
|
||||
]
|
||||
}
|
@@ -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}
|
||||
)
|
||||
|
Submodule lib/N64ModernRuntime updated: ec7e81b45d...df7e820d8c
Submodule lib/RmlUi updated: a893ea6386...7a06f27db0
257
lib/SlotMap/README.md
Normal file
257
lib/SlotMap/README.md
Normal 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
|
||||
|
||||
[](https://github.com/SergeyMakeev/slot_map/actions)
|
||||
[](https://ci.appveyor.com/project/SergeyMakeev/slot-map)
|
||||
[](https://codecov.io/gh/SergeyMakeev/slot_map)
|
||||

|
||||
|
||||
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
1366
lib/SlotMap/slot_map.h
Normal file
File diff suppressed because it is too large
Load Diff
Submodule lib/lunasvg updated: 610b8bf514...4166d0cccf
2
lib/rt64
2
lib/rt64
Submodule lib/rt64 updated: e106c18965...23cab603c4
Submodule lib/sse2neon deleted from 42c704755d
6
mods/BUILTIN_MODS.md
Normal file
6
mods/BUILTIN_MODS.md
Normal 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
|
BIN
mods/mm_recomp_dpad_builtin.nrm
Normal file
BIN
mods/mm_recomp_dpad_builtin.nrm
Normal file
Binary file not shown.
13
patches.toml
13
patches.toml
@@ -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
|
||||
|
@@ -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
91
patches/actor_data.c
Normal 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
14
patches/actor_funcs.h
Normal 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
|
@@ -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 != ¶ms->player->actor))) {
|
||||
CollisionCheck_ResetDamage(&actor->colChkInfo);
|
||||
} else {
|
||||
Math_Vec3f_Copy(&actor->prevPos, &actor->world.pos);
|
||||
actor->xzDistToPlayer = Actor_WorldDistXZToActor(actor, ¶ms->player->actor);
|
||||
actor->playerHeightRel = Actor_HeightDiff(actor, ¶ms->player->actor);
|
||||
actor->xyzDistToPlayerSq = SQ(actor->xzDistToPlayer) + SQ(actor->playerHeightRel);
|
||||
|
||||
actor->yawTowardsPlayer = Actor_WorldYawTowardActor(actor, ¶ms->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);
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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
46
patches/custom_syms.toml
Normal 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 },
|
||||
]
|
@@ -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 |
@@ -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;
|
26
patches/dummy_headers/objects/object_pamera/object_pamera.h
Normal file
26
patches/dummy_headers/objects/object_pamera/object_pamera.h
Normal 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;
|
@@ -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;
|
||||
|
@@ -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
14
patches/extended_actors.h
Normal 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
|
201
patches/fixes.c
201
patches/fixes.c
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
14
patches/gamestate_patches.c
Normal file
14
patches/gamestate_patches.c
Normal 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();
|
||||
}
|
||||
}
|
||||
|
107
patches/gibdo_mask_cutscene_father_human.c
Normal file
107
patches/gibdo_mask_cutscene_father_human.c
Normal 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;
|
||||
}
|
26
patches/gibdo_mask_cutscene_mask.c
Normal file
26
patches/gibdo_mask_cutscene_mask.c
Normal 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);
|
||||
}
|
||||
}
|
74
patches/gibdo_mask_cutscene_pamela.c
Normal file
74
patches/gibdo_mask_cutscene_pamela.c
Normal 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;
|
||||
}
|
@@ -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);
|
||||
|
1829
patches/input.c
1829
patches/input.c
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
|
@@ -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();
|
||||
|
||||
|
@@ -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
42
patches/memory_patches.c
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#ifdef MIPS
|
||||
#include "ultra64.h"
|
||||
#else
|
||||
#include "librecomp/recomp.h"
|
||||
#include "recomp.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@@ -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);
|
||||
|
@@ -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
Reference in New Issue
Block a user