Compare commits
32 Commits
65083d2863
...
5ecdc93f8e
Author | SHA1 | Date |
---|---|---|
David Chavez | 5ecdc93f8e | |
David Chavez | 7b9091428c | |
David Chavez | ac7ee4f13e | |
dcvz | 79d7503704 | |
Mr-Wiseguy | db4d9c668d | |
LittleCube | e862a38135 | |
LittleCube | 0f92410620 | |
LittleCube | 21ca074bf7 | |
LittleCube | 8c8f5b889f | |
Mr-Wiseguy | e5699e59a1 | |
Mr-Wiseguy | fdbdf5edb0 | |
David Chavez | aeefc1b6bb | |
Mr-Wiseguy | cd12bc0c20 | |
LittleCube | 24c436a572 | |
Mr-Wiseguy | 5a8a1cb2ba | |
Mr-Wiseguy | 5dc260f35a | |
Mr-Wiseguy | a568261064 | |
Mr-Wiseguy | 33806821e2 | |
Mr-Wiseguy | eda1979bf0 | |
Mr-Wiseguy | 765d1d32f0 | |
Mr-Wiseguy | 2463fe4613 | |
Mr-Wiseguy | 359f8a9279 | |
Mr-Wiseguy | 60b8d58e73 | |
Mr-Wiseguy | ea0cc6e6be | |
Mr-Wiseguy | e6892248c7 | |
Mr-Wiseguy | be70a2a8f1 | |
Mr-Wiseguy | 2c23d5f29b | |
Mr-Wiseguy | ba39a73dca | |
Mr-Wiseguy | cf1943fe6a | |
Mr-Wiseguy | 670bd61067 | |
lkoger | 0d0f64e32f | |
Mr-Wiseguy | d99a84f04f |
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Zelda64Recompiled</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.games</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>11</string>
|
||||
<key>LSEnvironment</key>
|
||||
<dict>
|
||||
<key>MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS</key>
|
||||
<string>1</string>
|
||||
<key>MVK_CONFIG_USE_METAL_PRIVATE_API</key>
|
||||
<string>1</string>
|
||||
<key>MVK_CONFIG_RESUME_LOST_DEVICE</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"file_format_version": "1.0.0",
|
||||
"ICD": {
|
||||
"library_path": "../../../Frameworks/libMoltenVK.dylib",
|
||||
"api_version": "1.2.0",
|
||||
"is_portability_driver": true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?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-library-validation</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,11 @@
|
|||
include(BundleUtilities)
|
||||
|
||||
# Use generator expressions to get the absolute path to the bundle and frameworks
|
||||
set(APPS "Zelda64Recompiled.app/Contents/MacOS/Zelda64Recompiled")
|
||||
set(DIRS "Zelda64Recompiled.app/Contents/Frameworks")
|
||||
|
||||
# The fixup_bundle command needs an absolute path
|
||||
file(REAL_PATH ${APPS} APPS)
|
||||
file(REAL_PATH ${DIRS} DIRS)
|
||||
|
||||
fixup_bundle("${APPS}" "" "${DIRS}")
|
|
@ -0,0 +1,14 @@
|
|||
version: '2.9.3'
|
||||
prefix: '/opt/local'
|
||||
variants:
|
||||
select:
|
||||
- aqua
|
||||
- metal
|
||||
deselect: x11
|
||||
ports:
|
||||
- name: clang-18
|
||||
- name: llvm-18
|
||||
- name: libsdl2
|
||||
select: universal
|
||||
- name: freetype
|
||||
select: universal
|
|
@ -9,11 +9,19 @@ on:
|
|||
N64RECOMP_COMMIT:
|
||||
type: string
|
||||
required: false
|
||||
default: 'f8d439aeee6048b7365d1cb3bcd2578ec27a0288'
|
||||
default: 'd33d38161798167929b114c2b0fd445f9670e10a'
|
||||
DXC_CHECKSUM:
|
||||
type: string
|
||||
required: false
|
||||
default: '4e6f4e52989aca69739880b40b9f988357f15d10ca03284377b81f1502463ff5'
|
||||
VULKAN_SDK_VERSION:
|
||||
type: string
|
||||
required: false
|
||||
default: '1.3.296.0'
|
||||
MOLTENVK_COMMIT:
|
||||
type: string
|
||||
required: false
|
||||
default: ''
|
||||
secrets:
|
||||
ZRE_REPO_WITH_PAT:
|
||||
required: true
|
||||
|
@ -22,7 +30,7 @@ concurrency:
|
|||
cancel-in-progress: true
|
||||
jobs:
|
||||
build-linux:
|
||||
runs-on: ${{ matrix.os }}
|
||||
runs-on: ${{ format('blaze/compute/{0}-amd64', matrix.os) }}
|
||||
container:
|
||||
image: dcvz/n64recomp:ubuntu-18.04
|
||||
volumes:
|
||||
|
@ -64,7 +72,7 @@ jobs:
|
|||
|
||||
# Build N64Recomp & RSPRecomp
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER=g++-11 -DCMAKE_C_COMPILER=gcc-11 -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S . -B cmake-build
|
||||
cmake --build cmake-build --config Release --target N64Recomp -j $(nproc)
|
||||
cmake --build cmake-build --config Release --target N64RecompCLI -j $(nproc)
|
||||
cmake --build cmake-build --config Release --target RSPRecomp -j $(nproc)
|
||||
|
||||
# Copy N64Recomp & RSPRecomp to root directory
|
||||
|
@ -95,7 +103,7 @@ jobs:
|
|||
rm -rf assets/scss
|
||||
tar -czf Zelda64Recompiled.tar.gz Zelda64Recompiled assets/ gamecontrollerdb.txt
|
||||
- name: Archive Zelda64Recomp
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Zelda64Recompiled-${{ runner.os }}-X64-${{ matrix.type }}
|
||||
path: Zelda64Recompiled.tar.gz
|
||||
|
@ -103,12 +111,12 @@ jobs:
|
|||
run: |-
|
||||
./.github/linux/appimage.sh
|
||||
- name: Zelda64Recomp AppImage
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Zelda64Recompiled-AppImage-X64-${{ matrix.type }}
|
||||
path: Zelda64Recompiled-*.AppImage
|
||||
build-linux-arm64:
|
||||
runs-on: ${{ format('blaze/{0}', matrix.os) }}
|
||||
runs-on: ${{ format('blaze/compute/{0}', matrix.os) }}
|
||||
strategy:
|
||||
matrix:
|
||||
type: [ Debug, Release ]
|
||||
|
@ -160,7 +168,7 @@ jobs:
|
|||
|
||||
# Build N64Recomp & RSPRecomp
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER=g++-11 -DCMAKE_C_COMPILER=gcc-11 -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S . -B cmake-build
|
||||
cmake --build cmake-build --config Release --target N64Recomp -j $(nproc)
|
||||
cmake --build cmake-build --config Release --target N64RecompCLI -j $(nproc)
|
||||
cmake --build cmake-build --config Release --target RSPRecomp -j $(nproc)
|
||||
|
||||
# Copy N64Recomp & RSPRecomp to root directory
|
||||
|
@ -199,7 +207,7 @@ jobs:
|
|||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
type: [ Debug, Release ]
|
||||
type: [ Debug, RelWithDebInfo ]
|
||||
name: windows (${{ matrix.type }})
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
@ -234,7 +242,7 @@ jobs:
|
|||
$cpuCores = (Get-CimInstance -ClassName Win32_Processor).NumberOfLogicalProcessors
|
||||
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S . -B cmake-build
|
||||
cmake --build cmake-build --config Release --target N64Recomp -j $cpuCores
|
||||
cmake --build cmake-build --config Release --target N64RecompCLI -j $cpuCores
|
||||
cmake --build cmake-build --config Release --target RSPRecomp -j $cpuCores
|
||||
|
||||
# Copy N64Recomp & RSPRecomp to root directory
|
||||
|
@ -264,6 +272,7 @@ jobs:
|
|||
Move-Item -Path "cmake-build/dxcompiler.dll" -Destination "dxcompiler.dll"
|
||||
Move-Item -Path "cmake-build/dxil.dll" -Destination "dxil.dll"
|
||||
Move-Item -Path "cmake-build/SDL2.dll" -Destination "SDL2.dll"
|
||||
Move-Item -Path "cmake-build/Zelda64Recompiled.pdb" -Destination "Zelda64Recompiled.pdb"
|
||||
Remove-Item -Path "assets/scss" -Recurse -Force
|
||||
- name: Archive Zelda64Recomp
|
||||
uses: actions/upload-artifact@v4
|
||||
|
@ -276,3 +285,125 @@ jobs:
|
|||
SDL2.dll
|
||||
assets/
|
||||
gamecontrollerdb.txt
|
||||
- name: Archive Debug Files
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Zelda64Recompiled-PDB-${{ matrix.type }}
|
||||
path: |
|
||||
Zelda64Recompiled.pdb
|
||||
build-macos:
|
||||
runs-on: blaze/macos-14
|
||||
strategy:
|
||||
matrix:
|
||||
type: [ Debug, Release ]
|
||||
name: macos (x64, arm64, ${{ matrix.type }})
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.ref }}
|
||||
submodules: recursive
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
key: ${{ runner.os }}-z64re-ccache-${{ matrix.type }}
|
||||
- name: Homebrew Setup
|
||||
run: |
|
||||
brew install ninja
|
||||
brew uninstall --ignore-dependencies libpng freetype
|
||||
- name: MacPorts Setup
|
||||
uses: melusina-org/setup-macports@v1
|
||||
id: 'macports'
|
||||
with:
|
||||
parameters: '.github/macos/macports.yaml'
|
||||
- name: Prepare Build
|
||||
run: |-
|
||||
git clone ${{ secrets.ZRE_REPO_WITH_PAT }}
|
||||
./zre/process.sh
|
||||
cp ./zre/mm_shader_cache.bin ./shadercache/
|
||||
- name: Build N64Recomp & RSPRecomp
|
||||
run: |
|
||||
git clone https://github.com/Mr-Wiseguy/N64Recomp.git --recurse-submodules N64RecompSource
|
||||
cd N64RecompSource
|
||||
git checkout ${{ inputs.N64RECOMP_COMMIT }}
|
||||
git submodule update --init --recursive
|
||||
|
||||
# enable ccache
|
||||
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
|
||||
|
||||
# Build N64Recomp & RSPRecomp
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S . -B cmake-build
|
||||
cmake --build cmake-build --config Release --target N64Recomp -j $(sysctl -n hw.ncpu)
|
||||
cmake --build cmake-build --config Release --target RSPRecomp -j $(sysctl -n hw.ncpu)
|
||||
|
||||
# Copy N64Recomp & RSPRecomp to root directory
|
||||
cp cmake-build/N64Recomp ..
|
||||
cp cmake-build/RSPRecomp ..
|
||||
- name: Run N64Recomp & RSPRecomp
|
||||
run: |
|
||||
./N64Recomp us.rev1.toml
|
||||
./RSPRecomp aspMain.us.rev1.toml
|
||||
./RSPRecomp njpgdspMain.us.rev1.toml
|
||||
- name: Cache Vulkan SDK
|
||||
id: cache-vulkan-sdk
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /Users/runner/VulkanSDK/${{ inputs.VULKAN_SDK_VERSION }}
|
||||
key: ${{ runner.os }}-${{ matrix.type }}-vulkan-sdk-${{ inputs.VULKAN_SDK_VERSION }}
|
||||
- name: Install Vulkan SDK
|
||||
if: steps.cache-vulkan-sdk.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
wget https://sdk.lunarg.com/sdk/download/${{ inputs.VULKAN_SDK_VERSION }}/mac/vulkansdk-macos-${{ inputs.VULKAN_SDK_VERSION }}.zip
|
||||
unzip vulkansdk-macos-${{ inputs.VULKAN_SDK_VERSION }}.zip
|
||||
sudo InstallVulkan.app/Contents/MacOS/InstallVulkan --root ~/VulkanSDK/${{ inputs.VULKAN_SDK_VERSION }} --accept-licenses --default-answer --confirm-command install
|
||||
- name: Checkout MoltenVK
|
||||
if: inputs.MOLTENVK_COMMIT != ''
|
||||
run: |
|
||||
git clone https://github.com/KhronosGroup/MoltenVK.git
|
||||
cd MoltenVK
|
||||
git checkout ${{ inputs.MOLTENVK_COMMIT }}
|
||||
- name: Cache MoltenVK Dependencies
|
||||
if: inputs.MOLTENVK_COMMIT != ''
|
||||
id: cache-mvk-dependencies
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
MoltenVK/External/build
|
||||
!MoltenVK/External/build/Intermediates
|
||||
key: ${{ runner.os }}-${{ matrix.type }}-${{ hashFiles('MoltenVK/fetchDependencies','MoltenVK/ExternalRevisions/**','MoltenVK/ExternalDependencies.xcodeproj/**','MoltenVK/Scripts/**') }}
|
||||
- name: Fetch MVK Dependencies (Use Built Cache)
|
||||
if: inputs.MOLTENVK_COMMIT != '' && steps.cache-mvk-dependencies.outputs.cache-hit == 'true'
|
||||
run: |
|
||||
cd MoltenVK
|
||||
./fetchDependencies -v --none
|
||||
- name: Fetch MVK Dependencies
|
||||
if: inputs.MOLTENVK_COMMIT != '' && steps.cache-mvk-dependencies.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd MoltenVK
|
||||
./fetchDependencies -v --macos
|
||||
- name: Build MoltenVK
|
||||
if: inputs.MOLTENVK_COMMIT != ''
|
||||
run: |
|
||||
cd MoltenVK
|
||||
make macos
|
||||
sudo cp Package/Latest/MoltenVK/dylib/macOS/libMoltenVK.dylib ~/VulkanSDK/${{ inputs.VULKAN_SDK_VERSION }}/macOS/lib/libMoltenVK.dylib
|
||||
- name: Build ZeldaRecomp
|
||||
run: |-
|
||||
# enable ccache
|
||||
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
|
||||
|
||||
cmake -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S . -B cmake-build \
|
||||
-DPATCHES_LD=/opt/local/bin/ld.lld-mp-18 -DPATCHES_OBJCOPY=/opt/local/bin/llvm-objcopy-mp-18 -DCMAKE_AR=/opt/local/bin/llvm-ar-mp-18 -DPATCHES_C_COMPILER=/opt/local/bin/clang-mp-18 \
|
||||
-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"
|
||||
cmake --build cmake-build --config ${{ matrix.type }} --target Zelda64Recompiled -j $(sysctl -n hw.ncpu)
|
||||
env:
|
||||
VULKAN_SDK: /Users/runner/VulkanSDK/${{ inputs.VULKAN_SDK_VERSION }}/macOS
|
||||
- name: Prepare Archive
|
||||
run: |
|
||||
mv cmake-build/Zelda64Recompiled.app Zelda64Recompiled.app
|
||||
zip -r -y Zelda64Recompiled.zip Zelda64Recompiled.app
|
||||
- name: Archive Zelda64Recomp
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Zelda64Recompiled-${{ runner.os }}-${{ matrix.type }}
|
||||
path: Zelda64Recompiled.zip
|
||||
|
|
|
@ -20,7 +20,7 @@ For Linux the instructions for Ubuntu are provided, but you can find the equival
|
|||
|
||||
```bash
|
||||
# For Ubuntu, simply run:
|
||||
sudo apt-get install cmake ninja libsdl2-dev libgtk-3-dev lld llvm clang-15
|
||||
sudo apt-get install cmake ninja-build libsdl2-dev libgtk-3-dev lld llvm clang
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
|
164
CMakeLists.txt
164
CMakeLists.txt
|
@ -1,5 +1,10 @@
|
|||
cmake_minimum_required(VERSION 3.20)
|
||||
project(Zelda64Recompiled)
|
||||
|
||||
if (APPLE) # has to be set before the first project() or enable_language()
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0" CACHE STRING "Minimum OS X deployment version")
|
||||
endif()
|
||||
|
||||
project(Zelda64Recompiled LANGUAGES C CXX)
|
||||
set(CMAKE_C_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
@ -15,6 +20,30 @@ if (WIN32)
|
|||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR")
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
enable_language(OBJC OBJCXX)
|
||||
|
||||
# Check if VULKAN_SDK environment variable is set
|
||||
if(NOT DEFINED ENV{VULKAN_SDK})
|
||||
message(FATAL_ERROR "VULKAN_SDK environment variable is not set.")
|
||||
else()
|
||||
set(VULKAN_SDK $ENV{VULKAN_SDK})
|
||||
endif()
|
||||
|
||||
# Define paths to Vulkan loader and MoltenVK libraries
|
||||
set(VULKAN_LOADER_PATH "${VULKAN_SDK}/lib/libvulkan.dylib")
|
||||
set(MOLTENVK_PATH "${VULKAN_SDK}/lib/libMoltenVK.dylib")
|
||||
|
||||
# Ensure the Vulkan loader and MoltenVK libraries exist
|
||||
if(NOT EXISTS "${VULKAN_LOADER_PATH}")
|
||||
message(FATAL_ERROR "Vulkan loader not found at ${VULKAN_LOADER_PATH}")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${MOLTENVK_PATH}")
|
||||
message(FATAL_ERROR "MoltenVK not found at ${MOLTENVK_PATH}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Avoid warning about DOWNLOAD_EXTRACT_TIMESTAMP in CMake 3.24:
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
|
||||
cmake_policy(SET CMP0135 NEW)
|
||||
|
@ -135,21 +164,12 @@ add_custom_target(DownloadGameControllerDB
|
|||
DEPENDS ${CMAKE_SOURCE_DIR}/gamecontrollerdb.txt)
|
||||
|
||||
# Main executable
|
||||
add_executable(Zelda64Recompiled)
|
||||
add_executable(Zelda64Recompiled MACOSX_BUNDLE)
|
||||
add_dependencies(Zelda64Recompiled DownloadGameControllerDB)
|
||||
|
||||
# Generate mm_shader_cache.c from the MM shader cache if it exists
|
||||
if (EXISTS ${CMAKE_SOURCE_DIR}/shadercache/mm_shader_cache.bin)
|
||||
set(HAS_MM_SHADER_CACHE TRUE)
|
||||
target_compile_definitions(Zelda64Recompiled PRIVATE HAS_MM_SHADER_CACHE)
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/mm_shader_cache.c ${CMAKE_CURRENT_BINARY_DIR}/mm_shader_cache.h
|
||||
COMMAND file_to_c ${CMAKE_SOURCE_DIR}/shadercache/mm_shader_cache.bin mm_shader_cache_bytes ${CMAKE_CURRENT_BINARY_DIR}/mm_shader_cache.c ${CMAKE_CURRENT_BINARY_DIR}/mm_shader_cache.h
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/shadercache/mm_shader_cache.bin
|
||||
)
|
||||
endif()
|
||||
|
||||
set (SOURCES
|
||||
${CMAKE_SOURCE_DIR}/src/main/main.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/main/support.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/main/register_overlays.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/main/register_patches.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/main/rt64_render_context.cpp
|
||||
|
@ -174,8 +194,8 @@ set (SOURCES
|
|||
${CMAKE_SOURCE_DIR}/lib/RmlUi/Backends/RmlUi_Platform_SDL.cpp
|
||||
)
|
||||
|
||||
if (HAS_MM_SHADER_CACHE)
|
||||
list(APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/mm_shader_cache.c)
|
||||
if (APPLE)
|
||||
list(APPEND SOURCES ${CMAKE_SOURCE_DIR}/src/main/support_apple.mm)
|
||||
endif()
|
||||
|
||||
target_include_directories(Zelda64Recompiled PRIVATE
|
||||
|
@ -210,6 +230,11 @@ else()
|
|||
)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
# Disable identical code folding, since this breaks mod function patching as multiple functions can get merged into one.
|
||||
target_link_options(Zelda64Recompiled PRIVATE /OPT:NOICF)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
include(FetchContent)
|
||||
|
||||
|
@ -250,6 +275,114 @@ if (WIN32)
|
|||
)
|
||||
|
||||
target_sources(Zelda64Recompiled PRIVATE ${CMAKE_SOURCE_DIR}/icons/app.rc)
|
||||
target_link_libraries(Zelda64Recompiled PRIVATE SDL2)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
find_package(SDL2 REQUIRED)
|
||||
target_include_directories(Zelda64Recompiled PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||
|
||||
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
target_link_libraries(Zelda64Recompiled PRIVATE ${CMAKE_DL_LIBS} Threads::Threads SDL2::SDL2)
|
||||
|
||||
# Set bundle properties
|
||||
set_target_properties(Zelda64Recompiled PROPERTIES
|
||||
MACOSX_BUNDLE TRUE
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "Zelda64Recompiled"
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "com.github.zelda64recompiled"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "1.0"
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "1.0"
|
||||
MACOSX_BUNDLE_ICON_FILE "AppIcon.icns"
|
||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_BINARY_DIR}/Info.plist
|
||||
)
|
||||
|
||||
set(ICON_SOURCE ${CMAKE_SOURCE_DIR}/icons/512.png)
|
||||
set(ICONSET_DIR ${CMAKE_BINARY_DIR}/AppIcon.iconset)
|
||||
set(ICNS_FILE ${CMAKE_BINARY_DIR}/resources/AppIcon.icns)
|
||||
|
||||
# Create iconset directory and add PNG file
|
||||
add_custom_command(
|
||||
OUTPUT ${ICONSET_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${ICONSET_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${ICON_SOURCE} ${ICONSET_DIR}/icon_512x512.png
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${ICON_SOURCE} ${ICONSET_DIR}/icon_512x512@2x.png
|
||||
COMMAND touch ${ICONSET_DIR}
|
||||
COMMENT "Creating iconset directory and copying PNG file"
|
||||
)
|
||||
|
||||
# Convert iconset to icns
|
||||
add_custom_command(
|
||||
OUTPUT ${ICNS_FILE}
|
||||
DEPENDS ${ICONSET_DIR}
|
||||
COMMAND iconutil -c icns ${ICONSET_DIR} -o ${ICNS_FILE}
|
||||
COMMENT "Converting iconset to icns"
|
||||
)
|
||||
|
||||
# Custom target to ensure icns creation
|
||||
add_custom_target(create_icns ALL DEPENDS ${ICNS_FILE})
|
||||
|
||||
# Set source file properties for the resulting icns file
|
||||
set_source_files_properties(${ICNS_FILE} PROPERTIES
|
||||
MACOSX_PACKAGE_LOCATION "Resources"
|
||||
)
|
||||
|
||||
# Add the icns file to the executable target
|
||||
target_sources(Zelda64Recompiled PRIVATE ${ICNS_FILE})
|
||||
|
||||
# Ensure Zelda64Recompiled depends on create_icns
|
||||
add_dependencies(Zelda64Recompiled create_icns)
|
||||
|
||||
# Configure Info.plist
|
||||
configure_file(${CMAKE_SOURCE_DIR}/.github/macos/Info.plist.in ${CMAKE_BINARY_DIR}/Info.plist @ONLY)
|
||||
|
||||
# Install the app bundle
|
||||
install(TARGETS Zelda64Recompiled BUNDLE DESTINATION .)
|
||||
|
||||
# Copy required frameworks to bundle
|
||||
target_link_libraries(Zelda64Recompiled PRIVATE ${MOLTENVK_PATH} ${VULKAN_LOADER_PATH})
|
||||
|
||||
# Define the path to the entitlements file
|
||||
set(ENTITLEMENTS_FILE ${CMAKE_SOURCE_DIR}/.github/macos/entitlements.plist)
|
||||
|
||||
# Ensure the entitlements file exists
|
||||
if(NOT EXISTS ${ENTITLEMENTS_FILE})
|
||||
message(FATAL_ERROR "Entitlements file not found at ${ENTITLEMENTS_FILE}")
|
||||
endif()
|
||||
|
||||
# Post-build steps for macOS bundle
|
||||
add_custom_command(TARGET Zelda64Recompiled POST_BUILD
|
||||
# Copy and fix frameworks first
|
||||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/.github/macos/fixup_bundle.cmake
|
||||
|
||||
# Copy all resources
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/assets ${CMAKE_BINARY_DIR}/temp_assets
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/temp_assets/scss
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_BINARY_DIR}/temp_assets $<TARGET_BUNDLE_DIR:Zelda64Recompiled>/Contents/Resources/assets
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/temp_assets
|
||||
|
||||
# Copy Vulkan ICD files
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory $<TARGET_BUNDLE_DIR:Zelda64Recompiled>/Contents/Resources/vulkan/icd.d
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/.github/macOS/MoltenVK_icd.json $<TARGET_BUNDLE_DIR:Zelda64Recompiled>/Contents/Resources/vulkan/icd.d/
|
||||
|
||||
# Copy controller database
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/gamecontrollerdb.txt $<TARGET_BUNDLE_DIR:Zelda64Recompiled>/Contents/Resources/
|
||||
|
||||
# Set RPATH
|
||||
COMMAND install_name_tool -add_rpath "@executable_path/../Frameworks/" $<TARGET_BUNDLE_DIR:Zelda64Recompiled>/Contents/MacOS/Zelda64Recompiled
|
||||
|
||||
# Apply JIT workaround
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Applying JIT compilation workaround"
|
||||
COMMAND /bin/bash -c "printf '\\x07' | dd of=$<TARGET_FILE:Zelda64Recompiled> bs=1 seek=160 count=1 conv=notrunc"
|
||||
|
||||
# Finally sign the whole bundle with runtime option and entitlements
|
||||
COMMAND codesign --deep --force --sign - --entitlements ${ENTITLEMENTS_FILE} $<TARGET_BUNDLE_DIR:Zelda64Recompiled>
|
||||
|
||||
COMMENT "Performing post-build steps for macOS bundle"
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
|
@ -283,7 +416,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
|||
message(STATUS "FREETYPE_LIBRARIES = ${FREETYPE_LIBRARIES}")
|
||||
|
||||
include_directories(${FREETYPE_LIBRARIES})
|
||||
target_link_libraries(Zelda64Recompiled PRIVATE ${FREETYPE_LIBRARIES})
|
||||
target_link_libraries(Zelda64Recompiled PRIVATE ${FREETYPE_LIBRARIES} SDL2::SDL2)
|
||||
|
||||
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
|
||||
|
@ -295,7 +428,6 @@ endif()
|
|||
target_link_libraries(Zelda64Recompiled PRIVATE
|
||||
PatchesLib
|
||||
RecompiledFuncs
|
||||
SDL2
|
||||
librecomp
|
||||
ultramodern
|
||||
rt64
|
||||
|
|
|
@ -15,7 +15,7 @@ Zelda 64: Recompiled is a project that uses [N64: Recompiled](https://github.com
|
|||
|
||||
<br style="display: none;"/>
|
||||
|
||||
_Thank you [Blaze](https://runblaze.dev) for supporting this project by providing Linux ARM64 and Apple Silicon macOS Github Action Runners!_
|
||||
_Special thanks to [Blaze](https://runblaze.dev) for their support of this project. They provide high-performance Linux (AMD64 & ARM64) and Apple Silicon macOS runners for GitHub Actions, greatly reducing our automated build times._
|
||||
|
||||
</div>
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="bottom-left">
|
||||
<label>{{version_number}}</label>
|
||||
<label>v{{version_number}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -120,7 +120,8 @@ namespace recomp {
|
|||
std::vector<InputField> apply_menu;
|
||||
};
|
||||
|
||||
constexpr const std::vector<InputField>& get_default_mapping_for_input(const DefaultN64Mappings& defaults, const GameInput input) {
|
||||
inline const std::vector<InputField>& get_default_mapping_for_input(const DefaultN64Mappings& defaults, const GameInput input) {
|
||||
static const std::vector<InputField> empty_input_field{};
|
||||
switch (input) {
|
||||
case GameInput::A: return defaults.a;
|
||||
case GameInput::B: return defaults.b;
|
||||
|
@ -143,7 +144,7 @@ namespace recomp {
|
|||
case GameInput::TOGGLE_MENU: return defaults.toggle_menu;
|
||||
case GameInput::ACCEPT_MENU: return defaults.accept_menu;
|
||||
case GameInput::APPLY_MENU: return defaults.apply_menu;
|
||||
default: return std::vector<InputField>();
|
||||
default: return empty_input_field;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
#ifndef __ZELDA_RENDER_H__
|
||||
#define __ZELDA_RENDER_H__
|
||||
|
||||
#include <unordered_set>
|
||||
#include <filesystem>
|
||||
|
||||
#include "common/rt64_user_configuration.h"
|
||||
#include "ultramodern/renderer_context.hpp"
|
||||
#include "librecomp/mods.hpp"
|
||||
|
||||
namespace RT64 {
|
||||
struct Application;
|
||||
|
@ -29,6 +33,7 @@ namespace zelda64 {
|
|||
|
||||
protected:
|
||||
std::unique_ptr<RT64::Application> app;
|
||||
std::unordered_set<std::filesystem::path> enabled_texture_packs;
|
||||
};
|
||||
|
||||
std::unique_ptr<ultramodern::renderer::RendererContext> create_render_context(uint8_t *rdram, ultramodern::renderer::WindowHandle window_handle, bool developer_mode);
|
||||
|
@ -36,6 +41,9 @@ namespace zelda64 {
|
|||
RT64::UserConfiguration::Antialiasing RT64MaxMSAA();
|
||||
bool RT64SamplePositionsSupported();
|
||||
bool RT64HighPrecisionFBEnabled();
|
||||
|
||||
void enable_texture_pack(const recomp::mods::ModHandle& mod);
|
||||
void disable_texture_pack(const recomp::mods::ModHandle& mod);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef __ZELDA_SUPPORT_H__
|
||||
#define __ZELDA_SUPPORT_H__
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace zelda64 {
|
||||
void dispatch_on_main_thread(std::function<void()> func);
|
||||
|
||||
#ifdef __APPLE__
|
||||
const char* get_bundle_resource_directory();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1 +1 @@
|
|||
Subproject commit 0a538553330ea5fdb1673708704bb92a854241b9
|
||||
Subproject commit 3e39c2ec34340e245a5b7e353560b5a793b9990b
|
2
lib/rt64
2
lib/rt64
|
@ -1 +1 @@
|
|||
Subproject commit 88c618c1f8d8089f94e78537e49e0d77798fc0be
|
||||
Subproject commit c648adae38ed8d0342c02ccf3a6063e99d746752
|
|
@ -9,8 +9,12 @@ single_file_output = true
|
|||
use_absolute_symbols = true
|
||||
# Point the recompiler at the symbol files so that it can resolve relocations during recompilation.
|
||||
func_reference_syms_file = "Zelda64RecompSyms/mm.us.rev1.syms.toml"
|
||||
data_reference_syms_files = [ "Zelda64RecompSyms/mm.us.rev1.datasyms.toml", "Zelda64RecompSyms/mm.us.rev1.datasyms_static.toml" ]
|
||||
data_reference_syms_files = [ "Zelda64RecompSyms/mm.us.rev1.datasyms.toml", "Zelda64RecompSyms/mm.us.rev1.datasyms_static.toml", "patches/custom_syms.toml" ]
|
||||
# Tell the recompiler to write the output binary. Doing this instead of using objcopy allows the recompiler to patch MIPS32 relocs.
|
||||
output_binary_path = "patches/patches.bin"
|
||||
# Do not emit warnings for unpaired LO16 values, as clang produces many of them.
|
||||
unpaired_lo16_warnings = false
|
||||
# Allow exporting functions and events for mods to use.
|
||||
allow_exports = true
|
||||
# # Enable strict patch mode, validates that patched symbols exist and that non-patch functions aren't symbols.
|
||||
strict_patch_mode = true
|
||||
|
|
|
@ -67,10 +67,10 @@ RECOMP_PATCH void KaleidoSetup_Update(PlayState* play) {
|
|||
|
||||
void Sram_SyncWriteToFlash(SramContext* sramCtx, s32 curPage, s32 numPages);
|
||||
|
||||
void autosave_reset_timer();
|
||||
void autosave_reset_timer_slow();
|
||||
void recomp_reset_autosave_timer();
|
||||
void recomp_reset_autosave_timer_slow();
|
||||
|
||||
void do_autosave(PlayState* play) {
|
||||
RECOMP_EXPORT void recomp_do_autosave(PlayState* play) {
|
||||
// Transfer the scene flags into the cycle flags.
|
||||
Play_SaveCycleSceneFlags(&play->state);
|
||||
// Transfer the cycle flags into the save buffer. Logic copied from func_8014546C.
|
||||
|
@ -99,13 +99,22 @@ void do_autosave(PlayState* play) {
|
|||
gSaveContext.save.isOwlSave = false;
|
||||
}
|
||||
|
||||
// @recomp Do not clear the save if the save was an autosave.
|
||||
bool loading_deletes_owl_save = true;
|
||||
|
||||
// @recomp_export void recomp_set_loading_deletes_owl_save(bool new_val): Set whether loading an owl save should also delete it.
|
||||
RECOMP_EXPORT void recomp_set_loading_deletes_owl_save(bool new_val)
|
||||
{
|
||||
loading_deletes_owl_save = new_val;
|
||||
}
|
||||
|
||||
// @recomp Do not clear the save if the save was an autosave, or if mods have disabled save deletion.
|
||||
RECOMP_PATCH void func_80147314(SramContext* sramCtx, s32 fileNum) {
|
||||
s32 save_type = gSaveContext.save.isOwlSave;
|
||||
gSaveContext.save.isOwlSave = false;
|
||||
|
||||
// @recomp Prevent owl save/autosave deletion if autosaving is enabled.
|
||||
if (!recomp_autosave_enabled()) {
|
||||
// @recomp Prevent owl save/autosave deletion if autosaving is enabled, and...
|
||||
// @recomp_use_export_var loading_deletes_owl_save: Prevent owl save deletion if mods disable it.
|
||||
if (!recomp_autosave_enabled() && loading_deletes_owl_save) {
|
||||
gSaveContext.save.saveInfo.playerData.newf[0] = '\0';
|
||||
gSaveContext.save.saveInfo.playerData.newf[1] = '\0';
|
||||
gSaveContext.save.saveInfo.playerData.newf[2] = '\0';
|
||||
|
@ -176,7 +185,7 @@ RECOMP_PATCH void func_8014546C(SramContext* sramCtx) {
|
|||
// @recomp Delete the owl save.
|
||||
delete_owl_save(sramCtx, gSaveContext.fileNum);
|
||||
// @recomp Reset the autosave timer.
|
||||
autosave_reset_timer();
|
||||
recomp_reset_autosave_timer();
|
||||
for (i = 0; i < ARRAY_COUNT(gSaveContext.cycleSceneFlags); i++) {
|
||||
gSaveContext.save.saveInfo.permanentSceneFlags[i].chest = gSaveContext.cycleSceneFlags[i].chest;
|
||||
gSaveContext.save.saveInfo.permanentSceneFlags[i].switch0 = gSaveContext.cycleSceneFlags[i].switch0;
|
||||
|
@ -344,11 +353,11 @@ void draw_autosave_icon(PlayState* play) {
|
|||
CLOSE_DISPS(play->state.gfxCtx);
|
||||
}
|
||||
|
||||
void show_autosave_icon() {
|
||||
RECOMP_EXPORT void recomp_show_autosave_icon() {
|
||||
autosave_icon_counter = AUTOSAVE_ICON_TOTAL_FRAMES;
|
||||
}
|
||||
|
||||
u32 recomp_autosave_interval() {
|
||||
RECOMP_EXPORT u32 recomp_autosave_interval() {
|
||||
return 2 * 60 * 1000;
|
||||
}
|
||||
|
||||
|
@ -367,12 +376,12 @@ bool reached_final_three_hours() {
|
|||
return false;
|
||||
}
|
||||
|
||||
void autosave_reset_timer() {
|
||||
RECOMP_EXPORT void recomp_reset_autosave_timer() {
|
||||
last_autosave_time = osGetTime();
|
||||
extra_autosave_delay_milliseconds = 0;
|
||||
}
|
||||
|
||||
void autosave_reset_timer_slow() {
|
||||
RECOMP_EXPORT void recomp_reset_autosave_timer_slow() {
|
||||
// Set the most recent autosave time in the future to give extra time before an autosave triggers.
|
||||
last_autosave_time = osGetTime();
|
||||
extra_autosave_delay_milliseconds = 2 * 60 * 1000;
|
||||
|
@ -425,20 +434,20 @@ void autosave_post_play_update(PlayState* play) {
|
|||
frames_since_autosave_ready >= MIN_FRAMES_SINCE_READY &&
|
||||
time_now - last_autosave_time > (OS_USEC_TO_CYCLES(1000 * (recomp_autosave_interval() + extra_autosave_delay_milliseconds)))
|
||||
) {
|
||||
do_autosave(play);
|
||||
show_autosave_icon();
|
||||
autosave_reset_timer();
|
||||
recomp_do_autosave(play);
|
||||
recomp_show_autosave_icon();
|
||||
recomp_reset_autosave_timer();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Update the last autosave time to the current time to prevent autosaving immediately if autosaves are turned back on.
|
||||
autosave_reset_timer();
|
||||
recomp_reset_autosave_timer();
|
||||
}
|
||||
gCanPause = false;
|
||||
}
|
||||
|
||||
void autosave_init() {
|
||||
autosave_reset_timer_slow();
|
||||
recomp_reset_autosave_timer_slow();
|
||||
Lib_MemCpy(&prev_save_ctx, &gSaveContext, offsetof(SaveContext, fileNum));
|
||||
}
|
||||
|
||||
|
@ -522,6 +531,9 @@ s32 spawn_entrance_from_autosave_entrance(s16 autosave_entrance) {
|
|||
}
|
||||
}
|
||||
|
||||
RECOMP_DECLARE_EVENT(recomp_on_load_save(FileSelectState* fileSelect, SramContext* sramCtx));
|
||||
RECOMP_DECLARE_EVENT(recomp_after_load_save(FileSelectState* fileSelect, SramContext* sramCtx));
|
||||
|
||||
// @recomp Patched to change the entrance for autosaves and initialize autosaves.
|
||||
RECOMP_PATCH void Sram_OpenSave(FileSelectState* fileSelect, SramContext* sramCtx) {
|
||||
s32 i;
|
||||
|
@ -530,6 +542,9 @@ RECOMP_PATCH void Sram_OpenSave(FileSelectState* fileSelect, SramContext* sramCt
|
|||
s32 pad1;
|
||||
s32 fileNum;
|
||||
|
||||
// @recomp_event recomp_on_load_save(FileSelectState* fileSelect, SramContext* sramCtx): A save-file was just chosen.
|
||||
recomp_on_load_save(fileSelect, sramCtx);
|
||||
|
||||
if (gSaveContext.flashSaveAvailable) {
|
||||
bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE);
|
||||
|
||||
|
@ -653,29 +668,49 @@ RECOMP_PATCH void Sram_OpenSave(FileSelectState* fileSelect, SramContext* sramCt
|
|||
|
||||
// @recomp Initialize the autosave state tracking.
|
||||
autosave_init();
|
||||
|
||||
// @recomp_event recomp_after_load_save(FileSelectState* fileSelect, SramContext* sramCtx): The save has finished loading.
|
||||
recomp_after_load_save(fileSelect, sramCtx);
|
||||
}
|
||||
|
||||
bool moon_crash_resets_save = true;
|
||||
|
||||
// @recomp_export void recomp_set_moon_crash_resets_save(bool new_val): Set whether a moon crash should revert the player's save data.
|
||||
RECOMP_EXPORT void recomp_set_moon_crash_resets_save(bool new_val)
|
||||
{
|
||||
moon_crash_resets_save = new_val;
|
||||
}
|
||||
|
||||
extern s32 Actor_ProcessTalkRequest(Actor* actor, GameState* gameState);
|
||||
|
||||
RECOMP_DECLARE_EVENT(recomp_on_moon_crash(SramContext* sramCtx));
|
||||
RECOMP_DECLARE_EVENT(recomp_after_moon_crash(SramContext* sramCtx));
|
||||
|
||||
// @recomp Reset the autosave timer when the moon crashes.
|
||||
RECOMP_PATCH void Sram_ResetSaveFromMoonCrash(SramContext* sramCtx) {
|
||||
s32 i;
|
||||
s32 cutsceneIndex = gSaveContext.save.cutsceneIndex;
|
||||
|
||||
bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE);
|
||||
// @recomp_event recomp_on_moon_crash(SramContext* sramCtx): A moon crash has just been triggered.
|
||||
recomp_on_moon_crash(sramCtx);
|
||||
|
||||
if (SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2],
|
||||
gFlashSaveNumPages[gSaveContext.fileNum * 2]) != 0) {
|
||||
SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2 + 1],
|
||||
gFlashSaveNumPages[gSaveContext.fileNum * 2 + 1]);
|
||||
if (moon_crash_resets_save)
|
||||
{
|
||||
bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE);
|
||||
|
||||
if (SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2],
|
||||
gFlashSaveNumPages[gSaveContext.fileNum * 2]) != 0) {
|
||||
SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2 + 1],
|
||||
gFlashSaveNumPages[gSaveContext.fileNum * 2 + 1]);
|
||||
}
|
||||
Lib_MemCpy(&gSaveContext.save, sramCtx->saveBuf, sizeof(Save));
|
||||
if (CHECK_NEWF(gSaveContext.save.saveInfo.playerData.newf)) {
|
||||
SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2 + 1],
|
||||
gFlashSaveNumPages[gSaveContext.fileNum * 2 + 1]);
|
||||
Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, sizeof(Save));
|
||||
}
|
||||
gSaveContext.save.cutsceneIndex = cutsceneIndex;
|
||||
}
|
||||
Lib_MemCpy(&gSaveContext.save, sramCtx->saveBuf, sizeof(Save));
|
||||
if (CHECK_NEWF(gSaveContext.save.saveInfo.playerData.newf)) {
|
||||
SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2 + 1],
|
||||
gFlashSaveNumPages[gSaveContext.fileNum * 2 + 1]);
|
||||
Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, sizeof(Save));
|
||||
}
|
||||
gSaveContext.save.cutsceneIndex = cutsceneIndex;
|
||||
|
||||
for (i = 0; i < ARRAY_COUNT(gSaveContext.eventInf); i++) {
|
||||
gSaveContext.eventInf[i] = 0;
|
||||
|
@ -705,25 +740,56 @@ RECOMP_PATCH void Sram_ResetSaveFromMoonCrash(SramContext* sramCtx) {
|
|||
gSaveContext.jinxTimer = 0;
|
||||
|
||||
// @recomp Use the slow autosave timer to give the player extra time to respond to the moon crashing to decide if they want to reload their autosave.
|
||||
autosave_reset_timer_slow();
|
||||
recomp_reset_autosave_timer_slow();
|
||||
|
||||
// @recomp_event recomp_after_moon_crash(SramContext* sramCtx): The effects of moon crash have been written.
|
||||
recomp_after_moon_crash(sramCtx);
|
||||
}
|
||||
|
||||
|
||||
// @recomp If autosave is enabled, skip the part of the owl statue dialog that talks about the file being deleted on load, since it's not true.
|
||||
bool owls_save_and_quit = true;
|
||||
|
||||
// @recomp_export void recomp_set_owls_save_and_quit(bool new_val): Set if owls should use their code to save and quit. If false is passed, owl saves now do nothing.
|
||||
RECOMP_EXPORT void recomp_set_owls_save_and_quit(bool new_val)
|
||||
{
|
||||
owls_save_and_quit = new_val;
|
||||
}
|
||||
|
||||
RECOMP_DECLARE_EVENT(recomp_on_owl_update(ObjWarpstone* this, PlayState* play));
|
||||
RECOMP_DECLARE_EVENT(recomp_on_owl_save(ObjWarpstone* this, PlayState* play));
|
||||
RECOMP_DECLARE_EVENT(recomp_after_owl_save(ObjWarpstone* this, PlayState* play));
|
||||
|
||||
// @recomp If autosave is enabled or owl save deletion is disabled, skip the part of the owl statue dialog that talks about the file being deleted on load, since it's not true.
|
||||
RECOMP_PATCH void ObjWarpstone_Update(Actor* thisx, PlayState* play) {
|
||||
ObjWarpstone* this = (ObjWarpstone*)thisx;
|
||||
s32 pad;
|
||||
|
||||
// @recomp_event recomp_on_owl_update(ObjWarpstone* this, PlayState* play): Allow mods to handle owl update frames.
|
||||
recomp_on_owl_update(this, play);
|
||||
|
||||
if (this->isTalking) {
|
||||
if (Actor_TextboxIsClosing(&this->dyna.actor, play)) {
|
||||
this->isTalking = false;
|
||||
} else if ((Message_GetState(&play->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(play)) {
|
||||
if (play->msgCtx.choiceIndex != 0) {
|
||||
// @recomp_event recomp_on_owl_save(ObjWarpstone* this, PlayState* play): The player chose to save from an owl statue.
|
||||
recomp_on_owl_save(this, play);
|
||||
|
||||
Audio_PlaySfx_MessageDecide();
|
||||
play->msgCtx.msgMode = MSGMODE_OWL_SAVE_0;
|
||||
|
||||
// @recomp_use_export_var owls_save_and_quit: Only use normal owl save if quit flag is set.
|
||||
if (owls_save_and_quit) {
|
||||
play->msgCtx.msgMode = MSGMODE_OWL_SAVE_0;
|
||||
} else {
|
||||
Message_CloseTextbox(play);
|
||||
}
|
||||
|
||||
play->msgCtx.unk120D6 = 0;
|
||||
play->msgCtx.unk120D4 = 0;
|
||||
gSaveContext.save.owlWarpId = OBJ_WARPSTONE_GET_OWL_WARP_ID(&this->dyna.actor);
|
||||
|
||||
// @recomp_event recomp_after_owl_save(ObjWarpstone* this, PlayState* play): Owl save is finished.
|
||||
recomp_after_owl_save(this, play);
|
||||
} else {
|
||||
Message_CloseTextbox(play);
|
||||
}
|
||||
|
@ -734,8 +800,8 @@ RECOMP_PATCH void ObjWarpstone_Update(Actor* thisx, PlayState* play) {
|
|||
Actor_OfferTalkNearColChkInfoCylinder(&this->dyna.actor, play);
|
||||
}
|
||||
|
||||
// @recomp Skip the text talking about the save being deleted on load, if autosave is enabled.
|
||||
if (recomp_autosave_enabled()) {
|
||||
// @recomp_use_export_var loading_deletes_owl_save: Skip the text talking about the save being deleted on load, if autosave is enabled or if owl save deletion is disabled.
|
||||
if (recomp_autosave_enabled() || !loading_deletes_owl_save) {
|
||||
if (this->isTalking && play->msgCtx.currentTextId == 0xC01 && play->msgCtx.msgBufPos == 269) {
|
||||
play->msgCtx.msgBufPos = 530;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
# Custom symbols used for patches.
|
||||
[[section]]
|
||||
name = "ABSOLUTE_SYMS"
|
||||
vram = 0x00000000
|
||||
size = 0x0
|
||||
|
||||
symbols = [
|
||||
# Alternate references to symbols that would otherwise be relocated
|
||||
{ name = "Setup_Init_NORELOCATE", vram = 0x80173338 }, # Not relocatable, but here for uniformity with the other gamestate funcs
|
||||
{ name = "MapSelect_Init_NORELOCATE", vram = 0x80801B4C },
|
||||
{ name = "ConsoleLogo_Init_NORELOCATE", vram = 0x8080074C },
|
||||
{ name = "Play_Init_NORELOCATE", vram = 0x8016A2C8 },
|
||||
{ name = "TitleSetup_Init_NORELOCATE", vram = 0x80803F30 },
|
||||
{ name = "FileSelect_Init_NORELOCATE", vram = 0x80813C98 },
|
||||
{ name = "DayTelop_Init_NORELOCATE", vram = 0x80815820 },
|
||||
]
|
126
patches/input.c
126
patches/input.c
|
@ -534,11 +534,31 @@ RECOMP_PATCH void Player_Action_86(Player *this, PlayState *play) {
|
|||
func_808550D0(play, this, this->unk_B10[4], this->unk_B10[5], (this->transformation == PLAYER_FORM_HUMAN) ? 0 : 1);
|
||||
}
|
||||
|
||||
bool no_bow_epona_fix = false;
|
||||
|
||||
// @recomp_export void recomp_set_no_bow_epona_fix(bool new_val): Set whether to enable the fix for getting on Epona without a bow.
|
||||
RECOMP_EXPORT void recomp_set_no_bow_epona_fix(bool new_val) {
|
||||
no_bow_epona_fix = new_val;
|
||||
}
|
||||
|
||||
bool h_and_d_no_sword_fix = false;
|
||||
|
||||
// @recomp_export void recomp_set_h_and_d_no_sword_fix(bool new_val): Set whether to enable the fix for playing Honey and Darling without a sword.
|
||||
RECOMP_EXPORT void recomp_set_h_and_d_no_sword_fix(bool new_val) {
|
||||
h_and_d_no_sword_fix = new_val;
|
||||
}
|
||||
|
||||
extern s16 sPictoState;
|
||||
extern s16 sPictoPhotoBeingTaken;
|
||||
extern void* gWorkBuffer;
|
||||
u16 func_801A5100(void);
|
||||
|
||||
#define ON_EPONA (player->stateFlags1 & PLAYER_STATE1_800000)
|
||||
#define EPONA_FIX_ACTIVE (no_bow_epona_fix && ON_EPONA)
|
||||
|
||||
#define AT_H_AND_D (play->sceneId == SCENE_BOWLING)
|
||||
#define H_AND_D_FIX_ACTIVE (h_and_d_no_sword_fix && AT_H_AND_D)
|
||||
|
||||
// @recomp Patched to update status of extra buttons via set_extra_item_slot_status.
|
||||
RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) {
|
||||
InterfaceContext* interfaceCtx = &play->interfaceCtx;
|
||||
|
@ -548,11 +568,11 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) {
|
|||
|
||||
if (gSaveContext.save.cutsceneIndex < 0xFFF0) {
|
||||
gSaveContext.hudVisibilityForceButtonAlphasByStatus = false;
|
||||
if ((player->stateFlags1 & PLAYER_STATE1_800000) || CHECK_WEEKEVENTREG(WEEKEVENTREG_08_01) ||
|
||||
if (ON_EPONA || CHECK_WEEKEVENTREG(WEEKEVENTREG_08_01) ||
|
||||
(!CHECK_EVENTINF(EVENTINF_41) && (play->unk_1887C >= 2))) {
|
||||
// Riding Epona OR Honey & Darling minigame OR Horseback balloon minigame OR related to swamp boat
|
||||
// (non-minigame?)
|
||||
if ((player->stateFlags1 & PLAYER_STATE1_800000) && (player->currentMask == PLAYER_MASK_BLAST) &&
|
||||
if (ON_EPONA && (player->currentMask == PLAYER_MASK_BLAST) &&
|
||||
(gSaveContext.bButtonStatus == BTN_DISABLED)) {
|
||||
// Riding Epona with blast mask?
|
||||
restoreHudVisibility = true;
|
||||
|
@ -603,24 +623,46 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) {
|
|||
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
|
||||
set_extra_item_slot_status(BTN_DISABLED);
|
||||
} else {
|
||||
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW;
|
||||
// @recomp_use_export_var no_bow_epona_fix: Part of the no bow Epona fix.
|
||||
if (EPONA_FIX_ACTIVE) {
|
||||
if (gSaveContext.save.saveInfo.inventory.items[SLOT_BOW] == ITEM_BOW) {
|
||||
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW;
|
||||
BUTTON_STATUS(EQUIP_SLOT_B) = BTN_ENABLED;
|
||||
}
|
||||
} else {
|
||||
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW;
|
||||
}
|
||||
|
||||
if (play->unk_1887C >= 2) {
|
||||
Interface_LoadItemIconImpl(play, EQUIP_SLOT_B);
|
||||
} else if (gSaveContext.save.saveInfo.inventory.items[SLOT_BOW] == ITEM_NONE) {
|
||||
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_NONE;
|
||||
// @recomp_use_export_var no_bow_epona_fix: Part of the no bow Epona fix.
|
||||
if (EPONA_FIX_ACTIVE) {
|
||||
gSaveContext.buttonStatus[EQUIP_SLOT_B] = BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B);
|
||||
BUTTON_STATUS(EQUIP_SLOT_B) = BTN_DISABLED;
|
||||
} else {
|
||||
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_NONE;
|
||||
}
|
||||
} else {
|
||||
Interface_LoadItemIconImpl(play, EQUIP_SLOT_B);
|
||||
}
|
||||
|
||||
BUTTON_STATUS(EQUIP_SLOT_C_LEFT) = BTN_DISABLED;
|
||||
BUTTON_STATUS(EQUIP_SLOT_C_DOWN) = BTN_DISABLED;
|
||||
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
|
||||
set_extra_item_slot_status(BTN_DISABLED);
|
||||
Interface_SetHudVisibility(HUD_VISIBILITY_A_HEARTS_MAGIC_MINIMAP_WITH_OVERWRITE);
|
||||
// @recomp_use_export_var no_bow_epona_fix: If the B button does not contain a sword, don't disable the UI.
|
||||
if (!EPONA_FIX_ACTIVE || BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) < ITEM_SWORD_KOKIRI ||
|
||||
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) > ITEM_SWORD_GILDED) {
|
||||
BUTTON_STATUS(EQUIP_SLOT_C_LEFT) = BTN_DISABLED;
|
||||
BUTTON_STATUS(EQUIP_SLOT_C_DOWN) = BTN_DISABLED;
|
||||
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
|
||||
set_extra_item_slot_status(BTN_DISABLED);
|
||||
Interface_SetHudVisibility(HUD_VISIBILITY_A_HEARTS_MAGIC_MINIMAP_WITH_OVERWRITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (BUTTON_STATUS(EQUIP_SLOT_B) == BTN_DISABLED && BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) == ITEM_BOW) {
|
||||
BUTTON_STATUS(EQUIP_SLOT_B) = BTN_ENABLED;
|
||||
}
|
||||
|
||||
if (play->transitionMode != TRANS_MODE_OFF) {
|
||||
Interface_SetHudVisibility(HUD_VISIBILITY_NONE);
|
||||
} else if ((gSaveContext.minigameStatus == MINIGAME_STATUS_ACTIVE) &&
|
||||
|
@ -640,12 +682,12 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) {
|
|||
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
|
||||
set_extra_item_slot_status(BTN_DISABLED);
|
||||
Interface_SetHudVisibility(HUD_VISIBILITY_A_B_MINIMAP);
|
||||
} else if (player->stateFlags1 & PLAYER_STATE1_800000) {
|
||||
} else if (ON_EPONA) {
|
||||
Interface_SetHudVisibility(HUD_VISIBILITY_A_B_MINIMAP);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (player->stateFlags1 & PLAYER_STATE1_800000) {
|
||||
if (ON_EPONA) {
|
||||
Interface_SetHudVisibility(HUD_VISIBILITY_A_B_MINIMAP);
|
||||
}
|
||||
|
||||
|
@ -657,32 +699,59 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) {
|
|||
} else {
|
||||
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW;
|
||||
}
|
||||
if (h_and_d_no_sword_fix) {
|
||||
BUTTON_STATUS(EQUIP_SLOT_B) = BTN_ENABLED;
|
||||
}
|
||||
BUTTON_STATUS(EQUIP_SLOT_C_LEFT) = BTN_DISABLED;
|
||||
BUTTON_STATUS(EQUIP_SLOT_C_DOWN) = BTN_DISABLED;
|
||||
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
|
||||
set_extra_item_slot_status(BTN_DISABLED);
|
||||
} else {
|
||||
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW;
|
||||
// @recomp_use_export_var no_bow_epona_fix: Part of the no bow Epona fix.
|
||||
if (EPONA_FIX_ACTIVE) {
|
||||
if (gSaveContext.save.saveInfo.inventory.items[SLOT_BOW] == ITEM_BOW) {
|
||||
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW;
|
||||
BUTTON_STATUS(EQUIP_SLOT_B) = BTN_ENABLED;
|
||||
}
|
||||
} else {
|
||||
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW;
|
||||
}
|
||||
}
|
||||
|
||||
if (play->unk_1887C >= 2) {
|
||||
Interface_LoadItemIconImpl(play, EQUIP_SLOT_B);
|
||||
} else if (gSaveContext.save.saveInfo.inventory.items[SLOT_BOW] == ITEM_NONE) {
|
||||
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_NONE;
|
||||
} else if (gSaveContext.save.saveInfo.inventory.items[SLOT_BOW] == ITEM_NONE && !H_AND_D_FIX_ACTIVE) {
|
||||
// @recomp_use_export_var no_bow_epona_fix: Part of the no bow Epona fix.
|
||||
if (EPONA_FIX_ACTIVE) {
|
||||
gSaveContext.buttonStatus[EQUIP_SLOT_B] = BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B);
|
||||
BUTTON_STATUS(EQUIP_SLOT_B) = BTN_DISABLED;
|
||||
} else {
|
||||
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_NONE;
|
||||
}
|
||||
} else {
|
||||
Interface_LoadItemIconImpl(play, EQUIP_SLOT_B);
|
||||
}
|
||||
|
||||
if (BUTTON_STATUS(EQUIP_SLOT_B) == BTN_DISABLED) {
|
||||
BUTTON_STATUS(EQUIP_SLOT_B) = BTN_ENABLED;
|
||||
restoreHudVisibility = true;
|
||||
// @recomp_use_export_var no_bow_epona_fix: Don't enable the B button unless it is being used for the bow.
|
||||
if (!no_bow_epona_fix || BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) == ITEM_BOW) {
|
||||
BUTTON_STATUS(EQUIP_SLOT_B) = BTN_ENABLED;
|
||||
}
|
||||
|
||||
// @recomp_use_export_var no_bow_epona_fix: Don't restore hud visibility from Epona without a sword.
|
||||
if (!no_bow_epona_fix || (player->stateFlags1 & PLAYER_STATE1_800000) == 0) {
|
||||
restoreHudVisibility = true;
|
||||
}
|
||||
}
|
||||
|
||||
BUTTON_STATUS(EQUIP_SLOT_C_LEFT) = BTN_DISABLED;
|
||||
BUTTON_STATUS(EQUIP_SLOT_C_DOWN) = BTN_DISABLED;
|
||||
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
|
||||
set_extra_item_slot_status(BTN_DISABLED);
|
||||
Interface_SetHudVisibility(HUD_VISIBILITY_A_HEARTS_MAGIC_MINIMAP_WITH_OVERWRITE);
|
||||
// @recomp_use_export_var no_bow_epona_fix: If the B button does not contain the bow, don't disable the UI.
|
||||
if ((!no_bow_epona_fix || BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) == ITEM_BOW) && !H_AND_D_FIX_ACTIVE) {
|
||||
BUTTON_STATUS(EQUIP_SLOT_C_LEFT) = BTN_DISABLED;
|
||||
BUTTON_STATUS(EQUIP_SLOT_C_DOWN) = BTN_DISABLED;
|
||||
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
|
||||
set_extra_item_slot_status(BTN_DISABLED);
|
||||
Interface_SetHudVisibility(HUD_VISIBILITY_A_HEARTS_MAGIC_MINIMAP_WITH_OVERWRITE);
|
||||
}
|
||||
|
||||
if (play->transitionMode != TRANS_MODE_OFF) {
|
||||
Interface_SetHudVisibility(HUD_VISIBILITY_NONE);
|
||||
|
@ -700,8 +769,11 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) {
|
|||
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
|
||||
set_extra_item_slot_status(BTN_DISABLED);
|
||||
Interface_SetHudVisibility(HUD_VISIBILITY_A_B_MINIMAP);
|
||||
} else if (player->stateFlags1 & PLAYER_STATE1_800000) {
|
||||
} else if (ON_EPONA) {
|
||||
Interface_SetHudVisibility(HUD_VISIBILITY_A_B_MINIMAP);
|
||||
} else if (H_AND_D_FIX_ACTIVE) {
|
||||
Interface_LoadItemIconImpl(play, EQUIP_SLOT_B);
|
||||
Interface_SetHudVisibility(HUD_VISIBILITY_B);
|
||||
}
|
||||
}
|
||||
} else if (sPictoState != PICTO_BOX_STATE_OFF) {
|
||||
|
@ -802,6 +874,13 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) {
|
|||
}
|
||||
}
|
||||
|
||||
bool fd_anywhere = false;
|
||||
|
||||
// @recomp_export void recomp_set_fd_anywhere(bool new_val): Set whether the Fierce Deity's Mask has scene restrictions.
|
||||
RECOMP_EXPORT void recomp_set_fd_anywhere(bool new_val) {
|
||||
fd_anywhere = new_val;
|
||||
}
|
||||
|
||||
/**
|
||||
* A continuation of the if-else chain from Interface_UpdateButtonsPart1
|
||||
* Also used directly when opening the pause menu i.e. skips part 1
|
||||
|
@ -1181,7 +1260,8 @@ RECOMP_PATCH void Interface_UpdateButtonsPart2(PlayState* play) {
|
|||
}
|
||||
} else if (GET_CUR_FORM_BTN_ITEM(i) == ITEM_MASK_FIERCE_DEITY) {
|
||||
// Fierce Deity's Mask is equipped
|
||||
if ((play->sceneId != SCENE_MITURIN_BS) && (play->sceneId != SCENE_HAKUGIN_BS) &&
|
||||
// @recomp_use_export_var fd_anywhere: Allow the player to use the Fierce Deity's Mask anywhere if mods enable it.
|
||||
if (!fd_anywhere && (play->sceneId != SCENE_MITURIN_BS) && (play->sceneId != SCENE_HAKUGIN_BS) &&
|
||||
(play->sceneId != SCENE_SEA_BS) && (play->sceneId != SCENE_INISIE_BS) &&
|
||||
(play->sceneId != SCENE_LAST_BS)) {
|
||||
if (BUTTON_STATUS(i) != BTN_DISABLED) {
|
||||
|
|
|
@ -1036,6 +1036,9 @@ RECOMP_PATCH void FileSelect_ConfirmFile(GameState *thisx) {
|
|||
}
|
||||
}
|
||||
|
||||
// Non-relocatable reference to the original address of Play_Init.
|
||||
void Play_Init_NORELOCATE(GameState*);
|
||||
|
||||
/**
|
||||
* Load the save for the appropriate file and start the game.
|
||||
* Update function for `SM_LOAD_GAME`
|
||||
|
@ -1060,7 +1063,7 @@ RECOMP_PATCH void FileSelect_LoadGame(GameState* thisx) {
|
|||
gSaveContext.gameMode = GAMEMODE_NORMAL;
|
||||
|
||||
STOP_GAMESTATE(&this->state);
|
||||
SET_NEXT_GAMESTATE(&this->state, Play_Init, sizeof(PlayState));
|
||||
SET_NEXT_GAMESTATE(&this->state, Play_Init_NORELOCATE, sizeof(PlayState));
|
||||
|
||||
gSaveContext.respawnFlag = 0;
|
||||
gSaveContext.respawn[RESPAWN_MODE_DOWN].entrance = ENTR_LOAD_OPENING;
|
||||
|
|
|
@ -4,6 +4,11 @@
|
|||
#define RECOMP_EXPORT __attribute__((section(".recomp_export")))
|
||||
#define RECOMP_PATCH __attribute__((section(".recomp_patch")))
|
||||
#define RECOMP_FORCE_PATCH __attribute__((section(".recomp_force_patch")))
|
||||
#define RECOMP_DECLARE_EVENT(func) \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wunused-parameter\"") \
|
||||
__attribute__((noinline, weak, used, section(".recomp_event"))) void func {} \
|
||||
_Pragma("GCC diagnostic pop")
|
||||
|
||||
// TODO fix renaming symbols in patch recompilation
|
||||
#define osCreateMesgQueue osCreateMesgQueue_recomp
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
RAMBASE = 0x80801000; /* Used to hold any new symbols */
|
||||
EXTRA_RAM_SIZE = 0x01000000; /* Amount of extra ram allocated by recomp */
|
||||
PATCH_RAM_END = 0x81000000; /* Amount of extra ram allocated by recomp */
|
||||
|
||||
MEMORY {
|
||||
extram : ORIGIN = RAMBASE, LENGTH = 64M
|
||||
|
@ -12,10 +12,11 @@ SECTIONS {
|
|||
.text : { *(.text*) } >extram AT >rom
|
||||
.recomp_patch : { *(.recomp_patch*) *(.recomp_force_patch*) } >extram AT >rom
|
||||
.recomp_export : { *(.recomp_export*) } >extram AT >rom
|
||||
.recomp_event : { *(.recomp_event*) } >extram AT >rom
|
||||
.rodata : { *(.rodata*) } >extram AT >rom
|
||||
.data : { *(.data*) } >extram AT >rom
|
||||
.bss (NOLOAD) : { *(.bss*) *(COMMON) } >extram
|
||||
ASSERT(. < RAMBASE + EXTRA_RAM_SIZE, "Maxed out recomp extra ram")
|
||||
ASSERT(. <= PATCH_RAM_END, "Maxed out recomp extra ram")
|
||||
|
||||
.reloc 0 : { *(.reloc*) }
|
||||
.symtab 0 : { *(.symtab) }
|
||||
|
|
|
@ -1,9 +1,37 @@
|
|||
#include "play_patches.h"
|
||||
#include "z64debug_display.h"
|
||||
#include "input.h"
|
||||
#include "prevent_bss_reordering.h"
|
||||
#include "z64.h"
|
||||
#include "regs.h"
|
||||
#include "functions.h"
|
||||
#include "z64vismono.h"
|
||||
#include "z64visfbuf.h"
|
||||
#include "buffers.h"
|
||||
|
||||
#include "variables.h"
|
||||
#include "macros.h"
|
||||
#include "buffers.h"
|
||||
#include "idle.h"
|
||||
#include "sys_cfb.h"
|
||||
#include "z64bombers_notebook.h"
|
||||
#include "z64quake.h"
|
||||
#include "z64rumble.h"
|
||||
#include "z64shrink_window.h"
|
||||
#include "z64view.h"
|
||||
|
||||
#include "overlays/gamestates/ovl_daytelop/z_daytelop.h"
|
||||
#include "overlays/gamestates/ovl_opening/z_opening.h"
|
||||
#include "overlays/gamestates/ovl_file_choose/z_file_select.h"
|
||||
#include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h"
|
||||
#include "debug.h"
|
||||
|
||||
extern Input D_801F6C18;
|
||||
|
||||
RECOMP_DECLARE_EVENT(recomp_on_play_main(PlayState* play));
|
||||
RECOMP_DECLARE_EVENT(recomp_on_play_update(PlayState* play));
|
||||
RECOMP_DECLARE_EVENT(recomp_after_play_update(PlayState* play));
|
||||
|
||||
void controls_play_update(PlayState* play) {
|
||||
gSaveContext.options.zTargetSetting = recomp_get_targeting_mode();
|
||||
}
|
||||
|
@ -13,6 +41,9 @@ RECOMP_PATCH void Play_Main(GameState* thisx) {
|
|||
static Input* prevInput = NULL;
|
||||
PlayState* this = (PlayState*)thisx;
|
||||
|
||||
// @recomp_event recomp_on_play_main(PlayState* play): Allow mods to execute code every frame.
|
||||
recomp_on_play_main(this);
|
||||
|
||||
// @recomp
|
||||
debug_play_update(this);
|
||||
controls_play_update(this);
|
||||
|
@ -32,7 +63,15 @@ RECOMP_PATCH void Play_Main(GameState* thisx) {
|
|||
this->state.gfxCtx = NULL;
|
||||
}
|
||||
camera_pre_play_update(this);
|
||||
|
||||
// @recomp_event recomp_on_play_update(PlayState* play): Play_Update is about to be called.
|
||||
recomp_on_play_update(this);
|
||||
|
||||
Play_Update(this);
|
||||
|
||||
// @recomp_event recomp_after_play_update(PlayState* play): Play_Update was called.
|
||||
recomp_after_play_update(this);
|
||||
|
||||
camera_post_play_update(this);
|
||||
analog_cam_post_play_update(this);
|
||||
autosave_post_play_update(this);
|
||||
|
@ -83,3 +122,322 @@ RECOMP_PATCH s32 Room_HandleLoadCallbacks(PlayState* play, RoomContext* roomCtx)
|
|||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ZeldaArena_Init(void* start, size_t size);
|
||||
|
||||
void Play_SpawnScene(PlayState* this, s32 sceneId, s32 spawn);
|
||||
void Play_InitMotionBlur(void);
|
||||
|
||||
extern s16 sTransitionFillTimer;
|
||||
extern Input D_801F6C18;
|
||||
extern TransitionTile sTransitionTile;
|
||||
extern s32 gTransitionTileState;
|
||||
extern VisMono sPlayVisMono;
|
||||
extern Color_RGBA8_u32 gVisMonoColor;
|
||||
extern VisFbuf sPlayVisFbuf;
|
||||
extern VisFbuf* sPlayVisFbufInstance;
|
||||
extern BombersNotebook sBombersNotebook;
|
||||
extern u8 sBombersNotebookOpen;
|
||||
extern u8 sMotionBlurStatus;
|
||||
|
||||
extern s32 gDbgCamEnabled;
|
||||
extern u8 D_801D0D54;
|
||||
|
||||
extern u8 gPictoPhotoI8[];
|
||||
extern u8 D_80784600[];
|
||||
|
||||
// Non-relocatable references to the original addresses of these game state functions.
|
||||
void DayTelop_Init_NORELOCATE(GameState*);
|
||||
void TitleSetup_Init_NORELOCATE(GameState*);
|
||||
|
||||
RECOMP_DECLARE_EVENT(recomp_on_play_init(PlayState* this));
|
||||
RECOMP_DECLARE_EVENT(recomp_after_play_init(PlayState* this));
|
||||
|
||||
bool allow_no_ocarina_tf = false;
|
||||
|
||||
// @recomp_export void recomp_set_allow_no_ocarina_tf(bool new_val): Set whether to force Termina Field to load normally even if Link has no ocarina.
|
||||
RECOMP_EXPORT void recomp_set_allow_no_ocarina_tf(bool new_val) {
|
||||
allow_no_ocarina_tf = new_val;
|
||||
}
|
||||
|
||||
RECOMP_PATCH void Play_Init(GameState* thisx) {
|
||||
PlayState* this = (PlayState*)thisx;
|
||||
GraphicsContext* gfxCtx = this->state.gfxCtx;
|
||||
s32 pad;
|
||||
uintptr_t zAlloc;
|
||||
s32 zAllocSize;
|
||||
Player* player;
|
||||
s32 i;
|
||||
s32 spawn;
|
||||
u8 sceneLayer;
|
||||
s32 scene;
|
||||
|
||||
// @recomp_event recomp_on_play_init(PlayState* this): A new PlayState is being initialized.
|
||||
recomp_on_play_init(this);
|
||||
|
||||
if ((gSaveContext.respawnFlag == -4) || (gSaveContext.respawnFlag == -0x63)) {
|
||||
if (CHECK_EVENTINF(EVENTINF_TRIGGER_DAYTELOP)) {
|
||||
CLEAR_EVENTINF(EVENTINF_TRIGGER_DAYTELOP);
|
||||
STOP_GAMESTATE(&this->state);
|
||||
// Use non-relocatable reference to DayTelop_Init instead.
|
||||
SET_NEXT_GAMESTATE(&this->state, DayTelop_Init_NORELOCATE, sizeof(DayTelopState));
|
||||
return;
|
||||
}
|
||||
|
||||
gSaveContext.unk_3CA7 = 1;
|
||||
if (gSaveContext.respawnFlag == -0x63) {
|
||||
gSaveContext.respawnFlag = 2;
|
||||
}
|
||||
} else {
|
||||
gSaveContext.unk_3CA7 = 0;
|
||||
}
|
||||
|
||||
if (gSaveContext.save.entrance == -1) {
|
||||
gSaveContext.save.entrance = 0;
|
||||
STOP_GAMESTATE(&this->state);
|
||||
// Use non-relocatable reference to TitleSetup_Init instead.
|
||||
SET_NEXT_GAMESTATE(&this->state, TitleSetup_Init_NORELOCATE, sizeof(TitleSetupState));
|
||||
return;
|
||||
}
|
||||
|
||||
if ((gSaveContext.nextCutsceneIndex == 0xFFEF) || (gSaveContext.nextCutsceneIndex == 0xFFF0)) {
|
||||
scene = ((void)0, gSaveContext.save.entrance) >> 9;
|
||||
spawn = (((void)0, gSaveContext.save.entrance) >> 4) & 0x1F;
|
||||
|
||||
if (CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_SNOWHEAD_TEMPLE)) {
|
||||
if (scene == ENTR_SCENE_MOUNTAIN_VILLAGE_WINTER) {
|
||||
scene = ENTR_SCENE_MOUNTAIN_VILLAGE_SPRING;
|
||||
} else if (scene == ENTR_SCENE_GORON_VILLAGE_WINTER) {
|
||||
scene = ENTR_SCENE_GORON_VILLAGE_SPRING;
|
||||
} else if (scene == ENTR_SCENE_PATH_TO_GORON_VILLAGE_WINTER) {
|
||||
scene = ENTR_SCENE_PATH_TO_GORON_VILLAGE_SPRING;
|
||||
} else if ((scene == ENTR_SCENE_SNOWHEAD) || (scene == ENTR_SCENE_PATH_TO_SNOWHEAD) ||
|
||||
(scene == ENTR_SCENE_PATH_TO_MOUNTAIN_VILLAGE) || (scene == ENTR_SCENE_GORON_SHRINE) ||
|
||||
(scene == ENTR_SCENE_GORON_RACETRACK)) {
|
||||
gSaveContext.nextCutsceneIndex = 0xFFF0;
|
||||
}
|
||||
}
|
||||
|
||||
if (CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_WOODFALL_TEMPLE)) {
|
||||
if (scene == ENTR_SCENE_SOUTHERN_SWAMP_POISONED) {
|
||||
scene = ENTR_SCENE_SOUTHERN_SWAMP_CLEARED;
|
||||
} else if (scene == ENTR_SCENE_WOODFALL) {
|
||||
gSaveContext.nextCutsceneIndex = 0xFFF1;
|
||||
}
|
||||
}
|
||||
|
||||
if (CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_STONE_TOWER_TEMPLE) && (scene == ENTR_SCENE_IKANA_CANYON)) {
|
||||
gSaveContext.nextCutsceneIndex = 0xFFF2;
|
||||
}
|
||||
|
||||
if (CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_GREAT_BAY_TEMPLE) &&
|
||||
((scene == ENTR_SCENE_GREAT_BAY_COAST) || (scene == ENTR_SCENE_ZORA_CAPE))) {
|
||||
gSaveContext.nextCutsceneIndex = 0xFFF0;
|
||||
}
|
||||
|
||||
// "First cycle" Termina Field
|
||||
// @recomp_use_export_var allow_no_ocarina_tf: Skip loading into "First cycle" Termina Field if mods enable it.
|
||||
if (!allow_no_ocarina_tf && INV_CONTENT(ITEM_OCARINA_OF_TIME) != ITEM_OCARINA_OF_TIME) {
|
||||
if ((scene == ENTR_SCENE_TERMINA_FIELD) &&
|
||||
(((void)0, gSaveContext.save.entrance) != ENTRANCE(TERMINA_FIELD, 10))) {
|
||||
gSaveContext.nextCutsceneIndex = 0xFFF4;
|
||||
}
|
||||
}
|
||||
//! FAKE:
|
||||
gSaveContext.save.entrance =
|
||||
Entrance_Create(((void)0, scene), spawn, ((void)0, gSaveContext.save.entrance) & 0xF);
|
||||
}
|
||||
|
||||
GameState_Realloc(&this->state, 0);
|
||||
KaleidoManager_Init(this);
|
||||
ShrinkWindow_Init();
|
||||
View_Init(&this->view, gfxCtx);
|
||||
Audio_SetExtraFilter(0);
|
||||
Quake_Init();
|
||||
Distortion_Init(this);
|
||||
|
||||
for (i = 0; i < ARRAY_COUNT(this->cameraPtrs); i++) {
|
||||
this->cameraPtrs[i] = NULL;
|
||||
}
|
||||
|
||||
Camera_Init(&this->mainCamera, &this->view, &this->colCtx, this);
|
||||
Camera_ChangeStatus(&this->mainCamera, CAM_STATUS_ACTIVE);
|
||||
|
||||
for (i = 0; i < ARRAY_COUNT(this->subCameras); i++) {
|
||||
Camera_Init(&this->subCameras[i], &this->view, &this->colCtx, this);
|
||||
Camera_ChangeStatus(&this->subCameras[i], CAM_STATUS_INACTIVE);
|
||||
}
|
||||
|
||||
this->cameraPtrs[CAM_ID_MAIN] = &this->mainCamera;
|
||||
this->cameraPtrs[CAM_ID_MAIN]->uid = CAM_ID_MAIN;
|
||||
this->activeCamId = CAM_ID_MAIN;
|
||||
|
||||
Camera_OverwriteStateFlags(&this->mainCamera, CAM_STATE_0 | CAM_STATE_CHECK_WATER | CAM_STATE_2 | CAM_STATE_3 |
|
||||
CAM_STATE_4 | CAM_STATE_DISABLE_MODE_CHANGE | CAM_STATE_6);
|
||||
Sram_Alloc(&this->state, &this->sramCtx);
|
||||
Regs_InitData(this);
|
||||
Message_Init(this);
|
||||
GameOver_Init(this);
|
||||
SoundSource_InitAll(this);
|
||||
EffFootmark_Init(this);
|
||||
Effect_Init(this);
|
||||
EffectSS_Init(this, 100);
|
||||
CollisionCheck_InitContext(this, &this->colChkCtx);
|
||||
AnimationContext_Reset(&this->animationCtx);
|
||||
Cutscene_InitContext(this, &this->csCtx);
|
||||
|
||||
if (gSaveContext.nextCutsceneIndex != 0xFFEF) {
|
||||
gSaveContext.save.cutsceneIndex = gSaveContext.nextCutsceneIndex;
|
||||
gSaveContext.nextCutsceneIndex = 0xFFEF;
|
||||
}
|
||||
|
||||
if (gSaveContext.save.cutsceneIndex == 0xFFFD) {
|
||||
gSaveContext.save.cutsceneIndex = 0;
|
||||
}
|
||||
|
||||
if (gSaveContext.nextDayTime != NEXT_TIME_NONE) {
|
||||
gSaveContext.save.time = gSaveContext.nextDayTime;
|
||||
gSaveContext.skyboxTime = gSaveContext.nextDayTime;
|
||||
}
|
||||
|
||||
if ((CURRENT_TIME >= CLOCK_TIME(18, 0)) || (CURRENT_TIME < CLOCK_TIME(6, 30))) {
|
||||
gSaveContext.save.isNight = true;
|
||||
} else {
|
||||
gSaveContext.save.isNight = false;
|
||||
}
|
||||
|
||||
func_800EDDB0(this);
|
||||
|
||||
if (((gSaveContext.gameMode != GAMEMODE_NORMAL) && (gSaveContext.gameMode != GAMEMODE_TITLE_SCREEN)) ||
|
||||
(gSaveContext.save.cutsceneIndex >= 0xFFF0)) {
|
||||
gSaveContext.nayrusLoveTimer = 0;
|
||||
Magic_Reset(this);
|
||||
gSaveContext.sceneLayer = (gSaveContext.save.cutsceneIndex & 0xF) + 1;
|
||||
|
||||
// Set saved cutscene to 0 so it doesn't immediately play, but instead let the `CutsceneManager` handle it.
|
||||
gSaveContext.save.cutsceneIndex = 0;
|
||||
} else {
|
||||
gSaveContext.sceneLayer = 0;
|
||||
}
|
||||
|
||||
sceneLayer = gSaveContext.sceneLayer;
|
||||
|
||||
Play_SpawnScene(
|
||||
this, Entrance_GetSceneIdAbsolute(((void)0, gSaveContext.save.entrance) + ((void)0, gSaveContext.sceneLayer)),
|
||||
Entrance_GetSpawnNum(((void)0, gSaveContext.save.entrance) + ((void)0, gSaveContext.sceneLayer)));
|
||||
KaleidoScopeCall_Init(this);
|
||||
Interface_Init(this);
|
||||
|
||||
if (gSaveContext.nextDayTime != NEXT_TIME_NONE) {
|
||||
if (gSaveContext.nextDayTime == NEXT_TIME_DAY) {
|
||||
gSaveContext.save.day++;
|
||||
gSaveContext.save.eventDayCount++;
|
||||
gSaveContext.dogIsLost = true;
|
||||
gSaveContext.nextDayTime = NEXT_TIME_DAY_SET;
|
||||
} else {
|
||||
gSaveContext.nextDayTime = NEXT_TIME_NIGHT_SET;
|
||||
}
|
||||
}
|
||||
|
||||
Play_InitMotionBlur();
|
||||
|
||||
R_PAUSE_BG_PRERENDER_STATE = PAUSE_BG_PRERENDER_OFF;
|
||||
R_PICTO_PHOTO_STATE = PICTO_PHOTO_STATE_OFF;
|
||||
|
||||
PreRender_Init(&this->pauseBgPreRender);
|
||||
PreRender_SetValuesSave(&this->pauseBgPreRender, gCfbWidth, gCfbHeight, NULL, NULL, NULL);
|
||||
PreRender_SetValues(&this->pauseBgPreRender, gCfbWidth, gCfbHeight, NULL, NULL);
|
||||
|
||||
this->unk_18E64 = gWorkBuffer;
|
||||
this->pictoPhotoI8 = gPictoPhotoI8;
|
||||
this->unk_18E68 = D_80784600;
|
||||
this->unk_18E58 = D_80784600;
|
||||
this->unk_18E60 = D_80784600;
|
||||
gTransitionTileState = TRANS_TILE_OFF;
|
||||
this->transitionMode = TRANS_MODE_OFF;
|
||||
D_801D0D54 = false;
|
||||
|
||||
FrameAdvance_Init(&this->frameAdvCtx);
|
||||
Rand_Seed(osGetTime());
|
||||
Matrix_Init(&this->state);
|
||||
|
||||
this->state.main = Play_Main;
|
||||
this->state.destroy = Play_Destroy;
|
||||
|
||||
this->transitionTrigger = TRANS_TRIGGER_END;
|
||||
this->worldCoverAlpha = 0;
|
||||
this->bgCoverAlpha = 0;
|
||||
this->haltAllActors = false;
|
||||
this->unk_18844 = false;
|
||||
|
||||
if (gSaveContext.gameMode != GAMEMODE_TITLE_SCREEN) {
|
||||
if (gSaveContext.nextTransitionType == TRANS_NEXT_TYPE_DEFAULT) {
|
||||
this->transitionType =
|
||||
(Entrance_GetTransitionFlags(((void)0, gSaveContext.save.entrance) + sceneLayer) >> 7) & 0x7F;
|
||||
} else {
|
||||
this->transitionType = gSaveContext.nextTransitionType;
|
||||
gSaveContext.nextTransitionType = TRANS_NEXT_TYPE_DEFAULT;
|
||||
}
|
||||
} else {
|
||||
this->transitionType = TRANS_TYPE_FADE_BLACK;
|
||||
}
|
||||
|
||||
TransitionFade_Init(&this->unk_18E48);
|
||||
TransitionFade_SetType(&this->unk_18E48, 3);
|
||||
TransitionFade_SetColor(&this->unk_18E48, RGBA8(160, 160, 160, 255));
|
||||
TransitionFade_Start(&this->unk_18E48);
|
||||
VisMono_Init(&sPlayVisMono);
|
||||
|
||||
gVisMonoColor.a = 0;
|
||||
sPlayVisFbufInstance = &sPlayVisFbuf;
|
||||
VisFbuf_Init(sPlayVisFbufInstance);
|
||||
sPlayVisFbufInstance->lodProportion = 0.0f;
|
||||
sPlayVisFbufInstance->mode = VIS_FBUF_MODE_GENERAL;
|
||||
sPlayVisFbufInstance->primColor.r = 0;
|
||||
sPlayVisFbufInstance->primColor.g = 0;
|
||||
sPlayVisFbufInstance->primColor.b = 0;
|
||||
sPlayVisFbufInstance->primColor.a = 0;
|
||||
sPlayVisFbufInstance->envColor.r = 0;
|
||||
sPlayVisFbufInstance->envColor.g = 0;
|
||||
sPlayVisFbufInstance->envColor.b = 0;
|
||||
sPlayVisFbufInstance->envColor.a = 0;
|
||||
CutsceneFlags_UnsetAll(this);
|
||||
THA_GetRemaining(&this->state.tha);
|
||||
zAllocSize = THA_GetRemaining(&this->state.tha);
|
||||
zAlloc = (uintptr_t)THA_AllocTailAlign16(&this->state.tha, zAllocSize);
|
||||
|
||||
//! @bug: Incorrect ALIGN16s
|
||||
ZeldaArena_Init((void*)((zAlloc + 8) & ~0xF), (zAllocSize - ((zAlloc + 8) & ~0xF)) + zAlloc);
|
||||
|
||||
Actor_InitContext(this, &this->actorCtx, this->linkActorEntry);
|
||||
|
||||
while (!Room_HandleLoadCallbacks(this, &this->roomCtx)) {}
|
||||
|
||||
if ((CURRENT_DAY != 0) && ((this->roomCtx.curRoom.behaviorType1 == ROOM_BEHAVIOR_TYPE1_1) ||
|
||||
(this->roomCtx.curRoom.behaviorType1 == ROOM_BEHAVIOR_TYPE1_5))) {
|
||||
Actor_Spawn(&this->actorCtx, this, ACTOR_EN_TEST4, 0.0f, 0.0f, 0.0f, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
player = GET_PLAYER(this);
|
||||
|
||||
Camera_InitFocalActorSettings(&this->mainCamera, &player->actor);
|
||||
gDbgCamEnabled = false;
|
||||
|
||||
if (PLAYER_GET_BG_CAM_INDEX(&player->actor) != 0xFF) {
|
||||
Camera_ChangeActorCsCamIndex(&this->mainCamera, PLAYER_GET_BG_CAM_INDEX(&player->actor));
|
||||
}
|
||||
|
||||
CutsceneManager_StoreCamera(&this->mainCamera);
|
||||
Interface_SetSceneRestrictions(this);
|
||||
Environment_PlaySceneSequence(this);
|
||||
gSaveContext.seqId = this->sequenceCtx.seqId;
|
||||
gSaveContext.ambienceId = this->sequenceCtx.ambienceId;
|
||||
AnimationContext_Update(this, &this->animationCtx);
|
||||
Cutscene_HandleEntranceTriggers(this);
|
||||
gSaveContext.respawnFlag = 0;
|
||||
sBombersNotebookOpen = false;
|
||||
BombersNotebook_Init(&sBombersNotebook);
|
||||
|
||||
// @recomp_event recomp_after_play_init(PlayState* this): The new PlayState has finished initializing.
|
||||
recomp_after_play_init(this);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ void* proutPrintf(void* dst, const char* fmt, size_t size) {
|
|||
return (void*)1;
|
||||
}
|
||||
|
||||
int recomp_printf(const char* fmt, ...) {
|
||||
RECOMP_EXPORT int recomp_printf(const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ void Main_InitMemory(void);
|
|||
void Main_InitScreen(void);
|
||||
|
||||
|
||||
RECOMP_DECLARE_EVENT(recomp_on_init());
|
||||
|
||||
// @recomp Patched to load the code segment in the recomp runtime.
|
||||
RECOMP_PATCH void Main_Init(void) {
|
||||
DmaRequest dmaReq;
|
||||
|
@ -14,6 +16,9 @@ RECOMP_PATCH void Main_Init(void) {
|
|||
OSMesg msg[1];
|
||||
size_t prevSize;
|
||||
|
||||
// @recomp_event recomp_on_init(): Allow mods to initialize themselves once.
|
||||
recomp_on_init();
|
||||
|
||||
osCreateMesgQueue(&mq, msg, ARRAY_COUNT(msg));
|
||||
|
||||
prevSize = gDmaMgrDmaBuffSize;
|
||||
|
|
|
@ -1266,7 +1266,7 @@ extern Gfx ovl_En_Tanron1_DL_001888[];
|
|||
extern Gfx ovl_En_Tanron1_DL_001900[];
|
||||
|
||||
// @recomp Patched to interpolate the moths that circle torches.
|
||||
void func_80BB5AAC(EnTanron1* this, PlayState* play) {
|
||||
RECOMP_PATCH void func_80BB5AAC(EnTanron1* this, PlayState* play) {
|
||||
EnTanron1Struct* ptrBase = &this->unk_160[0];
|
||||
s16 i;
|
||||
u8 flag = 0;
|
||||
|
|
|
@ -56,9 +56,10 @@ RECOMP_PATCH void Graph_SetNextGfxPool(GraphicsContext* gfxCtx) {
|
|||
gSPEndDisplayList(&gGfxMasterDL->disps[4]);
|
||||
gSPBranchList(&gGfxMasterDL->debugDisp[0], bigger_pool->debugBuffer);
|
||||
|
||||
// @recomp Enable RT64 extended GBI mode and set the current framerate
|
||||
// @recomp Enable RT64 extended GBI mode and extended rdram.
|
||||
OPEN_DISPS(gfxCtx);
|
||||
gEXEnable(POLY_OPA_DISP++);
|
||||
gEXSetRDRAMExtended(POLY_OPA_DISP++, 1);
|
||||
CLOSE_DISPS(gfxCtx);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,13 +13,14 @@
|
|||
#elif defined(__linux__)
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include "common/rt64_apple.h"
|
||||
#endif
|
||||
|
||||
constexpr std::u8string_view general_filename = u8"general.json";
|
||||
constexpr std::u8string_view graphics_filename = u8"graphics.json";
|
||||
constexpr std::u8string_view controls_filename = u8"controls.json";
|
||||
constexpr std::u8string_view sound_filename = u8"sound.json";
|
||||
constexpr std::u8string_view program_id = u8"Zelda64Recompiled";
|
||||
|
||||
constexpr auto res_default = ultramodern::renderer::Resolution::Auto;
|
||||
constexpr auto hr_default = ultramodern::renderer::HUDRatioMode::Clamp16x9;
|
||||
|
@ -146,8 +147,8 @@ std::filesystem::path zelda64::get_app_folder_path() {
|
|||
}
|
||||
|
||||
CoTaskMemFree(known_path);
|
||||
#elif defined(__linux__)
|
||||
// check for APP_FOLDER_PATH env var used by AppImage
|
||||
#elif defined(__linux__) || defined(__APPLE__)
|
||||
// check for APP_FOLDER_PATH env var
|
||||
if (getenv("APP_FOLDER_PATH") != nullptr) {
|
||||
return std::filesystem::path{getenv("APP_FOLDER_PATH")};
|
||||
}
|
||||
|
@ -155,7 +156,11 @@ std::filesystem::path zelda64::get_app_folder_path() {
|
|||
const char *homedir;
|
||||
|
||||
if ((homedir = getenv("HOME")) == nullptr) {
|
||||
#if defined(__linux__)
|
||||
homedir = getpwuid(getuid())->pw_dir;
|
||||
#elif defined(__APPLE__)
|
||||
homedir = GetHomeDirectory();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (homedir != nullptr) {
|
||||
|
|
|
@ -25,8 +25,10 @@
|
|||
#include "zelda_config.h"
|
||||
#include "zelda_sound.h"
|
||||
#include "zelda_render.h"
|
||||
#include "zelda_support.h"
|
||||
#include "ovl_patches.hpp"
|
||||
#include "librecomp/game.hpp"
|
||||
#include "librecomp/mods.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
@ -36,12 +38,14 @@
|
|||
|
||||
#include "../../lib/rt64/src/contrib/stb/stb_image.h"
|
||||
|
||||
const std::string version_string = "1.2.0-dev";
|
||||
|
||||
template<typename... Ts>
|
||||
void exit_error(const char* str, Ts ...args) {
|
||||
// TODO pop up an error
|
||||
((void)fprintf(stderr, str, args), ...);
|
||||
assert(false);
|
||||
std::quick_exit(EXIT_FAILURE);
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
}
|
||||
|
||||
ultramodern::gfx_callbacks_t::gfx_data_t create_gfx() {
|
||||
|
@ -115,7 +119,12 @@ bool SetImageAsIcon(const char* filename, SDL_Window* window)
|
|||
SDL_Window* window;
|
||||
|
||||
ultramodern::renderer::WindowHandle create_window(ultramodern::gfx_callbacks_t::gfx_data_t) {
|
||||
window = SDL_CreateWindow("Zelda 64: Recompiled", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1600, 960, SDL_WINDOW_RESIZABLE );
|
||||
uint32_t window_flags = SDL_WINDOW_RESIZABLE;
|
||||
#ifdef __APPLE__
|
||||
window_flags |= SDL_WINDOW_METAL;
|
||||
#endif
|
||||
|
||||
window = SDL_CreateWindow("Zelda 64: Recompiled", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1600, 960, window_flags );
|
||||
#if defined(__linux__)
|
||||
SetImageAsIcon("icons/512.png",window);
|
||||
if (ultramodern::renderer::get_graphics_config().wm_option == ultramodern::renderer::WindowMode::Fullscreen) { // TODO: Remove once RT64 gets native fullscreen support on Linux
|
||||
|
@ -143,6 +152,9 @@ ultramodern::renderer::WindowHandle create_window(ultramodern::gfx_callbacks_t::
|
|||
}
|
||||
|
||||
return ultramodern::renderer::WindowHandle{ wmInfo.info.x11.display, wmInfo.info.x11.window };
|
||||
#elif defined(__APPLE__)
|
||||
SDL_MetalView view = SDL_Metal_CreateView(window);
|
||||
return ultramodern::renderer::WindowHandle{ wmInfo.info.cocoa.window, SDL_Metal_GetLayer(view) };
|
||||
#else
|
||||
static_assert(false && "Unimplemented");
|
||||
#endif
|
||||
|
@ -327,6 +339,7 @@ std::vector<recomp::GameEntry> supported_games = {
|
|||
.rom_hash = 0xEF18B4A9E2386169ULL,
|
||||
.internal_name = "ZELDA MAJORA'S MASK",
|
||||
.game_id = u8"mm.n64.us.1.0",
|
||||
.mod_game_id = "mm",
|
||||
.is_enabled = true,
|
||||
.entrypoint_address = get_entrypoint_address(),
|
||||
.entrypoint = recomp_entrypoint,
|
||||
|
@ -524,7 +537,23 @@ void release_preload(PreloadContext& context) {
|
|||
|
||||
#endif
|
||||
|
||||
void enable_texture_pack(recomp::mods::ModContext& context, const recomp::mods::ModHandle& mod) {
|
||||
(void)context;
|
||||
zelda64::renderer::enable_texture_pack(mod);
|
||||
}
|
||||
|
||||
void disable_texture_pack(recomp::mods::ModContext& context, const recomp::mods::ModHandle& mod) {
|
||||
(void)context;
|
||||
zelda64::renderer::disable_texture_pack(mod);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
recomp::Version project_version{};
|
||||
if (!recomp::Version::from_string(version_string, project_version)) {
|
||||
ultramodern::error_handling::message_box(("Invalid version string: " + version_string).c_str());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Map this executable into memory and lock it, which should keep it in physical memory. This ensures
|
||||
// that there are no stutters from the OS having to load new pages of the executable whenever a new code page is run.
|
||||
PreloadContext preload_context;
|
||||
|
@ -563,11 +592,18 @@ int main(int argc, char** argv) {
|
|||
SDL_InitSubSystem(SDL_INIT_AUDIO);
|
||||
reset_audio(48000);
|
||||
|
||||
// Source controller mappings file
|
||||
if (SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt") < 0) {
|
||||
#if defined(__APPLE__)
|
||||
const char* resource_directory = zelda64::get_bundle_resource_directory();
|
||||
std::string mapping_file_path = std::string(resource_directory) + "gamecontrollerdb.txt";
|
||||
#else
|
||||
std::string mapping_file_path = "gamecontrollerdb.txt";
|
||||
#endif
|
||||
if (SDL_GameControllerAddMappingsFromFile(mapping_file_path.c_str()) < 0) {
|
||||
fprintf(stderr, "Failed to load controller mappings: %s\n", SDL_GetError());
|
||||
}
|
||||
|
||||
recomp::register_config_path(zelda64::get_app_folder_path());
|
||||
|
||||
// Register supported games and patches
|
||||
for (const auto& game : supported_games) {
|
||||
recomp::register_game(game);
|
||||
|
@ -575,8 +611,6 @@ int main(int argc, char** argv) {
|
|||
|
||||
zelda64::register_overlays();
|
||||
zelda64::register_patches();
|
||||
|
||||
recomp::register_config_path(zelda64::get_app_folder_path());
|
||||
zelda64::load_config();
|
||||
|
||||
recomp::rsp::callbacks_t rsp_callbacks{
|
||||
|
@ -619,7 +653,48 @@ int main(int argc, char** argv) {
|
|||
.get_game_thread_name = zelda64::get_game_thread_name,
|
||||
};
|
||||
|
||||
// Register the texture pack content type with rt64.json as its content file.
|
||||
recomp::mods::ModContentType texture_pack_content_type{
|
||||
.content_filename = "rt64.json",
|
||||
.allow_runtime_toggle = true,
|
||||
.on_enabled = enable_texture_pack,
|
||||
.on_disabled = disable_texture_pack,
|
||||
};
|
||||
auto texture_pack_content_type_id = recomp::mods::register_mod_content_type(texture_pack_content_type);
|
||||
|
||||
// Register the .rtz texture pack file format with the previous content type as its only allowed content type.
|
||||
recomp::mods::register_mod_container_type("rtz", std::vector{ texture_pack_content_type_id }, false);
|
||||
|
||||
recomp::mods::scan_mods();
|
||||
|
||||
printf("Found mods:\n");
|
||||
for (const auto& mod : recomp::mods::get_mod_details("mm")) {
|
||||
printf(" %s(%s)\n", mod.mod_id.c_str(), mod.version.to_string().c_str());
|
||||
if (!mod.authors.empty()) {
|
||||
printf(" Authors: %s", mod.authors[0].c_str());
|
||||
for (size_t author_index = 1; author_index < mod.authors.size(); author_index++) {
|
||||
const std::string& author = mod.authors[author_index];
|
||||
printf(", %s", author.c_str());
|
||||
}
|
||||
printf("\n");
|
||||
printf(" Runtime toggleable: %d\n", mod.runtime_toggleable);
|
||||
}
|
||||
if (!mod.dependencies.empty()) {
|
||||
printf(" Dependencies: %s:%s", mod.dependencies[0].mod_id.c_str(), mod.dependencies[0].version.to_string().c_str());
|
||||
for (size_t dep_index = 1; dep_index < mod.dependencies.size(); dep_index++) {
|
||||
const recomp::mods::Dependency& dep = mod.dependencies[dep_index];
|
||||
printf(", %s:%s", dep.mod_id.c_str(), dep.version.to_string().c_str());
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
// TODO load all mods as a temporary solution to not having a UI yet.
|
||||
recomp::mods::enable_mod(mod.mod_id, true);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
recomp::start(
|
||||
64 * 1024 * 1024, // 64MB to have plenty of room for loading mods
|
||||
project_version,
|
||||
{},
|
||||
rsp_callbacks,
|
||||
renderer_callbacks,
|
||||
|
|
|
@ -7,4 +7,6 @@
|
|||
|
||||
void zelda64::register_patches() {
|
||||
recomp::overlays::register_patches(mm_patches_bin, sizeof(mm_patches_bin), section_table, ARRLEN(section_table));
|
||||
recomp::overlays::register_base_exports(export_table);
|
||||
recomp::overlays::register_base_events(event_names);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <memory>
|
||||
#include <cstring>
|
||||
#include <variant>
|
||||
|
||||
#define HLSL_CPU
|
||||
#include "hle/rt64_application.h"
|
||||
|
@ -10,6 +11,13 @@
|
|||
|
||||
#include "zelda_render.h"
|
||||
#include "recomp_ui.h"
|
||||
#include "concurrentqueue.h"
|
||||
|
||||
// Helper class for variant visiting.
|
||||
template<class... Ts>
|
||||
struct overloaded : Ts... { using Ts::operator()...; };
|
||||
template<class... Ts>
|
||||
overloaded(Ts...) -> overloaded<Ts...>;
|
||||
|
||||
static RT64::UserConfiguration::Antialiasing device_max_msaa = RT64::UserConfiguration::Antialiasing::None;
|
||||
static bool sample_positions_supported = false;
|
||||
|
@ -18,6 +26,18 @@ static bool high_precision_fb_enabled = false;
|
|||
static uint8_t DMEM[0x1000];
|
||||
static uint8_t IMEM[0x1000];
|
||||
|
||||
struct TexturePackEnableAction {
|
||||
std::filesystem::path path;
|
||||
};
|
||||
|
||||
struct TexturePackDisableAction {
|
||||
std::filesystem::path path;
|
||||
};
|
||||
|
||||
using TexturePackAction = std::variant<TexturePackEnableAction, TexturePackDisableAction>;
|
||||
|
||||
static moodycamel::ConcurrentQueue<TexturePackAction> texture_pack_action_queue;
|
||||
|
||||
unsigned int MI_INTR_REG = 0;
|
||||
|
||||
unsigned int DPC_START_REG = 0;
|
||||
|
@ -187,7 +207,7 @@ zelda64::renderer::RT64Context::RT64Context(uint8_t* rdram, ultramodern::rendere
|
|||
appCore.window.window = window_handle.window;
|
||||
#elif defined(__APPLE__)
|
||||
appCore.window.window = window_handle.window;
|
||||
appCore.window.view = window_handle.view;
|
||||
appCore.window.layer = window_handle.view;
|
||||
#endif
|
||||
|
||||
appCore.checkInterrupts = dummy_check_interrupts;
|
||||
|
@ -286,6 +306,32 @@ zelda64::renderer::RT64Context::RT64Context(uint8_t* rdram, ultramodern::rendere
|
|||
zelda64::renderer::RT64Context::~RT64Context() = default;
|
||||
|
||||
void zelda64::renderer::RT64Context::send_dl(const OSTask* task) {
|
||||
bool packs_disabled = false;
|
||||
TexturePackAction cur_action;
|
||||
while (texture_pack_action_queue.try_dequeue(cur_action)) {
|
||||
std::visit(overloaded{
|
||||
[&](TexturePackDisableAction& to_disable) {
|
||||
enabled_texture_packs.erase(to_disable.path);
|
||||
packs_disabled = true;
|
||||
},
|
||||
[&](TexturePackEnableAction& to_enable) {
|
||||
enabled_texture_packs.insert(to_enable.path);
|
||||
// Load the pack now if no packs have been disabled.
|
||||
if (!packs_disabled) {
|
||||
app->textureCache->loadReplacementDirectory(to_enable.path);
|
||||
}
|
||||
}
|
||||
}, cur_action);
|
||||
}
|
||||
|
||||
// If any packs were disabled, unload all packs and load all the active ones.
|
||||
if (packs_disabled) {
|
||||
app->textureCache->clearReplacementDirectories();
|
||||
for (const std::filesystem::path& cur_pack_path : enabled_texture_packs) {
|
||||
app->textureCache->loadReplacementDirectory(cur_pack_path);
|
||||
}
|
||||
}
|
||||
|
||||
app->state->rsp->reset();
|
||||
app->interpreter->loadUCodeGBI(task->t.ucode & 0x3FFFFFF, task->t.ucode_data & 0x3FFFFFF, true);
|
||||
app->processDisplayLists(app->core.RDRAM, task->t.data_ptr & 0x3FFFFFF, 0, true);
|
||||
|
@ -376,3 +422,11 @@ bool zelda64::renderer::RT64SamplePositionsSupported() {
|
|||
bool zelda64::renderer::RT64HighPrecisionFBEnabled() {
|
||||
return high_precision_fb_enabled;
|
||||
}
|
||||
|
||||
void zelda64::renderer::enable_texture_pack(const recomp::mods::ModHandle& mod) {
|
||||
texture_pack_action_queue.enqueue(TexturePackEnableAction{mod.manifest.mod_root_path});
|
||||
}
|
||||
|
||||
void zelda64::renderer::disable_texture_pack(const recomp::mods::ModHandle& mod) {
|
||||
texture_pack_action_queue.enqueue(TexturePackDisableAction{mod.manifest.mod_root_path});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef __APPLE__
|
||||
|
||||
#include "zelda_support.h"
|
||||
#include <functional>
|
||||
|
||||
void zelda64::dispatch_on_main_thread(std::function<void()> func) {
|
||||
func();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,13 @@
|
|||
#include "zelda_support.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
void zelda64::dispatch_on_main_thread(std::function<void()> func) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
func();
|
||||
});
|
||||
}
|
||||
|
||||
const char* zelda64::get_bundle_resource_directory() {
|
||||
NSString *bundlePath = [[NSBundle mainBundle] resourcePath];
|
||||
return strdup([bundlePath UTF8String]);
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
#include "recomp_input.h"
|
||||
#include "zelda_sound.h"
|
||||
#include "zelda_config.h"
|
||||
#include "zelda_support.h"
|
||||
#include "zelda_debug.h"
|
||||
#include "zelda_render.h"
|
||||
#include "promptfont.h"
|
||||
|
@ -519,6 +520,11 @@ public:
|
|||
|
||||
}
|
||||
Rml::ElementDocument* load_document(Rml::Context* context) override {
|
||||
#if defined(__APPLE__)
|
||||
const Rml::String asset = "/assets/config_menu.rml";
|
||||
return context->LoadDocument(zelda64::get_bundle_resource_directory() + asset);
|
||||
#endif
|
||||
|
||||
return context->LoadDocument("assets/config_menu.rml");
|
||||
}
|
||||
void register_events(recompui::UiEventListenerInstancer& listener) override {
|
||||
|
@ -725,7 +731,7 @@ public:
|
|||
throw std::runtime_error("Failed to make RmlUi data model for the controls config menu");
|
||||
}
|
||||
|
||||
constructor.BindFunc("input_count", [](Rml::Variant& out) { out = recomp::get_num_inputs(); } );
|
||||
constructor.BindFunc("input_count", [](Rml::Variant& out) { out = static_cast<uint64_t>(recomp::get_num_inputs()); } );
|
||||
constructor.BindFunc("input_device_is_keyboard", [](Rml::Variant& out) { out = cur_device == recomp::InputDevice::Keyboard; } );
|
||||
|
||||
constructor.RegisterTransformFunc("get_input_name", [](const Rml::VariantList& inputs) {
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
#include "recomp_ui.h"
|
||||
#include "zelda_config.h"
|
||||
#include "zelda_support.h"
|
||||
#include "librecomp/game.hpp"
|
||||
#include "ultramodern/ultramodern.hpp"
|
||||
#include "RmlUi/Core.h"
|
||||
#include "nfd.h"
|
||||
#include <filesystem>
|
||||
|
||||
std::string version_number = "v1.1.1";
|
||||
static std::string version_string;
|
||||
|
||||
Rml::DataModelHandle model_handle;
|
||||
bool mm_rom_valid = false;
|
||||
|
@ -15,41 +16,43 @@ extern std::vector<recomp::GameEntry> supported_games;
|
|||
|
||||
void select_rom() {
|
||||
nfdnchar_t* native_path = nullptr;
|
||||
nfdresult_t result = NFD_OpenDialogN(&native_path, nullptr, 0, nullptr);
|
||||
zelda64::dispatch_on_main_thread([&native_path] {
|
||||
nfdresult_t result = NFD_OpenDialogN(&native_path, nullptr, 0, nullptr);
|
||||
|
||||
if (result == NFD_OKAY) {
|
||||
std::filesystem::path path{native_path};
|
||||
if (result == NFD_OKAY) {
|
||||
std::filesystem::path path{native_path};
|
||||
|
||||
NFD_FreePathN(native_path);
|
||||
native_path = nullptr;
|
||||
NFD_FreePathN(native_path);
|
||||
native_path = nullptr;
|
||||
|
||||
recomp::RomValidationError rom_error = recomp::select_rom(path, supported_games[0].game_id);
|
||||
switch (rom_error) {
|
||||
case recomp::RomValidationError::Good:
|
||||
mm_rom_valid = true;
|
||||
model_handle.DirtyVariable("mm_rom_valid");
|
||||
break;
|
||||
case recomp::RomValidationError::FailedToOpen:
|
||||
recompui::message_box("Failed to open ROM file.");
|
||||
break;
|
||||
case recomp::RomValidationError::NotARom:
|
||||
recompui::message_box("This is not a valid ROM file.");
|
||||
break;
|
||||
case recomp::RomValidationError::IncorrectRom:
|
||||
recompui::message_box("This ROM is not the correct game.");
|
||||
break;
|
||||
case recomp::RomValidationError::NotYet:
|
||||
recompui::message_box("This game isn't supported yet.");
|
||||
break;
|
||||
case recomp::RomValidationError::IncorrectVersion:
|
||||
recompui::message_box(
|
||||
"This ROM is the correct game, but the wrong version.\nThis project requires the NTSC-U N64 version of the game.");
|
||||
break;
|
||||
case recomp::RomValidationError::OtherError:
|
||||
recompui::message_box("An unknown error has occurred.");
|
||||
break;
|
||||
recomp::RomValidationError rom_error = recomp::select_rom(path, supported_games[0].game_id);
|
||||
switch (rom_error) {
|
||||
case recomp::RomValidationError::Good:
|
||||
mm_rom_valid = true;
|
||||
model_handle.DirtyVariable("mm_rom_valid");
|
||||
break;
|
||||
case recomp::RomValidationError::FailedToOpen:
|
||||
recompui::message_box("Failed to open ROM file.");
|
||||
break;
|
||||
case recomp::RomValidationError::NotARom:
|
||||
recompui::message_box("This is not a valid ROM file.");
|
||||
break;
|
||||
case recomp::RomValidationError::IncorrectRom:
|
||||
recompui::message_box("This ROM is not the correct game.");
|
||||
break;
|
||||
case recomp::RomValidationError::NotYet:
|
||||
recompui::message_box("This game isn't supported yet.");
|
||||
break;
|
||||
case recomp::RomValidationError::IncorrectVersion:
|
||||
recompui::message_box(
|
||||
"This ROM is the correct game, but the wrong version.\nThis project requires the NTSC-U N64 version of the game.");
|
||||
break;
|
||||
case recomp::RomValidationError::OtherError:
|
||||
recompui::message_box("An unknown error has occurred.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
class LauncherMenu : public recompui::MenuController {
|
||||
|
@ -61,6 +64,11 @@ public:
|
|||
|
||||
}
|
||||
Rml::ElementDocument* load_document(Rml::Context* context) override {
|
||||
#if defined(__APPLE__)
|
||||
const Rml::String asset = "/assets/launcher.rml";
|
||||
return context->LoadDocument(zelda64::get_bundle_resource_directory() + asset);
|
||||
#endif
|
||||
|
||||
return context->LoadDocument("assets/launcher.rml");
|
||||
}
|
||||
void register_events(recompui::UiEventListenerInstancer& listener) override {
|
||||
|
@ -103,7 +111,9 @@ public:
|
|||
Rml::DataModelConstructor constructor = context->CreateDataModel("launcher_model");
|
||||
|
||||
constructor.Bind("mm_rom_valid", &mm_rom_valid);
|
||||
constructor.Bind("version_number", &version_number);
|
||||
|
||||
version_string = recomp::get_project_version().to_string();
|
||||
constructor.Bind("version_number", &version_string);
|
||||
|
||||
model_handle = constructor.GetModelHandle();
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "librecomp/game.hpp"
|
||||
#include "zelda_config.h"
|
||||
#include "ui_rml_hacks.hpp"
|
||||
#include "zelda_support.h"
|
||||
|
||||
#include "concurrentqueue.h"
|
||||
|
||||
|
@ -1144,8 +1145,6 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) {
|
|||
Rml::Debugger::Initialise(ui_context->rml.context);
|
||||
|
||||
{
|
||||
const Rml::String directory = "assets/";
|
||||
|
||||
struct FontFace {
|
||||
const char* filename;
|
||||
bool fallback_face;
|
||||
|
@ -1162,7 +1161,13 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) {
|
|||
};
|
||||
|
||||
for (const FontFace& face : font_faces) {
|
||||
#if defined(__APPLE__)
|
||||
const Rml::String directory = "/assets/";
|
||||
Rml::LoadFontFace(zelda64::get_bundle_resource_directory() + directory + face.filename, face.fallback_face);
|
||||
#else
|
||||
const Rml::String directory = "assets/";
|
||||
Rml::LoadFontFace(directory + face.filename, face.fallback_face);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1287,6 +1292,12 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
|
|||
static recompui::Menu prev_menu = recompui::Menu::None;
|
||||
recompui::Menu cur_menu = open_menu.load();
|
||||
|
||||
// Return to the launcher if no menu is open and the game isn't started.
|
||||
if (cur_menu == recompui::Menu::None && !ultramodern::is_game_started()) {
|
||||
cur_menu = recompui::Menu::Launcher;
|
||||
recompui::set_current_menu(cur_menu);
|
||||
}
|
||||
|
||||
if (reload_sheets) {
|
||||
ui_context->rml.load_documents();
|
||||
prev_menu = recompui::Menu::None;
|
||||
|
@ -1500,6 +1511,9 @@ recompui::Menu recompui::get_current_menu() {
|
|||
}
|
||||
|
||||
void recompui::message_box(const char* msg) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, zelda64::program_name.data(), msg, nullptr);
|
||||
std::string message(msg);
|
||||
zelda64::dispatch_on_main_thread([message] {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, zelda64::program_name.data(), message.c_str(), nullptr);
|
||||
});
|
||||
printf("[ERROR] %s\n", msg);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ entrypoint = 0x80080000
|
|||
# Paths are relative to the location of this config file.
|
||||
output_func_path = "RecompiledFuncs"
|
||||
relocatable_sections_path = "overlays.us.rev1.txt"
|
||||
# elf_path = "mm.us.rev1.rom_uncompressed.elf"
|
||||
symbols_file_path = "Zelda64RecompSyms/mm.us.rev1.syms.toml"
|
||||
rom_file_path = "mm.us.rev1.rom_uncompressed.z64"
|
||||
|
||||
|
|
Loading…
Reference in New Issue