Merge branch 'modding' into chore/custom-controller-db

This commit is contained in:
Wiseguy 2024-11-25 11:31:53 -05:00 committed by GitHub
commit d377a0f40f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
52 changed files with 1480 additions and 331 deletions

View File

@ -0,0 +1,64 @@
name: update-pr-artifacts
on:
workflow_run:
workflows: [validate-external, validate-internal]
types:
- completed
jobs:
update-pr-artifacts:
runs-on: ubuntu-latest
if: (github.event.workflow_run.event == 'pull_request' || github.event.workflow_run.event == 'pull_request_target') && github.event.workflow_run.conclusion == 'success'
name: Update PR Artifacts
steps:
- name: Get PR Number
id: pr-number
uses: actions/github-script@v6
with:
result-encoding: string
script: |
const { owner, repo } = context.repo;
const findPRNumber = async () => {
const pulls = await github.rest.pulls.list({ owner, repo });
for await (const { data } of github.paginate.iterator(pulls)) {
for (const pull of data) {
if (pull.head.sha === '${{ github.event.workflow_run.head_sha }}' && pull.user.id === ${{ github.event.sender.id }}) {
return pull.number;
}
}
}
return null;
};
const prNumber = await findPRNumber();
if (!prNumber) {
core.error(`No matching pull request found`);
} else {
return prNumber;
}
- name: Create Artifacts Content
id: artifacts-content
uses: actions/github-script@v6
with:
result-encoding: string
script: |
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
const nightlyLinks = artifacts.data.artifacts.reduce((acc, item) => {
acc += `- [${item.name}.zip](https://nightly.link/${context.repo.owner}/${context.repo.repo}/actions/artifacts/${item.id}.zip)\n`;
return acc;
}, '### Build Artifacts\n');
return nightlyLinks;
- name: Update PR Description
uses: garrettjoecox/pr-section@3.1.0
with:
section-name: 'artifacts'
repo-token: '${{ secrets.GITHUB_TOKEN }}'
pr-number: ${{ steps.pr-number.outputs.result }}
section-value: '${{ steps.artifacts-content.outputs.result }}'

View File

@ -9,7 +9,7 @@ on:
N64RECOMP_COMMIT:
type: string
required: false
default: 'ba4aede49c9a5302ecfc1fa599f7acc3925042f9'
default: 'd33d38161798167929b114c2b0fd445f9670e10a'
DXC_CHECKSUM:
type: string
required: false
@ -22,7 +22,7 @@ concurrency:
cancel-in-progress: true
jobs:
build-linux:
runs-on: ${{ matrix.os }}
runs-on: ${{ format('blaze/compute/{0}-amd64', matrix.os) }}
container:
image: dcvz/n64recomp:ubuntu-18.04
volumes:
@ -64,7 +64,7 @@ jobs:
# Build N64Recomp & RSPRecomp
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER=g++-11 -DCMAKE_C_COMPILER=gcc-11 -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S . -B cmake-build
cmake --build cmake-build --config Release --target N64Recomp -j $(nproc)
cmake --build cmake-build --config Release --target N64RecompCLI -j $(nproc)
cmake --build cmake-build --config Release --target RSPRecomp -j $(nproc)
# Copy N64Recomp & RSPRecomp to root directory
@ -108,7 +108,7 @@ jobs:
name: Zelda64Recompiled-AppImage-X64-${{ matrix.type }}
path: Zelda64Recompiled-*.AppImage
build-linux-arm64:
runs-on: ${{ format('blaze/{0}', matrix.os) }}
runs-on: ${{ format('blaze/compute/{0}', matrix.os) }}
strategy:
matrix:
type: [ Debug, Release ]
@ -160,7 +160,7 @@ jobs:
# Build N64Recomp & RSPRecomp
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER=g++-11 -DCMAKE_C_COMPILER=gcc-11 -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S . -B cmake-build
cmake --build cmake-build --config Release --target N64Recomp -j $(nproc)
cmake --build cmake-build --config Release --target N64RecompCLI -j $(nproc)
cmake --build cmake-build --config Release --target RSPRecomp -j $(nproc)
# Copy N64Recomp & RSPRecomp to root directory
@ -199,7 +199,7 @@ jobs:
runs-on: windows-latest
strategy:
matrix:
type: [ Debug, Release ]
type: [ Debug, RelWithDebInfo ]
name: windows (${{ matrix.type }})
steps:
- name: Checkout
@ -234,7 +234,7 @@ jobs:
$cpuCores = (Get-CimInstance -ClassName Win32_Processor).NumberOfLogicalProcessors
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S . -B cmake-build
cmake --build cmake-build --config Release --target N64Recomp -j $cpuCores
cmake --build cmake-build --config Release --target N64RecompCLI -j $cpuCores
cmake --build cmake-build --config Release --target RSPRecomp -j $cpuCores
# Copy N64Recomp & RSPRecomp to root directory
@ -264,6 +264,7 @@ jobs:
Move-Item -Path "cmake-build/dxcompiler.dll" -Destination "dxcompiler.dll"
Move-Item -Path "cmake-build/dxil.dll" -Destination "dxil.dll"
Move-Item -Path "cmake-build/SDL2.dll" -Destination "SDL2.dll"
Move-Item -Path "cmake-build/Zelda64Recompiled.pdb" -Destination "Zelda64Recompiled.pdb"
Remove-Item -Path "assets/scss" -Recurse -Force
- name: Archive Zelda64Recomp
uses: actions/upload-artifact@v4
@ -276,3 +277,9 @@ jobs:
SDL2.dll
assets/
recompcontrollerdb.txt
- name: Archive Debug Files
uses: actions/upload-artifact@v4
with:
name: Zelda64Recompiled-PDB-${{ matrix.type }}
path: |
Zelda64Recompiled.pdb

View File

@ -20,7 +20,7 @@ For Linux the instructions for Ubuntu are provided, but you can find the equival
```bash
# For Ubuntu, simply run:
sudo apt-get install cmake ninja libsdl2-dev libgtk-3-dev lld llvm clang-15
sudo apt-get install cmake ninja-build libsdl2-dev libgtk-3-dev lld llvm clang
```
### Windows

View File

@ -6,7 +6,14 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-everything -Wall -Wextra")
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-everything /W4")
endif()
# Opt out of constexpr mutex constructor on windows to prevent vcredist issues
if (WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR")
endif()
# Avoid warning about DOWNLOAD_EXTRACT_TIMESTAMP in CMake 3.24:
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
@ -29,6 +36,7 @@ SET(LUNASVG_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
add_subdirectory(${CMAKE_SOURCE_DIR}/lib/lunasvg)
# set(BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS_SAVED}")
SET(ENABLE_SVG_PLUGIN ON CACHE BOOL "" FORCE)
SET(RMLUI_TESTS_ENABLED OFF CACHE BOOL "" FORCE)
add_subdirectory(${CMAKE_SOURCE_DIR}/lib/RmlUi)
add_subdirectory(${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime)
@ -119,16 +127,6 @@ add_custom_command(OUTPUT
# Main executable
add_executable(Zelda64Recompiled)
# Generate mm_shader_cache.c from the MM shader cache if it exists
if (EXISTS ${CMAKE_SOURCE_DIR}/shadercache/mm_shader_cache.bin)
set(HAS_MM_SHADER_CACHE TRUE)
target_compile_definitions(Zelda64Recompiled PRIVATE HAS_MM_SHADER_CACHE)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/mm_shader_cache.c ${CMAKE_CURRENT_BINARY_DIR}/mm_shader_cache.h
COMMAND file_to_c ${CMAKE_SOURCE_DIR}/shadercache/mm_shader_cache.bin mm_shader_cache_bytes ${CMAKE_CURRENT_BINARY_DIR}/mm_shader_cache.c ${CMAKE_CURRENT_BINARY_DIR}/mm_shader_cache.h
DEPENDS ${CMAKE_SOURCE_DIR}/shadercache/mm_shader_cache.bin
)
endif()
set (SOURCES
${CMAKE_SOURCE_DIR}/src/main/main.cpp
${CMAKE_SOURCE_DIR}/src/main/register_overlays.cpp
@ -155,10 +153,6 @@ set (SOURCES
${CMAKE_SOURCE_DIR}/lib/RmlUi/Backends/RmlUi_Platform_SDL.cpp
)
if (HAS_MM_SHADER_CACHE)
list(APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/mm_shader_cache.c)
endif()
target_include_directories(Zelda64Recompiled PRIVATE
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/lib/concurrentqueue
@ -191,6 +185,11 @@ else()
)
endif()
if (MSVC)
# Disable identical code folding, since this breaks mod function patching as multiple functions can get merged into one.
target_link_options(Zelda64Recompiled PRIVATE /OPT:NOICF)
endif()
if (WIN32)
include(FetchContent)

View File

@ -15,7 +15,7 @@ Zelda 64: Recompiled is a project that uses [N64: Recompiled](https://github.com
<br style="display: none;"/>
_Thank you [Blaze](https://runblaze.dev) for supporting this project by providing Linux ARM64 and Apple Silicon macOS Github Action Runners!_
_Special thanks to [Blaze](https://runblaze.dev) for their support of this project. They provide high-performance Linux (AMD64 & ARM64) and Apple Silicon macOS runners for GitHub Actions, greatly reducing our automated build times._
</div>

View File

@ -58,7 +58,7 @@
</div>
</div>
<div class="bottom-left">
<label>{{version_number}}</label>
<label>v{{version_number}}</label>
</div>
</div>
</div>

View File

@ -120,7 +120,8 @@ namespace recomp {
std::vector<InputField> apply_menu;
};
constexpr const std::vector<InputField>& get_default_mapping_for_input(const DefaultN64Mappings& defaults, const GameInput input) {
inline const std::vector<InputField>& get_default_mapping_for_input(const DefaultN64Mappings& defaults, const GameInput input) {
static const std::vector<InputField> empty_input_field{};
switch (input) {
case GameInput::A: return defaults.a;
case GameInput::B: return defaults.b;
@ -143,7 +144,7 @@ namespace recomp {
case GameInput::TOGGLE_MENU: return defaults.toggle_menu;
case GameInput::ACCEPT_MENU: return defaults.accept_menu;
case GameInput::APPLY_MENU: return defaults.apply_menu;
default: return std::vector<InputField>();
default: return empty_input_field;
}
}

View File

@ -1,8 +1,12 @@
#ifndef __ZELDA_RENDER_H__
#define __ZELDA_RENDER_H__
#include <unordered_set>
#include <filesystem>
#include "common/rt64_user_configuration.h"
#include "ultramodern/renderer_context.hpp"
#include "librecomp/mods.hpp"
namespace RT64 {
struct Application;
@ -10,7 +14,7 @@ namespace RT64 {
namespace zelda64 {
namespace renderer {
class RT64Context : public ultramodern::renderer::RendererContext {
class RT64Context final : public ultramodern::renderer::RendererContext {
public:
~RT64Context() override;
RT64Context(uint8_t *rdram, ultramodern::renderer::WindowHandle window_handle, bool developer_mode);
@ -25,10 +29,10 @@ namespace zelda64 {
void shutdown() override;
uint32_t get_display_framerate() const override;
float get_resolution_scale() const override;
void load_shader_cache(std::span<const char> cache_binary) override;
protected:
std::unique_ptr<RT64::Application> app;
std::unordered_set<std::filesystem::path> enabled_texture_packs;
};
std::unique_ptr<ultramodern::renderer::RendererContext> create_render_context(uint8_t *rdram, ultramodern::renderer::WindowHandle window_handle, bool developer_mode);
@ -36,6 +40,9 @@ namespace zelda64 {
RT64::UserConfiguration::Antialiasing RT64MaxMSAA();
bool RT64SamplePositionsSupported();
bool RT64HighPrecisionFBEnabled();
void enable_texture_pack(const recomp::mods::ModHandle& mod);
void disable_texture_pack(const recomp::mods::ModHandle& mod);
}
}

@ -1 +1 @@
Subproject commit ce68e96c171f7f036e29f539e22a604da04af824
Subproject commit d5c81d0a6bf2e5f36747a095a7a060d7623bbf58

@ -1 +1 @@
Subproject commit 36e3114cd23a80683504c257729ff7a419735f50
Subproject commit 67422c3b647058d3d38f2813a2abe79cf1638f13

View File

@ -9,8 +9,12 @@ single_file_output = true
use_absolute_symbols = true
# Point the recompiler at the symbol files so that it can resolve relocations during recompilation.
func_reference_syms_file = "Zelda64RecompSyms/mm.us.rev1.syms.toml"
data_reference_syms_files = [ "Zelda64RecompSyms/mm.us.rev1.datasyms.toml", "Zelda64RecompSyms/mm.us.rev1.datasyms_static.toml" ]
data_reference_syms_files = [ "Zelda64RecompSyms/mm.us.rev1.datasyms.toml", "Zelda64RecompSyms/mm.us.rev1.datasyms_static.toml", "patches/custom_syms.toml" ]
# Tell the recompiler to write the output binary. Doing this instead of using objcopy allows the recompiler to patch MIPS32 relocs.
output_binary_path = "patches/patches.bin"
# Do not emit warnings for unpaired LO16 values, as clang produces many of them.
unpaired_lo16_warnings = false
# Allow exporting functions and events for mods to use.
allow_exports = true
# # Enable strict patch mode, validates that patched symbols exist and that non-patch functions aren't symbols.
strict_patch_mode = true

View File

@ -8,7 +8,7 @@ extern FaultClient sActorFaultClient;
Actor* Actor_Delete(ActorContext* actorCtx, Actor* actor, PlayState* play);
void ZeldaArena_Free(void* ptr);
void Actor_CleanupContext(ActorContext* actorCtx, PlayState* play) {
RECOMP_PATCH void Actor_CleanupContext(ActorContext* actorCtx, PlayState* play) {
s32 i;
Fault_RemoveClient(&sActorFaultClient);
@ -47,7 +47,7 @@ u32 create_actor_transform_id() {
return ret;
}
void Actor_Init(Actor* actor, PlayState* play) {
RECOMP_PATCH void Actor_Init(Actor* actor, PlayState* play) {
Actor_SetWorldToHome(actor);
Actor_SetShapeRotToWorld(actor);
Actor_SetFocus(actor, 0.0f);
@ -135,7 +135,7 @@ Gfx* pop_post_limb_matrix_group(Gfx* dlist, Actor* actor) {
/*
* Draws the limb at `limbIndex` with a level of detail display lists index by `dListIndex`
*/
void SkelAnime_DrawLimbLod(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
RECOMP_PATCH void SkelAnime_DrawLimbLod(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, Actor* actor, s32 lod) {
LodLimb* limb;
Gfx* dList;
@ -197,7 +197,7 @@ void SkelAnime_DrawLimbLod(PlayState* play, s32 limbIndex, void** skeleton, Vec3
* Draw all limbs of type `LodLimb` in a given skeleton
* Near or far display list is specified via `lod`
*/
void SkelAnime_DrawLod(PlayState* play, void** skeleton, Vec3s* jointTable, OverrideLimbDrawOpa overrideLimbDraw,
RECOMP_PATCH void SkelAnime_DrawLod(PlayState* play, void** skeleton, Vec3s* jointTable, OverrideLimbDrawOpa overrideLimbDraw,
PostLimbDrawOpa postLimbDraw, Actor* actor, s32 lod) {
LodLimb* rootLimb;
s32 pad;
@ -261,7 +261,7 @@ void SkelAnime_DrawLod(PlayState* play, void** skeleton, Vec3s* jointTable, Over
* Draw a limb of type `LodLimb` contained within a flexible skeleton
* Near or far display list is specified via `lod`
*/
void SkelAnime_DrawFlexLimbLod(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
RECOMP_PATCH void SkelAnime_DrawFlexLimbLod(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
OverrideLimbDrawFlex overrideLimbDraw, PostLimbDrawFlex postLimbDraw, Actor* actor,
s32 lod, Mtx** mtx) {
LodLimb* limb;
@ -332,7 +332,7 @@ void SkelAnime_DrawFlexLimbLod(PlayState* play, s32 limbIndex, void** skeleton,
* Limbs in a flexible skeleton have meshes that can stretch to line up with other limbs.
* An array of matrices is dynamically allocated so each limb can access any transform to ensure its meshes line up.
*/
void SkelAnime_DrawFlexLod(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dListCount,
RECOMP_PATCH void SkelAnime_DrawFlexLod(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dListCount,
OverrideLimbDrawFlex overrideLimbDraw, PostLimbDrawFlex postLimbDraw, Actor* actor,
s32 lod) {
LodLimb* rootLimb;
@ -403,7 +403,7 @@ void SkelAnime_DrawFlexLod(PlayState* play, void** skeleton, Vec3s* jointTable,
/*
* Draws the limb of the Skeleton `skeleton` at `limbIndex`
*/
void SkelAnime_DrawLimbOpa(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
RECOMP_PATCH void SkelAnime_DrawLimbOpa(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, Actor* actor) {
StandardLimb* limb;
Gfx* dList;
@ -463,7 +463,7 @@ void SkelAnime_DrawLimbOpa(PlayState* play, s32 limbIndex, void** skeleton, Vec3
/**
* Draw all limbs of type `StandardLimb` in a given skeleton to the polyOpa buffer
*/
void SkelAnime_DrawOpa(PlayState* play, void** skeleton, Vec3s* jointTable, OverrideLimbDrawOpa overrideLimbDraw,
RECOMP_PATCH void SkelAnime_DrawOpa(PlayState* play, void** skeleton, Vec3s* jointTable, OverrideLimbDrawOpa overrideLimbDraw,
PostLimbDrawOpa postLimbDraw, Actor* actor) {
StandardLimb* rootLimb;
s32 pad;
@ -521,7 +521,7 @@ void SkelAnime_DrawOpa(PlayState* play, void** skeleton, Vec3s* jointTable, Over
CLOSE_DISPS(play->state.gfxCtx);
}
void SkelAnime_DrawFlexLimbOpa(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
RECOMP_PATCH void SkelAnime_DrawFlexLimbOpa(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, Actor* actor,
Mtx** limbMatricies) {
StandardLimb* limb;
@ -591,7 +591,7 @@ void SkelAnime_DrawFlexLimbOpa(PlayState* play, s32 limbIndex, void** skeleton,
* Limbs in a flexible skeleton have meshes that can stretch to line up with other limbs.
* An array of matrices is dynamically allocated so each limb can access any transform to ensure its meshes line up.
*/
void SkelAnime_DrawFlexOpa(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dListCount,
RECOMP_PATCH void SkelAnime_DrawFlexOpa(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dListCount,
OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, Actor* actor) {
StandardLimb* rootLimb;
s32 pad;
@ -661,7 +661,7 @@ void SkelAnime_DrawFlexOpa(PlayState* play, void** skeleton, Vec3s* jointTable,
CLOSE_DISPS(play->state.gfxCtx);
}
void SkelAnime_DrawTransformFlexLimbOpa(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
RECOMP_PATCH void SkelAnime_DrawTransformFlexLimbOpa(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw,
TransformLimbDrawOpa transformLimbDraw, Actor* actor, Mtx** mtx) {
StandardLimb* limb;
@ -744,7 +744,7 @@ void SkelAnime_DrawTransformFlexLimbOpa(PlayState* play, s32 limbIndex, void** s
* coordinates.
* Note that the `TransformLimbDraw` does not have a NULL check, so must be provided even if empty.
*/
void SkelAnime_DrawTransformFlexOpa(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dListCount,
RECOMP_PATCH void SkelAnime_DrawTransformFlexOpa(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dListCount,
OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw,
TransformLimbDrawOpa transformLimbDraw, Actor* actor) {
StandardLimb* rootLimb;
@ -825,7 +825,7 @@ void SkelAnime_DrawTransformFlexOpa(PlayState* play, void** skeleton, Vec3s* joi
* Draws the Skeleton `skeleton`'s limb at index `limbIndex`. Appends all generated graphics commands to
* `gfx`. Returns a pointer to the next gfx to be appended to.
*/
Gfx* SkelAnime_DrawLimb(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
RECOMP_PATCH Gfx* SkelAnime_DrawLimb(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
OverrideLimbDraw overrideLimbDraw, PostLimbDraw postLimbDraw, Actor* actor, Gfx* gfx) {
StandardLimb* limb;
Gfx* dList;
@ -884,7 +884,7 @@ Gfx* SkelAnime_DrawLimb(PlayState* play, s32 limbIndex, void** skeleton, Vec3s*
* Draws the Skeleton `skeleton` Appends all generated graphics to `gfx`, and returns a pointer to the
* next gfx to be appended to.
*/
Gfx* SkelAnime_Draw(PlayState* play, void** skeleton, Vec3s* jointTable, OverrideLimbDraw overrideLimbDraw,
RECOMP_PATCH Gfx* SkelAnime_Draw(PlayState* play, void** skeleton, Vec3s* jointTable, OverrideLimbDraw overrideLimbDraw,
PostLimbDraw postLimbDraw, Actor* actor, Gfx* gfx) {
StandardLimb* rootLimb;
s32 pad;
@ -944,7 +944,7 @@ Gfx* SkelAnime_Draw(PlayState* play, void** skeleton, Vec3s* jointTable, Overrid
/**
* Draw a limb of type `StandardLimb` contained within a flexible skeleton to the specified display buffer
*/
Gfx* SkelAnime_DrawFlexLimb(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
RECOMP_PATCH Gfx* SkelAnime_DrawFlexLimb(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable,
OverrideLimbDraw overrideLimbDraw, PostLimbDraw postLimbDraw, Actor* actor, Mtx** mtx,
Gfx* gfx) {
StandardLimb* limb;
@ -1014,7 +1014,7 @@ Gfx* SkelAnime_DrawFlexLimb(PlayState* play, s32 limbIndex, void** skeleton, Vec
* Limbs in a flexible skeleton have meshes that can stretch to line up with other limbs.
* An array of matrices is dynamically allocated so each limb can access any transform to ensure its meshes line up.
*/
Gfx* SkelAnime_DrawFlex(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dListCount,
RECOMP_PATCH Gfx* SkelAnime_DrawFlex(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dListCount,
OverrideLimbDraw overrideLimbDraw, PostLimbDraw postLimbDraw, Actor* actor, Gfx* gfx) {
StandardLimb* rootLimb;
s32 pad;
@ -1085,7 +1085,7 @@ Gfx* SkelAnime_DrawFlex(PlayState* play, void** skeleton, Vec3s* jointTable, s32
extern MtxF gSkinLimbMatrices[];
void Skin_DrawImpl(Actor* actor, PlayState* play, Skin* skin, SkinPostDraw postDraw,
RECOMP_PATCH void Skin_DrawImpl(Actor* actor, PlayState* play, Skin* skin, SkinPostDraw postDraw,
SkinOverrideLimbDraw overrideLimbDraw, s32 setTranslation, s32 arg6, s32 drawFlags) {
s32 i;
SkinLimb** skeleton;
@ -1145,7 +1145,7 @@ close_disps:;
CLOSE_DISPS(gfxCtx);
}
__attribute__((noinline)) s32 scan_for_matrices(Gfx* start, Gfx* end) {
s32 scan_for_matrices(Gfx* start, Gfx* end) {
s32 matrix_count = 0;
Gfx* cur = start;
// Count any G_MTX commands between the start and end commands.
@ -1201,7 +1201,7 @@ void tag_actor_displaylists(Actor* actor, PlayState* play, Gfx* opa_start, Gfx*
}
// @recomp Patched to automatically add transform tagging to actor matrices based on what DL commands they write in their draw function
void Actor_Draw(PlayState* play, Actor* actor) {
RECOMP_PATCH void Actor_Draw(PlayState* play, Actor* actor) {
Lights* light;
OPEN_DISPS(play->state.gfxCtx);

View File

@ -14,7 +14,7 @@ s32 ShrinkWindow_Letterbox_GetSizeTarget(void);
void ShrinkWindow_Letterbox_SetSizeTarget(s32 target);
// @recomp Patched function to set a global variable if the player can pause
void KaleidoSetup_Update(PlayState* play) {
RECOMP_PATCH void KaleidoSetup_Update(PlayState* play) {
Input* input = CONTROLLER1(&play->state);
MessageContext* msgCtx = &play->msgCtx;
Player* player = GET_PLAYER(play);
@ -67,10 +67,10 @@ void KaleidoSetup_Update(PlayState* play) {
void Sram_SyncWriteToFlash(SramContext* sramCtx, s32 curPage, s32 numPages);
void autosave_reset_timer();
void autosave_reset_timer_slow();
void recomp_reset_autosave_timer();
void recomp_reset_autosave_timer_slow();
void do_autosave(PlayState* play) {
RECOMP_EXPORT void recomp_do_autosave(PlayState* play) {
// Transfer the scene flags into the cycle flags.
Play_SaveCycleSceneFlags(&play->state);
// Transfer the cycle flags into the save buffer. Logic copied from func_8014546C.
@ -99,13 +99,22 @@ void do_autosave(PlayState* play) {
gSaveContext.save.isOwlSave = false;
}
// @recomp Do not clear the save if the save was an autosave.
void func_80147314(SramContext* sramCtx, s32 fileNum) {
bool loading_deletes_owl_save = true;
// @recomp_export void recomp_set_loading_deletes_owl_save(bool new_val): Set whether loading an owl save should also delete it.
RECOMP_EXPORT void recomp_set_loading_deletes_owl_save(bool new_val)
{
loading_deletes_owl_save = new_val;
}
// @recomp Do not clear the save if the save was an autosave, or if mods have disabled save deletion.
RECOMP_PATCH void func_80147314(SramContext* sramCtx, s32 fileNum) {
s32 save_type = gSaveContext.save.isOwlSave;
gSaveContext.save.isOwlSave = false;
// @recomp Prevent owl save/autosave deletion if autosaving is enabled.
if (!recomp_autosave_enabled()) {
// @recomp Prevent owl save/autosave deletion if autosaving is enabled, and...
// @recomp_use_export_var loading_deletes_owl_save: Prevent owl save deletion if mods disable it.
if (!recomp_autosave_enabled() && loading_deletes_owl_save) {
gSaveContext.save.saveInfo.playerData.newf[0] = '\0';
gSaveContext.save.saveInfo.playerData.newf[1] = '\0';
gSaveContext.save.saveInfo.playerData.newf[2] = '\0';
@ -156,7 +165,7 @@ void delete_owl_save(SramContext* sramCtx, s32 fileNum) {
}
// @recomp Patched to delete owl saves when making regular saves.
void func_8014546C(SramContext* sramCtx) {
RECOMP_PATCH void func_8014546C(SramContext* sramCtx) {
s32 i;
if (gSaveContext.save.isOwlSave) {
@ -176,7 +185,7 @@ void func_8014546C(SramContext* sramCtx) {
// @recomp Delete the owl save.
delete_owl_save(sramCtx, gSaveContext.fileNum);
// @recomp Reset the autosave timer.
autosave_reset_timer();
recomp_reset_autosave_timer();
for (i = 0; i < ARRAY_COUNT(gSaveContext.cycleSceneFlags); i++) {
gSaveContext.save.saveInfo.permanentSceneFlags[i].chest = gSaveContext.cycleSceneFlags[i].chest;
gSaveContext.save.saveInfo.permanentSceneFlags[i].switch0 = gSaveContext.cycleSceneFlags[i].switch0;
@ -199,7 +208,7 @@ extern u16 D_801F6AF0;
extern u8 D_801F6AF2;
// @recomp Patched to call the new owl save deletion function.
void Sram_EraseSave(FileSelectState* fileSelect2, SramContext* sramCtx, s32 fileNum) {
RECOMP_PATCH void Sram_EraseSave(FileSelectState* fileSelect2, SramContext* sramCtx, s32 fileNum) {
FileSelectState* fileSelect = fileSelect2;
s32 pad;
@ -344,11 +353,11 @@ void draw_autosave_icon(PlayState* play) {
CLOSE_DISPS(play->state.gfxCtx);
}
void show_autosave_icon() {
RECOMP_EXPORT void recomp_show_autosave_icon() {
autosave_icon_counter = AUTOSAVE_ICON_TOTAL_FRAMES;
}
u32 recomp_autosave_interval() {
RECOMP_EXPORT u32 recomp_autosave_interval() {
return 2 * 60 * 1000;
}
@ -367,12 +376,12 @@ bool reached_final_three_hours() {
return false;
}
void autosave_reset_timer() {
RECOMP_EXPORT void recomp_reset_autosave_timer() {
last_autosave_time = osGetTime();
extra_autosave_delay_milliseconds = 0;
}
void autosave_reset_timer_slow() {
RECOMP_EXPORT void recomp_reset_autosave_timer_slow() {
// Set the most recent autosave time in the future to give extra time before an autosave triggers.
last_autosave_time = osGetTime();
extra_autosave_delay_milliseconds = 2 * 60 * 1000;
@ -425,20 +434,20 @@ void autosave_post_play_update(PlayState* play) {
frames_since_autosave_ready >= MIN_FRAMES_SINCE_READY &&
time_now - last_autosave_time > (OS_USEC_TO_CYCLES(1000 * (recomp_autosave_interval() + extra_autosave_delay_milliseconds)))
) {
do_autosave(play);
show_autosave_icon();
autosave_reset_timer();
recomp_do_autosave(play);
recomp_show_autosave_icon();
recomp_reset_autosave_timer();
}
}
else {
// Update the last autosave time to the current time to prevent autosaving immediately if autosaves are turned back on.
autosave_reset_timer();
recomp_reset_autosave_timer();
}
gCanPause = false;
}
void autosave_init() {
autosave_reset_timer_slow();
recomp_reset_autosave_timer_slow();
Lib_MemCpy(&prev_save_ctx, &gSaveContext, offsetof(SaveContext, fileNum));
}
@ -468,7 +477,7 @@ extern s16 sSceneCutsceneCount;
bool skip_entry_cutscene = false;
// @recomp Patched to skip the entrance cutscene if the flag is enabled.
s16 CutsceneManager_FindEntranceCsId(void) {
RECOMP_PATCH s16 CutsceneManager_FindEntranceCsId(void) {
PlayState* play;
s32 csId;
@ -522,14 +531,20 @@ s32 spawn_entrance_from_autosave_entrance(s16 autosave_entrance) {
}
}
RECOMP_DECLARE_EVENT(recomp_on_load_save(FileSelectState* fileSelect, SramContext* sramCtx));
RECOMP_DECLARE_EVENT(recomp_after_load_save(FileSelectState* fileSelect, SramContext* sramCtx));
// @recomp Patched to change the entrance for autosaves and initialize autosaves.
void Sram_OpenSave(FileSelectState* fileSelect, SramContext* sramCtx) {
RECOMP_PATCH void Sram_OpenSave(FileSelectState* fileSelect, SramContext* sramCtx) {
s32 i;
s32 pad;
s32 phi_t1 = 0;
s32 pad1;
s32 fileNum;
// @recomp_event recomp_on_load_save(FileSelectState* fileSelect, SramContext* sramCtx): A save-file was just chosen.
recomp_on_load_save(fileSelect, sramCtx);
if (gSaveContext.flashSaveAvailable) {
bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE);
@ -653,29 +668,49 @@ void Sram_OpenSave(FileSelectState* fileSelect, SramContext* sramCtx) {
// @recomp Initialize the autosave state tracking.
autosave_init();
// @recomp_event recomp_after_load_save(FileSelectState* fileSelect, SramContext* sramCtx): The save has finished loading.
recomp_after_load_save(fileSelect, sramCtx);
}
bool moon_crash_resets_save = true;
// @recomp_export void recomp_set_moon_crash_resets_save(bool new_val): Set whether a moon crash should revert the player's save data.
RECOMP_EXPORT void recomp_set_moon_crash_resets_save(bool new_val)
{
moon_crash_resets_save = new_val;
}
extern s32 Actor_ProcessTalkRequest(Actor* actor, GameState* gameState);
RECOMP_DECLARE_EVENT(recomp_on_moon_crash(SramContext* sramCtx));
RECOMP_DECLARE_EVENT(recomp_after_moon_crash(SramContext* sramCtx));
// @recomp Reset the autosave timer when the moon crashes.
void Sram_ResetSaveFromMoonCrash(SramContext* sramCtx) {
RECOMP_PATCH void Sram_ResetSaveFromMoonCrash(SramContext* sramCtx) {
s32 i;
s32 cutsceneIndex = gSaveContext.save.cutsceneIndex;
bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE);
// @recomp_event recomp_on_moon_crash(SramContext* sramCtx): A moon crash has just been triggered.
recomp_on_moon_crash(sramCtx);
if (SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2],
gFlashSaveNumPages[gSaveContext.fileNum * 2]) != 0) {
SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2 + 1],
gFlashSaveNumPages[gSaveContext.fileNum * 2 + 1]);
if (moon_crash_resets_save)
{
bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE);
if (SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2],
gFlashSaveNumPages[gSaveContext.fileNum * 2]) != 0) {
SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2 + 1],
gFlashSaveNumPages[gSaveContext.fileNum * 2 + 1]);
}
Lib_MemCpy(&gSaveContext.save, sramCtx->saveBuf, sizeof(Save));
if (CHECK_NEWF(gSaveContext.save.saveInfo.playerData.newf)) {
SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2 + 1],
gFlashSaveNumPages[gSaveContext.fileNum * 2 + 1]);
Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, sizeof(Save));
}
gSaveContext.save.cutsceneIndex = cutsceneIndex;
}
Lib_MemCpy(&gSaveContext.save, sramCtx->saveBuf, sizeof(Save));
if (CHECK_NEWF(gSaveContext.save.saveInfo.playerData.newf)) {
SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2 + 1],
gFlashSaveNumPages[gSaveContext.fileNum * 2 + 1]);
Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, sizeof(Save));
}
gSaveContext.save.cutsceneIndex = cutsceneIndex;
for (i = 0; i < ARRAY_COUNT(gSaveContext.eventInf); i++) {
gSaveContext.eventInf[i] = 0;
@ -705,25 +740,56 @@ void Sram_ResetSaveFromMoonCrash(SramContext* sramCtx) {
gSaveContext.jinxTimer = 0;
// @recomp Use the slow autosave timer to give the player extra time to respond to the moon crashing to decide if they want to reload their autosave.
autosave_reset_timer_slow();
recomp_reset_autosave_timer_slow();
// @recomp_event recomp_after_moon_crash(SramContext* sramCtx): The effects of moon crash have been written.
recomp_after_moon_crash(sramCtx);
}
// @recomp If autosave is enabled, skip the part of the owl statue dialog that talks about the file being deleted on load, since it's not true.
void ObjWarpstone_Update(Actor* thisx, PlayState* play) {
bool owls_save_and_quit = true;
// @recomp_export void recomp_set_owls_save_and_quit(bool new_val): Set if owls should use their code to save and quit. If false is passed, owl saves now do nothing.
RECOMP_EXPORT void recomp_set_owls_save_and_quit(bool new_val)
{
owls_save_and_quit = new_val;
}
RECOMP_DECLARE_EVENT(recomp_on_owl_update(ObjWarpstone* this, PlayState* play));
RECOMP_DECLARE_EVENT(recomp_on_owl_save(ObjWarpstone* this, PlayState* play));
RECOMP_DECLARE_EVENT(recomp_after_owl_save(ObjWarpstone* this, PlayState* play));
// @recomp If autosave is enabled or owl save deletion is disabled, skip the part of the owl statue dialog that talks about the file being deleted on load, since it's not true.
RECOMP_PATCH void ObjWarpstone_Update(Actor* thisx, PlayState* play) {
ObjWarpstone* this = (ObjWarpstone*)thisx;
s32 pad;
// @recomp_event recomp_on_owl_update(ObjWarpstone* this, PlayState* play): Allow mods to handle owl update frames.
recomp_on_owl_update(this, play);
if (this->isTalking) {
if (Actor_TextboxIsClosing(&this->dyna.actor, play)) {
this->isTalking = false;
} else if ((Message_GetState(&play->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(play)) {
if (play->msgCtx.choiceIndex != 0) {
// @recomp_event recomp_on_owl_save(ObjWarpstone* this, PlayState* play): The player chose to save from an owl statue.
recomp_on_owl_save(this, play);
Audio_PlaySfx_MessageDecide();
play->msgCtx.msgMode = MSGMODE_OWL_SAVE_0;
// @recomp_use_export_var owls_save_and_quit: Only use normal owl save if quit flag is set.
if (owls_save_and_quit) {
play->msgCtx.msgMode = MSGMODE_OWL_SAVE_0;
} else {
Message_CloseTextbox(play);
}
play->msgCtx.unk120D6 = 0;
play->msgCtx.unk120D4 = 0;
gSaveContext.save.owlWarpId = OBJ_WARPSTONE_GET_OWL_WARP_ID(&this->dyna.actor);
// @recomp_event recomp_after_owl_save(ObjWarpstone* this, PlayState* play): Owl save is finished.
recomp_after_owl_save(this, play);
} else {
Message_CloseTextbox(play);
}
@ -734,8 +800,8 @@ void ObjWarpstone_Update(Actor* thisx, PlayState* play) {
Actor_OfferTalkNearColChkInfoCylinder(&this->dyna.actor, play);
}
// @recomp Skip the text talking about the save being deleted on load, if autosave is enabled.
if (recomp_autosave_enabled()) {
// @recomp_use_export_var loading_deletes_owl_save: Skip the text talking about the save being deleted on load, if autosave is enabled or if owl save deletion is disabled.
if (recomp_autosave_enabled() || !loading_deletes_owl_save) {
if (this->isTalking && play->msgCtx.currentTextId == 0xC01 && play->msgCtx.msgBufPos == 269) {
play->msgCtx.msgBufPos = 530;
}

View File

@ -61,7 +61,7 @@ void edit_billboard_groups(PlayState* play) {
CLOSE_DISPS(play->state.gfxCtx);
}
Mtx* Matrix_NewMtx(GraphicsContext* gfxCtx) {
RECOMP_PATCH Mtx* Matrix_NewMtx(GraphicsContext* gfxCtx) {
Mtx* ret = Matrix_ToMtx(GRAPH_ALLOC(gfxCtx, sizeof(Mtx)));
if (*current_billboard_state) {
@ -77,7 +77,7 @@ Mtx* Matrix_NewMtx(GraphicsContext* gfxCtx) {
return ret;
}
void Matrix_Init(GameState* gameState) {
RECOMP_PATCH void Matrix_Init(GameState* gameState) {
sMatrixStack = THA_AllocTailAlign16(&gameState->tha, MATRIX_STACK_SIZE * sizeof(MtxF));
sCurrentMatrix = sMatrixStack;
@ -94,7 +94,7 @@ void matrix_play_update(PlayState* play) {
play_billboard_matrix = &play->billboardMtxF;
}
void Matrix_Push(void) {
RECOMP_PATCH void Matrix_Push(void) {
MtxF* prev = sCurrentMatrix;
sCurrentMatrix++;
@ -106,20 +106,20 @@ void Matrix_Push(void) {
*current_billboard_state = *prev_billboard;
}
void Matrix_Pop(void) {
RECOMP_PATCH void Matrix_Pop(void) {
sCurrentMatrix--;
// @recomp Pop the matrix stack billboard state.
current_billboard_state--;
}
void Matrix_Put(MtxF* src) {
RECOMP_PATCH void Matrix_Put(MtxF* src) {
Matrix_MtxFCopy(sCurrentMatrix, src);
// @recomp Update the current billboard state.
*current_billboard_state = (src == play_billboard_matrix);
}
void Matrix_ReplaceRotation(MtxF* mf) {
RECOMP_PATCH void Matrix_ReplaceRotation(MtxF* mf) {
MtxF* cmf = sCurrentMatrix;
f32 acc;
f32 component;
@ -168,7 +168,7 @@ void Matrix_ReplaceRotation(MtxF* mf) {
*current_billboard_state = (mf == play_billboard_matrix);
}
void Matrix_Mult(MtxF* mf, MatrixMode mode) {
RECOMP_PATCH void Matrix_Mult(MtxF* mf, MatrixMode mode) {
MtxF* cmf = Matrix_GetCurrent();
if (mode == MTXMODE_APPLY) {
@ -183,7 +183,7 @@ void Matrix_Mult(MtxF* mf, MatrixMode mode) {
}
}
void Matrix_Translate(f32 x, f32 y, f32 z, MatrixMode mode) {
RECOMP_PATCH void Matrix_Translate(f32 x, f32 y, f32 z, MatrixMode mode) {
MtxF* cmf = sCurrentMatrix;
f32 tempX;
f32 tempY;
@ -209,7 +209,7 @@ void Matrix_Translate(f32 x, f32 y, f32 z, MatrixMode mode) {
}
}
void Matrix_Scale(f32 x, f32 y, f32 z, MatrixMode mode) {
RECOMP_PATCH void Matrix_Scale(f32 x, f32 y, f32 z, MatrixMode mode) {
MtxF* cmf = sCurrentMatrix;
if (mode == MTXMODE_APPLY) {
@ -233,7 +233,7 @@ void Matrix_Scale(f32 x, f32 y, f32 z, MatrixMode mode) {
}
}
void Matrix_RotateXS(s16 x, MatrixMode mode) {
RECOMP_PATCH void Matrix_RotateXS(s16 x, MatrixMode mode) {
MtxF* cmf;
f32 sin;
f32 cos;
@ -300,7 +300,7 @@ void Matrix_RotateXS(s16 x, MatrixMode mode) {
}
}
void Matrix_RotateXF(f32 x, MatrixMode mode) {
RECOMP_PATCH void Matrix_RotateXF(f32 x, MatrixMode mode) {
MtxF* cmf;
f32 sin;
f32 cos;
@ -369,7 +369,7 @@ void Matrix_RotateXF(f32 x, MatrixMode mode) {
}
}
void Matrix_RotateXFNew(f32 x) {
RECOMP_PATCH void Matrix_RotateXFNew(f32 x) {
MtxF* cmf = sCurrentMatrix;
s32 pad[2];
f32 sin;
@ -406,7 +406,7 @@ void Matrix_RotateXFNew(f32 x) {
// @recomp Clear the current billboard state.
*current_billboard_state = false;
}
void Matrix_RotateYS(s16 y, MatrixMode mode) {
RECOMP_PATCH void Matrix_RotateYS(s16 y, MatrixMode mode) {
MtxF* cmf;
f32 sin;
f32 cos;
@ -473,7 +473,7 @@ void Matrix_RotateYS(s16 y, MatrixMode mode) {
}
}
void Matrix_RotateYF(f32 y, MatrixMode mode) {
RECOMP_PATCH void Matrix_RotateYF(f32 y, MatrixMode mode) {
MtxF* cmf;
f32 sin;
f32 cos;
@ -542,7 +542,7 @@ void Matrix_RotateYF(f32 y, MatrixMode mode) {
}
}
void Matrix_RotateZS(s16 z, MatrixMode mode) {
RECOMP_PATCH void Matrix_RotateZS(s16 z, MatrixMode mode) {
MtxF* cmf;
f32 sin;
f32 cos;
@ -611,7 +611,7 @@ void Matrix_RotateZS(s16 z, MatrixMode mode) {
}
}
void Matrix_RotateZF(f32 z, MatrixMode mode) {
RECOMP_PATCH void Matrix_RotateZF(f32 z, MatrixMode mode) {
MtxF* cmf;
f32 sin;
f32 cos;
@ -678,7 +678,7 @@ void Matrix_RotateZF(f32 z, MatrixMode mode) {
}
}
void Matrix_RotateZYX(s16 x, s16 y, s16 z, MatrixMode mode) {
RECOMP_PATCH void Matrix_RotateZYX(s16 x, s16 y, s16 z, MatrixMode mode) {
MtxF* cmf = sCurrentMatrix;
f32 temp1;
f32 temp2;
@ -768,7 +768,7 @@ void Matrix_RotateZYX(s16 x, s16 y, s16 z, MatrixMode mode) {
}
}
void Matrix_SetTranslateRotateYXZ(f32 x, f32 y, f32 z, Vec3s* rot) {
RECOMP_PATCH void Matrix_SetTranslateRotateYXZ(f32 x, f32 y, f32 z, Vec3s* rot) {
MtxF* cmf = sCurrentMatrix;
f32 sinY = Math_SinS(rot->y);
f32 cosY = Math_CosS(rot->y);
@ -829,7 +829,7 @@ void Matrix_SetTranslateRotateYXZ(f32 x, f32 y, f32 z, Vec3s* rot) {
*current_billboard_state = false;
}
void Matrix_RotateAxisF(f32 angle, Vec3f* axis, MatrixMode mode) {
RECOMP_PATCH void Matrix_RotateAxisF(f32 angle, Vec3f* axis, MatrixMode mode) {
MtxF* cmf;
f32 sin;
f32 cos;
@ -926,7 +926,7 @@ void Matrix_RotateAxisF(f32 angle, Vec3f* axis, MatrixMode mode) {
}
}
void Matrix_RotateAxisS(s16 angle, Vec3f* axis, MatrixMode mode) {
RECOMP_PATCH void Matrix_RotateAxisS(s16 angle, Vec3f* axis, MatrixMode mode) {
MtxF* cmf;
f32 cos;
f32 sin;

View File

@ -9,6 +9,8 @@
#include "z64shrink_window.h"
#include "z64player.h"
static bool camera_fixes = false;
static bool prev_analog_cam_active = false;
static bool can_use_analog_cam = false;
static bool analog_cam_active = false;
@ -26,6 +28,10 @@ float analog_camera_y_sensitivity = 500.0f;
static const float analog_cam_threshold = 0.1f;
RECOMP_EXPORT void recomp_set_camera_fixes(bool new_val) {
camera_fixes = new_val;
}
void update_analog_camera_params(Camera* camera) {
// recomp_printf("Camera at: %.2f %.2f %.2f\n"
// " eye: %.2f %.2f %.2f\n"
@ -176,7 +182,7 @@ void Camera_UpdateInterface(s32 interfaceFlags);
s32 func_800CB7CC(Camera* camera);
s32 func_800CB854(Camera* camera);
Vec3s Camera_Update(Camera* camera) {
RECOMP_PATCH Vec3s Camera_Update(Camera* camera) {
Vec3f viewAt;
Vec3f viewEye;
Vec3f viewUp;
@ -461,7 +467,7 @@ s32 func_800CBA7C(Camera* camera);
#define RELOAD_PARAMS(camera) ((camera->animState == 0) || (camera->animState == 10) || (camera->animState == 20))
// @recomp Patched for analog cam.
s32 Camera_Normal1(Camera* camera) {
RECOMP_PATCH s32 Camera_Normal1(Camera* camera) {
Vec3f* eye = &camera->eye;
Vec3f* at = &camera->at;
Vec3f* eyeNext = &camera->eyeNext;
@ -870,7 +876,7 @@ s32 Camera_Normal1(Camera* camera) {
* Camera for climbing structures
*/
// @recomp Patched for analog cam.
s32 Camera_Jump2(Camera* camera) {
RECOMP_PATCH s32 Camera_Jump2(Camera* camera) {
Vec3f* eye = &camera->eye;
Vec3f* at = &camera->at;
Vec3f* eyeNext = &camera->eyeNext;
@ -1063,7 +1069,11 @@ s32 Camera_Jump2(Camera* camera) {
* Used for targeting
*/
// @recomp Patched for analog cam.
s32 Camera_Parallel1(Camera* camera) {
RECOMP_PATCH s32 Camera_Parallel1(Camera* camera) {
// @recomp
static bool prev_targeting_held = false;
bool isChargingDekuFlowerDive = !!(((Player*)camera->focalActor)->stateFlags3 & PLAYER_STATE3_100);
Vec3f* eye = &camera->eye;
Vec3f* at = &camera->at;
Vec3f* eyeNext = &camera->eyeNext;
@ -1089,6 +1099,10 @@ s32 Camera_Parallel1(Camera* camera) {
CameraModeValue* values;
f32 yNormal;
// @recomp Read timer4 from timer2.
s16 timer4 = rwData->timer2 >> 5; // @recomp Used to check if z-target can be quit. Mirrors timer2 values during z-target in unmodified function.
rwData->timer2 &= 0x001F;
if (!RELOAD_PARAMS(camera)) {
} else {
values = sCameraSettings[camera->setting].cameraModes[camera->mode].values;
@ -1151,6 +1165,8 @@ s32 Camera_Parallel1(Camera* camera) {
rwData->timer2 = 20;
} else {
rwData->timer2 = 6;
// @recomp Initiate timer4 for z-target.
timer4 = 6;
}
if ((camera->focalActor == &GET_PLAYER(camera->play)->actor) && (camera->mode == CAM_MODE_CHARGE)) {
@ -1187,12 +1203,55 @@ s32 Camera_Parallel1(Camera* camera) {
rwData->unk_26 = 1;
camera->animState = 1;
sCameraInterfaceFlags = roData->interfaceFlags;
// @recomp Reset prev_targeting_held after transition.
prev_targeting_held = false;
break;
}
// @recomp Change behavior for z-target only.
if (camera_fixes) {
if ((roData->interfaceFlags & (PARALLEL1_FLAG_3 | PARALLEL1_FLAG_2 | PARALLEL1_FLAG_1)) == PARALLEL1_FLAG_1) {
Player* player = GET_PLAYER(camera->play);
bool targeting_held = func_80123434(player) || player->lockOnActor != NULL;
// @recomp Fix camera rotating with player if z-target gets released too fast after transition.
if ((targeting_held) && (!prev_targeting_held)) {
// @recomp Reset timer2 to avoid immediate rotation, if player presses, releases and presses z-target in a very short time-window.
rwData->timer2 = 6;
rwData->unk_1E = BINANG_ROT180(camera->focalActorPosRot.rot.y) + roData->unk_22;
}
// @recomp Maintain vanilla behavior for quitting z-target.
if ((timer4 == 0) && (!targeting_held)) {
rwData->timer2 = 0;
}
// @recomp Decrease timer4 only in cases where timer2 would be decreased.
if ((timer4 > 0)
&& (rwData->timer3 <= 0)) {
timer4--;
}
prev_targeting_held = targeting_held;
}
}
if (rwData->timer2 != 0) {
switch (roData->interfaceFlags & (PARALLEL1_FLAG_3 | PARALLEL1_FLAG_2 | PARALLEL1_FLAG_1)) {
case PARALLEL1_FLAG_1:
if (camera_fixes) {
// @recomp Fix camera rotating with player if z-target gets released too fast after transition.
if (isChargingDekuFlowerDive) {
// @recomp Fix camera wiggle during dive into deku flower.
rwData->unk_1E = BINANG_ROT180(camera->focalActorPosRot.rot.y) + roData->unk_22;
}
rwData->unk_20 = roData->unk_20;
break;
}
// @recomp fallthrough
case (PARALLEL1_FLAG_3 | PARALLEL1_FLAG_2 | PARALLEL1_FLAG_1):
rwData->unk_1E = BINANG_ROT180(camera->focalActorPosRot.rot.y) + roData->unk_22;
rwData->unk_20 = roData->unk_20;
@ -1400,10 +1459,20 @@ s32 Camera_Parallel1(Camera* camera) {
func_800CBFA4(camera, at, eye, 3);
}
if (rwData->timer2 != 0) {
sUpdateCameraDirection = true;
if (camera_fixes) {
// @recomp Fix camera not updating input dir for the first few frames after transition.
if ((isChargingDekuFlowerDive) && (rwData->timer2 != 0)) {
// @recomp Fix camera wiggle during dive into deku flower.
sUpdateCameraDirection = true;
} else {
sUpdateCameraDirection = false;
}
} else {
sUpdateCameraDirection = false;
if (rwData->timer2 != 0) {
sUpdateCameraDirection = true;
} else {
sUpdateCameraDirection = false;
}
}
}
@ -1412,6 +1481,9 @@ s32 Camera_Parallel1(Camera* camera) {
camera->atLerpStepScale = Camera_ClampLerpScale(camera, sp72 ? roData->unk_1C : roData->unk_18);
rwData->unk_26 &= ~1;
// @recomp Save timer4 in unused bits of timer2.
rwData->timer2 |= timer4 << 5;
return 1;
}
@ -1421,7 +1493,7 @@ s32 Camera_Parallel1(Camera* camera) {
* Riding Epona and Zora
*/
// @recomp Patched for analog cam.
s32 Camera_Normal3(Camera* camera) {
RECOMP_PATCH s32 Camera_Normal3(Camera* camera) {
Normal3ReadOnlyData* roData = &camera->paramData.norm3.roData;
Normal3ReadWriteData* rwData = &camera->paramData.norm3.rwData;
f32 sp8C;
@ -1620,7 +1692,7 @@ s32 Camera_Normal3(Camera* camera) {
* e.g. Gyorg, Pinnacle Rock, whirlpool, water
*/
// @recomp Patched for analog cam.
s32 Camera_Jump3(Camera* camera) {
RECOMP_PATCH s32 Camera_Jump3(Camera* camera) {
Vec3f* sp48 = &camera->eye;
Vec3f* sp44 = &camera->at;
Vec3f* sp40 = &camera->eyeNext;
@ -1905,7 +1977,7 @@ extern u8 D_809EE4D0;
// @recomp Patch the Wart boss fight in the Great Bay temple so that the fight starts if you look at it with the right stick analog camera,
// instead of requiring entering first person mode mode.
void func_809EC568(Boss04* this, PlayState* play) {
RECOMP_PATCH void func_809EC568(Boss04* this, PlayState* play) {
Player* player = GET_PLAYER(play);
f32 x;
f32 y;

View File

@ -95,7 +95,7 @@ void force_camera_ignore_tracking() {
camera_ignore_tracking = true;
}
void KaleidoScope_SetView(PauseContext* pauseCtx, f32 eyeX, f32 eyeY, f32 eyeZ) {
RECOMP_PATCH void KaleidoScope_SetView(PauseContext* pauseCtx, f32 eyeX, f32 eyeY, f32 eyeZ) {
Vec3f eye;
Vec3f at;
Vec3f up;
@ -117,7 +117,7 @@ void KaleidoScope_SetView(PauseContext* pauseCtx, f32 eyeX, f32 eyeY, f32 eyeZ)
}
void FileSelect_SetView(FileSelectState* this, f32 eyeX, f32 eyeY, f32 eyeZ) {
RECOMP_PATCH void FileSelect_SetView(FileSelectState* this, f32 eyeX, f32 eyeY, f32 eyeZ) {
Vec3f eye;
Vec3f lookAt;
Vec3f up;
@ -197,7 +197,7 @@ bool should_interpolate_perspective(Vec3f* eye, Vec3f* at) {
/**
* Apply view to POLY_OPA_DISP, POLY_XLU_DISP (and OVERLAY_DISP if ortho)
*/
void View_Apply(View* view, s32 mask) {
RECOMP_PATCH void View_Apply(View* view, s32 mask) {
mask = (view->flags & mask) | (mask >> 4);
// @recomp Determine if the camera should be interpolated this frame.
@ -292,7 +292,7 @@ Vec3f Camera_Vec3sToVec3f(Vec3s* src);
* Used for many fixed-based camera settings i.e. camera is fixed in rotation, and often position (but not always)
*/
// @recomp Modified to not force interpolation while panning.
s32 Camera_Fixed1(Camera* camera) {
RECOMP_PATCH s32 Camera_Fixed1(Camera* camera) {
s32 pad[2];
s32 yawDiff;
VecGeo eyeOffset;

View File

@ -1,7 +1,7 @@
#include "patches.h"
// Disable frustum culling for actors, but leave distance culling intact
s32 func_800BA2FC(PlayState* play, Actor* actor, Vec3f* projectedPos, f32 projectedW) {
RECOMP_PATCH s32 func_800BA2FC(PlayState* play, Actor* actor, Vec3f* projectedPos, f32 projectedW) {
if ((-actor->uncullZoneScale < projectedPos->z) &&
(projectedPos->z < (actor->uncullZoneForward + actor->uncullZoneScale))) {
// f32 phi_f12;
@ -30,7 +30,7 @@ s32 func_800BA2FC(PlayState* play, Actor* actor, Vec3f* projectedPos, f32 projec
}
// Disable frustum culling for bush spawning
// s32 EnWood02_SpawnZoneCheck(EnWood02* this, PlayState* play, Vec3f* arg2) {
// RECOMP_PATCH s32 EnWood02_SpawnZoneCheck(EnWood02* this, PlayState* play, Vec3f* arg2) {
// f32 phi_f12;
// SkinMatrix_Vec3fMtxFMultXYZW(&play->viewProjectionMtxF, arg2, &this->actor.projectedPos, &this->actor.projectedW);
@ -52,7 +52,7 @@ s32 func_800BA2FC(PlayState* play, Actor* actor, Vec3f* projectedPos, f32 projec
// }
// Disable frustum culling for grass
s32 func_809A9110(PlayState* play, Vec3f* pos) {
RECOMP_PATCH s32 func_809A9110(PlayState* play, Vec3f* pos) {
f32 w;
Vec3f projectedPos;
@ -73,7 +73,7 @@ s32 func_809A9110(PlayState* play, Vec3f* pos) {
// Replace point light glow effect with RT64 point Z test so it works in widescreen
void Lights_GlowCheck(PlayState* play) {
RECOMP_PATCH void Lights_GlowCheck(PlayState* play) {
LightNode* light = play->lightCtx.listHead;
while (light != NULL) {
@ -107,7 +107,7 @@ extern Gfx gameplay_keep_DL_029CF0[];
Vtx light_test_vert = VTX(0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF);
void Lights_DrawGlow(PlayState* play) {
RECOMP_PATCH void Lights_DrawGlow(PlayState* play) {
Gfx* dl;
LightPoint* params;
LightNode* light = play->lightCtx.listHead;

16
patches/custom_syms.toml Normal file
View 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 },
]

View File

@ -6,7 +6,7 @@
CsCmdActorCue* prev_cues_checked[ARRAY_COUNT(((CutsceneContext*)0)->actorCues)] = {0};
void Cutscene_ActorTranslate(Actor* actor, PlayState* play, s32 cueChannel) {
RECOMP_PATCH void Cutscene_ActorTranslate(Actor* actor, PlayState* play, s32 cueChannel) {
Vec3f startPos;
Vec3f endPos;
CsCmdActorCue* cue = play->csCtx.actorCues[cueChannel];
@ -39,7 +39,7 @@ extern EnHorseCsFunc D_808890F0[];
extern EnHorseCsFunc D_8088911C[];
// @recomp Patched to skip interpolation on Epona when she's teleported by a cutscene.
void func_80884718(EnHorse* this, PlayState* play) {
RECOMP_PATCH void func_80884718(EnHorse* this, PlayState* play) {
CsCmdActorCue* cue;
if (Cutscene_IsCueInChannel(play, CS_CMD_ACTOR_CUE_112)) {
@ -75,7 +75,7 @@ void func_80884718(EnHorse* this, PlayState* play) {
}
// @recomp Patched to skip interpolation on Epona when she's teleported by a cutscene.
void func_80883B70(EnHorse* this, CsCmdActorCue* cue) {
RECOMP_PATCH void func_80883B70(EnHorse* this, CsCmdActorCue* cue) {
// @recomp Being teleported by a new cue, so skip interpolation.
actor_set_interpolation_skipped(&this->actor);
@ -90,7 +90,7 @@ void func_80883B70(EnHorse* this, CsCmdActorCue* cue) {
}
// @recomp Patched to skip interpolation on Link when he's teleported to a new cue.
void Player_Cutscene_SetPosAndYawToStart(Player* this, CsCmdActorCue* cue) {
RECOMP_PATCH void Player_Cutscene_SetPosAndYawToStart(Player* this, CsCmdActorCue* cue) {
// @recomp Being teleported by a new cue, so skip interpolation.
actor_set_interpolation_skipped(&this->actor);
@ -104,7 +104,7 @@ void Player_Cutscene_SetPosAndYawToStart(Player* this, CsCmdActorCue* cue) {
CsCmdActorCue* prev_link_cue = NULL;
// @recomp Patched to skip interpolation on Link when he's teleported to a new cue.
void Player_Cutscene_Translate(PlayState* play, Player* this, CsCmdActorCue* cue) {
RECOMP_PATCH void Player_Cutscene_Translate(PlayState* play, Player* this, CsCmdActorCue* cue) {
f32 startX = cue->startPos.x;
f32 startY = cue->startPos.y;
f32 startZ = cue->startPos.z;

View File

@ -9,7 +9,7 @@ extern Gfx sTransWipe3DL[];
#define THIS ((TransitionWipe3*)thisx)
// @recomp patched to scale the transition based on aspect ratio
void TransitionWipe3_Draw(void* thisx, Gfx** gfxP) {
RECOMP_PATCH void TransitionWipe3_Draw(void* thisx, Gfx** gfxP) {
Gfx* gfx = *gfxP;
Mtx* modelView = &THIS->modelView[THIS->frame];
f32 scale = 14.8f;
@ -53,7 +53,7 @@ extern s32 gFramerateDivisor;
// @recomp Motion blur works fine normally, but when running at a higher framerate the effect is much less pronounced
// as the previous frames decay quicker due to there being more frames drawn in the same period of time.
void Play_DrawMotionBlur(PlayState* this) {
RECOMP_PATCH void Play_DrawMotionBlur(PlayState* this) {
GraphicsContext* gfxCtx = this->state.gfxCtx;
s32 alpha;
Gfx* gfx;
@ -126,7 +126,7 @@ void Play_DrawMotionBlur(PlayState* this) {
}
// @recomp Patched to increase the scale based on the aspect ratio.
void Actor_DrawLensOverlay(Gfx** gfxP, s32 lensMaskSize) {
RECOMP_PATCH void Actor_DrawLensOverlay(Gfx** gfxP, s32 lensMaskSize) {
// @recomp Calculate the increase in aspect ratio.
f32 original_aspect_ratio = (float)SCREEN_WIDTH / SCREEN_HEIGHT;
f32 aspect_ratio_scale = recomp_get_aspect_ratio(original_aspect_ratio) / original_aspect_ratio;
@ -139,7 +139,7 @@ void Actor_DrawLensOverlay(Gfx** gfxP, s32 lensMaskSize) {
// @recomp Patched to use ortho tris for interpolation and to prevent the telescope and lens effects from getting stretched wide.
void TransitionCircle_LoadAndSetTexture(Gfx** gfxp, TexturePtr texture, s32 fmt, s32 arg3, s32 masks, s32 maskt,
RECOMP_PATCH void TransitionCircle_LoadAndSetTexture(Gfx** gfxp, TexturePtr texture, s32 fmt, s32 arg3, s32 masks, s32 maskt,
f32 arg6) {
Gfx* gfx = *gfxp;
s32 xh = gCfbWidth;

View File

@ -13,7 +13,7 @@ extern Gfx gEffDustDL[];
((&(particle).unk_1C)[1])
// @recomp Patched to record when a particle is moved to skip interpolation.
void func_808DC454(ObjectKankyo* this, PlayState* play) {
RECOMP_PATCH void func_808DC454(ObjectKankyo* this, PlayState* play) {
s16 i;
s32 pad1;
f32 phi_f20;
@ -155,7 +155,7 @@ void func_808DC454(ObjectKankyo* this, PlayState* play) {
}
}
void func_808DD3C8(Actor* thisx, PlayState* play2) {
RECOMP_PATCH void func_808DD3C8(Actor* thisx, PlayState* play2) {
PlayState* play = play2;
ObjectKankyo* this = (ObjectKankyo*)thisx;
Vec3f worldPos;
@ -331,7 +331,7 @@ static inline void pop_effect_tag(GraphicsContext* gfxCtx) {
}
// @recomp Patched to tag effects.
void Effect_DrawAll(GraphicsContext* gfxCtx) {
RECOMP_PATCH void Effect_DrawAll(GraphicsContext* gfxCtx) {
s32 i;
@ -442,7 +442,7 @@ static TexturePtr sWaterSplashTextures[] = {
* applies to all effects of that type while drawing the first effect of that type.
*/
// @recomp Patched to tag matrices.
void EnClearTag_DrawEffects(Actor* thisx, PlayState* play) {
RECOMP_PATCH void EnClearTag_DrawEffects(Actor* thisx, PlayState* play) {
u8 isMaterialApplied = false;
s16 i;
s16 j;
@ -694,7 +694,7 @@ void EnClearTag_DrawEffects(Actor* thisx, PlayState* play) {
}
// @recomp Patched to tag the two custom lens flares (used by the Igos du Ikana curtains).
void Environment_DrawCustomLensFlare(PlayState* play) {
RECOMP_PATCH void Environment_DrawCustomLensFlare(PlayState* play) {
Vec3f pos;
// @recomp Set up the graphics context.
@ -739,7 +739,7 @@ void Environment_DrawCustomLensFlare(PlayState* play) {
}
// @recomp Patched to tag the sun lens flare.
void Environment_DrawSunLensFlare(PlayState* play, EnvironmentContext* envCtx, View* view, GraphicsContext* gfxCtx,
RECOMP_PATCH void Environment_DrawSunLensFlare(PlayState* play, EnvironmentContext* envCtx, View* view, GraphicsContext* gfxCtx,
Vec3f vec) {
if ((play->envCtx.precipitation[PRECIP_RAIN_CUR] == 0) &&
!(GET_ACTIVE_CAM(play)->stateFlags & CAM_STATE_UNDERWATER) && (play->skyboxId == SKYBOX_NORMAL_SKY)) {

View File

@ -25,7 +25,7 @@ s16 sVtxPageGameOverSaveQuadsY[VTX_PAGE_SAVE_QUADS] = {
};
// @recomp patched to draw as strips with bilerp compensation instead of tiles.
s16 KaleidoScope_SetPageVertices(PlayState* play, Vtx* vtx, s16 vtxPage, s16 numQuads) {
RECOMP_PATCH s16 KaleidoScope_SetPageVertices(PlayState* play, Vtx* vtx, s16 vtxPage, s16 numQuads) {
PauseContext* pauseCtx = &play->pauseCtx;
GameOverContext* gameOverCtx = &play->gameOverCtx;
s16* quadsX;
@ -35,11 +35,11 @@ s16 KaleidoScope_SetPageVertices(PlayState* play, Vtx* vtx, s16 vtxPage, s16 num
s32 cur_y;
u32 row;
cur_y = PAGE_BG_HEIGHT / 2;
cur_y = (PAGE_BG_HEIGHT + 2) / 2;
// 2 verts per row plus 2 extra verts at the start and the end.
for (row = 0; row < RECOMP_PAGE_ROW_COUNT + 2; row++) {
s32 next_y = MAX(cur_y - RECOMP_PAGE_ROW_HEIGHT, -PAGE_BG_HEIGHT / 2);
s32 next_y = MAX(cur_y - RECOMP_PAGE_ROW_HEIGHT, -(PAGE_BG_HEIGHT + 2) / 2);
vtx[4 * row + 0].v.ob[0] = -PAGE_BG_WIDTH / 2;
vtx[4 * row + 1].v.ob[0] = PAGE_BG_WIDTH / 2;
@ -204,7 +204,7 @@ void KaleidoDrawWrapper(PlayState* play) {
}
}
void KaleidoScopeCall_Init(PlayState* play) {
RECOMP_PATCH void KaleidoScopeCall_Init(PlayState* play) {
// @recomp Set the update and draw func pointers to the wrappers instead of the actual functions.
sKaleidoScopeUpdateFunc = KaleidoUpdateWrapper;
sKaleidoScopeDrawFunc = KaleidoDrawWrapper;
@ -212,7 +212,7 @@ void KaleidoScopeCall_Init(PlayState* play) {
}
// @recomp patched to fix bilerp seams.
Gfx* KaleidoScope_DrawPageSections(Gfx* gfx, Vtx* vertices, TexturePtr* textures) {
RECOMP_PATCH Gfx* KaleidoScope_DrawPageSections(Gfx* gfx, Vtx* vertices, TexturePtr* textures) {
s32 i;
s32 j;
@ -234,7 +234,7 @@ Gfx* KaleidoScope_DrawPageSections(Gfx* gfx, Vtx* vertices, TexturePtr* textures
// Draw the rows.
for (u32 bg_row = 0; bg_row < RECOMP_PAGE_ROW_COUNT; bg_row++) {
u32 cur_row_height = MIN(RECOMP_PAGE_ROW_HEIGHT, PAGE_BG_HEIGHT - bg_row * RECOMP_PAGE_ROW_HEIGHT);
u32 cur_row_height = MIN(RECOMP_PAGE_ROW_HEIGHT, PAGE_BG_HEIGHT + 1 - bg_row * RECOMP_PAGE_ROW_HEIGHT);
gDPLoadTextureTile(gfx++, *cur_image,
G_IM_FMT_IA, G_IM_SIZ_8b, // fmt, siz
PAGE_BG_WIDTH + 2, PAGE_BG_HEIGHT + 2, // width, height
@ -264,7 +264,7 @@ int extra_vis = 0;
// @recomp Patch the giants cutscene to make certain frames take longer to mimic performance on console.
// This prevents the music from desyncing from the cutscene as it was designed around the console's frame times.
void Cutscene_UpdateScripted(PlayState* play, CutsceneContext* csCtx) {
RECOMP_PATCH void Cutscene_UpdateScripted(PlayState* play, CutsceneContext* csCtx) {
if ((gSaveContext.cutsceneTrigger != 0) && (play->transitionTrigger == TRANS_TRIGGER_START)) {
gSaveContext.cutsceneTrigger = 0;
}
@ -297,7 +297,7 @@ void Cutscene_UpdateScripted(PlayState* play, CutsceneContext* csCtx) {
}
// @recomp Fix a texture scroll using an incorrect tile size, which resulted in the scroll jumping during the animation.
s32 DemoEffect_OverrideLimbDrawTimewarp(PlayState* play, SkelCurve* skelCurve, s32 limbIndex, Actor* thisx) {
RECOMP_PATCH s32 DemoEffect_OverrideLimbDrawTimewarp(PlayState* play, SkelCurve* skelCurve, s32 limbIndex, Actor* thisx) {
s32 pad;
DemoEffect* this = (DemoEffect*)thisx;
u32 frames = play->gameplayFrames;
@ -333,7 +333,7 @@ void DayTelop_Noop(DayTelopState* this);
void DayTelop_LoadGraphics(DayTelopState* this);
// @recomp Increase the length of the "Dawn of the X Day" screen to account for faster loading.
void DayTelop_Init(GameState* thisx) {
RECOMP_PATCH void DayTelop_Init(GameState* thisx) {
DayTelopState* this = (DayTelopState*)thisx;
GameState_SetFramerateDivisor(&this->state, 1);
@ -357,3 +357,111 @@ void DayTelop_Init(GameState* thisx) {
DayTelop_LoadGraphics(this);
Audio_PlaySfx(NA_SE_OC_TELOP_IMPACT);
}
extern PlayerAnimationHeader* D_8085D17C[PLAYER_FORM_MAX];
void Player_TalkWithPlayer(PlayState* play, Actor* actor);
void Player_Action_88(Player* this, PlayState* play);
void Player_SetAction_PreserveItemAction(PlayState* play, Player* this, PlayerActionFunc actionFunc, s32 arg3);
void Player_AnimationPlayOnceReverse(PlayState* play, Player* this, PlayerAnimationHeader* anim);
s32 Player_ActionChange_13(Player* this, PlayState* play);
s32 func_8085B28C(PlayState* play, Player* this, PlayerCsAction csAction);
void func_808525C4(PlayState* play, Player* this);
void func_8085255C(PlayState* play, Player* this);
void func_80836A5C(Player* this, PlayState* play);
s32 func_8082DA90(PlayState* play);
// @recomp Patched to fix the issue where ocarina inputs are discarded for the first 3 frames (150ms).
RECOMP_PATCH void Player_Action_63(Player* this, PlayState* play) {
if ((this->unk_AA5 != PLAYER_UNKAA5_4) && ((PlayerAnimation_Update(play, &this->skelAnime) &&
(this->skelAnime.animation == D_8085D17C[this->transformation])) ||
((this->skelAnime.mode == 0) && (this->av2.actionVar2 == 0)))) {
func_808525C4(play, this);
// @recomp Fix the bug where ocarina inputs are discarded for 3 frames by only running this on the first frame of this state.
if (this->av2.actionVar2 == 1) {
if (!(this->actor.flags & ACTOR_FLAG_20000000) || (this->unk_A90->id == ACTOR_EN_ZOT)) {
Message_DisplayOcarinaStaff(play, OCARINA_ACTION_FREE_PLAY);
}
}
} else if (this->av2.actionVar2 != 0) {
if (play->msgCtx.ocarinaMode == OCARINA_MODE_END) {
play->interfaceCtx.unk_222 = 0;
CutsceneManager_Stop(play->playerCsIds[PLAYER_CS_ID_ITEM_OCARINA]);
this->actor.flags &= ~ACTOR_FLAG_20000000;
if ((this->talkActor != NULL) && (this->talkActor == this->unk_A90) && (this->unk_A94 >= 0.0f)) {
Player_TalkWithPlayer(play, this->talkActor);
} else if (this->tatlTextId < 0) {
this->talkActor = this->tatlActor;
this->tatlActor->textId = -this->tatlTextId;
Player_TalkWithPlayer(play, this->talkActor);
} else if (!Player_ActionChange_13(this, play)) {
func_80836A5C(this, play);
Player_AnimationPlayOnceReverse(play, this, D_8085D17C[this->transformation]);
}
} else {
s32 var_v1 = (play->msgCtx.ocarinaMode >= OCARINA_MODE_WARP_TO_GREAT_BAY_COAST) &&
(play->msgCtx.ocarinaMode <= OCARINA_MODE_WARP_TO_ENTRANCE);
s32 pad[2];
if (var_v1 || (play->msgCtx.ocarinaMode == OCARINA_MODE_APPLY_SOT) ||
(play->msgCtx.ocarinaMode == OCARINA_MODE_APPLY_DOUBLE_SOT) ||
(play->msgCtx.ocarinaMode == OCARINA_MODE_APPLY_INV_SOT_FAST) ||
(play->msgCtx.ocarinaMode == OCARINA_MODE_APPLY_INV_SOT_SLOW)) {
if (play->msgCtx.ocarinaMode == OCARINA_MODE_APPLY_SOT) {
if (!func_8082DA90(play)) {
if (gSaveContext.save.saveInfo.playerData.threeDayResetCount == 1) {
play->nextEntrance = ENTRANCE(CUTSCENE, 1);
} else {
play->nextEntrance = ENTRANCE(CUTSCENE, 0);
}
gSaveContext.nextCutsceneIndex = 0xFFF7;
play->transitionTrigger = TRANS_TRIGGER_START;
}
} else {
Actor* actor;
play->interfaceCtx.unk_222 = 0;
CutsceneManager_Stop(play->playerCsIds[PLAYER_CS_ID_ITEM_OCARINA]);
this->actor.flags &= ~ACTOR_FLAG_20000000;
actor = Actor_Spawn(&play->actorCtx, play, var_v1 ? ACTOR_EN_TEST7 : ACTOR_EN_TEST6,
this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0,
0, play->msgCtx.ocarinaMode);
if (actor != NULL) {
this->stateFlags1 &= ~PLAYER_STATE1_20000000;
this->csAction = PLAYER_CSACTION_NONE;
func_8085B28C(play, NULL, PLAYER_CSACTION_19);
this->stateFlags1 |= PLAYER_STATE1_10000000 | PLAYER_STATE1_20000000;
} else {
func_80836A5C(this, play);
Player_AnimationPlayOnceReverse(play, this, D_8085D17C[this->transformation]);
}
}
} else if ((play->msgCtx.ocarinaMode == OCARINA_MODE_EVENT) &&
(play->msgCtx.lastPlayedSong == OCARINA_SONG_ELEGY)) {
play->interfaceCtx.unk_222 = 0;
CutsceneManager_Stop(play->playerCsIds[PLAYER_CS_ID_ITEM_OCARINA]);
this->actor.flags &= ~ACTOR_FLAG_20000000;
Player_SetAction_PreserveItemAction(play, this, Player_Action_88, 0);
this->stateFlags1 |= PLAYER_STATE1_10000000 | PLAYER_STATE1_20000000;
} else if (this->unk_AA5 == PLAYER_UNKAA5_4) {
f32 temp_fa0 = this->skelAnime.jointTable[PLAYER_LIMB_ROOT - 1].x;
f32 temp_fa1 = this->skelAnime.jointTable[PLAYER_LIMB_ROOT - 1].z;
f32 var_fv1;
var_fv1 = sqrtf(SQ(temp_fa0) + SQ(temp_fa1));
if (var_fv1 != 0.0f) {
var_fv1 = (var_fv1 - 100.0f) / var_fv1;
var_fv1 = CLAMP_MIN(var_fv1, 0.0f);
}
this->skelAnime.jointTable[PLAYER_LIMB_ROOT - 1].x = temp_fa0 * var_fv1;
this->skelAnime.jointTable[PLAYER_LIMB_ROOT - 1].z = temp_fa1 * var_fv1;
} else {
func_8085255C(play, this);
}
}
}
}

View File

@ -6,21 +6,35 @@
#include "z64voice.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);
s16 func_80832754(Player* this, s32 arg1);
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.
s32 func_80847190(PlayState* play, Player* this, s32 arg2) {
RECOMP_PATCH s32 func_80847190(PlayState* play, Player* this, s32 arg2) {
s32 pad;
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.
s32 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_y = 0;
if (recomp_analog_cam_enabled()) {
if (recomp_analog_cam_enabled() || recomp_aiming_override_mode == RECOMP_AIMING_OVERRIDE_FORCE_RIGHT_STICK) {
float analog_x_float = 0.0f;
float analog_y_float = 0.0f;
recomp_get_camera_inputs(&analog_x_float, &analog_y_float);
@ -35,9 +49,13 @@ s32 func_80847190(PlayState* play, Player* this, s32 arg2) {
// play->state.input[0].rel.stick_x, play->state.input[0].rel.stick_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.
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).
if (!inverted_y) {
@ -46,7 +64,11 @@ s32 func_80847190(PlayState* play, Player* this, s32 arg2) {
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.
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
if (inverted_x) {
@ -97,7 +119,13 @@ s32 func_80847190(PlayState* play, Player* this, s32 arg2) {
// @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.
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) {
stick_y = -stick_y;
}
@ -118,7 +146,13 @@ s32 func_80847190(PlayState* play, Player* this, s32 arg2) {
// @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.
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) {
stick_x = -stick_x;
}
@ -130,6 +164,8 @@ s32 func_80847190(PlayState* play, Player* this, s32 arg2) {
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;
return func_80832754(this, (play->unk_1887C != 0) || func_800B7128(this) || func_8082EF20(this));
@ -147,7 +183,7 @@ extern Input* sPlayerControlInput;
* - B exits, using the RESPAWN_MODE_DOWN entrance
*/
// @recomp Patched for aiming inversion and supporting the right stick in dual analog.
void func_8083A98C(Actor* thisx, PlayState* play2) {
RECOMP_PATCH void func_8083A98C(Actor* thisx, PlayState* play2) {
PlayState* play = play2;
Player* this = (Player*)thisx;
s32 camMode;
@ -351,7 +387,7 @@ u8* get_button_item_equip_ptr(u32 form, u32 button) {
}
// Return currently-pressed button, in order of priority D-Pad, B, CLEFT, CDOWN, CRIGHT.
EquipSlot func_8082FDC4(void) {
RECOMP_PATCH EquipSlot func_8082FDC4(void) {
EquipSlot i;
for (int extra_slot_index = 0; extra_slot_index < ARRAY_COUNT(buttons_to_extra_slot); extra_slot_index++) {
@ -369,7 +405,7 @@ EquipSlot func_8082FDC4(void) {
return i;
}
ItemId Player_GetItemOnButton(PlayState* play, Player* player, EquipSlot slot) {
RECOMP_PATCH ItemId Player_GetItemOnButton(PlayState* play, Player* player, EquipSlot slot) {
if (slot >= EQUIP_SLOT_A) {
return ITEM_NONE;
}
@ -444,7 +480,7 @@ bool func_808323C0(Player *this, s16 csId);
void func_80855218(PlayState *play, Player *this, struct_8085D910 **arg2);
void func_808550D0(PlayState *play, Player *this, f32 arg2, f32 arg3, s32 arg4);
void Player_Action_86(Player *this, PlayState *play) {
RECOMP_PATCH void Player_Action_86(Player *this, PlayState *play) {
struct_8085D910 *sp4C = D_8085D910;
s32 sp48 = false;
@ -534,13 +570,33 @@ void Player_Action_86(Player *this, PlayState *play) {
func_808550D0(play, this, this->unk_B10[4], this->unk_B10[5], (this->transformation == PLAYER_FORM_HUMAN) ? 0 : 1);
}
bool no_bow_epona_fix = false;
// @recomp_export void recomp_set_no_bow_epona_fix(bool new_val): Set whether to enable the fix for getting on Epona without a bow.
RECOMP_EXPORT void recomp_set_no_bow_epona_fix(bool new_val) {
no_bow_epona_fix = new_val;
}
bool h_and_d_no_sword_fix = false;
// @recomp_export void recomp_set_h_and_d_no_sword_fix(bool new_val): Set whether to enable the fix for playing Honey and Darling without a sword.
RECOMP_EXPORT void recomp_set_h_and_d_no_sword_fix(bool new_val) {
h_and_d_no_sword_fix = new_val;
}
extern s16 sPictoState;
extern s16 sPictoPhotoBeingTaken;
extern void* gWorkBuffer;
u16 func_801A5100(void);
#define ON_EPONA (player->stateFlags1 & PLAYER_STATE1_800000)
#define EPONA_FIX_ACTIVE (no_bow_epona_fix && ON_EPONA)
#define AT_H_AND_D (play->sceneId == SCENE_BOWLING)
#define H_AND_D_FIX_ACTIVE (h_and_d_no_sword_fix && AT_H_AND_D)
// @recomp Patched to update status of extra buttons via set_extra_item_slot_status.
void Interface_UpdateButtonsPart1(PlayState* play) {
RECOMP_PATCH void Interface_UpdateButtonsPart1(PlayState* play) {
InterfaceContext* interfaceCtx = &play->interfaceCtx;
Player* player = GET_PLAYER(play);
s32 pad;
@ -548,11 +604,11 @@ void Interface_UpdateButtonsPart1(PlayState* play) {
if (gSaveContext.save.cutsceneIndex < 0xFFF0) {
gSaveContext.hudVisibilityForceButtonAlphasByStatus = false;
if ((player->stateFlags1 & PLAYER_STATE1_800000) || CHECK_WEEKEVENTREG(WEEKEVENTREG_08_01) ||
if (ON_EPONA || CHECK_WEEKEVENTREG(WEEKEVENTREG_08_01) ||
(!CHECK_EVENTINF(EVENTINF_41) && (play->unk_1887C >= 2))) {
// Riding Epona OR Honey & Darling minigame OR Horseback balloon minigame OR related to swamp boat
// (non-minigame?)
if ((player->stateFlags1 & PLAYER_STATE1_800000) && (player->currentMask == PLAYER_MASK_BLAST) &&
if (ON_EPONA && (player->currentMask == PLAYER_MASK_BLAST) &&
(gSaveContext.bButtonStatus == BTN_DISABLED)) {
// Riding Epona with blast mask?
restoreHudVisibility = true;
@ -603,24 +659,46 @@ void Interface_UpdateButtonsPart1(PlayState* play) {
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
set_extra_item_slot_status(BTN_DISABLED);
} else {
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW;
// @recomp_use_export_var no_bow_epona_fix: Part of the no bow Epona fix.
if (EPONA_FIX_ACTIVE) {
if (gSaveContext.save.saveInfo.inventory.items[SLOT_BOW] == ITEM_BOW) {
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW;
BUTTON_STATUS(EQUIP_SLOT_B) = BTN_ENABLED;
}
} else {
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW;
}
if (play->unk_1887C >= 2) {
Interface_LoadItemIconImpl(play, EQUIP_SLOT_B);
} else if (gSaveContext.save.saveInfo.inventory.items[SLOT_BOW] == ITEM_NONE) {
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_NONE;
// @recomp_use_export_var no_bow_epona_fix: Part of the no bow Epona fix.
if (EPONA_FIX_ACTIVE) {
gSaveContext.buttonStatus[EQUIP_SLOT_B] = BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B);
BUTTON_STATUS(EQUIP_SLOT_B) = BTN_DISABLED;
} else {
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_NONE;
}
} else {
Interface_LoadItemIconImpl(play, EQUIP_SLOT_B);
}
BUTTON_STATUS(EQUIP_SLOT_C_LEFT) = BTN_DISABLED;
BUTTON_STATUS(EQUIP_SLOT_C_DOWN) = BTN_DISABLED;
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
set_extra_item_slot_status(BTN_DISABLED);
Interface_SetHudVisibility(HUD_VISIBILITY_A_HEARTS_MAGIC_MINIMAP_WITH_OVERWRITE);
// @recomp_use_export_var no_bow_epona_fix: If the B button does not contain a sword, don't disable the UI.
if (!EPONA_FIX_ACTIVE || BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) < ITEM_SWORD_KOKIRI ||
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) > ITEM_SWORD_GILDED) {
BUTTON_STATUS(EQUIP_SLOT_C_LEFT) = BTN_DISABLED;
BUTTON_STATUS(EQUIP_SLOT_C_DOWN) = BTN_DISABLED;
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
set_extra_item_slot_status(BTN_DISABLED);
Interface_SetHudVisibility(HUD_VISIBILITY_A_HEARTS_MAGIC_MINIMAP_WITH_OVERWRITE);
}
}
}
if (BUTTON_STATUS(EQUIP_SLOT_B) == BTN_DISABLED && BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) == ITEM_BOW) {
BUTTON_STATUS(EQUIP_SLOT_B) = BTN_ENABLED;
}
if (play->transitionMode != TRANS_MODE_OFF) {
Interface_SetHudVisibility(HUD_VISIBILITY_NONE);
} else if ((gSaveContext.minigameStatus == MINIGAME_STATUS_ACTIVE) &&
@ -640,12 +718,12 @@ void Interface_UpdateButtonsPart1(PlayState* play) {
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
set_extra_item_slot_status(BTN_DISABLED);
Interface_SetHudVisibility(HUD_VISIBILITY_A_B_MINIMAP);
} else if (player->stateFlags1 & PLAYER_STATE1_800000) {
} else if (ON_EPONA) {
Interface_SetHudVisibility(HUD_VISIBILITY_A_B_MINIMAP);
}
}
} else {
if (player->stateFlags1 & PLAYER_STATE1_800000) {
if (ON_EPONA) {
Interface_SetHudVisibility(HUD_VISIBILITY_A_B_MINIMAP);
}
@ -657,32 +735,59 @@ void Interface_UpdateButtonsPart1(PlayState* play) {
} else {
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW;
}
if (h_and_d_no_sword_fix) {
BUTTON_STATUS(EQUIP_SLOT_B) = BTN_ENABLED;
}
BUTTON_STATUS(EQUIP_SLOT_C_LEFT) = BTN_DISABLED;
BUTTON_STATUS(EQUIP_SLOT_C_DOWN) = BTN_DISABLED;
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
set_extra_item_slot_status(BTN_DISABLED);
} else {
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW;
// @recomp_use_export_var no_bow_epona_fix: Part of the no bow Epona fix.
if (EPONA_FIX_ACTIVE) {
if (gSaveContext.save.saveInfo.inventory.items[SLOT_BOW] == ITEM_BOW) {
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW;
BUTTON_STATUS(EQUIP_SLOT_B) = BTN_ENABLED;
}
} else {
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_BOW;
}
}
if (play->unk_1887C >= 2) {
Interface_LoadItemIconImpl(play, EQUIP_SLOT_B);
} else if (gSaveContext.save.saveInfo.inventory.items[SLOT_BOW] == ITEM_NONE) {
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_NONE;
} else if (gSaveContext.save.saveInfo.inventory.items[SLOT_BOW] == ITEM_NONE && !H_AND_D_FIX_ACTIVE) {
// @recomp_use_export_var no_bow_epona_fix: Part of the no bow Epona fix.
if (EPONA_FIX_ACTIVE) {
gSaveContext.buttonStatus[EQUIP_SLOT_B] = BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B);
BUTTON_STATUS(EQUIP_SLOT_B) = BTN_DISABLED;
} else {
BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) = ITEM_NONE;
}
} else {
Interface_LoadItemIconImpl(play, EQUIP_SLOT_B);
}
if (BUTTON_STATUS(EQUIP_SLOT_B) == BTN_DISABLED) {
BUTTON_STATUS(EQUIP_SLOT_B) = BTN_ENABLED;
restoreHudVisibility = true;
// @recomp_use_export_var no_bow_epona_fix: Don't enable the B button unless it is being used for the bow.
if (!no_bow_epona_fix || BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) == ITEM_BOW) {
BUTTON_STATUS(EQUIP_SLOT_B) = BTN_ENABLED;
}
// @recomp_use_export_var no_bow_epona_fix: Don't restore hud visibility from Epona without a sword.
if (!no_bow_epona_fix || (player->stateFlags1 & PLAYER_STATE1_800000) == 0) {
restoreHudVisibility = true;
}
}
BUTTON_STATUS(EQUIP_SLOT_C_LEFT) = BTN_DISABLED;
BUTTON_STATUS(EQUIP_SLOT_C_DOWN) = BTN_DISABLED;
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
set_extra_item_slot_status(BTN_DISABLED);
Interface_SetHudVisibility(HUD_VISIBILITY_A_HEARTS_MAGIC_MINIMAP_WITH_OVERWRITE);
// @recomp_use_export_var no_bow_epona_fix: If the B button does not contain the bow, don't disable the UI.
if ((!no_bow_epona_fix || BUTTON_ITEM_EQUIP(CUR_FORM, EQUIP_SLOT_B) == ITEM_BOW) && !H_AND_D_FIX_ACTIVE) {
BUTTON_STATUS(EQUIP_SLOT_C_LEFT) = BTN_DISABLED;
BUTTON_STATUS(EQUIP_SLOT_C_DOWN) = BTN_DISABLED;
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
set_extra_item_slot_status(BTN_DISABLED);
Interface_SetHudVisibility(HUD_VISIBILITY_A_HEARTS_MAGIC_MINIMAP_WITH_OVERWRITE);
}
if (play->transitionMode != TRANS_MODE_OFF) {
Interface_SetHudVisibility(HUD_VISIBILITY_NONE);
@ -700,8 +805,11 @@ void Interface_UpdateButtonsPart1(PlayState* play) {
BUTTON_STATUS(EQUIP_SLOT_C_RIGHT) = BTN_DISABLED;
set_extra_item_slot_status(BTN_DISABLED);
Interface_SetHudVisibility(HUD_VISIBILITY_A_B_MINIMAP);
} else if (player->stateFlags1 & PLAYER_STATE1_800000) {
} else if (ON_EPONA) {
Interface_SetHudVisibility(HUD_VISIBILITY_A_B_MINIMAP);
} else if (H_AND_D_FIX_ACTIVE) {
Interface_LoadItemIconImpl(play, EQUIP_SLOT_B);
Interface_SetHudVisibility(HUD_VISIBILITY_B);
}
}
} else if (sPictoState != PICTO_BOX_STATE_OFF) {
@ -802,12 +910,19 @@ void Interface_UpdateButtonsPart1(PlayState* play) {
}
}
bool fd_anywhere = false;
// @recomp_export void recomp_set_fd_anywhere(bool new_val): Set whether the Fierce Deity's Mask has scene restrictions.
RECOMP_EXPORT void recomp_set_fd_anywhere(bool new_val) {
fd_anywhere = new_val;
}
/**
* A continuation of the if-else chain from Interface_UpdateButtonsPart1
* Also used directly when opening the pause menu i.e. skips part 1
*/
// @recomp Patched in the same way as Interface_UpdateButtonsPart1
void Interface_UpdateButtonsPart2(PlayState* play) {
RECOMP_PATCH void Interface_UpdateButtonsPart2(PlayState* play) {
MessageContext* msgCtx = &play->msgCtx;
InterfaceContext* interfaceCtx = &play->interfaceCtx;
Player* player = GET_PLAYER(play);
@ -1181,7 +1296,8 @@ void Interface_UpdateButtonsPart2(PlayState* play) {
}
} else if (GET_CUR_FORM_BTN_ITEM(i) == ITEM_MASK_FIERCE_DEITY) {
// Fierce Deity's Mask is equipped
if ((play->sceneId != SCENE_MITURIN_BS) && (play->sceneId != SCENE_HAKUGIN_BS) &&
// @recomp_use_export_var fd_anywhere: Allow the player to use the Fierce Deity's Mask anywhere if mods enable it.
if (!fd_anywhere && (play->sceneId != SCENE_MITURIN_BS) && (play->sceneId != SCENE_HAKUGIN_BS) &&
(play->sceneId != SCENE_SEA_BS) && (play->sceneId != SCENE_INISIE_BS) &&
(play->sceneId != SCENE_LAST_BS)) {
if (BUTTON_STATUS(i) != BTN_DISABLED) {
@ -1300,7 +1416,7 @@ void Interface_UpdateButtonsPart2(PlayState* play) {
* Sets the button alphas to be dimmed for disabled buttons, or to the requested alpha for non-disabled buttons
*/
// @recomp Patched to also set extra slot alpha values.
void Interface_UpdateButtonAlphasByStatus(PlayState* play, s16 risingAlpha) {
RECOMP_PATCH void Interface_UpdateButtonAlphasByStatus(PlayState* play, s16 risingAlpha) {
InterfaceContext* interfaceCtx = &play->interfaceCtx;
if ((gSaveContext.buttonStatus[EQUIP_SLOT_B] == BTN_DISABLED) || (gSaveContext.bButtonStatus == BTN_DISABLED)) {
@ -1373,7 +1489,7 @@ void Interface_UpdateButtonAlphasByStatus(PlayState* play, s16 risingAlpha) {
* depending on button status
*/
// @recomp Patched to also set extra slot alpha values.
void Interface_UpdateButtonAlphas(PlayState* play, s16 dimmingAlpha, s16 risingAlpha) {
RECOMP_PATCH void Interface_UpdateButtonAlphas(PlayState* play, s16 dimmingAlpha, s16 risingAlpha) {
InterfaceContext* interfaceCtx = &play->interfaceCtx;
if (gSaveContext.hudVisibilityForceButtonAlphasByStatus) {
@ -1410,7 +1526,7 @@ void Interface_UpdateButtonAlphas(PlayState* play, s16 dimmingAlpha, s16 risingA
}
// @recomp Patched to also set extra slot alpha values.
void Interface_UpdateHudAlphas(PlayState* play, s16 dimmingAlpha) {
RECOMP_PATCH void Interface_UpdateHudAlphas(PlayState* play, s16 dimmingAlpha) {
InterfaceContext* interfaceCtx = &play->interfaceCtx;
s16 risingAlpha = 255 - dimmingAlpha;
@ -2336,7 +2452,7 @@ extern s8 sOcarinaInstrumentId;
extern f32 AudioOcarina_BendPitchTwoSemitones(s8 bendIndex);
// @recomp Patch the function in order to read DPad inputs for the ocarina as well as CButton inputs.
void AudioOcarina_PlayControllerInput(u8 isOcarinaSfxSuppressedWhenCancelled) {
RECOMP_PATCH void AudioOcarina_PlayControllerInput(u8 isOcarinaSfxSuppressedWhenCancelled) {
u32 ocarinaBtnsHeld;
// Prevents two different ocarina notes from being played on two consecutive frames
@ -2453,7 +2569,7 @@ extern void AudioOcarina_CheckIfStartedSong(void);
extern void AudioOcarina_UpdateCurOcarinaSong(void);
// @recomp Patch the L button check (for free ocarina playing) to account for DPad ocarina.
void AudioOcarina_CheckSongsWithoutMusicStaff(void) {
RECOMP_PATCH void AudioOcarina_CheckSongsWithoutMusicStaff(void) {
u32 pitch;
u8 ocarinaStaffPlayingPosStart;
u8 songIndex;
@ -2528,7 +2644,7 @@ extern bool get_analog_cam_active();
extern void skip_analog_cam_once();
// @recomp Updates yaw while inside of deku flower.
void func_80855F9C(PlayState* play, Player* this) {
RECOMP_PATCH void func_80855F9C(PlayState* play, Player* this) {
f32 speedTarget;
s16 yawTarget;
@ -2553,7 +2669,7 @@ extern void Player_Action_4(Player* this, PlayState* play);
extern s32 Player_SetAction(PlayState* play, Player* this, PlayerActionFunc actionFunc, s32 arg3);
extern LinkAnimationHeader gPlayerAnim_pg_maru_change;
s32 func_80857950(PlayState* play, Player* this) {
RECOMP_PATCH s32 func_80857950(PlayState* play, Player* this) {
// @recomp track if newly going from non-spike roll to spike roll (spike rolling when this->unk_B86[1] == 1)
static bool wasOff = true;
bool isOff = this->unk_B86[1] == 0;
@ -2597,7 +2713,7 @@ extern void func_8082DC38(Player* this);
extern void func_80836A5C(Player* this, PlayState* play);
// @recomp Patch the shielding function to respect the aiming axis inversion setting.
void Player_Action_18(Player* this, PlayState* play) {
RECOMP_PATCH void Player_Action_18(Player* this, PlayState* play) {
func_80832F24(this);
if (this->transformation == PLAYER_FORM_GORON) {

View File

@ -8,7 +8,14 @@ typedef enum {
RECOMP_CAMERA_DUALANALOG,
} RecompCameraMode;
typedef enum {
RECOMP_AIMING_OVERRIDE_OFF = 0,
RECOMP_AIMING_OVERRIDE_DISABLE_LEFT_STICK = 1,
RECOMP_AIMING_OVERRIDE_FORCE_RIGHT_STICK = 2
} RecompAimingOverideMode;
extern RecompCameraMode recomp_camera_mode;
extern RecompAimingOverideMode recomp_aiming_override_mode;
DECLARE_FUNC(void, recomp_get_gyro_deltas, float* x, float* y);
DECLARE_FUNC(void, recomp_get_mouse_deltas, float* x, float* y);

View File

@ -34,7 +34,7 @@ typedef enum {
/* 2 */ VOICE_INIT_SUCCESS // voice initialized
} VoiceInitStatus;
void PadMgr_HandleRetrace(void) {
RECOMP_PATCH void PadMgr_HandleRetrace(void) {
// Execute rumble callback
if (sPadMgrInstance->rumbleRetraceCallback != NULL) {
sPadMgrInstance->rumbleRetraceCallback(sPadMgrInstance->rumbleRetraceArg);
@ -70,8 +70,11 @@ void poll_inputs(void) {
// Begin reading controller data
osContStartReadData(serialEventQueue);
bool needs_right_stick = recomp_analog_cam_enabled() || recomp_aiming_override_mode == RECOMP_AIMING_OVERRIDE_FORCE_RIGHT_STICK;
// Suppress the right analog stick if analog camera is active unless the ocarina is in use.
recomp_set_right_analog_suppressed(recomp_analog_cam_enabled() && sOcarinaInstrumentId == OCARINA_INSTRUMENT_OFF);
recomp_set_right_analog_suppressed(needs_right_stick && sOcarinaInstrumentId == OCARINA_INSTRUMENT_OFF);
// Resets this flag for the next frame;
recomp_aiming_override_mode = RECOMP_AIMING_OVERRIDE_OFF;
// Wait for controller data
osRecvMesg(serialEventQueue, NULL, OS_MESG_BLOCK);
@ -113,7 +116,7 @@ void poll_inputs(void) {
}
// @recomp Patched to do the actual input polling.
void PadMgr_GetInput(Input* inputs, s32 gameRequest) {
RECOMP_PATCH void PadMgr_GetInput(Input* inputs, s32 gameRequest) {
// @recomp Do an actual poll if gameRequest is true.
if (gameRequest) {
poll_inputs();
@ -126,7 +129,7 @@ void PadMgr_GetInput(Input* inputs, s32 gameRequest) {
}
// @recomp Just call PadMgr_GetInput.
void PadMgr_GetInput2(Input* inputs, s32 gameRequest) {
RECOMP_PATCH void PadMgr_GetInput2(Input* inputs, s32 gameRequest) {
PadMgr_GetInput(inputs, gameRequest);
}
@ -138,7 +141,7 @@ void* osViGetCurrentFramebuffer_recomp();
OSMesgQueue *rdp_queue_ptr = NULL;
// @recomp Immediately sends the graphics task instead of queueing it in the scheduler.
void Graph_TaskSet00(GraphicsContext* gfxCtx, GameState* gameState) {
RECOMP_PATCH void Graph_TaskSet00(GraphicsContext* gfxCtx, GameState* gameState) {
static s32 retryCount = 10;
static s32 cfbIdx = 0;
OSTask_t* task = &gfxCtx->task.list.t;
@ -271,7 +274,7 @@ extern VisZbuf sGameVisZbuf;
extern VisMono sGameVisMono;
extern ViMode sGameViMode;
void GameState_Destroy(GameState* gameState) {
RECOMP_PATCH void GameState_Destroy(GameState* gameState) {
AudioMgr_StopAllSfxExceptSystem();
Audio_Update();

View File

@ -16,7 +16,7 @@ extern Gfx gHookshotChainDL[];
void ArmsHook_Shoot(ArmsHook* this, PlayState* play);
void ArmsHook_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void ArmsHook_Draw(Actor* thisx, PlayState* play) {
ArmsHook* this = THIS;
f32 f0;
Player* player = GET_PLAYER(play);
@ -73,7 +73,7 @@ void ArmsHook_Draw(Actor* thisx, PlayState* play) {
#undef THIS
extern Gfx gHookshotReticleDL[];
void Player_DrawHookshotReticle(PlayState* play, Player* player, f32 hookshotDistance) {
RECOMP_PATCH void Player_DrawHookshotReticle(PlayState* play, Player* player, f32 hookshotDistance) {
static Vec3f D_801C094C = { -500.0f, -100.0f, 0.0f };
CollisionPoly* poly;
s32 bgId;
@ -143,7 +143,7 @@ Gfx bowstring_end_hook_dl[] = {
gsSPEndDisplayList(),
};
void Player_DrawGameplay(PlayState* play, Player* this, s32 lod, Gfx* cullDList,
RECOMP_PATCH void Player_DrawGameplay(PlayState* play, Player* this, s32 lod, Gfx* cullDList,
OverrideLimbDrawFlex overrideLimbDraw) {
OPEN_DISPS(play->state.gfxCtx);

View File

@ -1030,7 +1030,7 @@ void set_ocarina_vertex_alphas(Vtx* verts, s32 count, u8 alpha) {
extern Gfx sSongOfTimeFrustumMaterialDL[];
void OceffWipe_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void OceffWipe_Draw(Actor* thisx, PlayState* play) {
u32 scroll = play->state.frames & 0xFF;
OceffWipe* this = (OceffWipe*)thisx;
f32 z;
@ -1098,7 +1098,7 @@ void OceffWipe_Draw(Actor* thisx, PlayState* play) {
extern Gfx sEponaSongFrustumMaterialDL[];
void OceffWipe2_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void OceffWipe2_Draw(Actor* thisx, PlayState* play) {
u32 scroll = play->state.frames & 0xFF;
OceffWipe2* this = (OceffWipe2*)thisx;
f32 z;
@ -1152,7 +1152,7 @@ void OceffWipe2_Draw(Actor* thisx, PlayState* play) {
extern Gfx sSariaSongFrustrumMaterialDL[];
void OceffWipe3_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void OceffWipe3_Draw(Actor* thisx, PlayState* play) {
u32 scroll = play->state.frames & 0xFFF;
OceffWipe3* this = (OceffWipe3*)thisx;
f32 z;
@ -1207,7 +1207,7 @@ extern Gfx sScarecrowSongUnusedMaterialDL[];
extern Gfx sScarecrowSongMaterialDL[];
extern Gfx sScarecrowSongModelDL[];
void OceffWipe4_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void OceffWipe4_Draw(Actor* thisx, PlayState* play) {
u32 scroll = play->state.frames & 0xFFF;
OceffWipe4* this = (OceffWipe4*)thisx;
f32 z;
@ -1273,7 +1273,7 @@ static u8 sEnvColors[] = {
extern Gfx gOceff5DL[];
extern AnimatedMaterial gOceff5TexAnim[];
void OceffWipe5_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void OceffWipe5_Draw(Actor* thisx, PlayState* play) {
OceffWipe5* this = (OceffWipe5*)thisx;
f32 z;
s32 pad;
@ -1342,7 +1342,7 @@ void OceffWipe5_Draw(Actor* thisx, PlayState* play) {
extern Gfx gOceff6DL[];
extern AnimatedMaterial ovl_Oceff_Wipe6_Matanimheader_000338[];
void OceffWipe6_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void OceffWipe6_Draw(Actor* thisx, PlayState* play) {
OceffWipe6* this = (OceffWipe6*)thisx;
f32 z;
u8 alpha;
@ -1394,7 +1394,7 @@ void OceffWipe6_Draw(Actor* thisx, PlayState* play) {
extern Gfx sSongOfHealingEffectFrustumDL[];
extern AnimatedMaterial sSongofHealingEffectTexAnim[];
void OceffWipe7_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void OceffWipe7_Draw(Actor* thisx, PlayState* play) {
OceffWipe7* this = (OceffWipe7*)thisx;
f32 z;
u8 alpha;
@ -1445,7 +1445,7 @@ extern AnimatedMaterial object_stk2_Matanimheader_009F60[];
// @recomp Patch the Skull Kid curse effect as well, which works similarly to the ocarina effects.
// In this case, the patch also includes effect transform tagging patches.
void EffStk_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void EffStk_Draw(Actor* thisx, PlayState* play) {
EffStk* this = (EffStk*)thisx;
s32 pad;
Camera* activeCam = GET_ACTIVE_CAM(play);

View File

@ -55,7 +55,9 @@ extern s16 D_80814630[];
extern s16 D_80814638[];
extern s16 D_80814644[];
extern s16 D_8081464C[];
extern s16 D_80814650[];
// @recomp Added a third position for the rewind button.
s16 D_80814650_patched[] = { 940, 944, 948 };
void FileSelect_Main(GameState* thisx);
void FileSelect_InitContext(GameState* thisx);
@ -63,11 +65,11 @@ void FileSelect_DrawFileInfo(GameState *thisx, s16 fileIndex);
void FileSelect_SplitNumber(u16 value, u16 *hundreds, u16 *tens, u16 *ones);
// @recomp The options button is now the quit button, so close recomp instead of opening the options.
void FileSelect_RotateToOptions(GameState* thisx) {
RECOMP_PATCH void FileSelect_RotateToOptions(GameState* thisx) {
recomp_exit();
}
void FileSelect_Init(GameState* thisx) {
RECOMP_PATCH void FileSelect_Init(GameState* thisx) {
s32 pad;
FileSelectState* this = (FileSelectState*)thisx;
size_t size;
@ -107,7 +109,7 @@ void FileSelect_Init(GameState* thisx) {
}
void FileSelect_SetWindowContentVtx(GameState *thisx) {
RECOMP_PATCH void FileSelect_SetWindowContentVtx(GameState *thisx) {
FileSelectState *this = (FileSelectState *)thisx;
u16 vtxId;
s16 j;
@ -739,7 +741,7 @@ void FileSelect_SetWindowContentVtx(GameState *thisx) {
}
}
else {
j = D_80814650[this->confirmButtonIndex];
j = D_80814650_patched[this->confirmButtonIndex];
}
this->windowContentVtx[vtxId + 0].v.ob[0] = this->windowContentVtx[vtxId + 2].v.ob[0] = this->windowPosX - 0xA;
@ -784,7 +786,7 @@ void FileSelect_SetWindowContentVtx(GameState *thisx) {
* Draw most window contents including buttons, labels, and icons.
* Does not include anything from the keyboard and settings windows.
*/
void FileSelect_DrawWindowContents(GameState *thisx) {
RECOMP_PATCH void FileSelect_DrawWindowContents(GameState *thisx) {
FileSelectState *this = (FileSelectState *)thisx;
s16 fileIndex;
s16 temp;
@ -989,7 +991,7 @@ void FileSelect_DrawWindowContents(GameState *thisx) {
CLOSE_DISPS(this->state.gfxCtx);
}
void FileSelect_ConfirmFile(GameState *thisx) {
RECOMP_PATCH void FileSelect_ConfirmFile(GameState *thisx) {
FileSelectState *this = (FileSelectState *)thisx;
Input *input = CONTROLLER1(&this->state);
@ -1034,11 +1036,14 @@ void FileSelect_ConfirmFile(GameState *thisx) {
}
}
// Non-relocatable reference to the original address of Play_Init.
void Play_Init_NORELOCATE(GameState*);
/**
* Load the save for the appropriate file and start the game.
* Update function for `SM_LOAD_GAME`
*/
void FileSelect_LoadGame(GameState* thisx) {
RECOMP_PATCH void FileSelect_LoadGame(GameState* thisx) {
FileSelectState* this = (FileSelectState*)thisx;
u16 i;
@ -1058,7 +1063,7 @@ void FileSelect_LoadGame(GameState* thisx) {
gSaveContext.gameMode = GAMEMODE_NORMAL;
STOP_GAMESTATE(&this->state);
SET_NEXT_GAMESTATE(&this->state, Play_Init, sizeof(PlayState));
SET_NEXT_GAMESTATE(&this->state, Play_Init_NORELOCATE, sizeof(PlayState));
gSaveContext.respawnFlag = 0;
gSaveContext.respawn[RESPAWN_MODE_DOWN].entrance = ENTR_LOAD_OPENING;

View File

@ -10,7 +10,7 @@ extern EffectSsInfo sEffectSsInfo;
u8 particle_reset_list[MAX_PARTICLES];
// @recomp Patched to track that the particle has been reset.
void EffectSS_ResetEntry(EffectSs* particle) {
RECOMP_PATCH void EffectSS_ResetEntry(EffectSs* particle) {
u32 i;
particle->type = EFFECT_SS_MAX;
@ -39,7 +39,7 @@ void EffectSS_ResetEntry(EffectSs* particle) {
}
// @recomp Check numEntries to be sure enough space has been allocated for tracking particle statuses.
void EffectSS_Init(PlayState* play, s32 numEntries) {
RECOMP_PATCH void EffectSS_Init(PlayState* play, s32 numEntries) {
u32 i;
EffectSs* effectsSs;
EffectSsOverlay* overlay;
@ -66,7 +66,7 @@ void EffectSS_Init(PlayState* play, s32 numEntries) {
}
// @recomp Add transform tags to particles
void EffectSS_DrawParticle(PlayState* play, s32 index) {
RECOMP_PATCH void EffectSS_DrawParticle(PlayState* play, s32 index) {
EffectSs* entry = &sEffectSsInfo.dataTable[index];
OPEN_DISPS(play->state.gfxCtx);
@ -164,7 +164,7 @@ void func_80B22FA8_patched(Actor* thisx, EnHanabiStruct* arg0, PlayState* play2)
}
// @recomp Patched to call a custom version of a vanilla function.
void EnHanabi_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void EnHanabi_Draw(Actor* thisx, PlayState* play) {
EnHanabi* this = (EnHanabi*)thisx;
Matrix_Push();
@ -176,7 +176,7 @@ void EnHanabi_Draw(Actor* thisx, PlayState* play) {
Vec3f kankyo_prev_pos_base[DEMOKANKYO_EFFECT_COUNT] = {0};
// @recomp Patched to draw the lost woods particles outside the 4:3 region and tag their transforms.
void DemoKakyo_DrawLostWoodsSparkle(Actor* thisx, PlayState* play2) {
RECOMP_PATCH void DemoKakyo_DrawLostWoodsSparkle(Actor* thisx, PlayState* play2) {
PlayState* play = play2;
DemoKankyo* this = (DemoKankyo*)thisx;
s16 i;
@ -286,7 +286,7 @@ extern Gfx gBubbleDL[];
extern Gfx gLightOrbModelDL[];
// @recomp Patched to draw the lost woods particles outside the 4:3 region and tag their transforms.
void DemoKankyo_DrawMoonAndGiant(Actor* thisx, PlayState* play2) {
RECOMP_PATCH void DemoKankyo_DrawMoonAndGiant(Actor* thisx, PlayState* play2) {
PlayState* play = play2;
DemoKankyo* this = (DemoKankyo*)thisx;
s16 i;
@ -386,7 +386,7 @@ static Vec3f D_80A5AFB0 = { 0.0f, 0.0f, 0.0f };
#define WATER_EFFECT_RESPAWNED(ptr) (&(ptr)->unk_01)[1]
// @recomp Mark respawned water effect particles so they can be skipped for the first frame.
void func_80A599E8(EnWaterEffect* this, Vec3f* arg1, u8 arg2) {
RECOMP_PATCH void func_80A599E8(EnWaterEffect* this, Vec3f* arg1, u8 arg2) {
s16 i;
EnWaterEffectStruct* ptr = &this->unk_144[0];
@ -433,7 +433,7 @@ extern Gfx object_water_effect_DL_0043E8[];
extern Gfx gameplay_keep_DL_06AB30[];
// @recomp Tag the transforms for falling fire rocks.
void func_80A5A184(Actor* thisx, PlayState* play2) {
RECOMP_PATCH void func_80A5A184(Actor* thisx, PlayState* play2) {
PlayState* play = play2;
EnWaterEffect* this = (EnWaterEffect*)thisx;
GraphicsContext* gfxCtx = play->state.gfxCtx;

View File

@ -1,6 +1,15 @@
#ifndef __PATCHES_H__
#define __PATCHES_H__
#define RECOMP_EXPORT __attribute__((section(".recomp_export")))
#define RECOMP_PATCH __attribute__((section(".recomp_patch")))
#define RECOMP_FORCE_PATCH __attribute__((section(".recomp_force_patch")))
#define RECOMP_DECLARE_EVENT(func) \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wunused-parameter\"") \
__attribute__((noinline, weak, used, section(".recomp_event"))) void func {} \
_Pragma("GCC diagnostic pop")
// TODO fix renaming symbols in patch recompilation
#define osCreateMesgQueue osCreateMesgQueue_recomp
#define osRecvMesg osRecvMesg_recomp

View File

@ -1,5 +1,5 @@
RAMBASE = 0x80801000; /* Used to hold any new symbols */
EXTRA_RAM_SIZE = 0x01000000; /* Amount of extra ram allocated by recomp */
PATCH_RAM_END = 0x81000000; /* Amount of extra ram allocated by recomp */
MEMORY {
extram : ORIGIN = RAMBASE, LENGTH = 64M
@ -7,13 +7,16 @@ MEMORY {
}
SECTIONS {
.ctors : { *(.ctors*) *(.init_array*) } >extram AT >rom
.dtors : { *(.dtors*) } >extram AT >rom
.text : { *(.text*) } >extram AT >rom
.rodata : { *(.rodata*) } >extram AT >rom
.data : { *(.data*) } >extram AT >rom
.bss (NOLOAD) : { *(.bss*) *(COMMON) } >extram
ASSERT(. < RAMBASE + EXTRA_RAM_SIZE, "Maxed out recomp extra ram")
.ctors : { *(.ctors*) *(.init_array*) } >extram AT >rom
.dtors : { *(.dtors*) } >extram AT >rom
.text : { *(.text*) } >extram AT >rom
.recomp_patch : { *(.recomp_patch*) *(.recomp_force_patch*) } >extram AT >rom
.recomp_export : { *(.recomp_export*) } >extram AT >rom
.recomp_event : { *(.recomp_event*) } >extram AT >rom
.rodata : { *(.rodata*) } >extram AT >rom
.data : { *(.data*) } >extram AT >rom
.bss (NOLOAD) : { *(.bss*) *(COMMON) } >extram
ASSERT(. <= PATCH_RAM_END, "Maxed out recomp extra ram")
.reloc 0 : { *(.reloc*) }
.symtab 0 : { *(.symtab) }

View File

@ -1,18 +1,49 @@
#include "play_patches.h"
#include "z64debug_display.h"
#include "input.h"
#include "prevent_bss_reordering.h"
#include "z64.h"
#include "regs.h"
#include "functions.h"
#include "z64vismono.h"
#include "z64visfbuf.h"
#include "buffers.h"
#include "variables.h"
#include "macros.h"
#include "buffers.h"
#include "idle.h"
#include "sys_cfb.h"
#include "z64bombers_notebook.h"
#include "z64quake.h"
#include "z64rumble.h"
#include "z64shrink_window.h"
#include "z64view.h"
#include "overlays/gamestates/ovl_daytelop/z_daytelop.h"
#include "overlays/gamestates/ovl_opening/z_opening.h"
#include "overlays/gamestates/ovl_file_choose/z_file_select.h"
#include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h"
#include "debug.h"
extern Input D_801F6C18;
RECOMP_DECLARE_EVENT(recomp_on_play_main(PlayState* play));
RECOMP_DECLARE_EVENT(recomp_on_play_update(PlayState* play));
RECOMP_DECLARE_EVENT(recomp_after_play_update(PlayState* play));
void controls_play_update(PlayState* play) {
gSaveContext.options.zTargetSetting = recomp_get_targeting_mode();
}
// @recomp Patched to add hooks for various added functionality.
void Play_Main(GameState* thisx) {
RECOMP_PATCH void Play_Main(GameState* thisx) {
static Input* prevInput = NULL;
PlayState* this = (PlayState*)thisx;
// @recomp_event recomp_on_play_main(PlayState* play): Allow mods to execute code every frame.
recomp_on_play_main(this);
// @recomp
debug_play_update(this);
controls_play_update(this);
@ -32,7 +63,15 @@ void Play_Main(GameState* thisx) {
this->state.gfxCtx = NULL;
}
camera_pre_play_update(this);
// @recomp_event recomp_on_play_update(PlayState* play): Play_Update is about to be called.
recomp_on_play_update(this);
Play_Update(this);
// @recomp_event recomp_after_play_update(PlayState* play): Play_Update was called.
recomp_after_play_update(this);
camera_post_play_update(this);
analog_cam_post_play_update(this);
autosave_post_play_update(this);
@ -54,7 +93,7 @@ void Play_Main(GameState* thisx) {
}
// @recomp Patched to add load a hook for loading rooms.
s32 Room_HandleLoadCallbacks(PlayState* play, RoomContext* roomCtx) {
RECOMP_PATCH s32 Room_HandleLoadCallbacks(PlayState* play, RoomContext* roomCtx) {
if (roomCtx->status == 1) {
if (osRecvMesg(&roomCtx->loadQueue, NULL, OS_MESG_NOBLOCK) == 0) {
roomCtx->status = 0;
@ -83,3 +122,322 @@ s32 Room_HandleLoadCallbacks(PlayState* play, RoomContext* roomCtx) {
return 1;
}
void ZeldaArena_Init(void* start, size_t size);
void Play_SpawnScene(PlayState* this, s32 sceneId, s32 spawn);
void Play_InitMotionBlur(void);
extern s16 sTransitionFillTimer;
extern Input D_801F6C18;
extern TransitionTile sTransitionTile;
extern s32 gTransitionTileState;
extern VisMono sPlayVisMono;
extern Color_RGBA8_u32 gVisMonoColor;
extern VisFbuf sPlayVisFbuf;
extern VisFbuf* sPlayVisFbufInstance;
extern BombersNotebook sBombersNotebook;
extern u8 sBombersNotebookOpen;
extern u8 sMotionBlurStatus;
extern s32 gDbgCamEnabled;
extern u8 D_801D0D54;
extern u8 gPictoPhotoI8[];
extern u8 D_80784600[];
// Non-relocatable references to the original addresses of these game state functions.
void DayTelop_Init_NORELOCATE(GameState*);
void TitleSetup_Init_NORELOCATE(GameState*);
RECOMP_DECLARE_EVENT(recomp_on_play_init(PlayState* this));
RECOMP_DECLARE_EVENT(recomp_after_play_init(PlayState* this));
bool allow_no_ocarina_tf = false;
// @recomp_export void recomp_set_allow_no_ocarina_tf(bool new_val): Set whether to force Termina Field to load normally even if Link has no ocarina.
RECOMP_EXPORT void recomp_set_allow_no_ocarina_tf(bool new_val) {
allow_no_ocarina_tf = new_val;
}
RECOMP_PATCH void Play_Init(GameState* thisx) {
PlayState* this = (PlayState*)thisx;
GraphicsContext* gfxCtx = this->state.gfxCtx;
s32 pad;
uintptr_t zAlloc;
s32 zAllocSize;
Player* player;
s32 i;
s32 spawn;
u8 sceneLayer;
s32 scene;
// @recomp_event recomp_on_play_init(PlayState* this): A new PlayState is being initialized.
recomp_on_play_init(this);
if ((gSaveContext.respawnFlag == -4) || (gSaveContext.respawnFlag == -0x63)) {
if (CHECK_EVENTINF(EVENTINF_TRIGGER_DAYTELOP)) {
CLEAR_EVENTINF(EVENTINF_TRIGGER_DAYTELOP);
STOP_GAMESTATE(&this->state);
// Use non-relocatable reference to DayTelop_Init instead.
SET_NEXT_GAMESTATE(&this->state, DayTelop_Init_NORELOCATE, sizeof(DayTelopState));
return;
}
gSaveContext.unk_3CA7 = 1;
if (gSaveContext.respawnFlag == -0x63) {
gSaveContext.respawnFlag = 2;
}
} else {
gSaveContext.unk_3CA7 = 0;
}
if (gSaveContext.save.entrance == -1) {
gSaveContext.save.entrance = 0;
STOP_GAMESTATE(&this->state);
// Use non-relocatable reference to TitleSetup_Init instead.
SET_NEXT_GAMESTATE(&this->state, TitleSetup_Init_NORELOCATE, sizeof(TitleSetupState));
return;
}
if ((gSaveContext.nextCutsceneIndex == 0xFFEF) || (gSaveContext.nextCutsceneIndex == 0xFFF0)) {
scene = ((void)0, gSaveContext.save.entrance) >> 9;
spawn = (((void)0, gSaveContext.save.entrance) >> 4) & 0x1F;
if (CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_SNOWHEAD_TEMPLE)) {
if (scene == ENTR_SCENE_MOUNTAIN_VILLAGE_WINTER) {
scene = ENTR_SCENE_MOUNTAIN_VILLAGE_SPRING;
} else if (scene == ENTR_SCENE_GORON_VILLAGE_WINTER) {
scene = ENTR_SCENE_GORON_VILLAGE_SPRING;
} else if (scene == ENTR_SCENE_PATH_TO_GORON_VILLAGE_WINTER) {
scene = ENTR_SCENE_PATH_TO_GORON_VILLAGE_SPRING;
} else if ((scene == ENTR_SCENE_SNOWHEAD) || (scene == ENTR_SCENE_PATH_TO_SNOWHEAD) ||
(scene == ENTR_SCENE_PATH_TO_MOUNTAIN_VILLAGE) || (scene == ENTR_SCENE_GORON_SHRINE) ||
(scene == ENTR_SCENE_GORON_RACETRACK)) {
gSaveContext.nextCutsceneIndex = 0xFFF0;
}
}
if (CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_WOODFALL_TEMPLE)) {
if (scene == ENTR_SCENE_SOUTHERN_SWAMP_POISONED) {
scene = ENTR_SCENE_SOUTHERN_SWAMP_CLEARED;
} else if (scene == ENTR_SCENE_WOODFALL) {
gSaveContext.nextCutsceneIndex = 0xFFF1;
}
}
if (CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_STONE_TOWER_TEMPLE) && (scene == ENTR_SCENE_IKANA_CANYON)) {
gSaveContext.nextCutsceneIndex = 0xFFF2;
}
if (CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_GREAT_BAY_TEMPLE) &&
((scene == ENTR_SCENE_GREAT_BAY_COAST) || (scene == ENTR_SCENE_ZORA_CAPE))) {
gSaveContext.nextCutsceneIndex = 0xFFF0;
}
// "First cycle" Termina Field
// @recomp_use_export_var allow_no_ocarina_tf: Skip loading into "First cycle" Termina Field if mods enable it.
if (!allow_no_ocarina_tf && INV_CONTENT(ITEM_OCARINA_OF_TIME) != ITEM_OCARINA_OF_TIME) {
if ((scene == ENTR_SCENE_TERMINA_FIELD) &&
(((void)0, gSaveContext.save.entrance) != ENTRANCE(TERMINA_FIELD, 10))) {
gSaveContext.nextCutsceneIndex = 0xFFF4;
}
}
//! FAKE:
gSaveContext.save.entrance =
Entrance_Create(((void)0, scene), spawn, ((void)0, gSaveContext.save.entrance) & 0xF);
}
GameState_Realloc(&this->state, 0);
KaleidoManager_Init(this);
ShrinkWindow_Init();
View_Init(&this->view, gfxCtx);
Audio_SetExtraFilter(0);
Quake_Init();
Distortion_Init(this);
for (i = 0; i < ARRAY_COUNT(this->cameraPtrs); i++) {
this->cameraPtrs[i] = NULL;
}
Camera_Init(&this->mainCamera, &this->view, &this->colCtx, this);
Camera_ChangeStatus(&this->mainCamera, CAM_STATUS_ACTIVE);
for (i = 0; i < ARRAY_COUNT(this->subCameras); i++) {
Camera_Init(&this->subCameras[i], &this->view, &this->colCtx, this);
Camera_ChangeStatus(&this->subCameras[i], CAM_STATUS_INACTIVE);
}
this->cameraPtrs[CAM_ID_MAIN] = &this->mainCamera;
this->cameraPtrs[CAM_ID_MAIN]->uid = CAM_ID_MAIN;
this->activeCamId = CAM_ID_MAIN;
Camera_OverwriteStateFlags(&this->mainCamera, CAM_STATE_0 | CAM_STATE_CHECK_WATER | CAM_STATE_2 | CAM_STATE_3 |
CAM_STATE_4 | CAM_STATE_DISABLE_MODE_CHANGE | CAM_STATE_6);
Sram_Alloc(&this->state, &this->sramCtx);
Regs_InitData(this);
Message_Init(this);
GameOver_Init(this);
SoundSource_InitAll(this);
EffFootmark_Init(this);
Effect_Init(this);
EffectSS_Init(this, 100);
CollisionCheck_InitContext(this, &this->colChkCtx);
AnimationContext_Reset(&this->animationCtx);
Cutscene_InitContext(this, &this->csCtx);
if (gSaveContext.nextCutsceneIndex != 0xFFEF) {
gSaveContext.save.cutsceneIndex = gSaveContext.nextCutsceneIndex;
gSaveContext.nextCutsceneIndex = 0xFFEF;
}
if (gSaveContext.save.cutsceneIndex == 0xFFFD) {
gSaveContext.save.cutsceneIndex = 0;
}
if (gSaveContext.nextDayTime != NEXT_TIME_NONE) {
gSaveContext.save.time = gSaveContext.nextDayTime;
gSaveContext.skyboxTime = gSaveContext.nextDayTime;
}
if ((CURRENT_TIME >= CLOCK_TIME(18, 0)) || (CURRENT_TIME < CLOCK_TIME(6, 30))) {
gSaveContext.save.isNight = true;
} else {
gSaveContext.save.isNight = false;
}
func_800EDDB0(this);
if (((gSaveContext.gameMode != GAMEMODE_NORMAL) && (gSaveContext.gameMode != GAMEMODE_TITLE_SCREEN)) ||
(gSaveContext.save.cutsceneIndex >= 0xFFF0)) {
gSaveContext.nayrusLoveTimer = 0;
Magic_Reset(this);
gSaveContext.sceneLayer = (gSaveContext.save.cutsceneIndex & 0xF) + 1;
// Set saved cutscene to 0 so it doesn't immediately play, but instead let the `CutsceneManager` handle it.
gSaveContext.save.cutsceneIndex = 0;
} else {
gSaveContext.sceneLayer = 0;
}
sceneLayer = gSaveContext.sceneLayer;
Play_SpawnScene(
this, Entrance_GetSceneIdAbsolute(((void)0, gSaveContext.save.entrance) + ((void)0, gSaveContext.sceneLayer)),
Entrance_GetSpawnNum(((void)0, gSaveContext.save.entrance) + ((void)0, gSaveContext.sceneLayer)));
KaleidoScopeCall_Init(this);
Interface_Init(this);
if (gSaveContext.nextDayTime != NEXT_TIME_NONE) {
if (gSaveContext.nextDayTime == NEXT_TIME_DAY) {
gSaveContext.save.day++;
gSaveContext.save.eventDayCount++;
gSaveContext.dogIsLost = true;
gSaveContext.nextDayTime = NEXT_TIME_DAY_SET;
} else {
gSaveContext.nextDayTime = NEXT_TIME_NIGHT_SET;
}
}
Play_InitMotionBlur();
R_PAUSE_BG_PRERENDER_STATE = PAUSE_BG_PRERENDER_OFF;
R_PICTO_PHOTO_STATE = PICTO_PHOTO_STATE_OFF;
PreRender_Init(&this->pauseBgPreRender);
PreRender_SetValuesSave(&this->pauseBgPreRender, gCfbWidth, gCfbHeight, NULL, NULL, NULL);
PreRender_SetValues(&this->pauseBgPreRender, gCfbWidth, gCfbHeight, NULL, NULL);
this->unk_18E64 = gWorkBuffer;
this->pictoPhotoI8 = gPictoPhotoI8;
this->unk_18E68 = D_80784600;
this->unk_18E58 = D_80784600;
this->unk_18E60 = D_80784600;
gTransitionTileState = TRANS_TILE_OFF;
this->transitionMode = TRANS_MODE_OFF;
D_801D0D54 = false;
FrameAdvance_Init(&this->frameAdvCtx);
Rand_Seed(osGetTime());
Matrix_Init(&this->state);
this->state.main = Play_Main;
this->state.destroy = Play_Destroy;
this->transitionTrigger = TRANS_TRIGGER_END;
this->worldCoverAlpha = 0;
this->bgCoverAlpha = 0;
this->haltAllActors = false;
this->unk_18844 = false;
if (gSaveContext.gameMode != GAMEMODE_TITLE_SCREEN) {
if (gSaveContext.nextTransitionType == TRANS_NEXT_TYPE_DEFAULT) {
this->transitionType =
(Entrance_GetTransitionFlags(((void)0, gSaveContext.save.entrance) + sceneLayer) >> 7) & 0x7F;
} else {
this->transitionType = gSaveContext.nextTransitionType;
gSaveContext.nextTransitionType = TRANS_NEXT_TYPE_DEFAULT;
}
} else {
this->transitionType = TRANS_TYPE_FADE_BLACK;
}
TransitionFade_Init(&this->unk_18E48);
TransitionFade_SetType(&this->unk_18E48, 3);
TransitionFade_SetColor(&this->unk_18E48, RGBA8(160, 160, 160, 255));
TransitionFade_Start(&this->unk_18E48);
VisMono_Init(&sPlayVisMono);
gVisMonoColor.a = 0;
sPlayVisFbufInstance = &sPlayVisFbuf;
VisFbuf_Init(sPlayVisFbufInstance);
sPlayVisFbufInstance->lodProportion = 0.0f;
sPlayVisFbufInstance->mode = VIS_FBUF_MODE_GENERAL;
sPlayVisFbufInstance->primColor.r = 0;
sPlayVisFbufInstance->primColor.g = 0;
sPlayVisFbufInstance->primColor.b = 0;
sPlayVisFbufInstance->primColor.a = 0;
sPlayVisFbufInstance->envColor.r = 0;
sPlayVisFbufInstance->envColor.g = 0;
sPlayVisFbufInstance->envColor.b = 0;
sPlayVisFbufInstance->envColor.a = 0;
CutsceneFlags_UnsetAll(this);
THA_GetRemaining(&this->state.tha);
zAllocSize = THA_GetRemaining(&this->state.tha);
zAlloc = (uintptr_t)THA_AllocTailAlign16(&this->state.tha, zAllocSize);
//! @bug: Incorrect ALIGN16s
ZeldaArena_Init((void*)((zAlloc + 8) & ~0xF), (zAllocSize - ((zAlloc + 8) & ~0xF)) + zAlloc);
Actor_InitContext(this, &this->actorCtx, this->linkActorEntry);
while (!Room_HandleLoadCallbacks(this, &this->roomCtx)) {}
if ((CURRENT_DAY != 0) && ((this->roomCtx.curRoom.behaviorType1 == ROOM_BEHAVIOR_TYPE1_1) ||
(this->roomCtx.curRoom.behaviorType1 == ROOM_BEHAVIOR_TYPE1_5))) {
Actor_Spawn(&this->actorCtx, this, ACTOR_EN_TEST4, 0.0f, 0.0f, 0.0f, 0, 0, 0, 0);
}
player = GET_PLAYER(this);
Camera_InitFocalActorSettings(&this->mainCamera, &player->actor);
gDbgCamEnabled = false;
if (PLAYER_GET_BG_CAM_INDEX(&player->actor) != 0xFF) {
Camera_ChangeActorCsCamIndex(&this->mainCamera, PLAYER_GET_BG_CAM_INDEX(&player->actor));
}
CutsceneManager_StoreCamera(&this->mainCamera);
Interface_SetSceneRestrictions(this);
Environment_PlaySceneSequence(this);
gSaveContext.seqId = this->sequenceCtx.seqId;
gSaveContext.ambienceId = this->sequenceCtx.ambienceId;
AnimationContext_Update(this, &this->animationCtx);
Cutscene_HandleEntranceTriggers(this);
gSaveContext.respawnFlag = 0;
sBombersNotebookOpen = false;
BombersNotebook_Init(&sBombersNotebook);
// @recomp_event recomp_after_play_init(PlayState* this): The new PlayState has finished initializing.
recomp_after_play_init(this);
}

View File

@ -6,7 +6,7 @@ void* proutPrintf(void* dst, const char* fmt, size_t size) {
return (void*)1;
}
int recomp_printf(const char* fmt, ...) {
RECOMP_EXPORT int recomp_printf(const char* fmt, ...) {
va_list args;
va_start(args, fmt);

View File

@ -7,13 +7,18 @@ void Main_InitMemory(void);
void Main_InitScreen(void);
RECOMP_DECLARE_EVENT(recomp_on_init());
// @recomp Patched to load the code segment in the recomp runtime.
void Main_Init(void) {
RECOMP_PATCH void Main_Init(void) {
DmaRequest dmaReq;
OSMesgQueue mq;
OSMesg msg[1];
size_t prevSize;
// @recomp_event recomp_on_init(): Allow mods to initialize themselves once.
recomp_on_init();
osCreateMesgQueue(&mq, msg, ARRAY_COUNT(msg));
prevSize = gDmaMgrDmaBuffSize;
@ -36,7 +41,7 @@ void Main_Init(void) {
void Overlay_Relocate(void* allocatedRamAddr, OverlayRelocationSection* ovlRelocs, uintptr_t vramStart);
// @recomp Patched to load the overlay in the recomp runtime.
size_t Overlay_Load(uintptr_t vromStart, uintptr_t vromEnd, void* ramStart, void* ramEnd, void* allocatedRamAddr) {
RECOMP_PATCH size_t Overlay_Load(uintptr_t vromStart, uintptr_t vromEnd, void* ramStart, void* ramEnd, void* allocatedRamAddr) {
uintptr_t vramStart = (uintptr_t)ramStart;
uintptr_t vramEnd = (uintptr_t)ramEnd;
s32 size = vromEnd - vromStart;

View File

@ -8,7 +8,7 @@ s32 SysFlashrom_IsInit(void);
void Sleep_Msec(u32 ms);
// @recomp Patched to not wait a hardcoded amount of time for the save to complete.
void Sram_UpdateWriteToFlashDefault(SramContext* sramCtx) {
RECOMP_PATCH void Sram_UpdateWriteToFlashDefault(SramContext* sramCtx) {
if (sramCtx->status == 2) {
if (SysFlashrom_IsBusy() != 0) { // if task running
if (SysFlashrom_AwaitResult() == 0) { // wait for task done
@ -26,7 +26,7 @@ void Sram_UpdateWriteToFlashDefault(SramContext* sramCtx) {
}
// @recomp Patched to not wait a hardcoded amount of time for the save to complete.
void Sram_UpdateWriteToFlashOwlSave(SramContext* sramCtx) {
RECOMP_PATCH void Sram_UpdateWriteToFlashOwlSave(SramContext* sramCtx) {
if (sramCtx->status == 7) {
if (SysFlashrom_IsBusy() != 0) { // Is task running
if (SysFlashrom_AwaitResult() == 0) { // Wait for task done

View File

@ -7,7 +7,7 @@
void func_80AF118C(PlayState* play, OwlWarpFeather* feathers, EnTest7* this, s32 arg3, s32 arg4); // EnTest7_UpdateFeathers
void func_80AF2350(EnTest7* this, PlayState* play); // EnTest7_WarpCsWarp
void EnTest7_Update(Actor* thisx, PlayState* play) {
RECOMP_PATCH void EnTest7_Update(Actor* thisx, PlayState* play) {
EnTest7* this = THIS;
this->actionFunc(this, play);

View File

@ -5,7 +5,7 @@
extern Mtx* sSkyboxDrawMatrix;
void Skybox_Draw(SkyboxContext* skyboxCtx, GraphicsContext* gfxCtx, s16 skyboxId, s16 blend, f32 x, f32 y, f32 z) {
RECOMP_PATCH void Skybox_Draw(SkyboxContext* skyboxCtx, GraphicsContext* gfxCtx, s16 skyboxId, s16 blend, f32 x, f32 y, f32 z) {
OPEN_DISPS(gfxCtx);
Gfx_SetupDL40_Opa(gfxCtx);
@ -112,7 +112,7 @@ f32 view_aspect_ratio(View* view) {
}
// @recomp Patched to set up the RSP for drawing stars with ortho rects and tag star transforms.
void Environment_DrawSkyboxStarsImpl(PlayState* play, Gfx** gfxP) {
RECOMP_PATCH void Environment_DrawSkyboxStarsImpl(PlayState* play, Gfx** gfxP) {
static const Vec3s D_801DD880[] = {
{ 0x0384, 0x2328, 0xD508 }, { 0x09C4, 0x2328, 0xDA1C }, { 0x0E74, 0x22D8, 0xDA1C }, { 0x1450, 0x2468, 0xD8F0 },
{ 0x1C84, 0x28A0, 0xCBA8 }, { 0x1F40, 0x2134, 0xD8F0 }, { 0x1F40, 0x28A0, 0xDAE4 }, { 0xE4A8, 0x4A38, 0x4A38 },

View File

@ -15,7 +15,7 @@ s32 func_80AF31D0(PlayState* play, SkeletonInfo* skeletonInfo, s32 limbIndex, Gf
extern AnimatedMaterial gSoaringWarpCsWindCapsuleTexAnim;
extern Gfx gSoaringWarpCsWindCapsuleDL[];
void EnTest7_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void EnTest7_Draw(Actor* thisx, PlayState* play) {
s32 pad[2];
EnTest7* this = (EnTest7*)thisx;
s32 sp40;
@ -104,7 +104,7 @@ extern SoTCsAmmoDrops sSoTCsAmmoDrops[12];
/**
* Draws clocks in a double spiral above and below player
*/
void EnTest6_DrawThreeDayResetSoTCutscene(EnTest6* this, PlayState* play) {
RECOMP_PATCH void EnTest6_DrawThreeDayResetSoTCutscene(EnTest6* this, PlayState* play) {
s16 clock1Yaw;
s16 clock2Yaw;
s16 angle;

View File

@ -17,7 +17,7 @@ bool is_bgm_player(u8 player_index) {
/**
* Update different commands and requests for active sequences
*/
void AudioSeq_UpdateActiveSequences(void) {
RECOMP_PATCH void AudioSeq_UpdateActiveSequences(void) {
u32 tempoCmd;
u16 tempoPrev;
u16 seqId;
@ -338,7 +338,7 @@ void AudioSeq_UpdateActiveSequences(void) {
}
// @recomp Patched to add the ability to turn off low health beeps.
void LifeMeter_UpdateSizeAndBeep(PlayState* play) {
RECOMP_PATCH void LifeMeter_UpdateSizeAndBeep(PlayState* play) {
InterfaceContext* interfaceCtx = &play->interfaceCtx;
if (interfaceCtx->lifeSizeChangeDirection != 0) {

View File

@ -11,6 +11,7 @@
#include "overlays/actors/ovl_En_Goroiwa/z_en_goroiwa.h"
#include "overlays/actors/ovl_En_Twig/z_en_twig.h"
#include "overlays/actors/ovl_En_Honotrap/z_en_honotrap.h"
#include "overlays/actors/ovl_En_Tanron1/z_en_tanron1.h"
// Decomp renames, TODO update decomp and remove these
#define EnHonotrap_FlameGroup func_8092F878
@ -30,7 +31,7 @@ extern Gfx gEffWaterRippleDL[];
u8 special_effect_reset_states[MAX_SPECIAL_EFFECTS];
// @recomp Tag Wart's bubbles
void EnTanron2_Draw(Actor* thisx, PlayState* play2) {
RECOMP_PATCH void EnTanron2_Draw(Actor* thisx, PlayState* play2) {
PlayState* play = play2;
s32 i;
s32 j;
@ -153,7 +154,7 @@ void EnTanron2_Draw(Actor* thisx, PlayState* play2) {
}
// @recomp Track this effect's reset state.
void Boss03_SpawnEffectWetSpot(PlayState* play, Vec3f* pos) {
RECOMP_PATCH void Boss03_SpawnEffectWetSpot(PlayState* play, Vec3f* pos) {
s16 i;
GyorgEffect* eff = play->specialEffects;
@ -177,7 +178,7 @@ void Boss03_SpawnEffectWetSpot(PlayState* play, Vec3f* pos) {
}
// @recomp Track this effect's reset state.
void Boss03_SpawnEffectDroplet(PlayState* play, Vec3f* pos) {
RECOMP_PATCH void Boss03_SpawnEffectDroplet(PlayState* play, Vec3f* pos) {
s16 i;
GyorgEffect* eff = play->specialEffects;
@ -204,7 +205,7 @@ void Boss03_SpawnEffectDroplet(PlayState* play, Vec3f* pos) {
}
// @recomp Track this effect's reset state.
void Boss03_SpawnEffectSplash(PlayState* play, Vec3f* pos, Vec3f* velocity) {
RECOMP_PATCH void Boss03_SpawnEffectSplash(PlayState* play, Vec3f* pos, Vec3f* velocity) {
Vec3f accel = { 0.0f, -1.0f, 0.0f };
f32 temp_f2;
GyorgEffect* eff = play->specialEffects;
@ -231,7 +232,7 @@ void Boss03_SpawnEffectSplash(PlayState* play, Vec3f* pos, Vec3f* velocity) {
}
// @recomp Track this effect's reset state.
void Boss03_SpawnEffectBubble(PlayState* play, Vec3f* pos) {
RECOMP_PATCH void Boss03_SpawnEffectBubble(PlayState* play, Vec3f* pos) {
s16 i;
GyorgEffect* eff = play->specialEffects;
@ -261,7 +262,7 @@ extern u8 gEffDust1Tex[];
void Boss03_SetObject(PlayState* play, s16 objectId);
// @recomp Tag Gyorg's effects.
void Boss03_DrawEffects(PlayState* play) {
RECOMP_PATCH void Boss03_DrawEffects(PlayState* play) {
u8 flag = false;
s16 i;
GraphicsContext* gfxCtx = play->state.gfxCtx;
@ -410,7 +411,7 @@ extern Gfx object_water_effect_DL_000420[];
extern Gfx object_water_effect_DL_000A48[];
extern Gfx object_water_effect_DL_000CD8[];
void func_80A5A6B8(Actor* thisx, PlayState* play2) {
RECOMP_PATCH void func_80A5A6B8(Actor* thisx, PlayState* play2) {
PlayState* play = play2;
EnWaterEffect* this = (EnWaterEffect*)thisx;
EnWaterEffectStruct* ptr = &this->unk_144[0];
@ -547,7 +548,7 @@ void func_80A5A6B8(Actor* thisx, PlayState* play2) {
}
// @recomp Tag normal water effects.
void EnWaterEffect_Draw(Actor* thisx, PlayState* play2) {
RECOMP_PATCH void EnWaterEffect_Draw(Actor* thisx, PlayState* play2) {
PlayState* play = play2;
GraphicsContext* gfxCtx = play->state.gfxCtx;
EnWaterEffect* this = (EnWaterEffect*)thisx;
@ -641,7 +642,7 @@ extern Gfx gGohtStalactiteMaterialDL[];
extern Gfx gGohtStalactiteModelDL[];
// @recomp Tag Goht's rocks.
void func_80B0C398(BossHakugin* this, PlayState* play) {
RECOMP_PATCH void func_80B0C398(BossHakugin* this, PlayState* play) {
BossHakuginEffect* effect;
s32 i;
@ -722,7 +723,7 @@ void EnOsn_HandleCsAction(EnOsn* this, PlayState* play);
void EnOsn_Idle(EnOsn* this, PlayState* play);
// @recomp Patched to skip interpolation when the Happy Mask Salesman changes animations.
void EnOsn_ChooseAction(EnOsn* this, PlayState* play) {
RECOMP_PATCH void EnOsn_ChooseAction(EnOsn* this, PlayState* play) {
u32 isSwitchFlagSet = Flags_GetSwitch(play, 0);
this->csId = this->actor.csId;
@ -743,7 +744,7 @@ void EnOsn_LookFromMask(EnOsn* this);
void EnOsn_FadeOut(EnOsn* this);
// @recomp Patched to skip interpolation when the Happy Mask Salesman changes animations.
void EnOsn_HandleCsAction(EnOsn* this, PlayState* play) {
RECOMP_PATCH void EnOsn_HandleCsAction(EnOsn* this, PlayState* play) {
u8 pad;
s32 cueChannel;
@ -880,7 +881,7 @@ void EnOsn_HandleCsAction(EnOsn* this, PlayState* play) {
}
// @recomp Patched to tag this actor's draws using linear order matching.
void EnFall2_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void EnFall2_Draw(Actor* thisx, PlayState* play) {
s32 pad;
EnFall2* this = (EnFall2*)thisx;
Mtx* mtx;
@ -909,7 +910,7 @@ void EnFall2_Draw(Actor* thisx, PlayState* play) {
}
// @recomp Skip interpolation on item pickups the frame they're collected.
void func_800A6A40(EnItem00* this, PlayState* play) {
RECOMP_PATCH void func_800A6A40(EnItem00* this, PlayState* play) {
Player* player = GET_PLAYER(play);
if (this->getItemId != GI_NONE) {
@ -952,7 +953,7 @@ extern Vtx ovl_Obj_Entotu_Vtx_000D10[7];
extern Gfx object_f53_obj_DL_001C00[];
// @recomp Skip rotation interpolation for the Clock Town chimney's smoke when the camera skips interolation.
void func_80A34B28(ObjEntotu* this, PlayState* play) {
RECOMP_PATCH void func_80A34B28(ObjEntotu* this, PlayState* play) {
u8 sp57;
u8 sp56;
s32 i;
@ -1002,7 +1003,7 @@ void func_80A34B28(ObjEntotu* this, PlayState* play) {
extern Gfx object_f53_obj_DL_000158[];
// @recomp Skip rotation interpolation for the Clock Town chimney when the camera skips interolation.
void func_80A34A44(ObjEntotu* this, PlayState* play) {
RECOMP_PATCH void func_80A34A44(ObjEntotu* this, PlayState* play) {
Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW);
this->actor.shape.rot.y = BINANG_ROT180(Camera_GetCamDirYaw(GET_ACTIVE_CAM(play)));
Matrix_RotateYS(this->actor.shape.rot.y, MTXMODE_APPLY);
@ -1032,7 +1033,7 @@ void func_80A34A44(ObjEntotu* this, PlayState* play) {
void Environment_DrawRainImpl(PlayState* play, View* view, GraphicsContext* gfxCtx);
// @recomp Skip interpolation for the splashes that raindrops make.
void Environment_DrawRain(PlayState* play, View* view, GraphicsContext* gfxCtx) {
RECOMP_PATCH void Environment_DrawRain(PlayState* play, View* view, GraphicsContext* gfxCtx) {
if (!(GET_ACTIVE_CAM(play)->stateFlags & CAM_STATE_UNDERWATER) &&
(play->envCtx.precipitation[PRECIP_SNOW_CUR] == 0)) {
@ -1058,7 +1059,7 @@ void Environment_DrawRain(PlayState* play, View* view, GraphicsContext* gfxCtx)
}
// @recomp Skip interpolation on the boulders in the path to Snowhead and the path to Ikana Canyon when they teleport back to their home position.
void func_8093EE64(EnGoroiwa* this, s32 arg1) {
RECOMP_PATCH void func_8093EE64(EnGoroiwa* this, s32 arg1) {
Vec3s* temp_v0 = &this->pathPoints[arg1];
this->actor.world.pos.x = temp_v0->x;
@ -1075,7 +1076,7 @@ extern Gfx object_twig_DL_001C38[];
extern Gfx object_twig_DL_0014C8[];
// @recomp Skip interpolation on the rotation for the beaver race rings in order to retain the intended animation look.
void EnTwig_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void EnTwig_Draw(Actor* thisx, PlayState* play) {
EnTwig* this = (EnTwig*)thisx;
switch (this->unk_160) {
@ -1101,7 +1102,7 @@ extern Gfx gEffFire1DL[];
#define HONOTRAP_EXTRA_BYTE_1(flameGroup) (&(flameGroup)->flameList[0].isDrawn)[1]
#define HONOTRAP_EXTRA_BYTE_2(flameGroup) (&(flameGroup)->flameList[1].isDrawn)[1]
void EnHonotrap_FlameGroup(EnHonotrap* this, PlayState* play) {
RECOMP_PATCH void EnHonotrap_FlameGroup(EnHonotrap* this, PlayState* play) {
s32 i;
EnHonotrapFlameGroup* flameGroup = &this->flameGroup;
f32 var_fs0;
@ -1203,7 +1204,7 @@ void EnHonotrap_FlameGroup(EnHonotrap* this, PlayState* play) {
// @recomp Patched to tag the flames that come out of fire eyes.
void EnHonotrap_DrawFlameGroup(Actor* thisx, PlayState* play) {
RECOMP_PATCH void EnHonotrap_DrawFlameGroup(Actor* thisx, PlayState* play) {
s32 pad;
EnHonotrap* this = (EnHonotrap*)thisx;
EnHonotrapFlameElement* flameElem;
@ -1259,3 +1260,74 @@ void EnHonotrap_DrawFlameGroup(Actor* thisx, PlayState* play) {
CLOSE_DISPS(play->state.gfxCtx);
}
extern Gfx ovl_En_Tanron1_DL_001428[];
extern Gfx ovl_En_Tanron1_DL_001888[];
extern Gfx ovl_En_Tanron1_DL_001900[];
// @recomp Patched to interpolate the moths that circle torches.
RECOMP_PATCH void func_80BB5AAC(EnTanron1* this, PlayState* play) {
EnTanron1Struct* ptrBase = &this->unk_160[0];
s16 i;
u8 flag = 0;
EnTanron1Struct* ptr = ptrBase;
// @recomp Get actor transform id
u32 cur_transform_id = actor_transform_id(&this->actor);
OPEN_DISPS(play->state.gfxCtx);
Gfx_SetupDL25_Opa(play->state.gfxCtx);
for (i = 0; i < this->actor.params; i++, ptr++) {
if (ptr->unk_24 == 1) {
if (!flag) {
gSPDisplayList(POLY_OPA_DISP++, ovl_En_Tanron1_DL_001888);
flag++;
}
Matrix_Translate(ptr->unk_00.x, ptr->unk_00.y, ptr->unk_00.z, MTXMODE_NEW);
Matrix_RotateYS(ptr->unk_1A, MTXMODE_APPLY);
Matrix_RotateXS(ptr->unk_18 * -1, MTXMODE_APPLY);
Matrix_Scale(1.2f, ptr->unk_2C, 1.2f, MTXMODE_APPLY);
// @recomp Write matrix group for POLY_OPA
gEXMatrixGroupDecomposedNormal(POLY_OPA_DISP++, cur_transform_id + i, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, ovl_En_Tanron1_DL_001900);
// @recomp Pop matrix group
gEXPopMatrixGroup(POLY_OPA_DISP++, G_MTX_MODELVIEW);
}
}
flag = 0;
ptr = ptrBase;
for (i = 0; i < this->actor.params; i++, ptr++) {
if (ptr->unk_24 == 2) {
if (!flag) {
gSPDisplayList(POLY_OPA_DISP++, ovl_En_Tanron1_DL_001888);
gDPLoadTextureBlock(POLY_OPA_DISP++, ovl_En_Tanron1_DL_001428, G_IM_FMT_RGBA, G_IM_SIZ_16b, 16, 32, 0,
G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, 4, 5, G_TX_NOLOD,
G_TX_NOLOD);
flag++;
}
Matrix_Translate(ptr->unk_00.x, ptr->unk_00.y, ptr->unk_00.z, MTXMODE_NEW);
Matrix_RotateYS(ptr->unk_1A, MTXMODE_APPLY);
Matrix_RotateXS(ptr->unk_18 * -1, MTXMODE_APPLY);
Matrix_Scale(1.0f, ptr->unk_2C, 1.0f, MTXMODE_APPLY);
// @recomp Write matrix group for POLY_OPA
gEXMatrixGroupDecomposedNormal(POLY_OPA_DISP++, cur_transform_id + i, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, ovl_En_Tanron1_DL_001900);
// @recomp Pop matrix group
gEXPopMatrixGroup(POLY_OPA_DISP++, G_MTX_MODELVIEW);
}
}
CLOSE_DISPS(play->state.gfxCtx);
}

View File

@ -7,7 +7,7 @@ static Vec3f sZeroVec = { 0.0f, 0.0f, 0.0f };
extern RoomDrawHandler sRoomDrawHandlers[];
void Room_Draw(PlayState* play, Room* room, u32 flags) {
RECOMP_PATCH void Room_Draw(PlayState* play, Room* room, u32 flags) {
if (room->segment != NULL) {
gSegments[3] = OS_K0_TO_PHYSICAL(room->segment);
@ -51,7 +51,7 @@ extern Gfx gKeikokuDemoTallTreeStraightDL[];
extern Gfx gKeikokuDemoTallTreeStraightEmptyDL[];
// @recomp Tag the ground in the intro cutscene to not interpolate rotation.
void DmOpstage_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void DmOpstage_Draw(Actor* thisx, PlayState* play) {
DmOpstage* this = (DmOpstage*)thisx;
if (DMOPSTAGE_GET_TYPE(&this->dyna.actor) > DMOPSTAGE_TYPE_GROUND) {
@ -130,7 +130,7 @@ extern s16 D_80AAAAC8;
extern s16 D_80AAAACC;
// @recomp Patched to enable vertex interpolation for the dynamic water as Woodfall temple rises from below the water.
void DmChar01_Draw(Actor* thisx, PlayState* play) {
RECOMP_PATCH void DmChar01_Draw(Actor* thisx, PlayState* play) {
// @recomp Move function statics to externs so they still get reset on overlay load like normal.
DmChar01* this = (DmChar01*)thisx;
f32 temp_f12;

View File

@ -22,7 +22,7 @@ typedef struct {
BiggerGfxPool gBiggerGfxPools[2];
// @recomp Use the bigger gfx pools and enable RT64 extended GBI mode.
void Graph_SetNextGfxPool(GraphicsContext* gfxCtx) {
RECOMP_PATCH void Graph_SetNextGfxPool(GraphicsContext* gfxCtx) {
GfxPool* pool = &gGfxPools[gfxCtx->gfxPoolIdx % 2];
BiggerGfxPool* bigger_pool = &gBiggerGfxPools[gfxCtx->gfxPoolIdx % 2];
@ -56,9 +56,10 @@ void Graph_SetNextGfxPool(GraphicsContext* gfxCtx) {
gSPEndDisplayList(&gGfxMasterDL->disps[4]);
gSPBranchList(&gGfxMasterDL->debugDisp[0], bigger_pool->debugBuffer);
// @recomp Enable RT64 extended GBI mode and set the current framerate
// @recomp Enable RT64 extended GBI mode and extended rdram.
OPEN_DISPS(gfxCtx);
gEXEnable(POLY_OPA_DISP++);
gEXSetRDRAMExtended(POLY_OPA_DISP++, 1);
CLOSE_DISPS(gfxCtx);
}
@ -85,7 +86,7 @@ extern int extra_vis;
* Run the game state logic, then finalize the gfx buffer
* and run the graphics task for this frame.
*/
void Graph_ExecuteAndDraw(GraphicsContext* gfxCtx, GameState* gameState) {
RECOMP_PATCH void Graph_ExecuteAndDraw(GraphicsContext* gfxCtx, GameState* gameState) {
u32 problem;
gameState->unk_A3 = 0;
@ -238,7 +239,7 @@ void Interface_SetOrthoView(InterfaceContext* interfaceCtx);
void Interface_SetVertices(PlayState* play);
void Magic_DrawMeter(PlayState* play);
void Interface_Draw(PlayState* play) {
RECOMP_PATCH void Interface_Draw(PlayState* play) {
s32 pad;
InterfaceContext* interfaceCtx = &play->interfaceCtx;
Player* player = GET_PLAYER(play);
@ -756,7 +757,7 @@ extern u64 gArcheryScoreIconTex[];
extern u16 sMinigameScoreDigits[];
// @recomp Patched to draw the carrot icons with an extended gbi texrect so they don't inherit the current origin.
void Interface_DrawMinigameIcons(PlayState* play) {
RECOMP_PATCH void Interface_DrawMinigameIcons(PlayState* play) {
InterfaceContext* interfaceCtx = &play->interfaceCtx;
s16 i;
s16 numDigitsDrawn;
@ -881,7 +882,7 @@ extern s16 sTextboxTexHeight;
extern u64 gOcarinaTrebleClefTex[];
// @recomp Patch textboxes to use ortho tris with a matrix so they can be interpolated.
void Message_DrawTextBox(PlayState* play, Gfx** gfxP) {
RECOMP_PATCH void Message_DrawTextBox(PlayState* play, Gfx** gfxP) {
MessageContext* msgCtx = &play->msgCtx;
Gfx* gfx = *gfxP;
@ -1018,7 +1019,7 @@ void View_SetScissor(Gfx** gfx, s32 ulx, s32 uly, s32 lrx, s32 lry);
// @recomp Patched to not actually letterbox the scissor. The letterbox effect will be achieved by drawing an overlay on top instead, which
// will get interpolated unlike a scissor.
void View_ApplyLetterbox(View* view) {
RECOMP_PATCH void View_ApplyLetterbox(View* view) {
s32 letterboxY;
s32 letterboxX;
s32 pad1;
@ -1085,7 +1086,7 @@ typedef struct {
extern ShrinkWindow* sShrinkWindowPtr;
// @recomp Replace the rects used to letterbox with ortho tris so they can be interpolated.
void ShrinkWindow_Draw(GraphicsContext* gfxCtx) {
RECOMP_PATCH void ShrinkWindow_Draw(GraphicsContext* gfxCtx) {
Gfx* gfx;
s8 letterboxSize = sShrinkWindowPtr->letterboxSize;
s8 pillarboxSize = sShrinkWindowPtr->pillarboxSize;
@ -1189,7 +1190,7 @@ extern u64 gSceneTitleCardGradientTex[];
// @recomp Patch the scene title card (the one with purple background when entering a new scene)
// to not glitch out on the right edge, which is hidden by overscan on N64.
void Message_DrawSceneTitleCard(PlayState* play, Gfx** gfxP) {
RECOMP_PATCH void Message_DrawSceneTitleCard(PlayState* play, Gfx** gfxP) {
MessageContext* msgCtx = &play->msgCtx;
Gfx* gfx;

View File

@ -56,7 +56,7 @@ extern TexturePtr sMaskPageBgTextures[];
// @recomp Patched to set pageIndex to a dummy value when KaleidoScope_SetVertices is called to make it
// allocate vertices for all pages at all times. This is simpler than patching KaleidoScope_SetVertices directly.
void KaleidoScope_Draw(PlayState* play) {
RECOMP_PATCH void KaleidoScope_Draw(PlayState* play) {
s32 pad;
PauseContext* pauseCtx = &play->pauseCtx;
InterfaceContext* interfaceCtx = &play->interfaceCtx;
@ -137,7 +137,7 @@ void KaleidoScope_Draw(PlayState* play) {
CLOSE_DISPS(play->state.gfxCtx);
}
void KaleidoScope_DrawCursor(PlayState* play) {
RECOMP_PATCH void KaleidoScope_DrawCursor(PlayState* play) {
PauseContext* pauseCtx = &play->pauseCtx;
s16 i;
@ -219,7 +219,7 @@ extern s16 sPauseZRCursorAlpha; // 8082DA56 32899 -9642
*/
// @recomp Patched to tag the matrix for interpolating the vertices of the Z button, R button, and name panel.
void KaleidoScope_DrawInfoPanel(PlayState* play) {
RECOMP_PATCH void KaleidoScope_DrawInfoPanel(PlayState* play) {
static const s16 sPauseZRCursorColorTargets[][4] = {
{ 180, 210, 255, 220 },
{ 100, 100, 150, 220 },
@ -587,7 +587,7 @@ void KaleidoScope_DrawInfoPanel(PlayState* play) {
}
// @recomp Patched to draw always all 4 pages and tag their matrices.
void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) {
RECOMP_PATCH void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) {
static s16 sCursorColorTimer = 10;
static s16 sCursorColorTargetIndex = 0;
PauseContext* pauseCtx = &play->pauseCtx;

View File

@ -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 controls_filename = u8"controls.json";
constexpr std::u8string_view sound_filename = u8"sound.json";
constexpr std::u8string_view program_id = u8"Zelda64Recompiled";
constexpr auto res_default = ultramodern::renderer::Resolution::Auto;
constexpr auto hr_default = ultramodern::renderer::HUDRatioMode::Clamp16x9;

View File

@ -27,10 +27,7 @@
#include "zelda_render.h"
#include "ovl_patches.hpp"
#include "librecomp/game.hpp"
#ifdef HAS_MM_SHADER_CACHE
#include "mm_shader_cache.h"
#endif
#include "librecomp/mods.hpp"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
@ -38,9 +35,10 @@
#include "SDL_syswm.h"
#endif
#define STB_IMAGE_IMPLEMENTATION
#include "../../lib/rt64/src/contrib/stb/stb_image.h"
const std::string version_string = "1.2.0-dev";
template<typename... Ts>
void exit_error(const char* str, Ts ...args) {
// TODO pop up an error
@ -332,9 +330,8 @@ std::vector<recomp::GameEntry> supported_games = {
.rom_hash = 0xEF18B4A9E2386169ULL,
.internal_name = "ZELDA MAJORA'S MASK",
.game_id = u8"mm.n64.us.1.0",
#ifdef HAS_MM_SHADER_CACHE
.cache_data = {mm_shader_cache_bytes, sizeof(mm_shader_cache_bytes)},
#endif
.mod_game_id = "mm",
.save_type = recomp::SaveType::Flashram,
.is_enabled = true,
.entrypoint_address = get_entrypoint_address(),
.entrypoint = recomp_entrypoint,
@ -424,8 +421,139 @@ namespace zelda64 {
}
}
#ifdef _WIN32
struct PreloadContext {
HANDLE handle;
HANDLE mapping_handle;
SIZE_T size;
PVOID view;
};
bool preload_executable(PreloadContext& context) {
wchar_t module_name[MAX_PATH];
GetModuleFileNameW(NULL, module_name, MAX_PATH);
context.handle = CreateFileW(module_name, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (context.handle == INVALID_HANDLE_VALUE) {
fprintf(stderr, "Failed to load executable into memory!");
context = {};
return false;
}
LARGE_INTEGER module_size;
if (!GetFileSizeEx(context.handle, &module_size)) {
fprintf(stderr, "Failed to get size of executable!");
CloseHandle(context.handle);
context = {};
return false;
}
context.size = module_size.QuadPart;
context.mapping_handle = CreateFileMappingW(context.handle, nullptr, PAGE_READONLY, 0, 0, nullptr);
if (context.mapping_handle == nullptr) {
fprintf(stderr, "Failed to create file mapping of executable!");
CloseHandle(context.handle);
context = {};
return EXIT_FAILURE;
}
context.view = MapViewOfFile(context.mapping_handle, FILE_MAP_READ, 0, 0, 0);
if (context.view == nullptr) {
fprintf(stderr, "Failed to map view of of executable!");
CloseHandle(context.mapping_handle);
CloseHandle(context.handle);
context = {};
return false;
}
DWORD pid = GetCurrentProcessId();
HANDLE process_handle = OpenProcess(PROCESS_SET_QUOTA | PROCESS_QUERY_INFORMATION, FALSE, pid);
if (process_handle == nullptr) {
fprintf(stderr, "Failed to open own process!");
CloseHandle(context.mapping_handle);
CloseHandle(context.handle);
context = {};
return false;
}
SIZE_T minimum_set_size, maximum_set_size;
if (!GetProcessWorkingSetSize(process_handle, &minimum_set_size, &maximum_set_size)) {
fprintf(stderr, "Failed to get working set size!");
CloseHandle(context.mapping_handle);
CloseHandle(context.handle);
context = {};
return false;
}
if (!SetProcessWorkingSetSize(process_handle, minimum_set_size + context.size, maximum_set_size + context.size)) {
fprintf(stderr, "Failed to set working set size!");
CloseHandle(context.mapping_handle);
CloseHandle(context.handle);
context = {};
return false;
}
if (VirtualLock(context.view, context.size) == 0) {
fprintf(stderr, "Failed to lock view of executable! (Error: %08lx)\n", GetLastError());
CloseHandle(context.mapping_handle);
CloseHandle(context.handle);
context = {};
return false;
}
return true;
}
void release_preload(PreloadContext& context) {
VirtualUnlock(context.view, context.size);
CloseHandle(context.mapping_handle);
CloseHandle(context.handle);
context = {};
}
#else
struct PreloadContext {
};
// TODO implement on other platforms
bool preload_executable(PreloadContext& context) {
return false;
}
void release_preload(PreloadContext& context) {
}
#endif
void enable_texture_pack(recomp::mods::ModContext& context, const recomp::mods::ModHandle& mod) {
(void)context;
zelda64::renderer::enable_texture_pack(mod);
}
void disable_texture_pack(recomp::mods::ModContext& context, const recomp::mods::ModHandle& mod) {
(void)context;
zelda64::renderer::disable_texture_pack(mod);
}
int main(int argc, char** argv) {
recomp::Version project_version{};
if (!recomp::Version::from_string(version_string, project_version)) {
ultramodern::error_handling::message_box(("Invalid version string: " + version_string).c_str());
return EXIT_FAILURE;
}
// Map this executable into memory and lock it, which should keep it in physical memory. This ensures
// that there are no stutters from the OS having to load new pages of the executable whenever a new code page is run.
PreloadContext preload_context;
bool preloaded = preload_executable(preload_context);
if (!preloaded) {
fprintf(stderr, "Failed to preload executable!\n");
}
#ifdef _WIN32
// Set up console output to accept UTF-8 on windows
@ -461,6 +589,8 @@ int main(int argc, char** argv) {
fprintf(stderr, "Failed to load controller mappings: %s\n", SDL_GetError());
}
recomp::register_config_path(zelda64::get_app_folder_path());
// Register supported games and patches
for (const auto& game : supported_games) {
recomp::register_game(game);
@ -468,8 +598,6 @@ int main(int argc, char** argv) {
zelda64::register_overlays();
zelda64::register_patches();
recomp::register_config_path(zelda64::get_app_folder_path());
zelda64::load_config();
recomp::rsp::callbacks_t rsp_callbacks{
@ -512,7 +640,47 @@ int main(int argc, char** argv) {
.get_game_thread_name = zelda64::get_game_thread_name,
};
// Register the texture pack content type with rt64.json as its content file.
recomp::mods::ModContentType texture_pack_content_type{
.content_filename = "rt64.json",
.allow_runtime_toggle = true,
.on_enabled = enable_texture_pack,
.on_disabled = disable_texture_pack,
};
auto texture_pack_content_type_id = recomp::mods::register_mod_content_type(texture_pack_content_type);
// Register the .rtz texture pack file format with the previous content type as its only allowed content type.
recomp::mods::register_mod_container_type("rtz", std::vector{ texture_pack_content_type_id }, false);
recomp::mods::scan_mods();
printf("Found mods:\n");
for (const auto& mod : recomp::mods::get_mod_details("mm")) {
printf(" %s(%s)\n", mod.mod_id.c_str(), mod.version.to_string().c_str());
if (!mod.authors.empty()) {
printf(" Authors: %s", mod.authors[0].c_str());
for (size_t author_index = 1; author_index < mod.authors.size(); author_index++) {
const std::string& author = mod.authors[author_index];
printf(", %s", author.c_str());
}
printf("\n");
printf(" Runtime toggleable: %d\n", mod.runtime_toggleable);
}
if (!mod.dependencies.empty()) {
printf(" Dependencies: %s:%s", mod.dependencies[0].mod_id.c_str(), mod.dependencies[0].version.to_string().c_str());
for (size_t dep_index = 1; dep_index < mod.dependencies.size(); dep_index++) {
const recomp::mods::Dependency& dep = mod.dependencies[dep_index];
printf(", %s:%s", dep.mod_id.c_str(), dep.version.to_string().c_str());
}
printf("\n");
}
// TODO load all mods as a temporary solution to not having a UI yet.
recomp::mods::enable_mod(mod.mod_id, true);
}
printf("\n");
recomp::start(
project_version,
{},
rsp_callbacks,
renderer_callbacks,
@ -526,5 +694,9 @@ int main(int argc, char** argv) {
NFD_Quit();
if (preloaded) {
release_preload(preload_context);
}
return EXIT_SUCCESS;
}

View File

@ -6,5 +6,7 @@
#include "librecomp/game.hpp"
void zelda64::register_patches() {
recomp::overlays::register_patches(mm_patches_bin, sizeof(mm_patches_bin), 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);
}

View File

@ -1,5 +1,6 @@
#include <memory>
#include <cstring>
#include <variant>
#define HLSL_CPU
#include "hle/rt64_application.h"
@ -10,6 +11,13 @@
#include "zelda_render.h"
#include "recomp_ui.h"
#include "concurrentqueue.h"
// Helper class for variant visiting.
template<class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
static RT64::UserConfiguration::Antialiasing device_max_msaa = RT64::UserConfiguration::Antialiasing::None;
static bool sample_positions_supported = false;
@ -18,6 +26,18 @@ static bool high_precision_fb_enabled = false;
static uint8_t DMEM[0x1000];
static uint8_t IMEM[0x1000];
struct TexturePackEnableAction {
std::filesystem::path path;
};
struct TexturePackDisableAction {
std::filesystem::path path;
};
using TexturePackAction = std::variant<TexturePackEnableAction, TexturePackDisableAction>;
static moodycamel::ConcurrentQueue<TexturePackAction> texture_pack_action_queue;
unsigned int MI_INTR_REG = 0;
unsigned int DPC_START_REG = 0;
@ -286,6 +306,32 @@ zelda64::renderer::RT64Context::RT64Context(uint8_t* rdram, ultramodern::rendere
zelda64::renderer::RT64Context::~RT64Context() = default;
void zelda64::renderer::RT64Context::send_dl(const OSTask* task) {
bool packs_disabled = false;
TexturePackAction cur_action;
while (texture_pack_action_queue.try_dequeue(cur_action)) {
std::visit(overloaded{
[&](TexturePackDisableAction& to_disable) {
enabled_texture_packs.erase(to_disable.path);
packs_disabled = true;
},
[&](TexturePackEnableAction& to_enable) {
enabled_texture_packs.insert(to_enable.path);
// Load the pack now if no packs have been disabled.
if (!packs_disabled) {
app->textureCache->loadReplacementDirectory(to_enable.path);
}
}
}, cur_action);
}
// If any packs were disabled, unload all packs and load all the active ones.
if (packs_disabled) {
app->textureCache->clearReplacementDirectories();
for (const std::filesystem::path& cur_pack_path : enabled_texture_packs) {
app->textureCache->loadReplacementDirectory(cur_pack_path);
}
}
app->state->rsp->reset();
app->interpreter->loadUCodeGBI(task->t.ucode & 0x3FFFFFF, task->t.ucode_data & 0x3FFFFFF, true);
app->processDisplayLists(app->core.RDRAM, task->t.data_ptr & 0x3FFFFFF, 0, true);
@ -351,16 +397,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() {
return device_max_msaa;
}
@ -376,3 +412,11 @@ bool zelda64::renderer::RT64SamplePositionsSupported() {
bool zelda64::renderer::RT64HighPrecisionFBEnabled() {
return high_precision_fb_enabled;
}
void zelda64::renderer::enable_texture_pack(const recomp::mods::ModHandle& mod) {
texture_pack_action_queue.enqueue(TexturePackEnableAction{mod.manifest.mod_root_path});
}
void zelda64::renderer::disable_texture_pack(const recomp::mods::ModHandle& mod) {
texture_pack_action_queue.enqueue(TexturePackDisableAction{mod.manifest.mod_root_path});
}

View File

@ -6,7 +6,7 @@
#include "nfd.h"
#include <filesystem>
std::string version_number = "v1.1.1";
static std::string version_string;
Rml::DataModelHandle model_handle;
bool mm_rom_valid = false;
@ -103,7 +103,9 @@ public:
Rml::DataModelConstructor constructor = context->CreateDataModel("launcher_model");
constructor.Bind("mm_rom_valid", &mm_rom_valid);
constructor.Bind("version_number", &version_number);
version_string = recomp::get_project_version().to_string();
constructor.Bind("version_number", &version_string);
model_handle = constructor.GetModelHandle();
}

View File

@ -1287,6 +1287,12 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
static recompui::Menu prev_menu = recompui::Menu::None;
recompui::Menu cur_menu = open_menu.load();
// Return to the launcher if no menu is open and the game isn't started.
if (cur_menu == recompui::Menu::None && !ultramodern::is_game_started()) {
cur_menu = recompui::Menu::Launcher;
recompui::set_current_menu(cur_menu);
}
if (reload_sheets) {
ui_context->rml.load_documents();
prev_menu = recompui::Menu::None;

View File

@ -5,6 +5,7 @@ entrypoint = 0x80080000
# Paths are relative to the location of this config file.
output_func_path = "RecompiledFuncs"
relocatable_sections_path = "overlays.us.rev1.txt"
# elf_path = "mm.us.rev1.rom_uncompressed.elf"
symbols_file_path = "Zelda64RecompSyms/mm.us.rev1.syms.toml"
rom_file_path = "mm.us.rev1.rom_uncompressed.z64"