mirror of
https://github.com/BanjoRecomp/BanjoRecomp
synced 2026-06-11 12:39:51 -04:00
Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 73a8aa9918 | |||
| f82b014a2b | |||
| 80963dd419 | |||
| 1a0d1053e9 | |||
| 4982590054 | |||
| 8475fda0f1 | |||
| 91d1e2d272 | |||
| d124299373 | |||
| 6b387ed9d2 | |||
| 0159329eb1 | |||
| 661f848a8e | |||
| e143c61609 | |||
| 2cc5391511 | |||
| c82a7b78d0 | |||
| 066e40d158 | |||
| a218f16f25 | |||
| 5ef2c7e338 | |||
| f8ac690670 | |||
| 2fc6e70cb5 | |||
| 9a0b944f35 | |||
| 025215049a | |||
| 045b738297 | |||
| 269d9bec93 | |||
| 7cff85fa22 | |||
| 7242b70ba5 | |||
| c17aa22028 | |||
| e659ecb7ea | |||
| 5702422244 | |||
| d67eb10cff | |||
| 9d2d5b1473 | |||
| af730a3d4f | |||
| 6e81827a05 | |||
| 5ec2d3e5c2 | |||
| 5d0d15e9a3 | |||
| da393d7391 | |||
| 00c47eb17c | |||
| 0ccade9de8 | |||
| 444eaed1a7 | |||
| 92024a0518 | |||
| 60530b36bc | |||
| 6708aab4a9 | |||
| dbfb6714a8 | |||
| 44d0265852 | |||
| da24f09d16 |
@@ -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.
|
||||
@@ -0,0 +1,9 @@
|
||||
[Desktop Entry]
|
||||
Name=BanjoRecompiled
|
||||
Type=Application
|
||||
Terminal=false
|
||||
Icon=BanjoRecompiled
|
||||
Exec=BanjoRecompiled
|
||||
GenericName=BanjoRecompiled
|
||||
Categories=Game;
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
)
|
||||
@@ -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>
|
||||
@@ -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}")
|
||||
Executable
+73
@@ -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)
|
||||
@@ -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
|
||||
@@ -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 }}'
|
||||
@@ -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 }}
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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}
|
||||
|
||||
@@ -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!
|
||||
|
||||
[](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
-1
Submodule lib/N64ModernRuntime updated: 494217848a...ca568b6ad7
+1
-1
Submodule lib/RecompFrontend updated: 7771107139...b3b7ebb4ec
@@ -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
@@ -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
|
||||
+1
-1
Submodule lib/rt64 updated: b66eca4d45...f0d8c9f29b
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,5 +1,6 @@
|
||||
#include "patches.h"
|
||||
#include "core1/mlmtx.h"
|
||||
#include "functions.h"
|
||||
|
||||
typedef struct {
|
||||
s32 cmd_0;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user