mirror of
https://github.com/Mr-Wiseguy/Zelda64Recomp.git
synced 2025-02-23 06:37:19 +01:00
Mod Support (#499)
Integrates the modding functionality in N64ModernRuntime and adds several exported functions for mods to use. Also adds a ROM decompressor so that the runtime has access to the uncompressed code in the ROM for hooking purposes.
This commit is contained in:
parent
0d0f64e32f
commit
91db87632c
19
.github/workflows/validate.yml
vendored
19
.github/workflows/validate.yml
vendored
@ -9,7 +9,7 @@ on:
|
|||||||
N64RECOMP_COMMIT:
|
N64RECOMP_COMMIT:
|
||||||
type: string
|
type: string
|
||||||
required: false
|
required: false
|
||||||
default: '5b17bf8bb556d2544c6161487232a455eae8f188'
|
default: '198de1b5cf6e58415948588584750c51562d58dd'
|
||||||
DXC_CHECKSUM:
|
DXC_CHECKSUM:
|
||||||
type: string
|
type: string
|
||||||
required: false
|
required: false
|
||||||
@ -22,7 +22,7 @@ concurrency:
|
|||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
jobs:
|
jobs:
|
||||||
build-linux:
|
build-linux:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ format('blaze/compute/{0}-amd64', matrix.os) }}
|
||||||
container:
|
container:
|
||||||
image: dcvz/n64recomp:ubuntu-18.04
|
image: dcvz/n64recomp:ubuntu-18.04
|
||||||
volumes:
|
volumes:
|
||||||
@ -95,7 +95,7 @@ jobs:
|
|||||||
rm -rf assets/scss
|
rm -rf assets/scss
|
||||||
tar -czf Zelda64Recompiled.tar.gz Zelda64Recompiled assets/ gamecontrollerdb.txt
|
tar -czf Zelda64Recompiled.tar.gz Zelda64Recompiled assets/ gamecontrollerdb.txt
|
||||||
- name: Archive Zelda64Recomp
|
- name: Archive Zelda64Recomp
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: Zelda64Recompiled-${{ runner.os }}-X64-${{ matrix.type }}
|
name: Zelda64Recompiled-${{ runner.os }}-X64-${{ matrix.type }}
|
||||||
path: Zelda64Recompiled.tar.gz
|
path: Zelda64Recompiled.tar.gz
|
||||||
@ -103,12 +103,12 @@ jobs:
|
|||||||
run: |-
|
run: |-
|
||||||
./.github/linux/appimage.sh
|
./.github/linux/appimage.sh
|
||||||
- name: Zelda64Recomp AppImage
|
- name: Zelda64Recomp AppImage
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: Zelda64Recompiled-AppImage-X64-${{ matrix.type }}
|
name: Zelda64Recompiled-AppImage-X64-${{ matrix.type }}
|
||||||
path: Zelda64Recompiled-*.AppImage
|
path: Zelda64Recompiled-*.AppImage
|
||||||
build-linux-arm64:
|
build-linux-arm64:
|
||||||
runs-on: ${{ format('blaze/{0}', matrix.os) }}
|
runs-on: ${{ format('blaze/compute/{0}', matrix.os) }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
type: [ Debug, Release ]
|
type: [ Debug, Release ]
|
||||||
@ -199,7 +199,7 @@ jobs:
|
|||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
type: [ Debug, Release ]
|
type: [ Debug, RelWithDebInfo ]
|
||||||
name: windows (${{ matrix.type }})
|
name: windows (${{ matrix.type }})
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@ -264,6 +264,7 @@ jobs:
|
|||||||
Move-Item -Path "cmake-build/dxcompiler.dll" -Destination "dxcompiler.dll"
|
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/dxil.dll" -Destination "dxil.dll"
|
||||||
Move-Item -Path "cmake-build/SDL2.dll" -Destination "SDL2.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
|
Remove-Item -Path "assets/scss" -Recurse -Force
|
||||||
- name: Archive Zelda64Recomp
|
- name: Archive Zelda64Recomp
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
@ -276,3 +277,9 @@ jobs:
|
|||||||
SDL2.dll
|
SDL2.dll
|
||||||
assets/
|
assets/
|
||||||
gamecontrollerdb.txt
|
gamecontrollerdb.txt
|
||||||
|
- name: Archive Debug Files
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: Zelda64Recompiled-PDB-${{ matrix.type }}
|
||||||
|
path: |
|
||||||
|
Zelda64Recompiled.pdb
|
||||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -13,9 +13,6 @@
|
|||||||
[submodule "lib/lunasvg"]
|
[submodule "lib/lunasvg"]
|
||||||
path = lib/lunasvg
|
path = lib/lunasvg
|
||||||
url = https://github.com/sammycage/lunasvg
|
url = https://github.com/sammycage/lunasvg
|
||||||
[submodule "lib/sse2neon"]
|
|
||||||
path = lib/sse2neon
|
|
||||||
url = https://github.com/DLTcollab/sse2neon.git
|
|
||||||
[submodule "lib/N64ModernRuntime"]
|
[submodule "lib/N64ModernRuntime"]
|
||||||
path = lib/N64ModernRuntime
|
path = lib/N64ModernRuntime
|
||||||
url = https://github.com/N64Recomp/N64ModernRuntime.git
|
url = https://github.com/N64Recomp/N64ModernRuntime.git
|
||||||
|
@ -28,6 +28,7 @@ if (WIN32)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(RT64_STATIC TRUE)
|
set(RT64_STATIC TRUE)
|
||||||
|
set(RT64_SDL_WINDOW_VULKAN TRUE)
|
||||||
add_subdirectory(${CMAKE_SOURCE_DIR}/lib/rt64 ${CMAKE_BINARY_DIR}/rt64)
|
add_subdirectory(${CMAKE_SOURCE_DIR}/lib/rt64 ${CMAKE_BINARY_DIR}/rt64)
|
||||||
|
|
||||||
# set(BUILD_SHARED_LIBS_SAVED "${BUILD_SHARED_LIBS}")
|
# set(BUILD_SHARED_LIBS_SAVED "${BUILD_SHARED_LIBS}")
|
||||||
@ -57,6 +58,7 @@ target_include_directories(RecompiledFuncs PRIVATE
|
|||||||
${CMAKE_SOURCE_DIR}/include
|
${CMAKE_SOURCE_DIR}/include
|
||||||
${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/ultramodern/include
|
${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/ultramodern/include
|
||||||
${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/librecomp/include
|
${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/librecomp/include
|
||||||
|
${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/N64Recomp/include
|
||||||
)
|
)
|
||||||
|
|
||||||
file(GLOB FUNC_C_SOURCES ${CMAKE_SOURCE_DIR}/RecompiledFuncs/*.c)
|
file(GLOB FUNC_C_SOURCES ${CMAKE_SOURCE_DIR}/RecompiledFuncs/*.c)
|
||||||
@ -78,6 +80,7 @@ target_include_directories(PatchesLib PRIVATE
|
|||||||
${CMAKE_SOURCE_DIR}/include
|
${CMAKE_SOURCE_DIR}/include
|
||||||
${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/ultramodern/include
|
${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/ultramodern/include
|
||||||
${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/librecomp/include
|
${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/librecomp/include
|
||||||
|
${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/N64Recomp/include
|
||||||
)
|
)
|
||||||
|
|
||||||
target_sources(PatchesLib PRIVATE
|
target_sources(PatchesLib PRIVATE
|
||||||
@ -96,12 +99,8 @@ if(NOT DEFINED PATCHES_LD)
|
|||||||
set(PATCHES_LD ld.lld)
|
set(PATCHES_LD ld.lld)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT DEFINED PATCHES_OBJCOPY)
|
|
||||||
set(PATCHES_OBJCOPY llvm-objcopy)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_custom_target(PatchesBin
|
add_custom_target(PatchesBin
|
||||||
COMMAND ${CMAKE_COMMAND} -E env CC=${PATCHES_C_COMPILER} LD=${PATCHES_LD} OBJCOPY=${PATCHES_OBJCOPY} make
|
COMMAND ${CMAKE_COMMAND} -E env CC=${PATCHES_C_COMPILER} LD=${PATCHES_LD} make
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/patches
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/patches
|
||||||
BYPRODUCTS ${CMAKE_SOURCE_DIR}/patches/patches.elf
|
BYPRODUCTS ${CMAKE_SOURCE_DIR}/patches/patches.elf
|
||||||
)
|
)
|
||||||
@ -138,16 +137,6 @@ add_custom_target(DownloadGameControllerDB
|
|||||||
add_executable(Zelda64Recompiled)
|
add_executable(Zelda64Recompiled)
|
||||||
add_dependencies(Zelda64Recompiled DownloadGameControllerDB)
|
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
|
set (SOURCES
|
||||||
${CMAKE_SOURCE_DIR}/src/main/main.cpp
|
${CMAKE_SOURCE_DIR}/src/main/main.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/main/register_overlays.cpp
|
${CMAKE_SOURCE_DIR}/src/main/register_overlays.cpp
|
||||||
@ -161,6 +150,7 @@ set (SOURCES
|
|||||||
${CMAKE_SOURCE_DIR}/src/game/debug.cpp
|
${CMAKE_SOURCE_DIR}/src/game/debug.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/game/quicksaving.cpp
|
${CMAKE_SOURCE_DIR}/src/game/quicksaving.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/game/recomp_api.cpp
|
${CMAKE_SOURCE_DIR}/src/game/recomp_api.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/game/rom_decompression.cpp
|
||||||
|
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/ui_renderer.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/ui_renderer.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/ui_launcher.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/ui_launcher.cpp
|
||||||
@ -174,12 +164,9 @@ set (SOURCES
|
|||||||
${CMAKE_SOURCE_DIR}/lib/RmlUi/Backends/RmlUi_Platform_SDL.cpp
|
${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)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_include_directories(Zelda64Recompiled PRIVATE
|
target_include_directories(Zelda64Recompiled PRIVATE
|
||||||
${CMAKE_SOURCE_DIR}/include
|
${CMAKE_SOURCE_DIR}/include
|
||||||
|
${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/N64Recomp/include
|
||||||
${CMAKE_SOURCE_DIR}/lib/concurrentqueue
|
${CMAKE_SOURCE_DIR}/lib/concurrentqueue
|
||||||
${CMAKE_SOURCE_DIR}/lib/GamepadMotionHelpers
|
${CMAKE_SOURCE_DIR}/lib/GamepadMotionHelpers
|
||||||
${CMAKE_SOURCE_DIR}/lib/RmlUi/Include
|
${CMAKE_SOURCE_DIR}/lib/RmlUi/Include
|
||||||
@ -190,7 +177,6 @@ target_include_directories(Zelda64Recompiled PRIVATE
|
|||||||
${CMAKE_SOURCE_DIR}/lib/rt64/src
|
${CMAKE_SOURCE_DIR}/lib/rt64/src
|
||||||
${CMAKE_SOURCE_DIR}/lib/rt64/src/rhi
|
${CMAKE_SOURCE_DIR}/lib/rt64/src/rhi
|
||||||
${CMAKE_SOURCE_DIR}/lib/rt64/src/render
|
${CMAKE_SOURCE_DIR}/lib/rt64/src/render
|
||||||
${CMAKE_SOURCE_DIR}/lib/sse2neon
|
|
||||||
${CMAKE_SOURCE_DIR}/lib/freetype-windows-binaries/include
|
${CMAKE_SOURCE_DIR}/lib/freetype-windows-binaries/include
|
||||||
${CMAKE_SOURCE_DIR}/lib/rt64/src/contrib/nativefiledialog-extended/src/include
|
${CMAKE_SOURCE_DIR}/lib/rt64/src/contrib/nativefiledialog-extended/src/include
|
||||||
${CMAKE_BINARY_DIR}/shaders
|
${CMAKE_BINARY_DIR}/shaders
|
||||||
@ -210,6 +196,11 @@ else()
|
|||||||
)
|
)
|
||||||
endif()
|
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)
|
if (WIN32)
|
||||||
include(FetchContent)
|
include(FetchContent)
|
||||||
|
|
||||||
@ -256,6 +247,8 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
|||||||
find_package(SDL2 REQUIRED)
|
find_package(SDL2 REQUIRED)
|
||||||
find_package(X11 REQUIRED)
|
find_package(X11 REQUIRED)
|
||||||
|
|
||||||
|
add_compile_definitions("RT64_SDL_WINDOW_VULKAN")
|
||||||
|
|
||||||
# Generate icon_bytes.c from the app icon PNG.
|
# Generate icon_bytes.c from the app icon PNG.
|
||||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.c ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.h
|
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.c ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.h
|
||||||
COMMAND file_to_c ${CMAKE_SOURCE_DIR}/icons/512.png icon_bytes ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.c ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.h
|
COMMAND file_to_c ${CMAKE_SOURCE_DIR}/icons/512.png icon_bytes ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.c ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.h
|
||||||
|
@ -15,7 +15,7 @@ Zelda 64: Recompiled is a project that uses [N64: Recompiled](https://github.com
|
|||||||
|
|
||||||
<br style="display: none;"/>
|
<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>
|
</div>
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 50d63debd58ce9f01957142ea91b67b6c7018c96
|
Subproject commit a325f8fa5c48c925616bd43685ef14bbabc29537
|
@ -58,7 +58,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bottom-left">
|
<div class="bottom-left">
|
||||||
<label>{{version_number}}</label>
|
<label>v{{version_number}}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -120,7 +120,8 @@ namespace recomp {
|
|||||||
std::vector<InputField> apply_menu;
|
std::vector<InputField> apply_menu;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr const std::vector<InputField>& get_default_mapping_for_input(const DefaultN64Mappings& defaults, const GameInput input) {
|
inline const std::vector<InputField>& get_default_mapping_for_input(const DefaultN64Mappings& defaults, const GameInput input) {
|
||||||
|
static const std::vector<InputField> empty_input_field{};
|
||||||
switch (input) {
|
switch (input) {
|
||||||
case GameInput::A: return defaults.a;
|
case GameInput::A: return defaults.a;
|
||||||
case GameInput::B: return defaults.b;
|
case GameInput::B: return defaults.b;
|
||||||
@ -143,7 +144,7 @@ namespace recomp {
|
|||||||
case GameInput::TOGGLE_MENU: return defaults.toggle_menu;
|
case GameInput::TOGGLE_MENU: return defaults.toggle_menu;
|
||||||
case GameInput::ACCEPT_MENU: return defaults.accept_menu;
|
case GameInput::ACCEPT_MENU: return defaults.accept_menu;
|
||||||
case GameInput::APPLY_MENU: return defaults.apply_menu;
|
case GameInput::APPLY_MENU: return defaults.apply_menu;
|
||||||
default: return std::vector<InputField>();
|
default: return empty_input_field;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
#ifndef __ZELDA_GAME_H__
|
#ifndef __ZELDA_GAME_H__
|
||||||
#define __ZELDA_GAME_H__
|
#define __ZELDA_GAME_H__
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <span>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace zelda64 {
|
namespace zelda64 {
|
||||||
void quicksave_save();
|
void quicksave_save();
|
||||||
void quicksave_load();
|
void quicksave_load();
|
||||||
|
std::vector<uint8_t> decompress_mm(std::span<const uint8_t> compressed_rom);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
#ifndef __ZELDA_RENDER_H__
|
#ifndef __ZELDA_RENDER_H__
|
||||||
#define __ZELDA_RENDER_H__
|
#define __ZELDA_RENDER_H__
|
||||||
|
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
#include "common/rt64_user_configuration.h"
|
#include "common/rt64_user_configuration.h"
|
||||||
#include "ultramodern/renderer_context.hpp"
|
#include "ultramodern/renderer_context.hpp"
|
||||||
|
#include "librecomp/mods.hpp"
|
||||||
|
|
||||||
namespace RT64 {
|
namespace RT64 {
|
||||||
struct Application;
|
struct Application;
|
||||||
@ -10,7 +14,7 @@ namespace RT64 {
|
|||||||
|
|
||||||
namespace zelda64 {
|
namespace zelda64 {
|
||||||
namespace renderer {
|
namespace renderer {
|
||||||
class RT64Context : public ultramodern::renderer::RendererContext {
|
class RT64Context final : public ultramodern::renderer::RendererContext {
|
||||||
public:
|
public:
|
||||||
~RT64Context() override;
|
~RT64Context() override;
|
||||||
RT64Context(uint8_t *rdram, ultramodern::renderer::WindowHandle window_handle, bool developer_mode);
|
RT64Context(uint8_t *rdram, ultramodern::renderer::WindowHandle window_handle, bool developer_mode);
|
||||||
@ -25,10 +29,10 @@ namespace zelda64 {
|
|||||||
void shutdown() override;
|
void shutdown() override;
|
||||||
uint32_t get_display_framerate() const override;
|
uint32_t get_display_framerate() const override;
|
||||||
float get_resolution_scale() const override;
|
float get_resolution_scale() const override;
|
||||||
void load_shader_cache(std::span<const char> cache_binary) override;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::unique_ptr<RT64::Application> app;
|
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);
|
std::unique_ptr<ultramodern::renderer::RendererContext> create_render_context(uint8_t *rdram, ultramodern::renderer::WindowHandle window_handle, bool developer_mode);
|
||||||
@ -36,6 +40,9 @@ namespace zelda64 {
|
|||||||
RT64::UserConfiguration::Antialiasing RT64MaxMSAA();
|
RT64::UserConfiguration::Antialiasing RT64MaxMSAA();
|
||||||
bool RT64SamplePositionsSupported();
|
bool RT64SamplePositionsSupported();
|
||||||
bool RT64HighPrecisionFBEnabled();
|
bool RT64HighPrecisionFBEnabled();
|
||||||
|
|
||||||
|
void enable_texture_pack(const recomp::mods::ModHandle& mod);
|
||||||
|
void disable_texture_pack(const recomp::mods::ModHandle& mod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 0a538553330ea5fdb1673708704bb92a854241b9
|
Subproject commit 0afeb089a55cb391c24352f23b7683ab3c2ca854
|
2
lib/rt64
2
lib/rt64
@ -1 +1 @@
|
|||||||
Subproject commit 88c618c1f8d8089f94e78537e49e0d77798fc0be
|
Subproject commit 0ca92eeb6c2f58ce3581c65f87f7261b8ac0fea0
|
@ -1 +0,0 @@
|
|||||||
Subproject commit 42c704755d3ec218ed9126a122f0a667beeb630a
|
|
@ -9,8 +9,12 @@ single_file_output = true
|
|||||||
use_absolute_symbols = true
|
use_absolute_symbols = true
|
||||||
# Point the recompiler at the symbol files so that it can resolve relocations during recompilation.
|
# 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"
|
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.
|
# 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"
|
output_binary_path = "patches/patches.bin"
|
||||||
# Do not emit warnings for unpaired LO16 values, as clang produces many of them.
|
# Do not emit warnings for unpaired LO16 values, as clang produces many of them.
|
||||||
unpaired_lo16_warnings = false
|
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 Sram_SyncWriteToFlash(SramContext* sramCtx, s32 curPage, s32 numPages);
|
||||||
|
|
||||||
void autosave_reset_timer();
|
void recomp_reset_autosave_timer();
|
||||||
void autosave_reset_timer_slow();
|
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.
|
// Transfer the scene flags into the cycle flags.
|
||||||
Play_SaveCycleSceneFlags(&play->state);
|
Play_SaveCycleSceneFlags(&play->state);
|
||||||
// Transfer the cycle flags into the save buffer. Logic copied from func_8014546C.
|
// 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;
|
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) {
|
RECOMP_PATCH void func_80147314(SramContext* sramCtx, s32 fileNum) {
|
||||||
s32 save_type = gSaveContext.save.isOwlSave;
|
s32 save_type = gSaveContext.save.isOwlSave;
|
||||||
gSaveContext.save.isOwlSave = false;
|
gSaveContext.save.isOwlSave = false;
|
||||||
|
|
||||||
// @recomp Prevent owl save/autosave deletion if autosaving is enabled.
|
// @recomp Prevent owl save/autosave deletion if autosaving is enabled, and...
|
||||||
if (!recomp_autosave_enabled()) {
|
// @recomp_use_export_var loading_deletes_owl_save: Prevent owl save deletion if mods disable it.
|
||||||
|
if (!recomp_get_autosave_enabled() && loading_deletes_owl_save) {
|
||||||
gSaveContext.save.saveInfo.playerData.newf[0] = '\0';
|
gSaveContext.save.saveInfo.playerData.newf[0] = '\0';
|
||||||
gSaveContext.save.saveInfo.playerData.newf[1] = '\0';
|
gSaveContext.save.saveInfo.playerData.newf[1] = '\0';
|
||||||
gSaveContext.save.saveInfo.playerData.newf[2] = '\0';
|
gSaveContext.save.saveInfo.playerData.newf[2] = '\0';
|
||||||
@ -176,7 +185,7 @@ RECOMP_PATCH void func_8014546C(SramContext* sramCtx) {
|
|||||||
// @recomp Delete the owl save.
|
// @recomp Delete the owl save.
|
||||||
delete_owl_save(sramCtx, gSaveContext.fileNum);
|
delete_owl_save(sramCtx, gSaveContext.fileNum);
|
||||||
// @recomp Reset the autosave timer.
|
// @recomp Reset the autosave timer.
|
||||||
autosave_reset_timer();
|
recomp_reset_autosave_timer();
|
||||||
for (i = 0; i < ARRAY_COUNT(gSaveContext.cycleSceneFlags); i++) {
|
for (i = 0; i < ARRAY_COUNT(gSaveContext.cycleSceneFlags); i++) {
|
||||||
gSaveContext.save.saveInfo.permanentSceneFlags[i].chest = gSaveContext.cycleSceneFlags[i].chest;
|
gSaveContext.save.saveInfo.permanentSceneFlags[i].chest = gSaveContext.cycleSceneFlags[i].chest;
|
||||||
gSaveContext.save.saveInfo.permanentSceneFlags[i].switch0 = gSaveContext.cycleSceneFlags[i].switch0;
|
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);
|
CLOSE_DISPS(play->state.gfxCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_autosave_icon() {
|
RECOMP_EXPORT void recomp_show_autosave_icon() {
|
||||||
autosave_icon_counter = AUTOSAVE_ICON_TOTAL_FRAMES;
|
autosave_icon_counter = AUTOSAVE_ICON_TOTAL_FRAMES;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 recomp_autosave_interval() {
|
RECOMP_EXPORT u32 recomp_autosave_interval() {
|
||||||
return 2 * 60 * 1000;
|
return 2 * 60 * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -367,12 +376,12 @@ bool reached_final_three_hours() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void autosave_reset_timer() {
|
RECOMP_EXPORT void recomp_reset_autosave_timer() {
|
||||||
last_autosave_time = osGetTime();
|
last_autosave_time = osGetTime();
|
||||||
extra_autosave_delay_milliseconds = 0;
|
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.
|
// Set the most recent autosave time in the future to give extra time before an autosave triggers.
|
||||||
last_autosave_time = osGetTime();
|
last_autosave_time = osGetTime();
|
||||||
extra_autosave_delay_milliseconds = 2 * 60 * 1000;
|
extra_autosave_delay_milliseconds = 2 * 60 * 1000;
|
||||||
@ -381,7 +390,7 @@ void autosave_reset_timer_slow() {
|
|||||||
void autosave_post_play_update(PlayState* play) {
|
void autosave_post_play_update(PlayState* play) {
|
||||||
static int frames_since_save_changed = 0;
|
static int frames_since_save_changed = 0;
|
||||||
static int frames_since_autosave_ready = 0;
|
static int frames_since_autosave_ready = 0;
|
||||||
if (recomp_autosave_enabled()) {
|
if (recomp_get_autosave_enabled()) {
|
||||||
if (autosave_compare_saves(&gSaveContext, &prev_save_ctx)) {
|
if (autosave_compare_saves(&gSaveContext, &prev_save_ctx)) {
|
||||||
frames_since_save_changed = 0;
|
frames_since_save_changed = 0;
|
||||||
Lib_MemCpy(&prev_save_ctx, &gSaveContext, offsetof(SaveContext, fileNum));
|
Lib_MemCpy(&prev_save_ctx, &gSaveContext, offsetof(SaveContext, fileNum));
|
||||||
@ -425,20 +434,20 @@ void autosave_post_play_update(PlayState* play) {
|
|||||||
frames_since_autosave_ready >= MIN_FRAMES_SINCE_READY &&
|
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)))
|
time_now - last_autosave_time > (OS_USEC_TO_CYCLES(1000 * (recomp_autosave_interval() + extra_autosave_delay_milliseconds)))
|
||||||
) {
|
) {
|
||||||
do_autosave(play);
|
recomp_do_autosave(play);
|
||||||
show_autosave_icon();
|
recomp_show_autosave_icon();
|
||||||
autosave_reset_timer();
|
recomp_reset_autosave_timer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Update the last autosave time to the current time to prevent autosaving immediately if autosaves are turned back on.
|
// 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;
|
gCanPause = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void autosave_init() {
|
void autosave_init() {
|
||||||
autosave_reset_timer_slow();
|
recomp_reset_autosave_timer_slow();
|
||||||
Lib_MemCpy(&prev_save_ctx, &gSaveContext, offsetof(SaveContext, fileNum));
|
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 Patched to change the entrance for autosaves and initialize autosaves.
|
||||||
RECOMP_PATCH void Sram_OpenSave(FileSelectState* fileSelect, SramContext* sramCtx) {
|
RECOMP_PATCH void Sram_OpenSave(FileSelectState* fileSelect, SramContext* sramCtx) {
|
||||||
s32 i;
|
s32 i;
|
||||||
@ -530,6 +542,9 @@ RECOMP_PATCH void Sram_OpenSave(FileSelectState* fileSelect, SramContext* sramCt
|
|||||||
s32 pad1;
|
s32 pad1;
|
||||||
s32 fileNum;
|
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) {
|
if (gSaveContext.flashSaveAvailable) {
|
||||||
bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE);
|
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.
|
// @recomp Initialize the autosave state tracking.
|
||||||
autosave_init();
|
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);
|
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 Reset the autosave timer when the moon crashes.
|
||||||
RECOMP_PATCH void Sram_ResetSaveFromMoonCrash(SramContext* sramCtx) {
|
RECOMP_PATCH void Sram_ResetSaveFromMoonCrash(SramContext* sramCtx) {
|
||||||
s32 i;
|
s32 i;
|
||||||
s32 cutsceneIndex = gSaveContext.save.cutsceneIndex;
|
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],
|
if (moon_crash_resets_save)
|
||||||
gFlashSaveNumPages[gSaveContext.fileNum * 2]) != 0) {
|
{
|
||||||
SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2 + 1],
|
bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE);
|
||||||
gFlashSaveNumPages[gSaveContext.fileNum * 2 + 1]);
|
|
||||||
|
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++) {
|
for (i = 0; i < ARRAY_COUNT(gSaveContext.eventInf); i++) {
|
||||||
gSaveContext.eventInf[i] = 0;
|
gSaveContext.eventInf[i] = 0;
|
||||||
@ -705,25 +740,56 @@ RECOMP_PATCH void Sram_ResetSaveFromMoonCrash(SramContext* sramCtx) {
|
|||||||
gSaveContext.jinxTimer = 0;
|
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.
|
// @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) {
|
RECOMP_PATCH void ObjWarpstone_Update(Actor* thisx, PlayState* play) {
|
||||||
ObjWarpstone* this = (ObjWarpstone*)thisx;
|
ObjWarpstone* this = (ObjWarpstone*)thisx;
|
||||||
s32 pad;
|
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 (this->isTalking) {
|
||||||
if (Actor_TextboxIsClosing(&this->dyna.actor, play)) {
|
if (Actor_TextboxIsClosing(&this->dyna.actor, play)) {
|
||||||
this->isTalking = false;
|
this->isTalking = false;
|
||||||
} else if ((Message_GetState(&play->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(play)) {
|
} else if ((Message_GetState(&play->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(play)) {
|
||||||
if (play->msgCtx.choiceIndex != 0) {
|
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();
|
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.unk120D6 = 0;
|
||||||
play->msgCtx.unk120D4 = 0;
|
play->msgCtx.unk120D4 = 0;
|
||||||
gSaveContext.save.owlWarpId = OBJ_WARPSTONE_GET_OWL_WARP_ID(&this->dyna.actor);
|
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 {
|
} else {
|
||||||
Message_CloseTextbox(play);
|
Message_CloseTextbox(play);
|
||||||
}
|
}
|
||||||
@ -734,8 +800,8 @@ RECOMP_PATCH void ObjWarpstone_Update(Actor* thisx, PlayState* play) {
|
|||||||
Actor_OfferTalkNearColChkInfoCylinder(&this->dyna.actor, play);
|
Actor_OfferTalkNearColChkInfoCylinder(&this->dyna.actor, play);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @recomp Skip the text talking about the save being deleted on load, if autosave is 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()) {
|
if (recomp_get_autosave_enabled() || !loading_deletes_owl_save) {
|
||||||
if (this->isTalking && play->msgCtx.currentTextId == 0xC01 && play->msgCtx.msgBufPos == 269) {
|
if (this->isTalking && play->msgCtx.currentTextId == 0xC01 && play->msgCtx.msgBufPos == 269) {
|
||||||
play->msgCtx.msgBufPos = 530;
|
play->msgCtx.msgBufPos = 530;
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
#include "z64shrink_window.h"
|
#include "z64shrink_window.h"
|
||||||
#include "z64player.h"
|
#include "z64player.h"
|
||||||
|
|
||||||
|
static bool camera_fixes = false;
|
||||||
|
|
||||||
static bool prev_analog_cam_active = false;
|
static bool prev_analog_cam_active = false;
|
||||||
static bool can_use_analog_cam = false;
|
static bool can_use_analog_cam = false;
|
||||||
static bool analog_cam_active = false;
|
static bool analog_cam_active = false;
|
||||||
@ -26,6 +28,10 @@ float analog_camera_y_sensitivity = 500.0f;
|
|||||||
|
|
||||||
static const float analog_cam_threshold = 0.1f;
|
static const float analog_cam_threshold = 0.1f;
|
||||||
|
|
||||||
|
RECOMP_EXPORT void recomp_set_camera_fixes(bool new_val) {
|
||||||
|
camera_fixes = new_val;
|
||||||
|
}
|
||||||
|
|
||||||
void update_analog_camera_params(Camera* camera) {
|
void update_analog_camera_params(Camera* camera) {
|
||||||
// recomp_printf("Camera at: %.2f %.2f %.2f\n"
|
// recomp_printf("Camera at: %.2f %.2f %.2f\n"
|
||||||
// " eye: %.2f %.2f %.2f\n"
|
// " eye: %.2f %.2f %.2f\n"
|
||||||
@ -782,7 +788,7 @@ RECOMP_PATCH s32 Camera_Normal1(Camera* camera) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @recomp Update the analog camera.
|
// @recomp Update the analog camera.
|
||||||
if (recomp_analog_cam_enabled()) {
|
if (recomp_get_analog_cam_enabled()) {
|
||||||
update_analog_cam(camera);
|
update_analog_cam(camera);
|
||||||
|
|
||||||
if (analog_cam_active) {
|
if (analog_cam_active) {
|
||||||
@ -838,7 +844,7 @@ RECOMP_PATCH s32 Camera_Normal1(Camera* camera) {
|
|||||||
phi_f2 = (gSaveContext.save.saveInfo.playerData.health <= 0x10) ? 0.8f : 1.0f;
|
phi_f2 = (gSaveContext.save.saveInfo.playerData.health <= 0x10) ? 0.8f : 1.0f;
|
||||||
|
|
||||||
// @recomp Don't zoom in on low health when dual analog is used
|
// @recomp Don't zoom in on low health when dual analog is used
|
||||||
if (recomp_analog_cam_enabled()) {
|
if (recomp_get_analog_cam_enabled()) {
|
||||||
phi_f2 = 1.0f;
|
phi_f2 = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1022,7 +1028,7 @@ RECOMP_PATCH s32 Camera_Jump2(Camera* camera) {
|
|||||||
spB4.pitch = CLAMP_MIN(spB4.pitch, -DEG_TO_BINANG(60.43f));
|
spB4.pitch = CLAMP_MIN(spB4.pitch, -DEG_TO_BINANG(60.43f));
|
||||||
|
|
||||||
// @recomp Update the analog camera.
|
// @recomp Update the analog camera.
|
||||||
if (recomp_analog_cam_enabled()) {
|
if (recomp_get_analog_cam_enabled()) {
|
||||||
update_analog_cam(camera);
|
update_analog_cam(camera);
|
||||||
|
|
||||||
if (analog_cam_active) {
|
if (analog_cam_active) {
|
||||||
@ -1064,6 +1070,10 @@ RECOMP_PATCH s32 Camera_Jump2(Camera* camera) {
|
|||||||
*/
|
*/
|
||||||
// @recomp Patched for analog cam.
|
// @recomp Patched for analog cam.
|
||||||
RECOMP_PATCH s32 Camera_Parallel1(Camera* camera) {
|
RECOMP_PATCH s32 Camera_Parallel1(Camera* camera) {
|
||||||
|
// @recomp
|
||||||
|
static bool prev_targeting_held = false;
|
||||||
|
bool isChargingDekuFlowerDive = !!(((Player*)camera->focalActor)->stateFlags3 & PLAYER_STATE3_100);
|
||||||
|
|
||||||
Vec3f* eye = &camera->eye;
|
Vec3f* eye = &camera->eye;
|
||||||
Vec3f* at = &camera->at;
|
Vec3f* at = &camera->at;
|
||||||
Vec3f* eyeNext = &camera->eyeNext;
|
Vec3f* eyeNext = &camera->eyeNext;
|
||||||
@ -1089,6 +1099,10 @@ RECOMP_PATCH s32 Camera_Parallel1(Camera* camera) {
|
|||||||
CameraModeValue* values;
|
CameraModeValue* values;
|
||||||
f32 yNormal;
|
f32 yNormal;
|
||||||
|
|
||||||
|
// @recomp Read timer4 from timer2.
|
||||||
|
s16 timer4 = rwData->timer2 >> 5; // @recomp Used to check if z-target can be quit. Mirrors timer2 values during z-target in unmodified function.
|
||||||
|
rwData->timer2 &= 0x001F;
|
||||||
|
|
||||||
if (!RELOAD_PARAMS(camera)) {
|
if (!RELOAD_PARAMS(camera)) {
|
||||||
} else {
|
} else {
|
||||||
values = sCameraSettings[camera->setting].cameraModes[camera->mode].values;
|
values = sCameraSettings[camera->setting].cameraModes[camera->mode].values;
|
||||||
@ -1151,6 +1165,8 @@ RECOMP_PATCH s32 Camera_Parallel1(Camera* camera) {
|
|||||||
rwData->timer2 = 20;
|
rwData->timer2 = 20;
|
||||||
} else {
|
} else {
|
||||||
rwData->timer2 = 6;
|
rwData->timer2 = 6;
|
||||||
|
// @recomp Initiate timer4 for z-target.
|
||||||
|
timer4 = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((camera->focalActor == &GET_PLAYER(camera->play)->actor) && (camera->mode == CAM_MODE_CHARGE)) {
|
if ((camera->focalActor == &GET_PLAYER(camera->play)->actor) && (camera->mode == CAM_MODE_CHARGE)) {
|
||||||
@ -1187,12 +1203,55 @@ RECOMP_PATCH s32 Camera_Parallel1(Camera* camera) {
|
|||||||
rwData->unk_26 = 1;
|
rwData->unk_26 = 1;
|
||||||
camera->animState = 1;
|
camera->animState = 1;
|
||||||
sCameraInterfaceFlags = roData->interfaceFlags;
|
sCameraInterfaceFlags = roData->interfaceFlags;
|
||||||
|
|
||||||
|
// @recomp Reset prev_targeting_held after transition.
|
||||||
|
prev_targeting_held = false;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @recomp Change behavior for z-target only.
|
||||||
|
if (camera_fixes) {
|
||||||
|
if ((roData->interfaceFlags & (PARALLEL1_FLAG_3 | PARALLEL1_FLAG_2 | PARALLEL1_FLAG_1)) == PARALLEL1_FLAG_1) {
|
||||||
|
Player* player = GET_PLAYER(camera->play);
|
||||||
|
bool targeting_held = func_80123434(player) || player->lockOnActor != NULL;
|
||||||
|
|
||||||
|
// @recomp Fix camera rotating with player if z-target gets released too fast after transition.
|
||||||
|
if ((targeting_held) && (!prev_targeting_held)) {
|
||||||
|
// @recomp Reset timer2 to avoid immediate rotation, if player presses, releases and presses z-target in a very short time-window.
|
||||||
|
rwData->timer2 = 6;
|
||||||
|
rwData->unk_1E = BINANG_ROT180(camera->focalActorPosRot.rot.y) + roData->unk_22;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @recomp Maintain vanilla behavior for quitting z-target.
|
||||||
|
if ((timer4 == 0) && (!targeting_held)) {
|
||||||
|
rwData->timer2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @recomp Decrease timer4 only in cases where timer2 would be decreased.
|
||||||
|
if ((timer4 > 0)
|
||||||
|
&& (rwData->timer3 <= 0)) {
|
||||||
|
timer4--;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_targeting_held = targeting_held;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (rwData->timer2 != 0) {
|
if (rwData->timer2 != 0) {
|
||||||
switch (roData->interfaceFlags & (PARALLEL1_FLAG_3 | PARALLEL1_FLAG_2 | PARALLEL1_FLAG_1)) {
|
switch (roData->interfaceFlags & (PARALLEL1_FLAG_3 | PARALLEL1_FLAG_2 | PARALLEL1_FLAG_1)) {
|
||||||
case PARALLEL1_FLAG_1:
|
case PARALLEL1_FLAG_1:
|
||||||
|
if (camera_fixes) {
|
||||||
|
// @recomp Fix camera rotating with player if z-target gets released too fast after transition.
|
||||||
|
if (isChargingDekuFlowerDive) {
|
||||||
|
// @recomp Fix camera wiggle during dive into deku flower.
|
||||||
|
rwData->unk_1E = BINANG_ROT180(camera->focalActorPosRot.rot.y) + roData->unk_22;
|
||||||
|
}
|
||||||
|
rwData->unk_20 = roData->unk_20;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// @recomp fallthrough
|
||||||
|
|
||||||
case (PARALLEL1_FLAG_3 | PARALLEL1_FLAG_2 | PARALLEL1_FLAG_1):
|
case (PARALLEL1_FLAG_3 | PARALLEL1_FLAG_2 | PARALLEL1_FLAG_1):
|
||||||
rwData->unk_1E = BINANG_ROT180(camera->focalActorPosRot.rot.y) + roData->unk_22;
|
rwData->unk_1E = BINANG_ROT180(camera->focalActorPosRot.rot.y) + roData->unk_22;
|
||||||
rwData->unk_20 = roData->unk_20;
|
rwData->unk_20 = roData->unk_20;
|
||||||
@ -1372,7 +1431,7 @@ RECOMP_PATCH s32 Camera_Parallel1(Camera* camera) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @recomp Update the analog camera.
|
// @recomp Update the analog camera.
|
||||||
if (recomp_analog_cam_enabled()) {
|
if (recomp_get_analog_cam_enabled()) {
|
||||||
update_analog_cam(camera);
|
update_analog_cam(camera);
|
||||||
|
|
||||||
if (analog_cam_active) {
|
if (analog_cam_active) {
|
||||||
@ -1400,10 +1459,20 @@ RECOMP_PATCH s32 Camera_Parallel1(Camera* camera) {
|
|||||||
func_800CBFA4(camera, at, eye, 3);
|
func_800CBFA4(camera, at, eye, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rwData->timer2 != 0) {
|
if (camera_fixes) {
|
||||||
sUpdateCameraDirection = true;
|
// @recomp Fix camera not updating input dir for the first few frames after transition.
|
||||||
|
if ((isChargingDekuFlowerDive) && (rwData->timer2 != 0)) {
|
||||||
|
// @recomp Fix camera wiggle during dive into deku flower.
|
||||||
|
sUpdateCameraDirection = true;
|
||||||
|
} else {
|
||||||
|
sUpdateCameraDirection = false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
sUpdateCameraDirection = false;
|
if (rwData->timer2 != 0) {
|
||||||
|
sUpdateCameraDirection = true;
|
||||||
|
} else {
|
||||||
|
sUpdateCameraDirection = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1412,6 +1481,9 @@ RECOMP_PATCH s32 Camera_Parallel1(Camera* camera) {
|
|||||||
camera->atLerpStepScale = Camera_ClampLerpScale(camera, sp72 ? roData->unk_1C : roData->unk_18);
|
camera->atLerpStepScale = Camera_ClampLerpScale(camera, sp72 ? roData->unk_1C : roData->unk_18);
|
||||||
rwData->unk_26 &= ~1;
|
rwData->unk_26 &= ~1;
|
||||||
|
|
||||||
|
// @recomp Save timer4 in unused bits of timer2.
|
||||||
|
rwData->timer2 |= timer4 << 5;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1582,7 +1654,7 @@ RECOMP_PATCH s32 Camera_Normal3(Camera* camera) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @recomp Update the analog camera.
|
// @recomp Update the analog camera.
|
||||||
if (recomp_analog_cam_enabled()) {
|
if (recomp_get_analog_cam_enabled()) {
|
||||||
update_analog_cam(camera);
|
update_analog_cam(camera);
|
||||||
|
|
||||||
if (analog_cam_active) {
|
if (analog_cam_active) {
|
||||||
@ -1820,7 +1892,7 @@ RECOMP_PATCH s32 Camera_Jump3(Camera* camera) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @recomp Update the analog camera.
|
// @recomp Update the analog camera.
|
||||||
if (recomp_analog_cam_enabled()) {
|
if (recomp_get_analog_cam_enabled()) {
|
||||||
update_analog_cam(camera);
|
update_analog_cam(camera);
|
||||||
|
|
||||||
if (analog_cam_active) {
|
if (analog_cam_active) {
|
||||||
@ -1934,7 +2006,7 @@ RECOMP_PATCH void func_809EC568(Boss04* this, PlayState* play) {
|
|||||||
this->unk_2D0 = 2000.0f;
|
this->unk_2D0 = 2000.0f;
|
||||||
// @recomp do not require being in c-up mode if analog cam is enabled
|
// @recomp do not require being in c-up mode if analog cam is enabled
|
||||||
// also, use the new variable instead of the vanilla value to check if the player is looking at the boss.
|
// also, use the new variable instead of the vanilla value to check if the player is looking at the boss.
|
||||||
if (((player->stateFlags1 & PLAYER_STATE1_100000) || (recomp_analog_cam_enabled())) && (this->actor.projectedPos.z > 0.0f) &&
|
if (((player->stateFlags1 & PLAYER_STATE1_100000) || (recomp_get_analog_cam_enabled())) && (this->actor.projectedPos.z > 0.0f) &&
|
||||||
(fabsf(this->actor.projectedPos.x) < maxProjectedPosToStartFight) && (fabsf(this->actor.projectedPos.y) < maxProjectedPosToStartFight)) {
|
(fabsf(this->actor.projectedPos.x) < maxProjectedPosToStartFight) && (fabsf(this->actor.projectedPos.y) < maxProjectedPosToStartFight)) {
|
||||||
if ((this->unk_704 >= 15) && (CutsceneManager_GetCurrentCsId() == CS_ID_NONE)) {
|
if ((this->unk_704 >= 15) && (CutsceneManager_GetCurrentCsId() == CS_ID_NONE)) {
|
||||||
Actor* boss;
|
Actor* boss;
|
||||||
|
16
patches/custom_syms.toml
Normal file
16
patches/custom_syms.toml
Normal file
@ -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 },
|
||||||
|
]
|
@ -17,7 +17,7 @@ RECOMP_PATCH void TransitionWipe3_Draw(void* thisx, Gfx** gfxP) {
|
|||||||
|
|
||||||
// @recomp Modify the scale based on the aspect ratio to make sure the transition circle covers the whole screen
|
// @recomp Modify the scale based on the aspect ratio to make sure the transition circle covers the whole screen
|
||||||
float original_aspect_ratio = ((float)SCREEN_WIDTH) / ((float)SCREEN_HEIGHT);
|
float original_aspect_ratio = ((float)SCREEN_WIDTH) / ((float)SCREEN_HEIGHT);
|
||||||
scale *= recomp_get_aspect_ratio(original_aspect_ratio) / original_aspect_ratio;
|
scale *= recomp_get_target_aspect_ratio(original_aspect_ratio) / original_aspect_ratio;
|
||||||
|
|
||||||
THIS->frame ^= 1;
|
THIS->frame ^= 1;
|
||||||
gDPPipeSync(gfx++);
|
gDPPipeSync(gfx++);
|
||||||
@ -129,7 +129,7 @@ RECOMP_PATCH void Play_DrawMotionBlur(PlayState* this) {
|
|||||||
RECOMP_PATCH void Actor_DrawLensOverlay(Gfx** gfxP, s32 lensMaskSize) {
|
RECOMP_PATCH void Actor_DrawLensOverlay(Gfx** gfxP, s32 lensMaskSize) {
|
||||||
// @recomp Calculate the increase in aspect ratio.
|
// @recomp Calculate the increase in aspect ratio.
|
||||||
f32 original_aspect_ratio = (float)SCREEN_WIDTH / SCREEN_HEIGHT;
|
f32 original_aspect_ratio = (float)SCREEN_WIDTH / SCREEN_HEIGHT;
|
||||||
f32 aspect_ratio_scale = recomp_get_aspect_ratio(original_aspect_ratio) / original_aspect_ratio;
|
f32 aspect_ratio_scale = recomp_get_target_aspect_ratio(original_aspect_ratio) / original_aspect_ratio;
|
||||||
|
|
||||||
// @recomp Increase the circle's scale based on the aspect ratio scale. Also increase the base scaling
|
// @recomp Increase the circle's scale based on the aspect ratio scale. Also increase the base scaling
|
||||||
// from 0.003f to 0.004f to account for overscan removal.
|
// from 0.003f to 0.004f to account for overscan removal.
|
||||||
|
@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
#include "patch_helpers.h"
|
#include "patch_helpers.h"
|
||||||
|
|
||||||
DECLARE_FUNC(float, recomp_get_aspect_ratio, float);
|
DECLARE_FUNC(void, recomp_get_window_resolution, u32*, u32*);
|
||||||
|
DECLARE_FUNC(float, recomp_get_target_aspect_ratio, float);
|
||||||
DECLARE_FUNC(s32, recomp_get_target_framerate, s32);
|
DECLARE_FUNC(s32, recomp_get_target_framerate, s32);
|
||||||
DECLARE_FUNC(s32, recomp_high_precision_fb_enabled);
|
DECLARE_FUNC(s32, recomp_high_precision_fb_enabled);
|
||||||
DECLARE_FUNC(float, recomp_get_resolution_scale);
|
DECLARE_FUNC(float, recomp_get_resolution_scale);
|
||||||
|
178
patches/input.c
178
patches/input.c
@ -6,21 +6,35 @@
|
|||||||
#include "z64voice.h"
|
#include "z64voice.h"
|
||||||
#include "audiothread_cmd.h"
|
#include "audiothread_cmd.h"
|
||||||
|
|
||||||
|
RECOMP_DECLARE_EVENT(recomp_before_first_person_aiming_update_event(PlayState* play, Player* this, bool in_free_look, RecompAimingOverideMode* recomp_aiming_override_mode));
|
||||||
|
RECOMP_DECLARE_EVENT(recomp_after_first_person_aiming_update_event(PlayState* play, Player* this, bool in_free_look));
|
||||||
|
|
||||||
s32 func_80847190(PlayState* play, Player* this, s32 arg2);
|
s32 func_80847190(PlayState* play, Player* this, s32 arg2);
|
||||||
s16 func_80832754(Player* this, s32 arg1);
|
s16 func_80832754(Player* this, s32 arg1);
|
||||||
s32 func_8082EF20(Player* this);
|
s32 func_8082EF20(Player* this);
|
||||||
|
|
||||||
|
// This flag is reset every frame by 'poll_inputs()'.
|
||||||
|
RecompAimingOverideMode recomp_aiming_override_mode = RECOMP_AIMING_OVERRIDE_OFF;
|
||||||
|
|
||||||
// @recomp Patched to add gyro and mouse aiming.
|
// @recomp Patched to add gyro and mouse aiming.
|
||||||
RECOMP_PATCH s32 func_80847190(PlayState* play, Player* this, s32 arg2) {
|
RECOMP_PATCH s32 func_80847190(PlayState* play, Player* this, s32 arg2) {
|
||||||
s32 pad;
|
s32 pad;
|
||||||
s16 var_s0;
|
s16 var_s0;
|
||||||
|
|
||||||
|
// Checks if we're in free look (C-Up look around mode).
|
||||||
|
bool in_free_look = (!func_800B7128(this) && !func_8082EF20(this) && !arg2);
|
||||||
|
|
||||||
|
// Checking if any mods have disabled aiming with the left stick.
|
||||||
|
recomp_before_first_person_aiming_update_event(play, this, in_free_look, &recomp_aiming_override_mode);
|
||||||
|
|
||||||
// @recomp Get the aiming camera inversion state.
|
// @recomp Get the aiming camera inversion state.
|
||||||
s32 inverted_x, inverted_y;
|
s32 inverted_x, inverted_y;
|
||||||
recomp_get_inverted_axes(&inverted_x, &inverted_y);
|
recomp_get_inverted_axes(&inverted_x, &inverted_y);
|
||||||
// @recomp Get the analog camera input values if analog cam is enabled.
|
|
||||||
|
// @recomp Get the analog camera input values if analog cam is enabled, or right-stick aiming is being forced.
|
||||||
s32 analog_x = 0;
|
s32 analog_x = 0;
|
||||||
s32 analog_y = 0;
|
s32 analog_y = 0;
|
||||||
if (recomp_analog_cam_enabled()) {
|
if (recomp_get_analog_cam_enabled() || recomp_aiming_override_mode == RECOMP_AIMING_OVERRIDE_FORCE_RIGHT_STICK) {
|
||||||
float analog_x_float = 0.0f;
|
float analog_x_float = 0.0f;
|
||||||
float analog_y_float = 0.0f;
|
float analog_y_float = 0.0f;
|
||||||
recomp_get_camera_inputs(&analog_x_float, &analog_y_float);
|
recomp_get_camera_inputs(&analog_x_float, &analog_y_float);
|
||||||
@ -35,9 +49,13 @@ RECOMP_PATCH s32 func_80847190(PlayState* play, Player* this, s32 arg2) {
|
|||||||
// play->state.input[0].rel.stick_x, play->state.input[0].rel.stick_y,
|
// play->state.input[0].rel.stick_x, play->state.input[0].rel.stick_y,
|
||||||
// analog_x, analog_y);
|
// analog_x, analog_y);
|
||||||
|
|
||||||
if (!func_800B7128(this) && !func_8082EF20(this) && !arg2) {
|
if (in_free_look) {
|
||||||
// @recomp Add in the analog camera Y input. Clamp to prevent moving the camera twice as fast if both sticks are held.
|
// @recomp Add in the analog camera Y input. Clamp to prevent moving the camera twice as fast if both sticks are held.
|
||||||
var_s0 = CLAMP(play->state.input[0].rel.stick_y + analog_y, -61, 61) * 0xF0;
|
s32 cam_input_y = analog_y;
|
||||||
|
if (recomp_aiming_override_mode == RECOMP_AIMING_OVERRIDE_OFF) {
|
||||||
|
cam_input_y += play->state.input[0].rel.stick_y;
|
||||||
|
}
|
||||||
|
var_s0 = CLAMP(cam_input_y, -61, 61) * 0xF0;
|
||||||
|
|
||||||
// @recomp Invert the Y axis accordingly (default is inverted, so negate if not inverted).
|
// @recomp Invert the Y axis accordingly (default is inverted, so negate if not inverted).
|
||||||
if (!inverted_y) {
|
if (!inverted_y) {
|
||||||
@ -46,7 +64,11 @@ RECOMP_PATCH s32 func_80847190(PlayState* play, Player* this, s32 arg2) {
|
|||||||
Math_SmoothStepToS(&this->actor.focus.rot.x, var_s0, 0xE, 0xFA0, 0x1E);
|
Math_SmoothStepToS(&this->actor.focus.rot.x, var_s0, 0xE, 0xFA0, 0x1E);
|
||||||
|
|
||||||
// @recomp Add in the analog camera X input. Clamp to prevent moving the camera twice as fast if both sticks are held.
|
// @recomp Add in the analog camera X input. Clamp to prevent moving the camera twice as fast if both sticks are held.
|
||||||
var_s0 = CLAMP(play->state.input[0].rel.stick_x + analog_x, -61, 61) * -0x10;
|
s32 cam_input_x = analog_x;
|
||||||
|
if (recomp_aiming_override_mode == RECOMP_AIMING_OVERRIDE_OFF) {
|
||||||
|
cam_input_x += play->state.input[0].rel.stick_x;
|
||||||
|
}
|
||||||
|
var_s0 = CLAMP(cam_input_x, -61, 61) * -0x10;
|
||||||
|
|
||||||
// @recomp Invert the X axis accordingly
|
// @recomp Invert the X axis accordingly
|
||||||
if (inverted_x) {
|
if (inverted_x) {
|
||||||
@ -97,7 +119,13 @@ RECOMP_PATCH s32 func_80847190(PlayState* play, Player* this, s32 arg2) {
|
|||||||
|
|
||||||
// @recomp Invert the Y axis accordingly (default is inverted, so negate if not inverted).
|
// @recomp Invert the Y axis accordingly (default is inverted, so negate if not inverted).
|
||||||
// Also add in the analog camera Y input. Clamp to prevent moving the camera twice as fast if both sticks are held.
|
// Also add in the analog camera Y input. Clamp to prevent moving the camera twice as fast if both sticks are held.
|
||||||
s32 stick_y = CLAMP(play->state.input[0].rel.stick_y + analog_y, -61, 61);
|
s32 cam_input_y = analog_y;
|
||||||
|
if (recomp_aiming_override_mode == RECOMP_AIMING_OVERRIDE_OFF) {
|
||||||
|
cam_input_y += play->state.input[0].rel.stick_y;
|
||||||
|
}
|
||||||
|
s32 stick_y;
|
||||||
|
stick_y = CLAMP(cam_input_y, -61, 61);
|
||||||
|
|
||||||
if (!inverted_y) {
|
if (!inverted_y) {
|
||||||
stick_y = -stick_y;
|
stick_y = -stick_y;
|
||||||
}
|
}
|
||||||
@ -118,7 +146,13 @@ RECOMP_PATCH s32 func_80847190(PlayState* play, Player* this, s32 arg2) {
|
|||||||
|
|
||||||
// @recomp Invert the X axis accordingly. Also add in the analog camera Y input.
|
// @recomp Invert the X axis accordingly. Also add in the analog camera Y input.
|
||||||
// Clamp to prevent moving the camera twice as fast if both sticks are held.
|
// Clamp to prevent moving the camera twice as fast if both sticks are held.
|
||||||
s32 stick_x = CLAMP(play->state.input[0].rel.stick_x + analog_x, -61, 61);
|
s32 cam_input_x = analog_x;
|
||||||
|
if (recomp_aiming_override_mode == RECOMP_AIMING_OVERRIDE_OFF) {
|
||||||
|
cam_input_x += play->state.input[0].rel.stick_x;
|
||||||
|
}
|
||||||
|
s32 stick_x;
|
||||||
|
stick_x = CLAMP(cam_input_x, -61, 61);
|
||||||
|
|
||||||
if (inverted_x) {
|
if (inverted_x) {
|
||||||
stick_x = -stick_x;
|
stick_x = -stick_x;
|
||||||
}
|
}
|
||||||
@ -130,6 +164,8 @@ RECOMP_PATCH s32 func_80847190(PlayState* play, Player* this, s32 arg2) {
|
|||||||
this->actor.focus.rot.y = CLAMP(var_s0, -0x4AAA, 0x4AAA) + this->actor.shape.rot.y;
|
this->actor.focus.rot.y = CLAMP(var_s0, -0x4AAA, 0x4AAA) + this->actor.shape.rot.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
recomp_after_first_person_aiming_update_event(play, this, in_free_look);
|
||||||
|
|
||||||
this->unk_AA6 |= 2;
|
this->unk_AA6 |= 2;
|
||||||
|
|
||||||
return func_80832754(this, (play->unk_1887C != 0) || func_800B7128(this) || func_8082EF20(this));
|
return func_80832754(this, (play->unk_1887C != 0) || func_800B7128(this) || func_8082EF20(this));
|
||||||
@ -162,7 +198,7 @@ RECOMP_PATCH void func_8083A98C(Actor* thisx, PlayState* play2) {
|
|||||||
// @recomp Get the analog camera input values if analog cam is enabled.
|
// @recomp Get the analog camera input values if analog cam is enabled.
|
||||||
s32 analog_x = 0;
|
s32 analog_x = 0;
|
||||||
s32 analog_y = 0;
|
s32 analog_y = 0;
|
||||||
if (recomp_analog_cam_enabled()) {
|
if (recomp_get_analog_cam_enabled()) {
|
||||||
float analog_x_float = 0.0f;
|
float analog_x_float = 0.0f;
|
||||||
float analog_y_float = 0.0f;
|
float analog_y_float = 0.0f;
|
||||||
recomp_get_camera_inputs(&analog_x_float, &analog_y_float);
|
recomp_get_camera_inputs(&analog_x_float, &analog_y_float);
|
||||||
@ -534,11 +570,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);
|
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 sPictoState;
|
||||||
extern s16 sPictoPhotoBeingTaken;
|
extern s16 sPictoPhotoBeingTaken;
|
||||||
extern void* gWorkBuffer;
|
extern void* gWorkBuffer;
|
||||||
u16 func_801A5100(void);
|
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 Patched to update status of extra buttons via set_extra_item_slot_status.
|
||||||
RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) {
|
RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) {
|
||||||
InterfaceContext* interfaceCtx = &play->interfaceCtx;
|
InterfaceContext* interfaceCtx = &play->interfaceCtx;
|
||||||
@ -548,11 +604,11 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) {
|
|||||||
|
|
||||||
if (gSaveContext.save.cutsceneIndex < 0xFFF0) {
|
if (gSaveContext.save.cutsceneIndex < 0xFFF0) {
|
||||||
gSaveContext.hudVisibilityForceButtonAlphasByStatus = false;
|
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))) {
|
(!CHECK_EVENTINF(EVENTINF_41) && (play->unk_1887C >= 2))) {
|
||||||
// Riding Epona OR Honey & Darling minigame OR Horseback balloon minigame OR related to swamp boat
|
// Riding Epona OR Honey & Darling minigame OR Horseback balloon minigame OR related to swamp boat
|
||||||
// (non-minigame?)
|
// (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)) {
|
(gSaveContext.bButtonStatus == BTN_DISABLED)) {
|
||||||
// Riding Epona with blast mask?
|
// Riding Epona with blast mask?
|
||||||
restoreHudVisibility = true;
|
restoreHudVisibility = true;
|
||||||
@ -603,24 +659,46 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) {
|
|||||||
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
|
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
|
||||||
set_extra_item_slot_status(BTN_DISABLED);
|
set_extra_item_slot_status(BTN_DISABLED);
|
||||||
} else {
|
} 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) {
|
if (play->unk_1887C >= 2) {
|
||||||
Interface_LoadItemIconImpl(play, EQUIP_SLOT_B);
|
Interface_LoadItemIconImpl(play, EQUIP_SLOT_B);
|
||||||
} else if (gSaveContext.save.saveInfo.inventory.items[SLOT_BOW] == ITEM_NONE) {
|
} 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 {
|
} else {
|
||||||
Interface_LoadItemIconImpl(play, EQUIP_SLOT_B);
|
Interface_LoadItemIconImpl(play, EQUIP_SLOT_B);
|
||||||
}
|
}
|
||||||
|
|
||||||
BUTTON_STATUS(EQUIP_SLOT_C_LEFT) = BTN_DISABLED;
|
// @recomp_use_export_var no_bow_epona_fix: If the B button does not contain a sword, don't disable the UI.
|
||||||
BUTTON_STATUS(EQUIP_SLOT_C_DOWN) = BTN_DISABLED;
|
if (!EPONA_FIX_ACTIVE || BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) < ITEM_SWORD_KOKIRI ||
|
||||||
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
|
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) > ITEM_SWORD_GILDED) {
|
||||||
set_extra_item_slot_status(BTN_DISABLED);
|
BUTTON_STATUS(EQUIP_SLOT_C_LEFT) = BTN_DISABLED;
|
||||||
Interface_SetHudVisibility(HUD_VISIBILITY_A_HEARTS_MAGIC_MINIMAP_WITH_OVERWRITE);
|
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) {
|
if (play->transitionMode != TRANS_MODE_OFF) {
|
||||||
Interface_SetHudVisibility(HUD_VISIBILITY_NONE);
|
Interface_SetHudVisibility(HUD_VISIBILITY_NONE);
|
||||||
} else if ((gSaveContext.minigameStatus == MINIGAME_STATUS_ACTIVE) &&
|
} else if ((gSaveContext.minigameStatus == MINIGAME_STATUS_ACTIVE) &&
|
||||||
@ -640,12 +718,12 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) {
|
|||||||
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
|
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
|
||||||
set_extra_item_slot_status(BTN_DISABLED);
|
set_extra_item_slot_status(BTN_DISABLED);
|
||||||
Interface_SetHudVisibility(HUD_VISIBILITY_A_B_MINIMAP);
|
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);
|
Interface_SetHudVisibility(HUD_VISIBILITY_A_B_MINIMAP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (player->stateFlags1 & PLAYER_STATE1_800000) {
|
if (ON_EPONA) {
|
||||||
Interface_SetHudVisibility(HUD_VISIBILITY_A_B_MINIMAP);
|
Interface_SetHudVisibility(HUD_VISIBILITY_A_B_MINIMAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -657,32 +735,59 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) {
|
|||||||
} else {
|
} else {
|
||||||
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW;
|
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_LEFT) = BTN_DISABLED;
|
||||||
BUTTON_STATUS(EQUIP_SLOT_C_DOWN) = BTN_DISABLED;
|
BUTTON_STATUS(EQUIP_SLOT_C_DOWN) = BTN_DISABLED;
|
||||||
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
|
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
|
||||||
set_extra_item_slot_status(BTN_DISABLED);
|
set_extra_item_slot_status(BTN_DISABLED);
|
||||||
} else {
|
} 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) {
|
if (play->unk_1887C >= 2) {
|
||||||
Interface_LoadItemIconImpl(play, EQUIP_SLOT_B);
|
Interface_LoadItemIconImpl(play, EQUIP_SLOT_B);
|
||||||
} else if (gSaveContext.save.saveInfo.inventory.items[SLOT_BOW] == ITEM_NONE) {
|
} else if (gSaveContext.save.saveInfo.inventory.items[SLOT_BOW] == ITEM_NONE && !H_AND_D_FIX_ACTIVE) {
|
||||||
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 {
|
} else {
|
||||||
Interface_LoadItemIconImpl(play, EQUIP_SLOT_B);
|
Interface_LoadItemIconImpl(play, EQUIP_SLOT_B);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BUTTON_STATUS(EQUIP_SLOT_B) == BTN_DISABLED) {
|
if (BUTTON_STATUS(EQUIP_SLOT_B) == BTN_DISABLED) {
|
||||||
BUTTON_STATUS(EQUIP_SLOT_B) = BTN_ENABLED;
|
// @recomp_use_export_var no_bow_epona_fix: Don't enable the B button unless it is being used for the bow.
|
||||||
restoreHudVisibility = true;
|
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;
|
// @recomp_use_export_var no_bow_epona_fix: If the B button does not contain the bow, don't disable the UI.
|
||||||
BUTTON_STATUS(EQUIP_SLOT_C_DOWN) = BTN_DISABLED;
|
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_RIGHT) = BTN_DISABLED;
|
BUTTON_STATUS(EQUIP_SLOT_C_LEFT) = BTN_DISABLED;
|
||||||
set_extra_item_slot_status(BTN_DISABLED);
|
BUTTON_STATUS(EQUIP_SLOT_C_DOWN) = BTN_DISABLED;
|
||||||
Interface_SetHudVisibility(HUD_VISIBILITY_A_HEARTS_MAGIC_MINIMAP_WITH_OVERWRITE);
|
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) {
|
if (play->transitionMode != TRANS_MODE_OFF) {
|
||||||
Interface_SetHudVisibility(HUD_VISIBILITY_NONE);
|
Interface_SetHudVisibility(HUD_VISIBILITY_NONE);
|
||||||
@ -700,8 +805,11 @@ RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) {
|
|||||||
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
|
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
|
||||||
set_extra_item_slot_status(BTN_DISABLED);
|
set_extra_item_slot_status(BTN_DISABLED);
|
||||||
Interface_SetHudVisibility(HUD_VISIBILITY_A_B_MINIMAP);
|
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);
|
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) {
|
} else if (sPictoState != PICTO_BOX_STATE_OFF) {
|
||||||
@ -802,6 +910,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
|
* A continuation of the if-else chain from Interface_UpdateButtonsPart1
|
||||||
* Also used directly when opening the pause menu i.e. skips part 1
|
* Also used directly when opening the pause menu i.e. skips part 1
|
||||||
@ -1181,7 +1296,8 @@ RECOMP_PATCH void Interface_UpdateButtonsPart2(PlayState* play) {
|
|||||||
}
|
}
|
||||||
} else if (GET_CUR_FORM_BTN_ITEM(i) == ITEM_MASK_FIERCE_DEITY) {
|
} else if (GET_CUR_FORM_BTN_ITEM(i) == ITEM_MASK_FIERCE_DEITY) {
|
||||||
// Fierce Deity's Mask is equipped
|
// 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_SEA_BS) && (play->sceneId != SCENE_INISIE_BS) &&
|
||||||
(play->sceneId != SCENE_LAST_BS)) {
|
(play->sceneId != SCENE_LAST_BS)) {
|
||||||
if (BUTTON_STATUS(i) != BTN_DISABLED) {
|
if (BUTTON_STATUS(i) != BTN_DISABLED) {
|
||||||
|
@ -8,13 +8,20 @@ typedef enum {
|
|||||||
RECOMP_CAMERA_DUALANALOG,
|
RECOMP_CAMERA_DUALANALOG,
|
||||||
} RecompCameraMode;
|
} RecompCameraMode;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
RECOMP_AIMING_OVERRIDE_OFF = 0,
|
||||||
|
RECOMP_AIMING_OVERRIDE_DISABLE_LEFT_STICK = 1,
|
||||||
|
RECOMP_AIMING_OVERRIDE_FORCE_RIGHT_STICK = 2
|
||||||
|
} RecompAimingOverideMode;
|
||||||
|
|
||||||
extern RecompCameraMode recomp_camera_mode;
|
extern RecompCameraMode recomp_camera_mode;
|
||||||
|
extern RecompAimingOverideMode recomp_aiming_override_mode;
|
||||||
|
|
||||||
DECLARE_FUNC(void, recomp_get_gyro_deltas, float* x, float* y);
|
DECLARE_FUNC(void, recomp_get_gyro_deltas, float* x, float* y);
|
||||||
DECLARE_FUNC(void, recomp_get_mouse_deltas, float* x, float* y);
|
DECLARE_FUNC(void, recomp_get_mouse_deltas, float* x, float* y);
|
||||||
DECLARE_FUNC(s32, recomp_get_targeting_mode);
|
DECLARE_FUNC(s32, recomp_get_targeting_mode);
|
||||||
DECLARE_FUNC(void, recomp_get_inverted_axes, s32* x, s32* y);
|
DECLARE_FUNC(void, recomp_get_inverted_axes, s32* x, s32* y);
|
||||||
DECLARE_FUNC(s32, recomp_analog_cam_enabled);
|
DECLARE_FUNC(s32, recomp_get_analog_cam_enabled);
|
||||||
DECLARE_FUNC(void, recomp_get_analog_inverted_axes, s32* x, s32* y);
|
DECLARE_FUNC(void, recomp_get_analog_inverted_axes, s32* x, s32* y);
|
||||||
DECLARE_FUNC(void, recomp_get_camera_inputs, float* x, float* y);
|
DECLARE_FUNC(void, recomp_get_camera_inputs, float* x, float* y);
|
||||||
DECLARE_FUNC(void, recomp_set_right_analog_suppressed, s32 suppressed);
|
DECLARE_FUNC(void, recomp_set_right_analog_suppressed, s32 suppressed);
|
||||||
|
@ -70,8 +70,11 @@ void poll_inputs(void) {
|
|||||||
// Begin reading controller data
|
// Begin reading controller data
|
||||||
osContStartReadData(serialEventQueue);
|
osContStartReadData(serialEventQueue);
|
||||||
|
|
||||||
|
bool needs_right_stick = recomp_get_analog_cam_enabled() || recomp_aiming_override_mode == RECOMP_AIMING_OVERRIDE_FORCE_RIGHT_STICK;
|
||||||
// Suppress the right analog stick if analog camera is active unless the ocarina is in use.
|
// Suppress the right analog stick if analog camera is active unless the ocarina is in use.
|
||||||
recomp_set_right_analog_suppressed(recomp_analog_cam_enabled() && sOcarinaInstrumentId == OCARINA_INSTRUMENT_OFF);
|
recomp_set_right_analog_suppressed(needs_right_stick && sOcarinaInstrumentId == OCARINA_INSTRUMENT_OFF);
|
||||||
|
// Resets this flag for the next frame;
|
||||||
|
recomp_aiming_override_mode = RECOMP_AIMING_OVERRIDE_OFF;
|
||||||
|
|
||||||
// Wait for controller data
|
// Wait for controller data
|
||||||
osRecvMesg(serialEventQueue, NULL, OS_MESG_BLOCK);
|
osRecvMesg(serialEventQueue, NULL, OS_MESG_BLOCK);
|
||||||
|
@ -10,6 +10,6 @@ DECLARE_FUNC(void, recomp_handle_quicksave_actions, OSMesgQueue* enter_mq, OSMes
|
|||||||
DECLARE_FUNC(void, recomp_handle_quicksave_actions_main, OSMesgQueue* enter_mq, OSMesgQueue* exit_mq);
|
DECLARE_FUNC(void, recomp_handle_quicksave_actions_main, OSMesgQueue* enter_mq, OSMesgQueue* exit_mq);
|
||||||
DECLARE_FUNC(u16, recomp_get_pending_warp);
|
DECLARE_FUNC(u16, recomp_get_pending_warp);
|
||||||
DECLARE_FUNC(u32, recomp_get_pending_set_time);
|
DECLARE_FUNC(u32, recomp_get_pending_set_time);
|
||||||
DECLARE_FUNC(s32, recomp_autosave_enabled);
|
DECLARE_FUNC(s32, recomp_get_autosave_enabled);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -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.
|
* Load the save for the appropriate file and start the game.
|
||||||
* Update function for `SM_LOAD_GAME`
|
* Update function for `SM_LOAD_GAME`
|
||||||
@ -1060,7 +1063,7 @@ RECOMP_PATCH void FileSelect_LoadGame(GameState* thisx) {
|
|||||||
gSaveContext.gameMode = GAMEMODE_NORMAL;
|
gSaveContext.gameMode = GAMEMODE_NORMAL;
|
||||||
|
|
||||||
STOP_GAMESTATE(&this->state);
|
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.respawnFlag = 0;
|
||||||
gSaveContext.respawn[RESPAWN_MODE_DOWN].entrance = ENTR_LOAD_OPENING;
|
gSaveContext.respawn[RESPAWN_MODE_DOWN].entrance = ENTR_LOAD_OPENING;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#ifdef MIPS
|
#ifdef MIPS
|
||||||
#include "ultra64.h"
|
#include "ultra64.h"
|
||||||
#else
|
#else
|
||||||
#include "librecomp/recomp.h"
|
#include "recomp.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -4,6 +4,11 @@
|
|||||||
#define RECOMP_EXPORT __attribute__((section(".recomp_export")))
|
#define RECOMP_EXPORT __attribute__((section(".recomp_export")))
|
||||||
#define RECOMP_PATCH __attribute__((section(".recomp_patch")))
|
#define RECOMP_PATCH __attribute__((section(".recomp_patch")))
|
||||||
#define RECOMP_FORCE_PATCH __attribute__((section(".recomp_force_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
|
// TODO fix renaming symbols in patch recompilation
|
||||||
#define osCreateMesgQueue osCreateMesgQueue_recomp
|
#define osCreateMesgQueue osCreateMesgQueue_recomp
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
RAMBASE = 0x80801000; /* Used to hold any new symbols */
|
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 {
|
MEMORY {
|
||||||
extram : ORIGIN = RAMBASE, LENGTH = 64M
|
extram : ORIGIN = RAMBASE, LENGTH = 64M
|
||||||
@ -12,10 +12,11 @@ SECTIONS {
|
|||||||
.text : { *(.text*) } >extram AT >rom
|
.text : { *(.text*) } >extram AT >rom
|
||||||
.recomp_patch : { *(.recomp_patch*) *(.recomp_force_patch*) } >extram AT >rom
|
.recomp_patch : { *(.recomp_patch*) *(.recomp_force_patch*) } >extram AT >rom
|
||||||
.recomp_export : { *(.recomp_export*) } >extram AT >rom
|
.recomp_export : { *(.recomp_export*) } >extram AT >rom
|
||||||
|
.recomp_event : { *(.recomp_event*) } >extram AT >rom
|
||||||
.rodata : { *(.rodata*) } >extram AT >rom
|
.rodata : { *(.rodata*) } >extram AT >rom
|
||||||
.data : { *(.data*) } >extram AT >rom
|
.data : { *(.data*) } >extram AT >rom
|
||||||
.bss (NOLOAD) : { *(.bss*) *(COMMON) } >extram
|
.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*) }
|
.reloc 0 : { *(.reloc*) }
|
||||||
.symtab 0 : { *(.symtab) }
|
.symtab 0 : { *(.symtab) }
|
||||||
|
@ -1,9 +1,37 @@
|
|||||||
#include "play_patches.h"
|
#include "play_patches.h"
|
||||||
#include "z64debug_display.h"
|
#include "z64debug_display.h"
|
||||||
#include "input.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;
|
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) {
|
void controls_play_update(PlayState* play) {
|
||||||
gSaveContext.options.zTargetSetting = recomp_get_targeting_mode();
|
gSaveContext.options.zTargetSetting = recomp_get_targeting_mode();
|
||||||
}
|
}
|
||||||
@ -13,6 +41,9 @@ RECOMP_PATCH void Play_Main(GameState* thisx) {
|
|||||||
static Input* prevInput = NULL;
|
static Input* prevInput = NULL;
|
||||||
PlayState* this = (PlayState*)thisx;
|
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
|
// @recomp
|
||||||
debug_play_update(this);
|
debug_play_update(this);
|
||||||
controls_play_update(this);
|
controls_play_update(this);
|
||||||
@ -32,7 +63,15 @@ RECOMP_PATCH void Play_Main(GameState* thisx) {
|
|||||||
this->state.gfxCtx = NULL;
|
this->state.gfxCtx = NULL;
|
||||||
}
|
}
|
||||||
camera_pre_play_update(this);
|
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);
|
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);
|
camera_post_play_update(this);
|
||||||
analog_cam_post_play_update(this);
|
analog_cam_post_play_update(this);
|
||||||
autosave_post_play_update(this);
|
autosave_post_play_update(this);
|
||||||
@ -83,3 +122,322 @@ RECOMP_PATCH s32 Room_HandleLoadCallbacks(PlayState* play, RoomContext* roomCtx)
|
|||||||
|
|
||||||
return 1;
|
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;
|
return (void*)1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int recomp_printf(const char* fmt, ...) {
|
RECOMP_EXPORT int recomp_printf(const char* fmt, ...) {
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
|
|
||||||
|
@ -7,6 +7,8 @@ void Main_InitMemory(void);
|
|||||||
void Main_InitScreen(void);
|
void Main_InitScreen(void);
|
||||||
|
|
||||||
|
|
||||||
|
RECOMP_DECLARE_EVENT(recomp_on_init());
|
||||||
|
|
||||||
// @recomp Patched to load the code segment in the recomp runtime.
|
// @recomp Patched to load the code segment in the recomp runtime.
|
||||||
RECOMP_PATCH void Main_Init(void) {
|
RECOMP_PATCH void Main_Init(void) {
|
||||||
DmaRequest dmaReq;
|
DmaRequest dmaReq;
|
||||||
@ -14,6 +16,9 @@ RECOMP_PATCH void Main_Init(void) {
|
|||||||
OSMesg msg[1];
|
OSMesg msg[1];
|
||||||
size_t prevSize;
|
size_t prevSize;
|
||||||
|
|
||||||
|
// @recomp_event recomp_on_init(): Allow mods to initialize themselves once.
|
||||||
|
recomp_on_init();
|
||||||
|
|
||||||
osCreateMesgQueue(&mq, msg, ARRAY_COUNT(msg));
|
osCreateMesgQueue(&mq, msg, ARRAY_COUNT(msg));
|
||||||
|
|
||||||
prevSize = gDmaMgrDmaBuffSize;
|
prevSize = gDmaMgrDmaBuffSize;
|
||||||
|
@ -150,7 +150,7 @@ RECOMP_PATCH void Environment_DrawSkyboxStarsImpl(PlayState* play, Gfx** gfxP) {
|
|||||||
f32(*viewProjectionMtxF)[4];
|
f32(*viewProjectionMtxF)[4];
|
||||||
// @recomp Get the original and actual aspect ratios.
|
// @recomp Get the original and actual aspect ratios.
|
||||||
f32 original_aspect_ratio = view_aspect_ratio(&play->view);
|
f32 original_aspect_ratio = view_aspect_ratio(&play->view);
|
||||||
f32 recomp_aspect_ratio = recomp_get_aspect_ratio(original_aspect_ratio);
|
f32 recomp_aspect_ratio = recomp_get_target_aspect_ratio(original_aspect_ratio);
|
||||||
f32 recomp_aspect_ratio_scale = recomp_aspect_ratio / original_aspect_ratio;
|
f32 recomp_aspect_ratio_scale = recomp_aspect_ratio / original_aspect_ratio;
|
||||||
|
|
||||||
// @recomp Store the original billboard matrix.
|
// @recomp Store the original billboard matrix.
|
||||||
|
@ -3,12 +3,12 @@ __start = 0x80000000;
|
|||||||
/* Dummy addresses that get recompiled into function calls */
|
/* Dummy addresses that get recompiled into function calls */
|
||||||
recomp_puts = 0x8F000000;
|
recomp_puts = 0x8F000000;
|
||||||
recomp_exit = 0x8F000004;
|
recomp_exit = 0x8F000004;
|
||||||
recomp_handle_quicksave_actions = 0x8F000008;
|
/* recomp_handle_quicksave_actions = 0x8F000008;
|
||||||
recomp_handle_quicksave_actions_main = 0x8F00000C;
|
recomp_handle_quicksave_actions_main = 0x8F00000C; */
|
||||||
osRecvMesg_recomp = 0x8F000010;
|
osRecvMesg_recomp = 0x8F000010;
|
||||||
osSendMesg_recomp = 0x8F000014;
|
osSendMesg_recomp = 0x8F000014;
|
||||||
recomp_get_gyro_deltas = 0x8F000018;
|
recomp_get_gyro_deltas = 0x8F000018;
|
||||||
recomp_get_aspect_ratio = 0x8F00001C;
|
recomp_get_target_aspect_ratio = 0x8F00001C;
|
||||||
recomp_get_pending_warp = 0x8F000020;
|
recomp_get_pending_warp = 0x8F000020;
|
||||||
recomp_powf = 0x8F000024;
|
recomp_powf = 0x8F000024;
|
||||||
recomp_get_target_framerate = 0x8F000028;
|
recomp_get_target_framerate = 0x8F000028;
|
||||||
@ -34,13 +34,14 @@ osContGetQuery_recomp = 0x8F00007C;
|
|||||||
recomp_get_mouse_deltas = 0x8F000080;
|
recomp_get_mouse_deltas = 0x8F000080;
|
||||||
bcmp_recomp = 0x8F000084;
|
bcmp_recomp = 0x8F000084;
|
||||||
osGetTime_recomp = 0x8F000088;
|
osGetTime_recomp = 0x8F000088;
|
||||||
recomp_autosave_enabled = 0x8F00008C;
|
recomp_get_autosave_enabled = 0x8F00008C;
|
||||||
recomp_load_overlays = 0x8F000090;
|
recomp_load_overlays = 0x8F000090;
|
||||||
osInvalICache_recomp = 0x8F000094;
|
osInvalICache_recomp = 0x8F000094;
|
||||||
recomp_analog_cam_enabled = 0x8F000098;
|
recomp_get_analog_cam_enabled = 0x8F000098;
|
||||||
recomp_get_camera_inputs = 0x8F00009C;
|
recomp_get_camera_inputs = 0x8F00009C;
|
||||||
recomp_set_right_analog_suppressed = 0x8F0000A0;
|
recomp_set_right_analog_suppressed = 0x8F0000A0;
|
||||||
recomp_get_inverted_axes = 0x8F0000A4;
|
recomp_get_inverted_axes = 0x8F0000A4;
|
||||||
recomp_high_precision_fb_enabled = 0x8F0000A8;
|
recomp_high_precision_fb_enabled = 0x8F0000A8;
|
||||||
recomp_get_resolution_scale = 0x8F0000AC;
|
recomp_get_resolution_scale = 0x8F0000AC;
|
||||||
recomp_get_analog_inverted_axes = 0x8F0000B0;
|
recomp_get_analog_inverted_axes = 0x8F0000B0;
|
||||||
|
recomp_get_window_resolution = 0x8F0000B4;
|
||||||
|
@ -56,9 +56,10 @@ RECOMP_PATCH void Graph_SetNextGfxPool(GraphicsContext* gfxCtx) {
|
|||||||
gSPEndDisplayList(&gGfxMasterDL->disps[4]);
|
gSPEndDisplayList(&gGfxMasterDL->disps[4]);
|
||||||
gSPBranchList(&gGfxMasterDL->debugDisp[0], bigger_pool->debugBuffer);
|
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);
|
OPEN_DISPS(gfxCtx);
|
||||||
gEXEnable(POLY_OPA_DISP++);
|
gEXEnable(POLY_OPA_DISP++);
|
||||||
|
gEXSetRDRAMExtended(POLY_OPA_DISP++, 1);
|
||||||
CLOSE_DISPS(gfxCtx);
|
CLOSE_DISPS(gfxCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@ constexpr std::u8string_view general_filename = u8"general.json";
|
|||||||
constexpr std::u8string_view graphics_filename = u8"graphics.json";
|
constexpr std::u8string_view graphics_filename = u8"graphics.json";
|
||||||
constexpr std::u8string_view controls_filename = u8"controls.json";
|
constexpr std::u8string_view controls_filename = u8"controls.json";
|
||||||
constexpr std::u8string_view sound_filename = u8"sound.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 res_default = ultramodern::renderer::Resolution::Auto;
|
||||||
constexpr auto hr_default = ultramodern::renderer::HUDRatioMode::Clamp16x9;
|
constexpr auto hr_default = ultramodern::renderer::HUDRatioMode::Clamp16x9;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#include "ultramodern/ultramodern.hpp"
|
#include "ultramodern/ultramodern.hpp"
|
||||||
#include "librecomp/recomp.h"
|
#include "recomp.h"
|
||||||
#include "recomp_input.h"
|
#include "recomp_input.h"
|
||||||
#include "zelda_config.h"
|
#include "zelda_config.h"
|
||||||
#include "recomp_ui.h"
|
#include "recomp_ui.h"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include "librecomp/recomp.h"
|
#include "recomp.h"
|
||||||
#include "librecomp/overlays.hpp"
|
#include "librecomp/overlays.hpp"
|
||||||
#include "zelda_config.h"
|
#include "zelda_config.h"
|
||||||
#include "recomp_input.h"
|
#include "recomp_input.h"
|
||||||
@ -58,7 +58,18 @@ extern "C" void recomp_get_target_framerate(uint8_t* rdram, recomp_context* ctx)
|
|||||||
_return(ctx, ultramodern::get_target_framerate(60 / frame_divisor));
|
_return(ctx, ultramodern::get_target_framerate(60 / frame_divisor));
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void recomp_get_aspect_ratio(uint8_t* rdram, recomp_context* ctx) {
|
extern "C" void recomp_get_window_resolution(uint8_t* rdram, recomp_context* ctx) {
|
||||||
|
int width, height;
|
||||||
|
recompui::get_window_size(width, height);
|
||||||
|
|
||||||
|
gpr width_out = _arg<0, PTR(u32)>(rdram, ctx);
|
||||||
|
gpr height_out = _arg<1, PTR(u32)>(rdram, ctx);
|
||||||
|
|
||||||
|
MEM_W(0, width_out) = (u32)width;
|
||||||
|
MEM_W(0, height_out) = (u32)height;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void recomp_get_target_aspect_ratio(uint8_t* rdram, recomp_context* ctx) {
|
||||||
ultramodern::renderer::GraphicsConfig graphics_config = ultramodern::renderer::get_graphics_config();
|
ultramodern::renderer::GraphicsConfig graphics_config = ultramodern::renderer::get_graphics_config();
|
||||||
float original = _arg<0, float>(rdram, ctx);
|
float original = _arg<0, float>(rdram, ctx);
|
||||||
int width, height;
|
int width, height;
|
||||||
@ -91,7 +102,7 @@ extern "C" void recomp_time_us(uint8_t* rdram, recomp_context* ctx) {
|
|||||||
_return(ctx, static_cast<u32>(std::chrono::duration_cast<std::chrono::microseconds>(ultramodern::time_since_start()).count()));
|
_return(ctx, static_cast<u32>(std::chrono::duration_cast<std::chrono::microseconds>(ultramodern::time_since_start()).count()));
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void recomp_autosave_enabled(uint8_t* rdram, recomp_context* ctx) {
|
extern "C" void recomp_get_autosave_enabled(uint8_t* rdram, recomp_context* ctx) {
|
||||||
_return(ctx, static_cast<s32>(zelda64::get_autosave_mode() == zelda64::AutosaveMode::On));
|
_return(ctx, static_cast<s32>(zelda64::get_autosave_mode() == zelda64::AutosaveMode::On));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +142,7 @@ extern "C" void recomp_get_analog_inverted_axes(uint8_t* rdram, recomp_context*
|
|||||||
*y_out = (mode == zelda64::CameraInvertMode::InvertY || mode == zelda64::CameraInvertMode::InvertBoth);
|
*y_out = (mode == zelda64::CameraInvertMode::InvertY || mode == zelda64::CameraInvertMode::InvertBoth);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void recomp_analog_cam_enabled(uint8_t* rdram, recomp_context* ctx) {
|
extern "C" void recomp_get_analog_cam_enabled(uint8_t* rdram, recomp_context* ctx) {
|
||||||
_return<s32>(ctx, zelda64::get_analog_cam_mode() == zelda64::AnalogCamMode::On);
|
_return<s32>(ctx, zelda64::get_analog_cam_mode() == zelda64::AnalogCamMode::On);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
168
src/game/rom_decompression.cpp
Normal file
168
src/game/rom_decompression.cpp
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "zelda_game.h"
|
||||||
|
|
||||||
|
void naive_copy(std::span<uint8_t> dst, std::span<const uint8_t> src) {
|
||||||
|
for (size_t i = 0; i < src.size(); i++) {
|
||||||
|
dst[i] = src[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void yaz0_decompress(std::span<const uint8_t> input, std::span<uint8_t> output) {
|
||||||
|
int32_t layoutBitIndex;
|
||||||
|
uint8_t layoutBits;
|
||||||
|
|
||||||
|
size_t input_pos = 0;
|
||||||
|
size_t output_pos = 0;
|
||||||
|
|
||||||
|
size_t input_size = input.size();
|
||||||
|
size_t output_size = output.size();
|
||||||
|
|
||||||
|
while (input_pos < input_size) {
|
||||||
|
int32_t layoutBitIndex = 0;
|
||||||
|
uint8_t layoutBits = input[input_pos++];
|
||||||
|
|
||||||
|
while (layoutBitIndex < 8 && input_pos < input_size && output_pos < output_size) {
|
||||||
|
if (layoutBits & 0x80) {
|
||||||
|
output[output_pos++] = input[input_pos++];
|
||||||
|
} else {
|
||||||
|
int32_t firstByte = input[input_pos++];
|
||||||
|
int32_t secondByte = input[input_pos++];
|
||||||
|
uint32_t bytes = firstByte << 8 | secondByte;
|
||||||
|
uint32_t offset = (bytes & 0x0FFF) + 1;
|
||||||
|
uint32_t length;
|
||||||
|
|
||||||
|
// Check how the group length is encoded
|
||||||
|
if ((firstByte & 0xF0) == 0) {
|
||||||
|
// 3 byte encoding, 0RRRNN
|
||||||
|
int32_t thirdByte = input[input_pos++];
|
||||||
|
length = thirdByte + 0x12;
|
||||||
|
} else {
|
||||||
|
// 2 byte encoding, NRRR
|
||||||
|
length = ((bytes & 0xF000) >> 12) + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
naive_copy(output.subspan(output_pos, length), output.subspan(output_pos - offset, length));
|
||||||
|
output_pos += length;
|
||||||
|
}
|
||||||
|
|
||||||
|
layoutBitIndex++;
|
||||||
|
layoutBits <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
inline uint32_t byteswap(uint32_t val) {
|
||||||
|
return _byteswap_ulong(val);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
constexpr uint32_t byteswap(uint32_t val) {
|
||||||
|
return __builtin_bswap32(val);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Produces a decompressed MM rom. This is only needed because the game has compressed code.
|
||||||
|
// For other recomps using this repo as an example, you can omit the decompression routine and
|
||||||
|
// set the corresponding fields in the GameEntry if the game doesn't have compressed code,
|
||||||
|
// even if it does have compressed data.
|
||||||
|
std::vector<uint8_t> zelda64::decompress_mm(std::span<const uint8_t> compressed_rom) {
|
||||||
|
// Sanity check the rom size and header. These should already be correct from the runtime's check,
|
||||||
|
// but it should prevent this file from accidentally being copied to another recomp.
|
||||||
|
if (compressed_rom.size() != 0x2000000) {
|
||||||
|
assert(false);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compressed_rom[0x3B] != 'N' || compressed_rom[0x3C] != 'Z' || compressed_rom[0x3D] != 'S' || compressed_rom[0x3E] != 'E') {
|
||||||
|
assert(false);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DmaDataEntry {
|
||||||
|
uint32_t vrom_start;
|
||||||
|
uint32_t vrom_end;
|
||||||
|
uint32_t rom_start;
|
||||||
|
uint32_t rom_end;
|
||||||
|
|
||||||
|
void bswap() {
|
||||||
|
vrom_start = byteswap(vrom_start);
|
||||||
|
vrom_end = byteswap(vrom_end);
|
||||||
|
rom_start = byteswap(rom_start);
|
||||||
|
rom_end = byteswap(rom_end);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
DmaDataEntry cur_entry{};
|
||||||
|
size_t cur_entry_index = 0;
|
||||||
|
|
||||||
|
constexpr size_t dma_data_rom_addr = 0x1A500;
|
||||||
|
|
||||||
|
std::vector<uint8_t> ret{};
|
||||||
|
ret.resize(0x2F00000);
|
||||||
|
|
||||||
|
size_t content_end = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
// Read the entry from the compressed rom.
|
||||||
|
size_t cur_entry_rom_address = dma_data_rom_addr + (cur_entry_index++) * sizeof(DmaDataEntry);
|
||||||
|
memcpy(&cur_entry, compressed_rom.data() + cur_entry_rom_address, sizeof(DmaDataEntry));
|
||||||
|
// Swap the entry to native endianness after reading from the big endian data.
|
||||||
|
cur_entry.bswap();
|
||||||
|
|
||||||
|
// Rom end being 0 means the data is already uncompressed, so copy it as-is to vrom start.
|
||||||
|
size_t entry_decompressed_size = cur_entry.vrom_end - cur_entry.vrom_start;
|
||||||
|
if (cur_entry.rom_end == 0) {
|
||||||
|
memcpy(ret.data() + cur_entry.vrom_start, compressed_rom.data() + cur_entry.rom_start, entry_decompressed_size);
|
||||||
|
|
||||||
|
// Edit the entry to account for it being in a new location now.
|
||||||
|
cur_entry.rom_start = cur_entry.vrom_start;
|
||||||
|
}
|
||||||
|
// Otherwise, decompress the input data into the output data.
|
||||||
|
else {
|
||||||
|
if (cur_entry.rom_end != cur_entry.rom_start) {
|
||||||
|
// Validate the presence of the yaz0 header.
|
||||||
|
if (compressed_rom[cur_entry.rom_start + 0] != 'Y' ||
|
||||||
|
compressed_rom[cur_entry.rom_start + 1] != 'a' ||
|
||||||
|
compressed_rom[cur_entry.rom_start + 2] != 'z' ||
|
||||||
|
compressed_rom[cur_entry.rom_start + 3] != '0')
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
// Skip the yaz0 header.
|
||||||
|
size_t compressed_data_rom_start = cur_entry.rom_start + 0x10;
|
||||||
|
size_t entry_compressed_size = cur_entry.rom_end - compressed_data_rom_start;
|
||||||
|
|
||||||
|
std::span input_span = std::span{ compressed_rom }.subspan(compressed_data_rom_start, entry_compressed_size);
|
||||||
|
std::span output_span = std::span{ ret }.subspan(cur_entry.vrom_start, entry_decompressed_size);
|
||||||
|
yaz0_decompress(input_span, output_span);
|
||||||
|
|
||||||
|
// Edit the entry to account for it being decompressed now.
|
||||||
|
cur_entry.rom_start = cur_entry.vrom_start;
|
||||||
|
cur_entry.rom_end = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry_decompressed_size != 0) {
|
||||||
|
if (cur_entry.vrom_end > content_end) {
|
||||||
|
content_end = cur_entry.vrom_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap the entry back to big endian for writing.
|
||||||
|
cur_entry.bswap();
|
||||||
|
// Write the modified entry to the decompressed rom.
|
||||||
|
memcpy(ret.data() + cur_entry_rom_address, &cur_entry, sizeof(DmaDataEntry));
|
||||||
|
} while (cur_entry.vrom_end != 0);
|
||||||
|
|
||||||
|
// Align the start of padding to the closest 0x1000 (matches decomp rom decompression behavior).
|
||||||
|
content_end = (content_end + 0x1000 - 1) & -0x1000;
|
||||||
|
|
||||||
|
// Write 0xFF as the padding.
|
||||||
|
std::fill(ret.begin() + content_end, ret.end(), 0xFF);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
@ -25,8 +25,16 @@
|
|||||||
#include "zelda_config.h"
|
#include "zelda_config.h"
|
||||||
#include "zelda_sound.h"
|
#include "zelda_sound.h"
|
||||||
#include "zelda_render.h"
|
#include "zelda_render.h"
|
||||||
|
#include "zelda_game.h"
|
||||||
#include "ovl_patches.hpp"
|
#include "ovl_patches.hpp"
|
||||||
#include "librecomp/game.hpp"
|
#include "librecomp/game.hpp"
|
||||||
|
#include "librecomp/mods.hpp"
|
||||||
|
#include "librecomp/helpers.hpp"
|
||||||
|
|
||||||
|
#include "../../patches/graphics.h"
|
||||||
|
#include "../../patches/input.h"
|
||||||
|
#include "../../patches/sound.h"
|
||||||
|
#include "../../patches/misc_funcs.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
@ -36,6 +44,8 @@
|
|||||||
|
|
||||||
#include "../../lib/rt64/src/contrib/stb/stb_image.h"
|
#include "../../lib/rt64/src/contrib/stb/stb_image.h"
|
||||||
|
|
||||||
|
const std::string version_string = "1.2.0-dev";
|
||||||
|
|
||||||
template<typename... Ts>
|
template<typename... Ts>
|
||||||
void exit_error(const char* str, Ts ...args) {
|
void exit_error(const char* str, Ts ...args) {
|
||||||
// TODO pop up an error
|
// TODO pop up an error
|
||||||
@ -52,14 +62,12 @@ ultramodern::gfx_callbacks_t::gfx_data_t create_gfx() {
|
|||||||
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
|
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
|
||||||
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
|
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
|
||||||
|
|
||||||
#if defined(__linux__)
|
|
||||||
SDL_SetHint(SDL_HINT_VIDEODRIVER, "x11");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) > 0) {
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) > 0) {
|
||||||
exit_error("Failed to initialize SDL2: %s\n", SDL_GetError());
|
exit_error("Failed to initialize SDL2: %s\n", SDL_GetError());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fprintf(stdout, "SDL Video Driver: %s\n", SDL_GetCurrentVideoDriver());
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +123,13 @@ bool SetImageAsIcon(const char* filename, SDL_Window* window)
|
|||||||
SDL_Window* window;
|
SDL_Window* window;
|
||||||
|
|
||||||
ultramodern::renderer::WindowHandle create_window(ultramodern::gfx_callbacks_t::gfx_data_t) {
|
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 flags = SDL_WINDOW_RESIZABLE;
|
||||||
|
|
||||||
|
#if defined(RT64_SDL_WINDOW_VULKAN)
|
||||||
|
flags |= SDL_WINDOW_VULKAN;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
window = SDL_CreateWindow("Zelda 64: Recompiled", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1600, 960, flags);
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
SetImageAsIcon("icons/512.png",window);
|
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
|
if (ultramodern::renderer::get_graphics_config().wm_option == ultramodern::renderer::WindowMode::Fullscreen) { // TODO: Remove once RT64 gets native fullscreen support on Linux
|
||||||
@ -135,14 +149,8 @@ ultramodern::renderer::WindowHandle create_window(ultramodern::gfx_callbacks_t::
|
|||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
return ultramodern::renderer::WindowHandle{ wmInfo.info.win.window, GetCurrentThreadId() };
|
return ultramodern::renderer::WindowHandle{ wmInfo.info.win.window, GetCurrentThreadId() };
|
||||||
#elif defined(__ANDROID__)
|
#elif defined(__linux__) || defined(__ANDROID__)
|
||||||
static_assert(false && "Unimplemented");
|
return ultramodern::renderer::WindowHandle{ window };
|
||||||
#elif defined(__linux__)
|
|
||||||
if (wmInfo.subsystem != SDL_SYSWM_X11) {
|
|
||||||
exit_error("Unsupported SDL2 video driver \"%s\". Only X11 is supported on Linux.\n", SDL_GetCurrentVideoDriver());
|
|
||||||
}
|
|
||||||
|
|
||||||
return ultramodern::renderer::WindowHandle{ wmInfo.info.x11.display, wmInfo.info.x11.window };
|
|
||||||
#else
|
#else
|
||||||
static_assert(false && "Unimplemented");
|
static_assert(false && "Unimplemented");
|
||||||
#endif
|
#endif
|
||||||
@ -327,7 +335,11 @@ std::vector<recomp::GameEntry> supported_games = {
|
|||||||
.rom_hash = 0xEF18B4A9E2386169ULL,
|
.rom_hash = 0xEF18B4A9E2386169ULL,
|
||||||
.internal_name = "ZELDA MAJORA'S MASK",
|
.internal_name = "ZELDA MAJORA'S MASK",
|
||||||
.game_id = u8"mm.n64.us.1.0",
|
.game_id = u8"mm.n64.us.1.0",
|
||||||
.is_enabled = true,
|
.mod_game_id = "mm",
|
||||||
|
.save_type = recomp::SaveType::Flashram,
|
||||||
|
.is_enabled = false,
|
||||||
|
.decompression_routine = zelda64::decompress_mm,
|
||||||
|
.has_compressed_code = true,
|
||||||
.entrypoint_address = get_entrypoint_address(),
|
.entrypoint_address = get_entrypoint_address(),
|
||||||
.entrypoint = recomp_entrypoint,
|
.entrypoint = recomp_entrypoint,
|
||||||
},
|
},
|
||||||
@ -524,7 +536,27 @@ void release_preload(PreloadContext& context) {
|
|||||||
|
|
||||||
#endif
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define REGISTER_FUNC(name) recomp::overlays::register_base_export(#name, name)
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
|
(void)argc;
|
||||||
|
(void)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
|
// 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.
|
// 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;
|
PreloadContext preload_context;
|
||||||
@ -568,15 +600,29 @@ int main(int argc, char** argv) {
|
|||||||
fprintf(stderr, "Failed to load controller mappings: %s\n", SDL_GetError());
|
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
|
// Register supported games and patches
|
||||||
for (const auto& game : supported_games) {
|
for (const auto& game : supported_games) {
|
||||||
recomp::register_game(game);
|
recomp::register_game(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
REGISTER_FUNC(recomp_get_window_resolution);
|
||||||
|
REGISTER_FUNC(recomp_get_target_aspect_ratio);
|
||||||
|
REGISTER_FUNC(recomp_get_target_framerate);
|
||||||
|
REGISTER_FUNC(recomp_get_autosave_enabled);
|
||||||
|
REGISTER_FUNC(recomp_get_analog_cam_enabled);
|
||||||
|
REGISTER_FUNC(recomp_get_camera_inputs);
|
||||||
|
REGISTER_FUNC(recomp_get_targeting_mode);
|
||||||
|
REGISTER_FUNC(recomp_get_bgm_volume);
|
||||||
|
REGISTER_FUNC(recomp_get_low_health_beeps_enabled);
|
||||||
|
REGISTER_FUNC(recomp_get_gyro_deltas);
|
||||||
|
REGISTER_FUNC(recomp_get_mouse_deltas);
|
||||||
|
REGISTER_FUNC(recomp_get_inverted_axes);
|
||||||
|
REGISTER_FUNC(recomp_get_analog_inverted_axes);
|
||||||
|
|
||||||
zelda64::register_overlays();
|
zelda64::register_overlays();
|
||||||
zelda64::register_patches();
|
zelda64::register_patches();
|
||||||
|
|
||||||
recomp::register_config_path(zelda64::get_app_folder_path());
|
|
||||||
zelda64::load_config();
|
zelda64::load_config();
|
||||||
|
|
||||||
recomp::rsp::callbacks_t rsp_callbacks{
|
recomp::rsp::callbacks_t rsp_callbacks{
|
||||||
@ -619,7 +665,47 @@ int main(int argc, char** argv) {
|
|||||||
.get_game_thread_name = zelda64::get_game_thread_name,
|
.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(
|
recomp::start(
|
||||||
|
project_version,
|
||||||
{},
|
{},
|
||||||
rsp_callbacks,
|
rsp_callbacks,
|
||||||
renderer_callbacks,
|
renderer_callbacks,
|
||||||
|
@ -7,4 +7,7 @@
|
|||||||
|
|
||||||
void zelda64::register_patches() {
|
void zelda64::register_patches() {
|
||||||
recomp::overlays::register_patches(mm_patches_bin, sizeof(mm_patches_bin), section_table, ARRLEN(section_table));
|
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);
|
||||||
|
recomp::overlays::register_manual_patch_symbols(manual_patch_symbols);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
#define HLSL_CPU
|
#define HLSL_CPU
|
||||||
#include "hle/rt64_application.h"
|
#include "hle/rt64_application.h"
|
||||||
@ -10,6 +11,13 @@
|
|||||||
|
|
||||||
#include "zelda_render.h"
|
#include "zelda_render.h"
|
||||||
#include "recomp_ui.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 RT64::UserConfiguration::Antialiasing device_max_msaa = RT64::UserConfiguration::Antialiasing::None;
|
||||||
static bool sample_positions_supported = false;
|
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 DMEM[0x1000];
|
||||||
static uint8_t IMEM[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 MI_INTR_REG = 0;
|
||||||
|
|
||||||
unsigned int DPC_START_REG = 0;
|
unsigned int DPC_START_REG = 0;
|
||||||
@ -180,11 +200,8 @@ zelda64::renderer::RT64Context::RT64Context(uint8_t* rdram, ultramodern::rendere
|
|||||||
RT64::Application::Core appCore{};
|
RT64::Application::Core appCore{};
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
appCore.window = window_handle.window;
|
appCore.window = window_handle.window;
|
||||||
#elif defined(__ANDROID__)
|
#elif defined(__linux__) || defined(__ANDROID__)
|
||||||
assert(false && "Unimplemented");
|
appCore.window = window_handle;
|
||||||
#elif defined(__linux__)
|
|
||||||
appCore.window.display = window_handle.display;
|
|
||||||
appCore.window.window = window_handle.window;
|
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
appCore.window.window = window_handle.window;
|
appCore.window.window = window_handle.window;
|
||||||
appCore.window.view = window_handle.view;
|
appCore.window.view = window_handle.view;
|
||||||
@ -286,6 +303,32 @@ zelda64::renderer::RT64Context::RT64Context(uint8_t* rdram, ultramodern::rendere
|
|||||||
zelda64::renderer::RT64Context::~RT64Context() = default;
|
zelda64::renderer::RT64Context::~RT64Context() = default;
|
||||||
|
|
||||||
void zelda64::renderer::RT64Context::send_dl(const OSTask* task) {
|
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->state->rsp->reset();
|
||||||
app->interpreter->loadUCodeGBI(task->t.ucode & 0x3FFFFFF, task->t.ucode_data & 0x3FFFFFF, true);
|
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);
|
app->processDisplayLists(app->core.RDRAM, task->t.data_ptr & 0x3FFFFFF, 0, true);
|
||||||
@ -351,16 +394,6 @@ float zelda64::renderer::RT64Context::get_resolution_scale() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void zelda64::renderer::RT64Context::load_shader_cache(std::span<const char> cache_binary) {
|
|
||||||
// TODO figure out how to avoid a copy here.
|
|
||||||
std::istringstream cache_stream{std::string{cache_binary.data(), cache_binary.size()}};
|
|
||||||
|
|
||||||
if (!app->rasterShaderCache->loadOfflineList(cache_stream)) {
|
|
||||||
printf("Failed to preload shader cache!\n");
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RT64::UserConfiguration::Antialiasing zelda64::renderer::RT64MaxMSAA() {
|
RT64::UserConfiguration::Antialiasing zelda64::renderer::RT64MaxMSAA() {
|
||||||
return device_max_msaa;
|
return device_max_msaa;
|
||||||
}
|
}
|
||||||
@ -376,3 +409,11 @@ bool zelda64::renderer::RT64SamplePositionsSupported() {
|
|||||||
bool zelda64::renderer::RT64HighPrecisionFBEnabled() {
|
bool zelda64::renderer::RT64HighPrecisionFBEnabled() {
|
||||||
return high_precision_fb_enabled;
|
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});
|
||||||
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include "nfd.h"
|
#include "nfd.h"
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
std::string version_number = "v1.1.1";
|
static std::string version_string;
|
||||||
|
|
||||||
Rml::DataModelHandle model_handle;
|
Rml::DataModelHandle model_handle;
|
||||||
bool mm_rom_valid = false;
|
bool mm_rom_valid = false;
|
||||||
@ -103,7 +103,9 @@ public:
|
|||||||
Rml::DataModelConstructor constructor = context->CreateDataModel("launcher_model");
|
Rml::DataModelConstructor constructor = context->CreateDataModel("launcher_model");
|
||||||
|
|
||||||
constructor.Bind("mm_rom_valid", &mm_rom_valid);
|
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();
|
model_handle = constructor.GetModelHandle();
|
||||||
}
|
}
|
||||||
|
@ -1287,6 +1287,12 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
|
|||||||
static recompui::Menu prev_menu = recompui::Menu::None;
|
static recompui::Menu prev_menu = recompui::Menu::None;
|
||||||
recompui::Menu cur_menu = open_menu.load();
|
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) {
|
if (reload_sheets) {
|
||||||
ui_context->rml.load_documents();
|
ui_context->rml.load_documents();
|
||||||
prev_menu = recompui::Menu::None;
|
prev_menu = recompui::Menu::None;
|
||||||
|
@ -5,6 +5,7 @@ entrypoint = 0x80080000
|
|||||||
# Paths are relative to the location of this config file.
|
# Paths are relative to the location of this config file.
|
||||||
output_func_path = "RecompiledFuncs"
|
output_func_path = "RecompiledFuncs"
|
||||||
relocatable_sections_path = "overlays.us.rev1.txt"
|
relocatable_sections_path = "overlays.us.rev1.txt"
|
||||||
|
# elf_path = "mm.us.rev1.rom_uncompressed.elf"
|
||||||
symbols_file_path = "Zelda64RecompSyms/mm.us.rev1.syms.toml"
|
symbols_file_path = "Zelda64RecompSyms/mm.us.rev1.syms.toml"
|
||||||
rom_file_path = "mm.us.rev1.rom_uncompressed.z64"
|
rom_file_path = "mm.us.rev1.rom_uncompressed.z64"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user