44 Commits

Author SHA1 Message Date
Darío 73a8aa9918 Increase version number to 1.0.1-dev. (#218)
* Increase version number to 1.0.1-dev.

* Change to 1.0.1
2026-01-29 20:36:34 -03:00
Darío f82b014a2b Add support for deprecated mods. (#221)
* Add always high poly model as deprecated mod.

* Add modern runtime with deprecated mods support.

* Update RecompFrontend for deprecated mod support.

* Switch deprecated mod.
2026-01-29 20:07:31 -03:00
Dario 80963dd419 Revert high-poly Banjo patch. Bring it back when heap allocation is expanded. 2026-01-29 19:57:05 -03:00
Dario 1a0d1053e9 Update RT64 to fix Vulkan Device Address being incorrectly used when unsupported. 2026-01-28 22:54:48 -03:00
Reonu 4982590054 Fixed typo in readme. 2026-01-28 22:07:36 +00:00
Reonu 8475fda0f1 Updated readme with thunderstore link and new FAQ entries (#217)
* Add new FAQ entries to readme

Updated FAQ

* Update README

* Update readme

* Add thunderstore link

* Update README.md

* Update README.md
2026-01-28 18:40:59 -03:00
Dario 91d1e2d272 Add missing extern for high poly Banjo patch. 2026-01-28 18:24:19 -03:00
Darío d124299373 Update RecompFrontend for axis accumulation changes. (#208)
* Update RecompFrontend for axis accumulation changes.

* Update RecompFrontend commit.
2026-01-28 18:14:12 -03:00
Darío 6b387ed9d2 Merge pull request #200 from BanjoRecomp/octagon-input
Update modern runtime to include N64 Octagon input mapping.
2026-01-28 18:03:20 -03:00
Darío 0159329eb1 Merge pull request #207 from BanjoRecomp/shine-scale-fix
Scale Jiggy shine back to zero on launcher animation on animation end.
2026-01-28 17:54:03 -03:00
Darío 661f848a8e Merge pull request #206 from BanjoRecomp/rt64-api-fallbacks
Update RT64 for new API creation fallbacks.
2026-01-28 17:51:42 -03:00
Darío e143c61609 Merge pull request #181 from TSRStormed/patch-1
Added patch for force high poly banjo
2026-01-28 17:47:16 -03:00
Darío 2cc5391511 Merge pull request #209 from BanjoRecomp/no-audio-device
Show error message when no audio device is available.
2026-01-28 17:42:50 -03:00
Dario c82a7b78d0 Update RT64 with AMD fallback. 2026-01-28 17:34:51 -03:00
Dario 066e40d158 Show error message when no audio device is available. 2026-01-28 00:28:41 -03:00
Dario a218f16f25 Update RT64 for new API creation fallbacks. 2026-01-27 22:43:23 -03:00
Dario 5ef2c7e338 Scale Jiggy shine back to zero on launcher animation on animation end. 2026-01-27 22:28:14 -03:00
Dario f8ac690670 Update modern runtime to include N64 Octagon input mapping. 2026-01-26 23:48:14 -03:00
TSRStormed 2fc6e70cb5 Update lod_patches.c
Added default:
2026-01-25 19:48:53 -07:00
TSRStormed 9a0b944f35 Update lod_patches.c 2026-01-25 19:29:36 -07:00
TSRStormed 025215049a Update lod_patches.c
Add force high poly player bk
2026-01-25 18:49:59 -07:00
Darío 045b738297 Merge pull request #173 from BanjoRecomp/ui-callbacks
Run UI callbacks per frame.
2026-01-25 21:21:05 -03:00
Dario 269d9bec93 Newline. 2026-01-25 20:48:59 -03:00
Dario 7cff85fa22 Run UI callbacks per frame. 2026-01-25 20:30:51 -03:00
Dario 7242b70ba5 Uncomment ARM64 workflow. 2026-01-25 19:03:18 -03:00
Dario c17aa22028 Update RT64 to force residency sets off in Metal. 2026-01-24 19:16:53 -03:00
Dario e659ecb7ea Update RecompFrontend to fix Mac menu issue. 2026-01-24 18:09:05 -03:00
Dario 5702422244 Set execute bit for ld64 2026-01-24 17:26:51 -03:00
Mr-Wiseguy d67eb10cff Update frontend for sse2neon include path 2026-01-24 15:10:05 -05:00
Mr-Wiseguy 9d2d5b1473 Add sse2neon include path 2026-01-24 14:58:26 -05:00
Dario af730a3d4f Remove scss folder from CI. 2026-01-24 16:51:45 -03:00
Mr-Wiseguy 6e81827a05 1.0.0 2026-01-24 13:35:01 -05:00
Darío 5ec2d3e5c2 CI Workflows. (#73) 2026-01-24 13:33:06 -05:00
Mr-Wiseguy 5d0d15e9a3 Add note about faithful audio to readme 2026-01-24 13:18:00 -05:00
Dario da393d7391 Add turning off state to christmas tree. 2026-01-24 15:08:25 -03:00
Mr-Wiseguy 00c47eb17c Add BK Reloaded link to readme 2026-01-24 12:57:47 -05:00
Mr-Wiseguy 0ccade9de8 Add building instructions 2026-01-24 02:27:25 -05:00
Mr-Wiseguy 444eaed1a7 Write readme 2026-01-24 02:03:49 -05:00
Mr-Wiseguy 92024a0518 Update RecompFrontend for readme changes.
Co-authored-by: thecozies <79979276+thecozies@users.noreply.github.com>
2026-01-24 02:02:05 -05:00
Mr-Wiseguy 60530b36bc Remove outdated cmakelists include dirs and headers 2026-01-24 01:39:47 -05:00
Dario 6708aab4a9 Lower interpolation threshold for first beach cutscene. Skip interpolation on lens flare when camera skips. 2026-01-23 22:20:45 -03:00
Dario dbfb6714a8 Lower interpolation threshold for fake ending cutscene. 2026-01-22 19:54:49 -03:00
Dario 44d0265852 Interpolate parade text. 2026-01-22 19:27:07 -03:00
Darío da24f09d16 Add tagging to marker that draws wood explosions and others. (#122) 2026-01-21 03:45:31 -05:00
31 changed files with 1183 additions and 4772 deletions
+45
View File
@@ -0,0 +1,45 @@
---
name: Bug report
about: Report a bug in the project
title: ''
labels: ''
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. 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?**
**Were you playing with intended mechanics, or using glitches? If it's the latter, which glitches?**
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. etc.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
Please attach a screenshot of the bug.
**Desktop (please complete the following information):**
- OS: [Windows 10, Windows 11, Linux distro]
- Version: [e.g. 1.0.0]
- CPU: [e.g. Intel Core ..., AMD Ryzen ..., etc.]
- 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.
+9
View File
@@ -0,0 +1,9 @@
[Desktop Entry]
Name=BanjoRecompiled
Type=Application
Terminal=false
Icon=BanjoRecompiled
Exec=BanjoRecompiled
GenericName=BanjoRecompiled
Categories=Game;
+26
View File
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>CFBundleExecutable</key>
<string>BanjoRecompiled</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
View File
@@ -0,0 +1,87 @@
# Define the path to the entitlements file
set(ENTITLEMENTS_FILE ${CMAKE_SOURCE_DIR}/.github/macos/entitlements.plist)
# Set bundle properties
set_target_properties(BanjoRecompiled PROPERTIES
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_BUNDLE_NAME "BanjoRecompiled"
MACOSX_BUNDLE_GUI_IDENTIFIER "com.github.Banjorecompiled"
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/app.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(BanjoRecompiled PRIVATE ${ICNS_FILE})
# Ensure BanjoRecompiled depends on create_icns
add_dependencies(BanjoRecompiled 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 BanjoRecompiled 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 BanjoRecompiled 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:BanjoRecompiled>/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:BanjoRecompiled>/Contents/Resources/
# Set RPATH
COMMAND install_name_tool -add_rpath "@executable_path/../Frameworks/" $<TARGET_BUNDLE_DIR:BanjoRecompiled>/Contents/MacOS/BanjoRecompiled
# Sign the bundle
COMMAND codesign --verbose=4 --options=runtime --no-strict --sign - --entitlements ${ENTITLEMENTS_FILE} --deep --force $<TARGET_BUNDLE_DIR:BanjoRecompiled>
COMMENT "Performing post-build steps for macOS bundle"
VERBATIM
)
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.disable-executable-page-protection</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>
+44
View File
@@ -0,0 +1,44 @@
include(BundleUtilities)
# Check for pkgx installation
find_program(PKGX_EXECUTABLE pkgx)
# Xcode generator puts the build type in the build directory
set(BUILD_PREFIX "")
if (CMAKE_GENERATOR STREQUAL "Xcode")
set(BUILD_PREFIX "${CMAKE_BUILD_TYPE}/")
endif()
# Use generator expressions to get the absolute path to the bundle
set(APPS "${BUILD_PREFIX}BanjoRecompiled.app/Contents/MacOS/BanjoRecompiled")
# Set up framework search paths
set(DIRS "${BUILD_PREFIX}BanjoRecompiled.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
View File
@@ -0,0 +1,73 @@
#!/usr/bin/python3
"""
Custom ld64 wrapper for macOS
This script wraps the standard macOS linker (/usr/bin/ld) to modify executable memory
protection flags in the resulting Mach-O binary. It works in three stages:
1. First, it passes through all arguments to the regular macOS linker to create the binary
2. Then, it parses command line arguments to identify output file and segment protection flags
3. Finally, it modifies the output binary's Mach-O headers to ensure segments (particularly __TEXT)
have the maximum protection flags (rwx) we specify, even if the default macOS linker would restrict them
This is necessary because macOS restricts writable+executable memory by default,
but certain applications need this capability for dynamic code generation or JIT compilation.
Usage: Same as the standard ld64 linker, with the added benefit that -segprot options
will have their max_prot values properly preserved in the output binary.
"""
import sys
import subprocess
from itertools import takewhile
from macholib import MachO, ptypes
def parse_rwx(text):
return ('r' in text and 1) | ('w' in text and 2) | ('x' in text and 4)
def apply_maxprots(path, maxprots):
mach = MachO.MachO(path)
header = mach.headers[0]
offset = ptypes.sizeof(header.mach_header)
for cload, ccmd, cdata in header.commands:
if not hasattr(ccmd, 'segname'):
break
if hasattr(ccmd.segname, 'to_str'):
segname = ccmd.segname.to_str().decode('utf-8').strip('\0')
else:
segname = ccmd.segname.decode('utf-8').strip('\0')
if segname in maxprots and ccmd.maxprot != maxprots[segname]:
fields = list(takewhile(lambda field: field[0] != 'maxprot', cload._fields_ + ccmd._fields_))
index = offset + sum(ptypes.sizeof(typ) for _, typ in fields)
with open(path, 'r+b') as fh:
fh.seek(index)
fh.write(bytes([maxprots[segname]]))
offset += cload.cmdsize
try:
subprocess.check_call(['/usr/bin/ld'] + sys.argv[1:])
except subprocess.CalledProcessError as ex:
sys.exit(ex.returncode)
output_file = 'a.out'
segprots = {'__TEXT': parse_rwx('rwx')} # maxprot = rwx
i = 1
while i < len(sys.argv):
if sys.argv[i] == '-o' and i + 1 < len(sys.argv):
output_file = sys.argv[i + 1]
i += 2
elif sys.argv[i] == '-segprot' and i + 3 < len(sys.argv):
segment = sys.argv[i + 1]
maxprot = sys.argv[i + 2]
segprots[segment] = parse_rwx(maxprot)
i += 4
else:
i += 1
apply_maxprots(output_file, segprots)
+16
View File
@@ -0,0 +1,16 @@
version: '2.10.6'
prefix: '/opt/local'
variants:
select:
- aqua
- metal
deselect: x11
ports:
- name: libiconv
select: universal
- name: libsdl2
select: universal
- name: freetype
select: universal
- name: clang-18
- name: llvm-18
+64
View File
@@ -0,0 +1,64 @@
name: update-pr-artifacts
on:
workflow_run:
workflows: [validate-external, validate-internal]
types:
- completed
jobs:
update-pr-artifacts:
runs-on: ubuntu-latest
if: (github.event.workflow_run.event == 'pull_request' || github.event.workflow_run.event == 'pull_request_target') && github.event.workflow_run.conclusion == 'success'
name: Update PR Artifacts
steps:
- name: Get PR Number
id: pr-number
uses: actions/github-script@v6
with:
result-encoding: string
script: |
const { owner, repo } = context.repo;
const findPRNumber = async () => {
const pulls = await github.rest.pulls.list({ owner, repo });
for await (const { data } of github.paginate.iterator(pulls)) {
for (const pull of data) {
if (pull.head.sha === '${{ github.event.workflow_run.head_sha }}' && pull.user.id === ${{ github.event.sender.id }}) {
return pull.number;
}
}
}
return null;
};
const prNumber = await findPRNumber();
if (!prNumber) {
core.error(`No matching pull request found`);
} else {
return prNumber;
}
- name: Create Artifacts Content
id: artifacts-content
uses: actions/github-script@v6
with:
result-encoding: string
script: |
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
const nightlyLinks = artifacts.data.artifacts.reduce((acc, item) => {
acc += `- [${item.name}.zip](https://nightly.link/${context.repo.owner}/${context.repo.repo}/actions/artifacts/${item.id}.zip)\n`;
return acc;
}, '### Build Artifacts\n');
return nightlyLinks;
- name: Update PR Description
uses: garrettjoecox/pr-section@3.1.0
with:
section-name: 'artifacts'
repo-token: '${{ secrets.GITHUB_TOKEN }}'
pr-number: ${{ steps.pr-number.outputs.result }}
section-value: '${{ steps.artifacts-content.outputs.result }}'
+19
View File
@@ -0,0 +1,19 @@
name: validate-external
on:
pull_request_target:
types: [opened, synchronize]
jobs:
authorize:
if: github.repository != github.event.pull_request.head.repo.full_name
environment:
${{ github.event_name == 'pull_request_target' &&
github.event.pull_request.head.repo.full_name != github.repository &&
'external' || 'internal' }}
runs-on: ubuntu-latest
steps:
- run: echo ✓
build:
needs: authorize
uses: ./.github/workflows/validate.yml
secrets:
ZRE_REPO_WITH_PAT: ${{ secrets.ZRE_REPO_WITH_PAT }}
+12
View File
@@ -0,0 +1,12 @@
name: validate-internal
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize]
jobs:
build:
if: github.event_name == 'push' || github.repository == github.event.pull_request.head.repo.full_name
uses: ./.github/workflows/validate.yml
secrets: inherit
+411
View File
@@ -0,0 +1,411 @@
name: validate
on:
workflow_call:
inputs:
SDL2_VERSION:
type: string
required: false
default: '2.30.3'
N64RECOMP_COMMIT:
type: string
required: false
default: '2b6f05688de2abc7d86da5b4a89b84c2c6acbabe'
secrets:
SECRET_NAME:
required: true
SECRET_TOKEN:
required: true
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build-linux:
runs-on: ubuntu-22.04
strategy:
matrix:
type: [ Debug, Release ]
os: [ ubuntu-22.04 ]
name: ubuntu-22.04 (x64, ${{ matrix.type }}, Native)
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 }}-banjo-ccache-${{ matrix.type }}-x64-${{ inputs.N64RECOMP_COMMIT }}
- name: Get extra dependencies
uses: actions/checkout@v4
with:
repository: ${{ secrets.SECRET_NAME }}
token: ${{ secrets.SECRET_TOKEN }}
path: extra
- name: Install Linux Dependencies
run: |
sudo apt-get update
sudo apt-get install -y ninja-build libgtk-3-dev lld llvm clang-15 libfuse2
# Install SDL2
echo ::group::install SDL2
# Enable ccache
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
wget https://github.com/libsdl-org/SDL/releases/download/release-${{ inputs.SDL2_VERSION }}/SDL2-${{ inputs.SDL2_VERSION }}.tar.gz
tar -xzf SDL2-${{ inputs.SDL2_VERSION }}.tar.gz
cd SDL2-${{ inputs.SDL2_VERSION }}
./configure
make -j 10
sudo make install
sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/
echo ::endgroup::
- 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: |
# Copy extra dependencies to root directory
cp extra/* .
./N64Recomp banjo.us.rev0.toml
./RSPRecomp n_aspMain.us.rev0.toml
- name: Build BanjoRecomp
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++-15 -DCMAKE_C_COMPILER=clang-15 -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S . -B cmake-build -DPATCHES_C_COMPILER=clang-15 -DPATCHES_LD=ld.lld-15
cmake --build cmake-build --config ${{ matrix.type }} --target BanjoRecompiled -j $(nproc)
- name: Prepare Archive
run: |
mv cmake-build/BanjoRecompiled BanjoRecompiled
tar -czf BanjoRecompiled.tar.gz BanjoRecompiled assets/ recompcontrollerdb.txt
- name: Archive BanjoRecomp
uses: actions/upload-artifact@v4
with:
name: BanjoRecompiled-${{ runner.os }}-X64-${{ matrix.type }}
path: BanjoRecompiled.tar.gz
build-linux-arm64:
runs-on: ubuntu-22.04-arm
strategy:
matrix:
type: [ Debug, Release ]
os: [ ubuntu-22.04 ]
name: ${{ matrix.os }} (arm64, ${{ matrix.type }}, Native)
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 }}-banjo-ccache-${{ matrix.type }}-arm64-${{ inputs.N64RECOMP_COMMIT }}
- name: Install Linux Dependencies
run: |
sudo apt-get update
sudo apt-get install -y ninja-build libgtk-3-dev lld llvm clang-15 libfuse2
# Install SDL2
echo ::group::install SDL2
# Enable ccache
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
wget https://github.com/libsdl-org/SDL/releases/download/release-${{ inputs.SDL2_VERSION }}/SDL2-${{ inputs.SDL2_VERSION }}.tar.gz
tar -xzf SDL2-${{ inputs.SDL2_VERSION }}.tar.gz
cd SDL2-${{ inputs.SDL2_VERSION }}
./configure
make -j 10
sudo make install
sudo cp -av /usr/local/lib/libSDL* /lib/aarch64-linux-gnu/
echo ::endgroup::
- name: Get extra dependencies
uses: actions/checkout@v4
with:
repository: ${{ secrets.SECRET_NAME }}
token: ${{ secrets.SECRET_TOKEN }}
path: extra
- 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: |
# Copy extra dependencies to root directory
cp extra/* .
./N64Recomp banjo.us.rev0.toml
./RSPRecomp n_aspMain.us.rev0.toml
- name: Build BanjoRecomp
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++-15 -DCMAKE_C_COMPILER=clang-15 -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S . -B cmake-build
cmake --build cmake-build --config ${{ matrix.type }} --target BanjoRecompiled -j $(nproc)
- name: Prepare Archive
run: |
mv cmake-build/BanjoRecompiled BanjoRecompiled
tar -czf BanjoRecompiled.tar.gz BanjoRecompiled assets/ recompcontrollerdb.txt
- name: Archive BanjoRecomp
uses: actions/upload-artifact@v4
with:
name: BanjoRecompiled-${{ runner.os }}-ARM64-${{ matrix.type }}
path: BanjoRecompiled.tar.gz
build-linux-flatpak:
runs-on: ubuntu-latest
env:
FLATPAK_ID: io.github.banjorecomp.banjorecomp
FREEDESKTOP_VERSION: 25.08
LLVM_VERSION: 20
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 }}-banjo-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: Get extra dependencies
uses: actions/checkout@v4
with:
repository: ${{ secrets.SECRET_NAME }}
token: ${{ secrets.SECRET_TOKEN }}
path: extra
- 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 BanjoRecomp
run: |-
cp extra/* .
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: BanjoRecompiled-Flatpak-X64-${{ matrix.type }}
path: ./${{ env.FLATPAK_ID }}.flatpak
build-windows:
runs-on: windows-latest
strategy:
matrix:
type: [ Debug, RelWithDebInfo ]
name: windows (${{ 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 }}-banjo-ccache-${{ matrix.type }}
- name: Install Windows Dependencies
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: Get extra dependencies
uses: actions/checkout@v4
with:
repository: ${{ secrets.SECRET_NAME }}
token: ${{ secrets.SECRET_TOKEN }}
path: extra
- 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
set $env:PATH="$env:USERPROFILE/.cargo/bin;$env:PATH"
$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 N64RecompCLI -j $cpuCores
cmake --build cmake-build --config Release --target RSPRecomp -j $cpuCores
# Copy N64Recomp & RSPRecomp to root directory
cp cmake-build/N64Recomp.exe ..
cp cmake-build/RSPRecomp.exe ..
- name: Run N64Recomp & RSPRecomp
run: |
# Copy extra dependencies to root directory
cp extra/* .
./N64Recomp banjo.us.rev0.toml
./RSPRecomp n_aspMain.us.rev0.toml
- name: Build BanjoRecomp
run: |-
# enable ccache
set $env:PATH="$env:USERPROFILE/.cargo/bin;$env:PATH"
$cpuCores = (Get-CimInstance -ClassName Win32_Processor).NumberOfLogicalProcessors
# 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 -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 BanjoRecompiled -j $cpuCores
env:
SDL2_VERSION: ${{ inputs.SDL2_VERSION }}
- name: Prepare Archive
run: |
Move-Item -Path "cmake-build/BanjoRecompiled.exe" -Destination "BanjoRecompiled.exe"
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/BanjoRecompiled.pdb" -Destination "BanjoRecompiled.pdb"
- name: Archive BanjoRecomp
uses: actions/upload-artifact@v4
with:
name: BanjoRecompiled-${{ runner.os }}-${{ matrix.type }}
path: |
BanjoRecompiled.exe
dxcompiler.dll
dxil.dll
SDL2.dll
assets/
recompcontrollerdb.txt
- name: Archive Debug Files
uses: actions/upload-artifact@v4
with:
name: BanjoRecompiled-PDB-${{ matrix.type }}
path: |
BanjoRecompiled.pdb
build-macos:
runs-on: 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 }}-banjo-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: Get extra dependencies
uses: actions/checkout@v4
with:
repository: ${{ secrets.SECRET_NAME }}
token: ${{ secrets.SECRET_TOKEN }}
path: extra
- 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: |
# Copy extra dependencies to root directory
cp extra/* .
./N64Recomp banjo.us.rev0.toml
./RSPRecomp n_aspMain.us.rev0.toml
- name: Build BanjoRecomp
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 BanjoRecompiled -j $(sysctl -n hw.ncpu)
- name: Prepare Archive
run: |
mv cmake-build/BanjoRecompiled.app BanjoRecompiled.app
zip -r -y BanjoRecompiled.zip BanjoRecompiled.app
- name: Archive BanjoRecomp
uses: actions/upload-artifact@v4
with:
name: BanjoRecompiled-${{ runner.os }}-${{ matrix.type }}
path: BanjoRecompiled.zip
+74
View File
@@ -0,0 +1,74 @@
# Building Guide
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.
These steps cover: decompressing the ROM, running the recompiler and finally building the project.
## 1. Clone the BanjoRecomp Repository
This project makes use of submodules so you will need to clone the repository with the `--recurse-submodules` flag.
```bash
git clone --recurse-submodules
# if you forgot to clone with --recurse-submodules
# cd /path/to/cloned/repo && git submodule update --init --recursive
```
## 2. Install Dependencies
### Linux
For Linux the instructions for Ubuntu are provided, but you can find the equivalent packages for your preferred distro.
```bash
# For Ubuntu, simply run:
sudo apt-get install cmake ninja-build libsdl2-dev libgtk-3-dev lld llvm clang
```
### Windows
You will need to install [Visual Studio 2022](https://visualstudio.microsoft.com/downloads/).
In the setup process you'll need to select the following options and tools for installation:
- Desktop development with C++
- C++ Clang Compiler for Windows
- C++ CMake tools for Windows
The other tool necessary will be `make` which can be installe via [Chocolatey](https://chocolatey.org/):
```bash
choco install make
```
## 3. Decompressing the target ROM
You will need to decompress the NTSC-U 1.0 N64 Banjo-Kazooie ROM (sha1: d6133ace5afaa0882cf214cf88daba39e266c078) before running the recompiler.
The most straightforward way to do this is to set up the [Banjo-Kazooie decompilation](https://gitlab.com/banjo.decomp/banjo-kazooie), which will decompress the ROM when building. Alternatively, you can run the [bk_rom_compressor tool](https://github.com/MittenzHugg/bk_rom_compressor) directly, which is what the decompilation uses to decompress the ROM.
Regardless of which method you use, copy the decompressed ROM to the root of the BanjoRecomp repository with this filename:
- `banjo.us.v10.decompressed.z64`
## 4. Generating the C code
Now that you have the required files, you must build [N64Recomp](https://github.com/N64Recomp/N64Recomp) and run it to generate the C code to be compiled. The building instructions can be found [here](https://github.com/N64Recomp/N64Recomp?tab=readme-ov-file#building). That will build the executables: `N64Recomp` and `RSPRecomp` which you should copy to the root of the BanjoRecomp repository.
After that, go back to the repository root, and run the following commands:
```bash
./N64Recomp banjo.us.rev0.toml
./RSPRecomp n_aspMain.us.rev0.toml
```
## 5. Building the Project
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 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
cmake --build build-cmake --target BanjoRecompiled -j$(nproc) --config Release # or Debug
```
## 6. Success
Voilà! You should now have a `BanjoRecompiled` 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]
> In the game itself, you should be using a standard ROM, not the decompressed one.
+1 -2
View File
@@ -159,7 +159,7 @@ set (SOURCES
target_include_directories(BanjoRecompiled PRIVATE
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/N64Recomp/include
${CMAKE_SOURCE_DIR}/lib/concurrentqueue
${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/thirdparty/sse2neon
${CMAKE_SOURCE_DIR}/lib/rt64/src/contrib
${CMAKE_SOURCE_DIR}/lib/rt64/src/contrib/hlslpp/include
${CMAKE_SOURCE_DIR}/lib/rt64/src/contrib/dxc/inc
@@ -169,7 +169,6 @@ target_include_directories(BanjoRecompiled PRIVATE
${CMAKE_SOURCE_DIR}/lib/RecompFrontend/recompinput/include
${CMAKE_SOURCE_DIR}/lib/RecompFrontend/recompui/include
${CMAKE_SOURCE_DIR}/lib/RecompFrontend/recompui/src
${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_CURRENT_BINARY_DIR}
+133
View File
@@ -0,0 +1,133 @@
# Banjo: Recompiled
Banjo: Recompiled is a project that uses [N64: Recompiled](https://github.com/N64Recomp/N64Recomp) to **statically recompile** Banjo-Kazooie 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/BanjoRecomp/BanjoRecomp/releases/latest).
Join the [N64: Recompiled Community Discord](https://discord.gg/AWZThJ4dPf) to discuss this and other N64: Recompiled projects!
[![Discord Invitation](https://discordapp.com/api/guilds/1374083583739826328/widget.png?style=banner2 'N64 Recomp')](https://discord.gg/AWZThJ4dPf)
### **This repository and its releases do not contain game assets. The original game is required to build or run this project.**
## Table of Contents
* [System Requirements](#system-requirements)
* [Features](#features)
* [Plug and Play](#plug-and-play)
* [Fully Intact N64 Effects](#fully-intact-n64-effects)
* [Faithful Audio](#faithful-audio)
* [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)
* [Note Saving](#note-saving)
* [Low Input Lag](#low-input-lag)
* [Instant Load Times](#instant-load-times)
* [Linux and Steam Deck Support](#linux-and-steam-deck-support)
* [Planned Features](#planned-features)
* [FAQ](#faq)
* [Known Issues](#known-issues)
* [Building](#building)
* [Libraries Used and Projects Referenced](#libraries-used-and-projects-referenced)
## System Requirements
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+
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.
## Features
#### Plug and Play
Simply provide your copy of the North American 1.0 version of the game in the main menu and start playing! This project will automatically load assets from the provided copy, so there is no need to go through a separate extraction step or build the game yourself.
#### Fully Intact N64 Effects
All advanced graphical effects, such as the jigsaw puzzle screen transition and Bottles' Moving Picture Game are fully intact and have been adapted for widescreen and high framerates. Fine details such as the game's original mipmapping preserved with a high degree of accuracy.
#### Faithful Audio
Music and sounds are perfectly faithful to the original N64 version of game, with all audio processing intact and no popping or stuttering. Scenes that synchronize audio to visuals have their timings corrected to ensure they remain synchronized as they did on original hardware.
#### Easy-to-Use Menus
Gameplay settings, graphics settings, input mappings, and audio settings can all be configured with the in-game config menu. The menus can all be used with mouse, controller, or keyboard for maximum convenience.
#### High Framerate Support
Play at any framerate you want thanks to functionality provided by RT64! Game objects and terrain, texture scrolling, screen effects, and all HUD elements are all rendered at high framerates. By default, this project is configured to run at your monitor's refresh rate. You can also play at the original framerate of the game if you prefer. **Changing framerate has no effect on gameplay.**
**Note**: External framerate limiters (such as the NVIDIA Control Panel) are known to potentially cause problems, so if you notice any stuttering then turn them off and use the manual framerate slider in the in-game graphics menu instead.
#### Widescreen and Ultrawide Support
Any aspect ratio is supported, with all effects modded to work correctly in widescreen. The HUD can also be positioned at 16:9 when using ultrawide aspect ratios if preferred. Game models have been extended where necessary to ensure cutscenes work correctly in 16:9 aspect ratio. Cutscenes can also be optionally pillarboxed to 16:9 or the game's original aspect ratio to hide any details that weren't meant to be visible.
#### 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/banjo-recompiled/. The Thunderstore mod manager/r2modman is not required or supported, so simply download the mods directly from the website.
If you're interested in making mods for this project, check out [the mod template](https://github.com/BanjoRecomp/BKRecompModTemplate) 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). You can also join the [N64: Recompiled Modding Discord server](https://discord.gg/Z8Nbm455D4).
#### Dual Analog Camera
Play with a dual analog control for finer camera control! When this option is enabled, the right analog stick will control the camera. Buttons mapped to the right analog stick will still work while crouching, so you can keep the C-buttons mapped to it even when this setting is enabled.
#### Note Saving
Keep your notes when leaving a level or dying like in later releases of the game!
This setting can be turned off to restore the N64 version's note score mechanic. Changing this setting will only take effect when returning to Grunty's Lair or restarting the game.
#### Low Input Lag
This project has been optimized to have as little input lag as possible, making the game feel more responsive than ever!
#### Instant Load Times
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 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 BanjoRecompiled executable file and select "Add to Steam". From there, you can return to Gaming mode and configure the controls as needed.
## Planned Features
* Ray Tracing via RT64
## FAQ
#### What is static recompilation?
Static recompilation is the process of automatically translating an application from one platform to another. For more details, check out the full description of how this project's recompilation works here: [N64: Recompiled](https://github.com/Mr-Wiseguy/N64Recomp).
#### How is this related to the decompilation project?
Unlike N64 ports in the past, this project is not based on the source code provided by a decompilation of the game. This is because static recompilation bypasses the need for decompiled source code when making a port, allowing ports to be made **without source code**. However, the reverse engineering work done by the decompilation team was invaluable for providing some of the enhancements featured in this project. For this reason, the project uses headers and some functions from the decompilation project in order to make modifications to the game. Many thanks to the decompilation team for all of the hard work they've done.
#### Where is the savefile stored?
- Windows: `%LOCALAPPDATA%\BanjoRecompiled\saves`
- Linux: `~/.config/BanjoRecompiled/saves`
- macOS: `~/Library/Application Support/BanjoRecompiled/saves`
#### How do I choose a different ROM?
**You don't.** This project is **only** a port of Banjo-Kazooie, and it will only accept one specific ROM: the US 1.0 version of the N64 release of Banjo-Kazooie. ROMs in formats other than .z64 will be automatically converted, as long as it is the correct ROM. **This is not an emulator, and it cannot run any arbitrary ROM.**
Instead, you can change the game by installing mods. See the [mod support](#mod-support) section for details.
#### 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.
#### Were the duplicate Mumbo token IDs in the original game fixed?
Yes. The incorrect Mumbo tokens now use previously unused token IDs. This won't affect mods that replace levels.
#### Does note saving have the same issue as the XBLA version where it collects notes during Bottles' puzzles and prevents you from getting 100% completion?
No. Care has been taken to ensure that notes are not saved during demos.
## Known Issues
* 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
Building is not required to play this project, as prebuilt binaries (which do not contain game assets) can be found in the [Releases](https://github.com/BanjoRecomp/BanjoRecomp/releases) section. Instructions on how to build this project can be found in the [BUILDING.md](BUILDING.md) file.
## Libraries Used and Projects Referenced
* [N64Recomp](https://github.com/N64Recomp/N64Recomp) for statically recompiling the game's code to run natively
* [RT64](https://github.com/rt64/rt64) for the project's rendering engine
* [N64ModernRuntime](https://github.com/N64Recomp/N64ModernRuntime) for replacing the original N64 runtime libraries and providing mod support
* [RecompFrontend](https://github.com/N64Recomp/RecompFrontend) for menus and input handling
* [Banjo-Kazooie Decompilation](https://gitlab.com/banjo.decomp/banjo-kazooie) for headers and some function definitions, used for making patches or some enhancements
@@ -1,582 +0,0 @@
// Provides an efficient blocking version of moodycamel::ConcurrentQueue.
// ©2015-2020 Cameron Desrochers. Distributed under the terms of the simplified
// BSD license, available at the top of concurrentqueue.h.
// Also dual-licensed under the Boost Software License (see LICENSE.md)
// Uses Jeff Preshing's semaphore implementation (under the terms of its
// separate zlib license, see lightweightsemaphore.h).
#pragma once
#include "concurrentqueue.h"
#include "lightweightsemaphore.h"
#include <type_traits>
#include <cerrno>
#include <memory>
#include <chrono>
#include <ctime>
namespace moodycamel
{
// This is a blocking version of the queue. It has an almost identical interface to
// the normal non-blocking version, with the addition of various wait_dequeue() methods
// and the removal of producer-specific dequeue methods.
template<typename T, typename Traits = ConcurrentQueueDefaultTraits>
class BlockingConcurrentQueue
{
private:
typedef ::moodycamel::ConcurrentQueue<T, Traits> ConcurrentQueue;
typedef ::moodycamel::LightweightSemaphore LightweightSemaphore;
public:
typedef typename ConcurrentQueue::producer_token_t producer_token_t;
typedef typename ConcurrentQueue::consumer_token_t consumer_token_t;
typedef typename ConcurrentQueue::index_t index_t;
typedef typename ConcurrentQueue::size_t size_t;
typedef typename std::make_signed<size_t>::type ssize_t;
static const size_t BLOCK_SIZE = ConcurrentQueue::BLOCK_SIZE;
static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = ConcurrentQueue::EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD;
static const size_t EXPLICIT_INITIAL_INDEX_SIZE = ConcurrentQueue::EXPLICIT_INITIAL_INDEX_SIZE;
static const size_t IMPLICIT_INITIAL_INDEX_SIZE = ConcurrentQueue::IMPLICIT_INITIAL_INDEX_SIZE;
static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = ConcurrentQueue::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE;
static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = ConcurrentQueue::EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE;
static const size_t MAX_SUBQUEUE_SIZE = ConcurrentQueue::MAX_SUBQUEUE_SIZE;
public:
// Creates a queue with at least `capacity` element slots; note that the
// actual number of elements that can be inserted without additional memory
// allocation depends on the number of producers and the block size (e.g. if
// the block size is equal to `capacity`, only a single block will be allocated
// up-front, which means only a single producer will be able to enqueue elements
// without an extra allocation -- blocks aren't shared between producers).
// This method is not thread safe -- it is up to the user to ensure that the
// queue is fully constructed before it starts being used by other threads (this
// includes making the memory effects of construction visible, possibly with a
// memory barrier).
explicit BlockingConcurrentQueue(size_t capacity = 6 * BLOCK_SIZE)
: inner(capacity), sema(create<LightweightSemaphore, ssize_t, int>(0, (int)Traits::MAX_SEMA_SPINS), &BlockingConcurrentQueue::template destroy<LightweightSemaphore>)
{
assert(reinterpret_cast<ConcurrentQueue*>((BlockingConcurrentQueue*)1) == &((BlockingConcurrentQueue*)1)->inner && "BlockingConcurrentQueue must have ConcurrentQueue as its first member");
if (!sema) {
MOODYCAMEL_THROW(std::bad_alloc());
}
}
BlockingConcurrentQueue(size_t minCapacity, size_t maxExplicitProducers, size_t maxImplicitProducers)
: inner(minCapacity, maxExplicitProducers, maxImplicitProducers), sema(create<LightweightSemaphore, ssize_t, int>(0, (int)Traits::MAX_SEMA_SPINS), &BlockingConcurrentQueue::template destroy<LightweightSemaphore>)
{
assert(reinterpret_cast<ConcurrentQueue*>((BlockingConcurrentQueue*)1) == &((BlockingConcurrentQueue*)1)->inner && "BlockingConcurrentQueue must have ConcurrentQueue as its first member");
if (!sema) {
MOODYCAMEL_THROW(std::bad_alloc());
}
}
// Disable copying and copy assignment
BlockingConcurrentQueue(BlockingConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION;
BlockingConcurrentQueue& operator=(BlockingConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION;
// Moving is supported, but note that it is *not* a thread-safe operation.
// Nobody can use the queue while it's being moved, and the memory effects
// of that move must be propagated to other threads before they can use it.
// Note: When a queue is moved, its tokens are still valid but can only be
// used with the destination queue (i.e. semantically they are moved along
// with the queue itself).
BlockingConcurrentQueue(BlockingConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT
: inner(std::move(other.inner)), sema(std::move(other.sema))
{ }
inline BlockingConcurrentQueue& operator=(BlockingConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT
{
return swap_internal(other);
}
// Swaps this queue's state with the other's. Not thread-safe.
// Swapping two queues does not invalidate their tokens, however
// the tokens that were created for one queue must be used with
// only the swapped queue (i.e. the tokens are tied to the
// queue's movable state, not the object itself).
inline void swap(BlockingConcurrentQueue& other) MOODYCAMEL_NOEXCEPT
{
swap_internal(other);
}
private:
BlockingConcurrentQueue& swap_internal(BlockingConcurrentQueue& other)
{
if (this == &other) {
return *this;
}
inner.swap(other.inner);
sema.swap(other.sema);
return *this;
}
public:
// Enqueues a single item (by copying it).
// Allocates memory if required. Only fails if memory allocation fails (or implicit
// production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0,
// or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
// Thread-safe.
inline bool enqueue(T const& item)
{
if ((details::likely)(inner.enqueue(item))) {
sema->signal();
return true;
}
return false;
}
// Enqueues a single item (by moving it, if possible).
// Allocates memory if required. Only fails if memory allocation fails (or implicit
// production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0,
// or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
// Thread-safe.
inline bool enqueue(T&& item)
{
if ((details::likely)(inner.enqueue(std::move(item)))) {
sema->signal();
return true;
}
return false;
}
// Enqueues a single item (by copying it) using an explicit producer token.
// Allocates memory if required. Only fails if memory allocation fails (or
// Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
// Thread-safe.
inline bool enqueue(producer_token_t const& token, T const& item)
{
if ((details::likely)(inner.enqueue(token, item))) {
sema->signal();
return true;
}
return false;
}
// Enqueues a single item (by moving it, if possible) using an explicit producer token.
// Allocates memory if required. Only fails if memory allocation fails (or
// Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
// Thread-safe.
inline bool enqueue(producer_token_t const& token, T&& item)
{
if ((details::likely)(inner.enqueue(token, std::move(item)))) {
sema->signal();
return true;
}
return false;
}
// Enqueues several items.
// Allocates memory if required. Only fails if memory allocation fails (or
// implicit production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE
// is 0, or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
// Note: Use std::make_move_iterator if the elements should be moved instead of copied.
// Thread-safe.
template<typename It>
inline bool enqueue_bulk(It itemFirst, size_t count)
{
if ((details::likely)(inner.enqueue_bulk(std::forward<It>(itemFirst), count))) {
sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
return true;
}
return false;
}
// Enqueues several items using an explicit producer token.
// Allocates memory if required. Only fails if memory allocation fails
// (or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
// Note: Use std::make_move_iterator if the elements should be moved
// instead of copied.
// Thread-safe.
template<typename It>
inline bool enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count)
{
if ((details::likely)(inner.enqueue_bulk(token, std::forward<It>(itemFirst), count))) {
sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
return true;
}
return false;
}
// Enqueues a single item (by copying it).
// Does not allocate memory. Fails if not enough room to enqueue (or implicit
// production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE
// is 0).
// Thread-safe.
inline bool try_enqueue(T const& item)
{
if (inner.try_enqueue(item)) {
sema->signal();
return true;
}
return false;
}
// Enqueues a single item (by moving it, if possible).
// Does not allocate memory (except for one-time implicit producer).
// Fails if not enough room to enqueue (or implicit production is
// disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0).
// Thread-safe.
inline bool try_enqueue(T&& item)
{
if (inner.try_enqueue(std::move(item))) {
sema->signal();
return true;
}
return false;
}
// Enqueues a single item (by copying it) using an explicit producer token.
// Does not allocate memory. Fails if not enough room to enqueue.
// Thread-safe.
inline bool try_enqueue(producer_token_t const& token, T const& item)
{
if (inner.try_enqueue(token, item)) {
sema->signal();
return true;
}
return false;
}
// Enqueues a single item (by moving it, if possible) using an explicit producer token.
// Does not allocate memory. Fails if not enough room to enqueue.
// Thread-safe.
inline bool try_enqueue(producer_token_t const& token, T&& item)
{
if (inner.try_enqueue(token, std::move(item))) {
sema->signal();
return true;
}
return false;
}
// Enqueues several items.
// Does not allocate memory (except for one-time implicit producer).
// Fails if not enough room to enqueue (or implicit production is
// disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0).
// Note: Use std::make_move_iterator if the elements should be moved
// instead of copied.
// Thread-safe.
template<typename It>
inline bool try_enqueue_bulk(It itemFirst, size_t count)
{
if (inner.try_enqueue_bulk(std::forward<It>(itemFirst), count)) {
sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
return true;
}
return false;
}
// Enqueues several items using an explicit producer token.
// Does not allocate memory. Fails if not enough room to enqueue.
// Note: Use std::make_move_iterator if the elements should be moved
// instead of copied.
// Thread-safe.
template<typename It>
inline bool try_enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count)
{
if (inner.try_enqueue_bulk(token, std::forward<It>(itemFirst), count)) {
sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
return true;
}
return false;
}
// Attempts to dequeue from the queue.
// Returns false if all producer streams appeared empty at the time they
// were checked (so, the queue is likely but not guaranteed to be empty).
// Never allocates. Thread-safe.
template<typename U>
inline bool try_dequeue(U& item)
{
if (sema->tryWait()) {
while (!inner.try_dequeue(item)) {
continue;
}
return true;
}
return false;
}
// Attempts to dequeue from the queue using an explicit consumer token.
// Returns false if all producer streams appeared empty at the time they
// were checked (so, the queue is likely but not guaranteed to be empty).
// Never allocates. Thread-safe.
template<typename U>
inline bool try_dequeue(consumer_token_t& token, U& item)
{
if (sema->tryWait()) {
while (!inner.try_dequeue(token, item)) {
continue;
}
return true;
}
return false;
}
// Attempts to dequeue several elements from the queue.
// Returns the number of items actually dequeued.
// Returns 0 if all producer streams appeared empty at the time they
// were checked (so, the queue is likely but not guaranteed to be empty).
// Never allocates. Thread-safe.
template<typename It>
inline size_t try_dequeue_bulk(It itemFirst, size_t max)
{
size_t count = 0;
max = (size_t)sema->tryWaitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
while (count != max) {
count += inner.template try_dequeue_bulk<It&>(itemFirst, max - count);
}
return count;
}
// Attempts to dequeue several elements from the queue using an explicit consumer token.
// Returns the number of items actually dequeued.
// Returns 0 if all producer streams appeared empty at the time they
// were checked (so, the queue is likely but not guaranteed to be empty).
// Never allocates. Thread-safe.
template<typename It>
inline size_t try_dequeue_bulk(consumer_token_t& token, It itemFirst, size_t max)
{
size_t count = 0;
max = (size_t)sema->tryWaitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
while (count != max) {
count += inner.template try_dequeue_bulk<It&>(token, itemFirst, max - count);
}
return count;
}
// Blocks the current thread until there's something to dequeue, then
// dequeues it.
// Never allocates. Thread-safe.
template<typename U>
inline void wait_dequeue(U& item)
{
while (!sema->wait()) {
continue;
}
while (!inner.try_dequeue(item)) {
continue;
}
}
// Blocks the current thread until either there's something to dequeue
// or the timeout (specified in microseconds) expires. Returns false
// without setting `item` if the timeout expires, otherwise assigns
// to `item` and returns true.
// Using a negative timeout indicates an indefinite timeout,
// and is thus functionally equivalent to calling wait_dequeue.
// Never allocates. Thread-safe.
template<typename U>
inline bool wait_dequeue_timed(U& item, std::int64_t timeout_usecs)
{
if (!sema->wait(timeout_usecs)) {
return false;
}
while (!inner.try_dequeue(item)) {
continue;
}
return true;
}
// Blocks the current thread until either there's something to dequeue
// or the timeout expires. Returns false without setting `item` if the
// timeout expires, otherwise assigns to `item` and returns true.
// Never allocates. Thread-safe.
template<typename U, typename Rep, typename Period>
inline bool wait_dequeue_timed(U& item, std::chrono::duration<Rep, Period> const& timeout)
{
return wait_dequeue_timed(item, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
}
// Blocks the current thread until there's something to dequeue, then
// dequeues it using an explicit consumer token.
// Never allocates. Thread-safe.
template<typename U>
inline void wait_dequeue(consumer_token_t& token, U& item)
{
while (!sema->wait()) {
continue;
}
while (!inner.try_dequeue(token, item)) {
continue;
}
}
// Blocks the current thread until either there's something to dequeue
// or the timeout (specified in microseconds) expires. Returns false
// without setting `item` if the timeout expires, otherwise assigns
// to `item` and returns true.
// Using a negative timeout indicates an indefinite timeout,
// and is thus functionally equivalent to calling wait_dequeue.
// Never allocates. Thread-safe.
template<typename U>
inline bool wait_dequeue_timed(consumer_token_t& token, U& item, std::int64_t timeout_usecs)
{
if (!sema->wait(timeout_usecs)) {
return false;
}
while (!inner.try_dequeue(token, item)) {
continue;
}
return true;
}
// Blocks the current thread until either there's something to dequeue
// or the timeout expires. Returns false without setting `item` if the
// timeout expires, otherwise assigns to `item` and returns true.
// Never allocates. Thread-safe.
template<typename U, typename Rep, typename Period>
inline bool wait_dequeue_timed(consumer_token_t& token, U& item, std::chrono::duration<Rep, Period> const& timeout)
{
return wait_dequeue_timed(token, item, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
}
// Attempts to dequeue several elements from the queue.
// Returns the number of items actually dequeued, which will
// always be at least one (this method blocks until the queue
// is non-empty) and at most max.
// Never allocates. Thread-safe.
template<typename It>
inline size_t wait_dequeue_bulk(It itemFirst, size_t max)
{
size_t count = 0;
max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
while (count != max) {
count += inner.template try_dequeue_bulk<It&>(itemFirst, max - count);
}
return count;
}
// Attempts to dequeue several elements from the queue.
// Returns the number of items actually dequeued, which can
// be 0 if the timeout expires while waiting for elements,
// and at most max.
// Using a negative timeout indicates an indefinite timeout,
// and is thus functionally equivalent to calling wait_dequeue_bulk.
// Never allocates. Thread-safe.
template<typename It>
inline size_t wait_dequeue_bulk_timed(It itemFirst, size_t max, std::int64_t timeout_usecs)
{
size_t count = 0;
max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max, timeout_usecs);
while (count != max) {
count += inner.template try_dequeue_bulk<It&>(itemFirst, max - count);
}
return count;
}
// Attempts to dequeue several elements from the queue.
// Returns the number of items actually dequeued, which can
// be 0 if the timeout expires while waiting for elements,
// and at most max.
// Never allocates. Thread-safe.
template<typename It, typename Rep, typename Period>
inline size_t wait_dequeue_bulk_timed(It itemFirst, size_t max, std::chrono::duration<Rep, Period> const& timeout)
{
return wait_dequeue_bulk_timed<It&>(itemFirst, max, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
}
// Attempts to dequeue several elements from the queue using an explicit consumer token.
// Returns the number of items actually dequeued, which will
// always be at least one (this method blocks until the queue
// is non-empty) and at most max.
// Never allocates. Thread-safe.
template<typename It>
inline size_t wait_dequeue_bulk(consumer_token_t& token, It itemFirst, size_t max)
{
size_t count = 0;
max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
while (count != max) {
count += inner.template try_dequeue_bulk<It&>(token, itemFirst, max - count);
}
return count;
}
// Attempts to dequeue several elements from the queue using an explicit consumer token.
// Returns the number of items actually dequeued, which can
// be 0 if the timeout expires while waiting for elements,
// and at most max.
// Using a negative timeout indicates an indefinite timeout,
// and is thus functionally equivalent to calling wait_dequeue_bulk.
// Never allocates. Thread-safe.
template<typename It>
inline size_t wait_dequeue_bulk_timed(consumer_token_t& token, It itemFirst, size_t max, std::int64_t timeout_usecs)
{
size_t count = 0;
max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max, timeout_usecs);
while (count != max) {
count += inner.template try_dequeue_bulk<It&>(token, itemFirst, max - count);
}
return count;
}
// Attempts to dequeue several elements from the queue using an explicit consumer token.
// Returns the number of items actually dequeued, which can
// be 0 if the timeout expires while waiting for elements,
// and at most max.
// Never allocates. Thread-safe.
template<typename It, typename Rep, typename Period>
inline size_t wait_dequeue_bulk_timed(consumer_token_t& token, It itemFirst, size_t max, std::chrono::duration<Rep, Period> const& timeout)
{
return wait_dequeue_bulk_timed<It&>(token, itemFirst, max, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
}
// Returns an estimate of the total number of elements currently in the queue. This
// estimate is only accurate if the queue has completely stabilized before it is called
// (i.e. all enqueue and dequeue operations have completed and their memory effects are
// visible on the calling thread, and no further operations start while this method is
// being called).
// Thread-safe.
inline size_t size_approx() const
{
return (size_t)sema->availableApprox();
}
// Returns true if the underlying atomic variables used by
// the queue are lock-free (they should be on most platforms).
// Thread-safe.
static constexpr bool is_lock_free()
{
return ConcurrentQueue::is_lock_free();
}
private:
template<typename U, typename A1, typename A2>
static inline U* create(A1&& a1, A2&& a2)
{
void* p = (Traits::malloc)(sizeof(U));
return p != nullptr ? new (p) U(std::forward<A1>(a1), std::forward<A2>(a2)) : nullptr;
}
template<typename U>
static inline void destroy(U* p)
{
if (p != nullptr) {
p->~U();
}
(Traits::free)(p);
}
private:
ConcurrentQueue inner;
std::unique_ptr<LightweightSemaphore, void (*)(LightweightSemaphore*)> sema;
};
template<typename T, typename Traits>
inline void swap(BlockingConcurrentQueue<T, Traits>& a, BlockingConcurrentQueue<T, Traits>& b) MOODYCAMEL_NOEXCEPT
{
a.swap(b);
}
} // end namespace moodycamel
File diff suppressed because it is too large Load Diff
-425
View File
@@ -1,425 +0,0 @@
// Provides an efficient implementation of a semaphore (LightweightSemaphore).
// This is an extension of Jeff Preshing's sempahore implementation (licensed
// under the terms of its separate zlib license) that has been adapted and
// extended by Cameron Desrochers.
#pragma once
#include <cstddef> // For std::size_t
#include <atomic>
#include <type_traits> // For std::make_signed<T>
#if defined(_WIN32)
// Avoid including windows.h in a header; we only need a handful of
// items, so we'll redeclare them here (this is relatively safe since
// the API generally has to remain stable between Windows versions).
// I know this is an ugly hack but it still beats polluting the global
// namespace with thousands of generic names or adding a .cpp for nothing.
extern "C" {
struct _SECURITY_ATTRIBUTES;
__declspec(dllimport) void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES* lpSemaphoreAttributes, long lInitialCount, long lMaximumCount, const wchar_t* lpName);
__declspec(dllimport) int __stdcall CloseHandle(void* hObject);
__declspec(dllimport) unsigned long __stdcall WaitForSingleObject(void* hHandle, unsigned long dwMilliseconds);
__declspec(dllimport) int __stdcall ReleaseSemaphore(void* hSemaphore, long lReleaseCount, long* lpPreviousCount);
}
#elif defined(__MACH__)
#include <mach/mach.h>
#elif defined(__unix__)
#include <semaphore.h>
#if defined(__GLIBC_PREREQ) && defined(_GNU_SOURCE)
#if __GLIBC_PREREQ(2,30)
#define MOODYCAMEL_LIGHTWEIGHTSEMAPHORE_MONOTONIC
#endif
#endif
#endif
namespace moodycamel
{
namespace details
{
// Code in the mpmc_sema namespace below is an adaptation of Jeff Preshing's
// portable + lightweight semaphore implementations, originally from
// https://github.com/preshing/cpp11-on-multicore/blob/master/common/sema.h
// LICENSE:
// Copyright (c) 2015 Jeff Preshing
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgement in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#if defined(_WIN32)
class Semaphore
{
private:
void* m_hSema;
Semaphore(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
Semaphore& operator=(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
public:
Semaphore(int initialCount = 0)
{
assert(initialCount >= 0);
const long maxLong = 0x7fffffff;
m_hSema = CreateSemaphoreW(nullptr, initialCount, maxLong, nullptr);
assert(m_hSema);
}
~Semaphore()
{
CloseHandle(m_hSema);
}
bool wait()
{
const unsigned long infinite = 0xffffffff;
return WaitForSingleObject(m_hSema, infinite) == 0;
}
bool try_wait()
{
return WaitForSingleObject(m_hSema, 0) == 0;
}
bool timed_wait(std::uint64_t usecs)
{
return WaitForSingleObject(m_hSema, (unsigned long)(usecs / 1000)) == 0;
}
void signal(int count = 1)
{
while (!ReleaseSemaphore(m_hSema, count, nullptr));
}
};
#elif defined(__MACH__)
//---------------------------------------------------------
// Semaphore (Apple iOS and OSX)
// Can't use POSIX semaphores due to http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html
//---------------------------------------------------------
class Semaphore
{
private:
semaphore_t m_sema;
Semaphore(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
Semaphore& operator=(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
public:
Semaphore(int initialCount = 0)
{
assert(initialCount >= 0);
kern_return_t rc = semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount);
assert(rc == KERN_SUCCESS);
(void)rc;
}
~Semaphore()
{
semaphore_destroy(mach_task_self(), m_sema);
}
bool wait()
{
return semaphore_wait(m_sema) == KERN_SUCCESS;
}
bool try_wait()
{
return timed_wait(0);
}
bool timed_wait(std::uint64_t timeout_usecs)
{
mach_timespec_t ts;
ts.tv_sec = static_cast<unsigned int>(timeout_usecs / 1000000);
ts.tv_nsec = static_cast<int>((timeout_usecs % 1000000) * 1000);
// added in OSX 10.10: https://developer.apple.com/library/prerelease/mac/documentation/General/Reference/APIDiffsMacOSX10_10SeedDiff/modules/Darwin.html
kern_return_t rc = semaphore_timedwait(m_sema, ts);
return rc == KERN_SUCCESS;
}
void signal()
{
while (semaphore_signal(m_sema) != KERN_SUCCESS);
}
void signal(int count)
{
while (count-- > 0)
{
while (semaphore_signal(m_sema) != KERN_SUCCESS);
}
}
};
#elif defined(__unix__)
//---------------------------------------------------------
// Semaphore (POSIX, Linux)
//---------------------------------------------------------
class Semaphore
{
private:
sem_t m_sema;
Semaphore(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
Semaphore& operator=(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
public:
Semaphore(int initialCount = 0)
{
assert(initialCount >= 0);
int rc = sem_init(&m_sema, 0, static_cast<unsigned int>(initialCount));
assert(rc == 0);
(void)rc;
}
~Semaphore()
{
sem_destroy(&m_sema);
}
bool wait()
{
// http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error
int rc;
do {
rc = sem_wait(&m_sema);
} while (rc == -1 && errno == EINTR);
return rc == 0;
}
bool try_wait()
{
int rc;
do {
rc = sem_trywait(&m_sema);
} while (rc == -1 && errno == EINTR);
return rc == 0;
}
bool timed_wait(std::uint64_t usecs)
{
struct timespec ts;
const int usecs_in_1_sec = 1000000;
const int nsecs_in_1_sec = 1000000000;
#ifdef MOODYCAMEL_LIGHTWEIGHTSEMAPHORE_MONOTONIC
clock_gettime(CLOCK_MONOTONIC, &ts);
#else
clock_gettime(CLOCK_REALTIME, &ts);
#endif
ts.tv_sec += (time_t)(usecs / usecs_in_1_sec);
ts.tv_nsec += (long)(usecs % usecs_in_1_sec) * 1000;
// sem_timedwait bombs if you have more than 1e9 in tv_nsec
// so we have to clean things up before passing it in
if (ts.tv_nsec >= nsecs_in_1_sec) {
ts.tv_nsec -= nsecs_in_1_sec;
++ts.tv_sec;
}
int rc;
do {
#ifdef MOODYCAMEL_LIGHTWEIGHTSEMAPHORE_MONOTONIC
rc = sem_clockwait(&m_sema, CLOCK_MONOTONIC, &ts);
#else
rc = sem_timedwait(&m_sema, &ts);
#endif
} while (rc == -1 && errno == EINTR);
return rc == 0;
}
void signal()
{
while (sem_post(&m_sema) == -1);
}
void signal(int count)
{
while (count-- > 0)
{
while (sem_post(&m_sema) == -1);
}
}
};
#else
#error Unsupported platform! (No semaphore wrapper available)
#endif
} // end namespace details
//---------------------------------------------------------
// LightweightSemaphore
//---------------------------------------------------------
class LightweightSemaphore
{
public:
typedef std::make_signed<std::size_t>::type ssize_t;
private:
std::atomic<ssize_t> m_count;
details::Semaphore m_sema;
int m_maxSpins;
bool waitWithPartialSpinning(std::int64_t timeout_usecs = -1)
{
ssize_t oldCount;
int spin = m_maxSpins;
while (--spin >= 0)
{
oldCount = m_count.load(std::memory_order_relaxed);
if ((oldCount > 0) && m_count.compare_exchange_strong(oldCount, oldCount - 1, std::memory_order_acquire, std::memory_order_relaxed))
return true;
std::atomic_signal_fence(std::memory_order_acquire); // Prevent the compiler from collapsing the loop.
}
oldCount = m_count.fetch_sub(1, std::memory_order_acquire);
if (oldCount > 0)
return true;
if (timeout_usecs < 0)
{
if (m_sema.wait())
return true;
}
if (timeout_usecs > 0 && m_sema.timed_wait((std::uint64_t)timeout_usecs))
return true;
// At this point, we've timed out waiting for the semaphore, but the
// count is still decremented indicating we may still be waiting on
// it. So we have to re-adjust the count, but only if the semaphore
// wasn't signaled enough times for us too since then. If it was, we
// need to release the semaphore too.
while (true)
{
oldCount = m_count.load(std::memory_order_acquire);
if (oldCount >= 0 && m_sema.try_wait())
return true;
if (oldCount < 0 && m_count.compare_exchange_strong(oldCount, oldCount + 1, std::memory_order_relaxed, std::memory_order_relaxed))
return false;
}
}
ssize_t waitManyWithPartialSpinning(ssize_t max, std::int64_t timeout_usecs = -1)
{
assert(max > 0);
ssize_t oldCount;
int spin = m_maxSpins;
while (--spin >= 0)
{
oldCount = m_count.load(std::memory_order_relaxed);
if (oldCount > 0)
{
ssize_t newCount = oldCount > max ? oldCount - max : 0;
if (m_count.compare_exchange_strong(oldCount, newCount, std::memory_order_acquire, std::memory_order_relaxed))
return oldCount - newCount;
}
std::atomic_signal_fence(std::memory_order_acquire);
}
oldCount = m_count.fetch_sub(1, std::memory_order_acquire);
if (oldCount <= 0)
{
if ((timeout_usecs == 0) || (timeout_usecs < 0 && !m_sema.wait()) || (timeout_usecs > 0 && !m_sema.timed_wait((std::uint64_t)timeout_usecs)))
{
while (true)
{
oldCount = m_count.load(std::memory_order_acquire);
if (oldCount >= 0 && m_sema.try_wait())
break;
if (oldCount < 0 && m_count.compare_exchange_strong(oldCount, oldCount + 1, std::memory_order_relaxed, std::memory_order_relaxed))
return 0;
}
}
}
if (max > 1)
return 1 + tryWaitMany(max - 1);
return 1;
}
public:
LightweightSemaphore(ssize_t initialCount = 0, int maxSpins = 10000) : m_count(initialCount), m_maxSpins(maxSpins)
{
assert(initialCount >= 0);
assert(maxSpins >= 0);
}
bool tryWait()
{
ssize_t oldCount = m_count.load(std::memory_order_relaxed);
while (oldCount > 0)
{
if (m_count.compare_exchange_weak(oldCount, oldCount - 1, std::memory_order_acquire, std::memory_order_relaxed))
return true;
}
return false;
}
bool wait()
{
return tryWait() || waitWithPartialSpinning();
}
bool wait(std::int64_t timeout_usecs)
{
return tryWait() || waitWithPartialSpinning(timeout_usecs);
}
// Acquires between 0 and (greedily) max, inclusive
ssize_t tryWaitMany(ssize_t max)
{
assert(max >= 0);
ssize_t oldCount = m_count.load(std::memory_order_relaxed);
while (oldCount > 0)
{
ssize_t newCount = oldCount > max ? oldCount - max : 0;
if (m_count.compare_exchange_weak(oldCount, newCount, std::memory_order_acquire, std::memory_order_relaxed))
return oldCount - newCount;
}
return 0;
}
// Acquires at least one, and (greedily) at most max
ssize_t waitMany(ssize_t max, std::int64_t timeout_usecs)
{
assert(max >= 0);
ssize_t result = tryWaitMany(max);
if (result == 0 && max > 0)
result = waitManyWithPartialSpinning(max, timeout_usecs);
return result;
}
ssize_t waitMany(ssize_t max)
{
ssize_t result = waitMany(max, -1);
assert(result > 0);
return result;
}
void signal(ssize_t count = 1)
{
assert(count >= 0);
ssize_t oldCount = m_count.fetch_add(count, std::memory_order_release);
ssize_t toRelease = -oldCount < count ? -oldCount : count;
if (toRelease > 0)
{
m_sema.signal((int)toRelease);
}
}
std::size_t availableApprox() const
{
ssize_t count = m_count.load(std::memory_order_relaxed);
return count > 0 ? static_cast<std::size_t>(count) : 0;
}
};
} // end namespace moodycamel
+12 -2
View File
@@ -21,6 +21,7 @@ bool perspective_interpolation_skipped() {
}
s32 getGameMode(void);
enum map_e map_get(void);
extern f32 sViewportFOVy;
extern f32 sViewportAspect;
@@ -200,6 +201,16 @@ void func_802BB4D8(f32 position[3], f32 rotation[3]);
void func_802BEFB0(void);
void func_802BBA84(void);
f32 get_camera_skip_threshold() {
switch (map_get()) {
case MAP_94_CS_INTRO_SPIRAL_7:
case MAP_96_CS_END_BEACH_1:
return 50.0f;
default:
return 100.0f;
}
}
// @recomp Patched to detect camera type changes or sudden camera movements and skip perspective interpolation when they happen.
RECOMP_PATCH void ncCamera_update(void) {
f32 vpPos[3];
@@ -250,8 +261,7 @@ RECOMP_PATCH void ncCamera_update(void) {
ml_vec3f_add(vpPosProjected, vpPosPrev, vpPosVel);
f32 distToProjected = ml_vec3f_distance(vpPos, vpPosProjected);
const f32 SkipThreshold = 100.0f;
if (distToProjected > SkipThreshold) {
if (distToProjected > get_camera_skip_threshold()) {
ml_vec3f_clear(vpPosVel);
skip_perspective_interpolation = TRUE;
}
+34
View File
@@ -140,6 +140,27 @@ extern struct {
u32 pad70_28 : 29;
} D_80383010;
typedef struct struct_1C_s {
u8 map;
s8 exit;
s16 x; //0x2
char* str; //0x4
s8 unk8;
}ParadeInfo;
extern struct {
u8 state;
u8 indx;
u8 count;
s8 y_position;
s8 scroll_frame;
u8 unk5;
u8 parade_id;
u8 jiggyscore; //jiggy total
s32 unk8;
ParadeInfo* parade_element;
}D_803830F0;
extern u32 cur_pushed_text_transform_id;
extern u32 cur_pushed_text_transform_origin;
extern u32 cur_pushed_text_transform_skip_interpolation;
@@ -886,6 +907,19 @@ RECOMP_PATCH void gcpausemenu_printTotalsHeader(s32 page_id) {
struct1Cs_1 *v0 = D_8036C58C + page_id;
print_bold_overlapping(v0->x, D_80383010.unk8, -1.05f, v0->string);
// @recomp Clear the ID text.
cur_pushed_text_transform_id = 0;
}
// @recomp Patched to interpolate the character names during the parade.
RECOMP_PATCH void gcparade_print(s32 index) {
ParadeInfo* v0 = D_803830F0.parade_element;
// @recomp Assign an ID for the totals text.
cur_pushed_text_transform_id = PARADE_PRINT_TRANSFORM_ID_START;
print_bold_overlapping(v0->x, D_803830F0.y_position, -1.2f, v0->str);
// @recomp Clear the ID text.
cur_pushed_text_transform_id = 0;
}
+1
View File
@@ -1,5 +1,6 @@
#include "patches.h"
#include "core1/mlmtx.h"
#include "functions.h"
typedef struct {
s32 cmd_0;
+17
View File
@@ -0,0 +1,17 @@
#include "functions.h"
#include "patches.h"
#include "ui_funcs.h"
extern s32 D_802823AC;
extern f32 D_80282420;
extern f32 D_80282424;
// @recomp Patched to run a callback per frame in a function that always gets called by mainLoop().
RECOMP_PATCH void baMotor_80250C08(void) {
// @recomp Patched to run UI callbacks.
recomp_run_ui_callbacks();
if (D_802823AC != 0) {
D_80282424 = MIN(D_80282420, D_80282424 + time_getDelta());
}
}
+2
View File
@@ -153,11 +153,13 @@ RECOMP_PATCH void func_80350818(Gfx **gfx, Mtx **mtx, Vtx **vtx) {
// @recomp Set the model transform ID before drawing the lens flare.
cur_drawn_model_transform_id = LENS_FLARE_TRANSFORM_ID_START + LENS_FLARE_TRANSFORM_ID_COUNT * i;
cur_drawn_model_skip_interpolation = perspective_interpolation_skipped();
modelRender_draw(gfx, mtx, spB8, spD0, temp_s2->unk4[i].unk14 * 0.25, NULL, D_80386170.unk8);
// @recomp Clear the model transform ID after drawing the lens flare.
cur_drawn_model_transform_id = 0;
cur_drawn_model_skip_interpolation = FALSE;
}
}
}
+57 -2
View File
@@ -4,6 +4,7 @@
#include "bk_api.h"
#define IDS_PER_BEE 8
#define IDS_PER_PIECE 8
typedef struct {
f32 unk0[3];
@@ -26,6 +27,26 @@ typedef struct {
s32 unk24;
}ActorLocal_core2_47BD0;
typedef struct struct_24_s {
s32 unk0;
BKModelBin* model_bin;
f32 unk8[3];
f32 unk14[3];
f32 unk20[3];
f32 unk2C;
f32 unk30[3];
ParticleEmitter* unk3C;
s32 unk40[4];
f32 unk50;
} Struct24s;
typedef struct struct_25_s {
Struct24s* begin;
Struct24s* current;
Struct24s* end;
Struct24s data[];
} Struct25s;
extern struct0* D_8037C200;
extern f32 func_8028E82C(void);
@@ -89,6 +110,40 @@ RECOMP_PATCH Actor *chBeeSwarm_draw(ActorMarker *marker, Gfx **gfx, Mtx **mtx, V
return this;
}
// @recomp Patched to give the actors which spawn multiple pieces individual IDs for each piece. This function is primarily used by explosions of wood and others.
RECOMP_PATCH Actor* func_802C8484(ActorMarker* marker, Gfx** gfx, Mtx** mtx, Vtx** vtx) {
Struct25s* temp_s1;
Struct24s* phi_s0;
f32 sp5C;
Actor* sp58;
u32 phi_v1;
s32 phi_s4;
sp58 = marker_getActorAndRotation(marker, &sp5C);
temp_s1 = (Struct25s*)(sp58->unk40);
phi_s4 = FALSE;
for (phi_s0 = temp_s1->begin; phi_s0 < temp_s1->current; phi_s0++) {
if ((phi_s0->unk0 != 0) && (phi_s0->model_bin != NULL)) {
// @recomp Set the model transform ID.
s32 cur_drawn_marker_spawn_index = bkrecomp_get_marker_spawn_index(marker);
u32 transform_id = MARKER_TRANSFORM_ID_START + cur_drawn_marker_spawn_index * MARKER_TRANSFORM_ID_COUNT + (phi_s0 - temp_s1->begin) * IDS_PER_PIECE;
u32 prev_transform_id = cur_drawn_model_transform_id;
cur_drawn_model_transform_id = transform_id;
modelRender_setDepthMode(MODEL_RENDER_DEPTH_FULL);
modelRender_draw(gfx, mtx, phi_s0->unk8, phi_s0->unk14, phi_s0->unk2C / 10.0f, NULL, phi_s0->model_bin);
phi_s4 = TRUE;
// @recomp Reset the model transform ID.
cur_drawn_model_transform_id = prev_transform_id;
}
}
if (phi_s4 == FALSE) {
marker_despawn(marker);
}
return sp58;
}
extern f32 D_8036E580[3];
extern void actor_postdrawMethod(ActorMarker *);
extern void actor_predrawMethod(Actor *);
@@ -104,8 +159,8 @@ RECOMP_PATCH Actor *actor_draw(ActorMarker *marker, Gfx **gfx, Mtx **mtx, Vtx **
// @recomp Check for the xmas tree model
if (marker->modelId == ASSET_488_MODEL_XMAS_TREE) {
// @recomp Check that the lights are currently turning on / flickering
if (this->state == 2 || this->state == 3) {
// @recomp Check that the lights are currently turning on, flickering or turning off.
if (this->state == 2 || this->state == 3 || this->state == 5) {
// @recomp Skip interpolation
cur_drawn_model_skip_interpolation = TRUE;
}
+2 -1
View File
@@ -50,4 +50,5 @@ recomp_get_note_saving_enabled = 0x8F0000B4;
recomp_get_cutscene_aspect_ratio = 0x8F0000B8;
recomp_get_flying_and_swimming_inverted_axes = 0x8F0000BC;
recomp_get_first_person_inverted_axes = 0x8F0000C0;
recomp_get_analog_cam_sensitivity = 0x8F0000C4;
recomp_get_analog_cam_sensitivity = 0x8F0000C4;
recomp_run_ui_callbacks = 0x8F0000C8;
+3
View File
@@ -95,6 +95,9 @@ typedef struct {
#define ZOOMBOX_PRINT_TRANSFORM_ID_START 0x00F42200
#define ZOOMBOX_PRINT_TRANSFORM_ID_COUNT 256
// Parade Print: 0x00F60000 - 0x00F600FF
#define PARADE_PRINT_TRANSFORM_ID_START 0x00F60000
// Normal Particles: 0x01000000 - 0x01FFFFFF
#define NORMAL_PARTICLE_TRANSFORM_ID_START 0x01000000
#define NORMAL_PARTICLE_ID_MAX 0x01000000
+3 -1
View File
@@ -249,7 +249,9 @@ void banjo::launcher_animation_setup(recompui::LauncherMenu *menu) {
launcher_context.jiggy_shine_svg.scale_keyframes = {
{ 0.0f, 0.0f, 0.0f },
{ jiggy_shine_start, 0.0f, 0.0f },
{ jiggy_shine_start + 0.01f, 1.0f, 1.0f },
{ jiggy_shine_start, 1.0f, 1.0f },
{ jiggy_shine_start + jiggy_shine_length, 1.0f, 1.0f },
{ jiggy_shine_start + jiggy_shine_length, 0.0f, 0.0f },
};
launcher_context.jiggy_shine_svg.position_animation.interpolation_method = InterpolationMethod::Smootherstep;
+21 -7
View File
@@ -64,7 +64,7 @@
#include "../../lib/rt64/src/contrib/stb/stb_image.h"
const std::string version_string = "1.0.0-rc2";
const std::string version_string = "1.0.1";
template<typename... Ts>
void exit_error(const char* str, Ts ...args) {
@@ -326,7 +326,7 @@ void set_frequency(uint32_t freq) {
update_audio_converter();
}
void reset_audio(uint32_t output_freq) {
bool reset_audio(uint32_t output_freq) {
SDL_AudioSpec spec_desired{
.freq = (int)output_freq,
.format = AUDIO_F32,
@@ -339,15 +339,19 @@ void reset_audio(uint32_t output_freq) {
.userdata = nullptr
};
audio_device = SDL_OpenAudioDevice(nullptr, false, &spec_desired, nullptr, 0);
if (audio_device == 0) {
exit_error("SDL error opening audio device: %s\n", SDL_GetError());
std::string audio_error = std::string("No audio device could be found. Please make sure an audio device is available.\nError opening audio device: ") + std::string(SDL_GetError());
recompui::message_box(audio_error.c_str());
return false;
}
SDL_PauseAudioDevice(audio_device, 0);
output_sample_rate = output_freq;
update_audio_converter();
return true;
}
extern RspUcodeFunc n_aspMain;
@@ -684,9 +688,16 @@ int main(int argc, char** argv) {
// Initialize native file dialogs.
NFD_Init();
// Initialize program settings.
recompui::programconfig::set_program_name(banjo::program_name);
recompui::programconfig::set_program_id(banjo::program_id);
// Initialize SDL audio and set the output frequency.
SDL_InitSubSystem(SDL_INIT_AUDIO);
reset_audio(48000);
if (!reset_audio(48000)) {
// It is not possible to initialize without an audio device.
return EXIT_FAILURE;
}
// Source controller mappings file
std::u8string controller_db_path = (recompui::file::get_program_path() / "recompcontrollerdb.txt").u8string();
@@ -694,10 +705,11 @@ int main(int argc, char** argv) {
fprintf(stderr, "Failed to load controller mappings: %s\n", SDL_GetError());
}
recompui::programconfig::set_program_name(banjo::program_name);
recompui::programconfig::set_program_id(banjo::program_id);
// Register fonts.
recompui::register_primary_font("InterVariable.ttf", "Inter Variable");
recompui::register_extra_font("Suplexmentary Comic NC.ttf");
// Register configuration path.
recomp::register_config_path(recompui::file::get_app_folder_path());
// Register supported games and patches
@@ -705,6 +717,8 @@ int main(int argc, char** argv) {
recomp::register_game(game);
}
recomp::mods::register_deprecated_mod("bk_recomp_mod_fov_slider", recomp::mods::DeprecationStatus::BrokenVersion, recomp::Version(1, 1, 0));
REGISTER_FUNC(recomp_get_window_resolution);
REGISTER_FUNC(recomp_get_target_aspect_ratio);
REGISTER_FUNC(recomp_get_target_framerate);