Compare commits

...

22 Commits

Author SHA1 Message Date
Howard Luck b0b1978c76 Fix dangling daMyna_c file-statics across scenes (#392) 2026-04-15 10:52:30 -06:00
Irastris 965e958222 Consolidate the Enhancements menu (#349)
* Consolidate the Enhancements menu

* Merge "main" branch again

* added Preferences separator
2026-04-15 09:11:30 -06:00
Pieter-Jan Briers 8de4863426 Validate that provided ISO is GZ2E01 in pre-launch UI (#390)
Fixes #377
2026-04-15 09:10:56 -06:00
Howard Luck 9235833413 Fix OSGetTime overflow on iOS/Android (#389)
* Fix OSGetTime overflow on iOS/Android

* Fetch latest aurora main
2026-04-15 09:10:45 -06:00
PJB3005 a0e9ee73a8 Fix mDoExt_3DlineMat0_c drawing
Fixes shadow beast hair etc, fixes #204

Regression introduced by https://github.com/TwilitRealm/dusk/commit/fe21abb1ec1a9f4fc320dfb191be58e864bc079c#diff-1c53210cfc3c1892c88d6c56110608fa017738f0b099e2cf5766f9107cefa4e8L2382-R2386
2026-04-15 15:17:03 +02:00
TakaRikka 99fd2f9bc5 save editor minigame tab / item amounts 2026-04-15 05:00:38 -07:00
TakaRikka 7eb61b71f1 some save editor cleanup 2026-04-15 04:29:33 -07:00
TakaRikka d8df116047 Merge pull request #386 from TwilitRealm/fix/telma_dialog
Fix Missing Telma Dialog During Escort
2026-04-14 20:39:32 -07:00
Pheenoh 5f69895326 Merge remote-tracking branch 'origin/main' into fix/telma_dialog 2026-04-14 21:05:29 -06:00
TakaRikka 194cb94d43 Merge pull request #380 from TwilitRealm/feature/disable-main-hud
Disable Main HUD Feature
2026-04-14 20:03:23 -07:00
TakaRikka 22535ba84d Merge pull request #379 from TwilitRealm/minigame-wide-hud
Widescreen Minigame Counters
2026-04-14 20:02:41 -07:00
Pheenoh ab65f42816 Wait to finish creating Telma actor during escort on PC until the player is loaded 2026-04-14 20:47:30 -06:00
Irastris 0508acaa79 Frame interp: Dreamworks' Turbo (2013) 2026-04-14 21:59:24 -04:00
Phillip Stephens d876c62822 Fix missing libusb in Linux AppImage (#384) 2026-04-14 17:49:08 -06:00
Luke Street c26b05b1dd Update CI for self-hosted runners 2026-04-14 16:15:55 -06:00
Irastris 97c459a614 Frame interp: GOAT IN! 2026-04-14 17:51:00 -04:00
Irastris cd9bb31b02 Frame interp: Howl 2 Electric Boogaloo 2026-04-14 17:17:29 -04:00
Irastris e37c912053 Frame interp: Stable keying for simple shadows 2026-04-14 16:55:24 -04:00
MelonSpeedruns 4e42af6cd8 Disable Main HUD Feature 2026-04-14 15:43:52 -04:00
Irastris 87a95fb1b8 Frame interp: Howl UI scrolling dot 2026-04-14 15:27:29 -04:00
MelonSpeedruns 83b210e26c Widescreen minigame counters 2026-04-14 15:10:28 -04:00
Luke Street 158f31d056 Patch Twilight Hack 2026-04-14 11:33:14 -06:00
33 changed files with 621 additions and 198 deletions
+35 -32
View File
@@ -8,8 +8,9 @@ on:
pull_request:
env:
SCCACHE_GHA_ENABLED: "true"
# SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: "sccache"
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
jobs:
build-linux:
@@ -21,13 +22,13 @@ jobs:
matrix:
include:
- name: GCC x86_64
runner: ubuntu-latest
runner: [self-hosted, Linux]
preset: gcc
artifact_arch: x86_64
- name: GCC aarch64
runner: ubuntu-24.04-arm
preset: gcc
artifact_arch: aarch64
# - name: GCC aarch64
# runner: ubuntu-24.04-arm
# preset: gcc
# artifact_arch: aarch64
# - name: Clang x86_64
# runner: ubuntu-latest
# preset: clang
@@ -40,17 +41,22 @@ jobs:
submodules: recursive
- name: Install dependencies
if: 'false' # disabled for self-hosted
run: |
sudo apt-get update
sudo apt-get -y install ninja-build clang lld openssl libcurl4-openssl-dev \
zlib1g-dev libglu1-mesa-dev libdbus-1-dev libvulkan-dev libxi-dev libxrandr-dev libasound2-dev \
libpulse-dev libudev-dev libpng-dev libncurses5-dev libx11-xcb-dev libfreetype-dev \
libxinerama-dev libxcursor-dev python3-markupsafe libgtk-3-dev libssl-dev \
libxss-dev libfuse2
libxss-dev libfuse2 libusb-1.0-0-dev libdecor-0-dev libpipewire-0.3-dev libunwind-dev
- name: Setup sccache
if: 'false' # disabled for self-hosted
uses: mozilla-actions/sccache-action@v0.0.9
- name: Print sccache stats
run: sccache --show-stats
- name: Configure CMake
run: cmake --preset x-linux-ci-${{matrix.preset}}
@@ -68,9 +74,9 @@ jobs:
build/install/Dusk-*.AppImage
build/install/debug.tar.*
build-apple:
name: Build Apple (${{matrix.name}})
if: 'false' # TODO enable when CI is free
runs-on: macos-latest
strategy:
fail-fast: false
@@ -80,14 +86,14 @@ jobs:
platform: macos
preset: x-macos-ci
artifact_name: macos-appleclang-universal
# - name: AppleClang iOS arm64 # TODO enable when CI is free
# platform: ios
# preset: x-ios-ci
# artifact_name: ios-appleclang-arm64
# - name: AppleClang tvOS arm64 # TODO enable when CI is free
# platform: tvos
# preset: x-tvos-ci
# artifact_name: tvos-appleclang-arm64
- name: AppleClang iOS arm64
platform: ios
preset: x-ios-ci
artifact_name: ios-appleclang-arm64
- name: AppleClang tvOS arm64
platform: tvos
preset: x-tvos-ci
artifact_name: tvos-appleclang-arm64
steps:
- uses: actions/checkout@v6
@@ -139,26 +145,22 @@ jobs:
name: Build Windows (${{matrix.name}})
runs-on: ${{matrix.runner}}
env:
BUILD_DIR: C:\build
SCCACHE_DIR: C:\sccache
strategy:
fail-fast: false
matrix:
include:
- name: MSVC x86_64
runner: windows-latest
runner: [self-hosted, Windows]
preset: msvc
msvc_arch: amd64
vcpkg_arch: x64
artifact_arch: x86_64
- name: MSVC arm64
runner: windows-11-arm
preset: arm64-msvc
msvc_arch: arm64
vcpkg_arch: arm64
artifact_arch: arm64
# - name: MSVC arm64
# runner: windows-11-arm
# preset: arm64-msvc
# msvc_arch: arm64
# vcpkg_arch: arm64
# artifact_arch: arm64
# - name: Clang x86_64
# runner: windows-latest
# preset: clang
@@ -185,11 +187,10 @@ jobs:
uses: mozilla-actions/sccache-action@v0.0.9
- name: Install dependencies
if: 'false' # disabled for self-hosted
run: |
choco install ninja
vcpkg install zlib:${{matrix.vcpkg_arch}}-windows-static bzip2:${{matrix.vcpkg_arch}}-windows-static `
zstd:${{matrix.vcpkg_arch}}-windows-static liblzma:${{matrix.vcpkg_arch}}-windows-static `
freetype:${{matrix.vcpkg_arch}}-windows-static
vcpkg install freetype:${{matrix.vcpkg_arch}}-windows-static zstd:${{matrix.vcpkg_arch}}-windows-static
- name: Configure CMake
run: cmake --preset x-windows-ci-${{matrix.preset}}
@@ -202,5 +203,7 @@ jobs:
with:
name: dusk-${{env.DUSK_VERSION}}-win32-msvc-${{matrix.artifact_arch}}
path: |
${{env.BUILD_DIR}}/install/*.exe
${{env.BUILD_DIR}}/install/debug.7z
build/install/*.exe
build/install/*.dll
build/install/res/
build/install/debug.7z
+7 -3
View File
@@ -113,7 +113,7 @@ set(DUSK_SENTRY_DSN "" CACHE STRING "Sentry DSN")
set(DUSK_SENTRY_ENVIRONMENT "development" CACHE STRING "Sentry environment")
if (DUSK_MOVIE_SUPPORT)
find_package(libjpeg-turbo QUIET)
find_package(libjpeg-turbo 3.0 CONFIG QUIET)
if (libjpeg-turbo_FOUND)
message(STATUS "dusk: Using system libjpeg-turbo")
else ()
@@ -297,8 +297,12 @@ endif ()
# Edit & Continue
if (MSVC)
add_compile_options("/ZI")
add_link_options("/INCREMENTAL")
if (CMAKE_MSVC_DEBUG_INFORMATION_FORMAT STREQUAL "" AND CMAKE_BUILD_TYPE STREQUAL Debug)
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "EditAndContinue")
endif ()
if (CMAKE_MSVC_DEBUG_INFORMATION_FORMAT STREQUAL "EditAndContinue")
add_link_options("/INCREMENTAL")
endif ()
endif ()
if(ANDROID)
+28 -18
View File
@@ -22,6 +22,20 @@
"CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreaded"
}
},
{
"name": "ci",
"hidden": true,
"cacheVariables": {
"CMAKE_C_COMPILER_LAUNCHER": "sccache",
"CMAKE_CXX_COMPILER_LAUNCHER": "sccache",
"DUSK_ENABLE_SENTRY_NATIVE": {
"type": "BOOL",
"value": true
},
"DUSK_SENTRY_DSN": "$env{SENTRY_DSN}",
"DUSK_SENTRY_ENVIRONMENT": "production"
}
},
{
"name": "linux-default",
"displayName": "Linux (default)",
@@ -343,12 +357,9 @@
"name": "x-linux-ci",
"hidden": true,
"inherits": [
"relwithdebinfo"
],
"cacheVariables": {
"CMAKE_C_COMPILER_LAUNCHER": "sccache",
"CMAKE_CXX_COMPILER_LAUNCHER": "sccache"
}
"relwithdebinfo",
"ci"
]
},
{
"name": "x-linux-ci-gcc",
@@ -367,12 +378,9 @@
{
"name": "x-macos-ci",
"inherits": [
"macos-default-relwithdebinfo"
],
"cacheVariables": {
"CMAKE_C_COMPILER_LAUNCHER": "sccache",
"CMAKE_CXX_COMPILER_LAUNCHER": "sccache"
}
"macos-default-relwithdebinfo",
"ci"
]
},
{
"name": "x-ios-ci",
@@ -398,14 +406,16 @@
"name": "x-windows-ci",
"hidden": true,
"inherits": [
"relwithdebinfo"
"relwithdebinfo",
"ci"
],
"binaryDir": "$env{BUILD_DIR}",
"cacheVariables": {
"CMAKE_INSTALL_PREFIX": "$env{BUILD_DIR}/install",
"CMAKE_C_COMPILER_LAUNCHER": "sccache",
"CMAKE_CXX_COMPILER_LAUNCHER": "sccache",
"CMAKE_MSVC_DEBUG_INFORMATION_FORMAT": "Embedded"
"CMAKE_MSVC_DEBUG_INFORMATION_FORMAT": "Embedded",
"CMAKE_TOOLCHAIN_FILE": {
"type": "FILEPATH",
"value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
},
"VCPKG_TARGET_TRIPLET": "x64-windows"
}
},
{
+2 -2
View File
@@ -9,10 +9,10 @@ chmod +x linuxdeploy-$(uname -m).AppImage
# Build AppImage
cd "$GITHUB_WORKSPACE"
mkdir -p build/appdir/usr/{bin,share/{applications,icons/hicolor}}
cp build/install/!(*.*) build/appdir/usr/bin
cp -r build/install/!(*.*) build/appdir/usr/bin
cp -r platforms/freedesktop/{16x16,32x32,48x48,64x64,128x128,256x256,512x512,1024x1024} build/appdir/usr/share/icons/hicolor
cp platforms/freedesktop/dusk.desktop build/appdir/usr/share/applications
cd build/install
VERSION="$DUSK_VERSION" NO_STRIP=1 "$RUNNER_WORKSPACE"/linuxdeploy-$(uname -m).AppImage \
--appdir "$GITHUB_WORKSPACE"/build/appdir --output appimage
-l /usr/lib/x86_64-linux-gnu/libusb-1.0.so --appdir "$GITHUB_WORKSPACE"/build/appdir --output appimage
+1 -1
+1
View File
@@ -1376,6 +1376,7 @@ set(DUSK_FILES
src/dusk/imgui/ImGuiSaveEditor.cpp
src/dusk/imgui/ImGuiStateShare.hpp
src/dusk/imgui/ImGuiStateShare.cpp
src/dusk/iso_validate.cpp
src/dusk/offset_ptr.cpp
src/dusk/OSContext.cpp
src/dusk/OSThread.cpp
+3
View File
@@ -44,6 +44,9 @@ public:
int draw();
int execute();
void drawMeter();
#if TARGET_PC
void updateOnWide();
#endif
void setComboCount(u8, u8);
void setScoreCount(u32);
void addScoreCount(cXyz*, u32, u8);
+4
View File
@@ -110,6 +110,10 @@ struct dMsgScrnHowl_c : public dMsgScrnBase_c {
/* 0x27A0 */ f32 field_0x27a0;
/* 0x27A4 */ f32 field_0x27a4;
/* 0x27A8 */ f32 field_0x27a8;
#if TARGET_PC
u8 showCursor;
#endif
};
#endif /* MSG_SCRN_D_MSG_SCRN_HOWL_H */
+18 -6
View File
@@ -487,19 +487,31 @@ public:
}
}
char* getPlayerName() const { return const_cast<char*>(mPlayerName); }
void setPlayerName(const char* i_name) { strcpy(mPlayerName, i_name); }
void setPlayerName(const char* i_name) {
#if AVOID_UB
strncpy(mPlayerName, i_name, sizeof(mPlayerName) - 1);
mPlayerName[sizeof(mPlayerName) - 1] = '\0';
#else
strcpy(mPlayerName, i_name);
#endif
}
char* getHorseName() const { return const_cast<char*>(mHorseName); }
void setHorseName(const char* i_name) { strcpy(mHorseName, i_name); }
void setHorseName(const char* i_name) {
#if AVOID_UB
strncpy(mHorseName, i_name, sizeof(mHorseName) - 1);
mHorseName[sizeof(mHorseName) - 1] = '\0';
#else
strcpy(mHorseName, i_name);
#endif
}
u8 getClearCount() const { return mClearCount; }
/* 0x00 */ BE(u64) unk0;
/* 0x08 */ BE(s64) mTotalTime;
/* 0x10 */ BE(u16) unk16;
/* 0x12 */ BE(u16) mDeathCount;
/* 0x14 */ char mPlayerName[16];
/* 0x24 */ u8 unk36;
/* 0x25 */ char mHorseName[16];
/* 0x35 */ u8 unk53;
/* 0x14 */ char mPlayerName[17];
/* 0x25 */ char mHorseName[17];
/* 0x36 */ u8 mClearCount;
/* 0x37 */ u8 unk55[5];
}; // Size: 0x40
+2
View File
@@ -34,6 +34,7 @@ void open_child(const void* key, int32_t id);
void close_child();
void record_camera(::camera_process_class* cam, int camera_id);
void record_final_mtx_raw(const Mtx* dest, const Mtx src);
void record_final_mtx_raw_tagged(const Mtx* dest, const Mtx src, uint64_t stable_tag);
bool lookup_replacement(const void* source, Mtx out);
bool lookup_concat_replacement(const void* lhs, const void* rhs, Mtx out);
@@ -41,6 +42,7 @@ bool lookup_concat_replacement(const void* lhs, const void* rhs, Mtx out);
void camera_eye_from_view_mtx(MtxP view_mtx, cXyz* o_eye);
bool build_star_view(Mtx o_view, Mtx o_cam_billboard_base, cXyz* o_anchor_eye, float* o_fovy);
uint64_t alloc_simple_shadow_pair_base();
} // namespace frame_interp
} // namespace dusk
#endif
+1
View File
@@ -66,6 +66,7 @@ struct UserSettings {
// Preferences
ConfigVar<bool> enableMirrorMode;
ConfigVar<bool> invertCameraXAxis;
ConfigVar<bool> disableMainHUD;
// Graphics
ConfigVar<BloomMode> bloomMode;
+1 -1
View File
@@ -393,7 +393,7 @@ static void waitPrecise(Limiter& limiter, Uint64 targetNs) {
static void waitForTick(u32 p1, u16 p2) {
#if TARGET_PC
if (dusk::getSettings().game.enableFrameInterpolation) {
if (dusk::getSettings().game.enableFrameInterpolation && !dusk::getTransientSettings().skipFrameRateLimit) {
return;
}
if (dusk::getTransientSettings().skipFrameRateLimit) {
+14
View File
@@ -268,6 +268,11 @@ int daBalloon2D_c::draw() {
int daBalloon2D_c::execute() {
setAllAlpha();
setComboAlpha();
#if TARGET_PC
updateOnWide();
#endif
setHIO(false);
return 1;
}
@@ -278,6 +283,15 @@ void daBalloon2D_c::drawMeter() {
drawAddScore();
}
#if TARGET_PC
void daBalloon2D_c::updateOnWide() {
// Combo HUD
l_HOSTIO = {};
l_HOSTIO.m.m2DNumberCombo2PosX = mDoGph_gInf_c::ScaleHUDXRight(l_HOSTIO.m.m2DNumberCombo2PosX);
l_HOSTIO.m.mAdjustmentToggle = 1;
}
#endif
void daBalloon2D_c::setComboCount(u8 size, u8 comboNum) {
if (mComboNum != comboNum) {
+12
View File
@@ -363,6 +363,18 @@ int daMyna_c::destroy() {
mpMorf->stopZelAnime();
}
#ifdef TARGET_PC
// !@bug d_a_myna.rel unload used to zero these file-statics; with static linking they dangle across scenes.
daMyna_LightActor = NULL;
daMyna_evtTagActor0 = NULL;
daMyna_evtTagActor1 = NULL;
daMyna_actor_count = 0;
for (int i = 0; i < 10; i++) {
daMyna_targetActor[i] = NULL;
daMyna_subActor[i] = NULL;
}
#endif
#if DEBUG
l_HOSTIO.removeHIO();
#endif
+11
View File
@@ -239,6 +239,17 @@ cPhs_Step daNpcTheB_c::create() {
fopAcM_ct(this, daNpcTheB_c);
cPhs_Step phase = dComIfG_resLoad(&mPhase, l_arcName);
#if TARGET_PC
// !@bug On PC (and presumably the WiiU version) during the wagon escort,
// Telma's main() can queue the PERSONAL_COMBAT_INTRO (Telma's initial dialog when the escort starts)
// before Link's create() sets getPlayer(0), so demoCheck drops the event and the dialog never shows up
if (phase == cPhs_COMPLEATE_e && dComIfGp_getPlayer(0) == NULL &&
strcmp(dComIfGp_getStartStageName(), "F_SP121") == 0 &&
dComIfG_play_c::getLayerNo(0) == 3)
{
return cPhs_INIT_e;
}
#endif
if (phase == cPhs_COMPLEATE_e) {
if (!fopAcM_entrySolidHeap(this, createHeapCallBack, 0x2CB0)) {
return cPhs_ERROR_e;
+19 -12
View File
@@ -1,28 +1,29 @@
#include "d/dolzel.h" // IWYU pragma: keep
#include <cstdio>
#include "JSystem/J2DGraph/J2DAnimation.h"
#include "JSystem/J2DGraph/J2DGrafContext.h"
#include "JSystem/J2DGraph/J2DScreen.h"
#include "JSystem/J3DGraphBase/J3DDrawBuffer.h"
#include "JSystem/JKernel/JKRHeap.h"
#include "SSystem/SComponent/c_bg_s_shdw_draw.h"
#include "SSystem/SComponent/c_math.h"
#include "d/d_com_inf_game.h"
#include "d/d_drawlist.h"
#include <typeindex>
#include "absl/container/flat_hash_map.h"
#include "client/TracyScoped.hpp"
#include "d/d_s_play.h"
#include "dusk/frame_interpolation.h"
#include "dusk/gx_helper.h"
#include "dusk/logging.h"
#include "m_Do/m_Do_graphic.h"
#include "m_Do/m_Do_lib.h"
#include "m_Do/m_Do_mtx.h"
#if TARGET_PC
#include <cstdio>
#include <typeindex>
#include "JSystem/JKernel/JKRHeap.h"
#include "absl/container/flat_hash_map.h"
#include "client/TracyScoped.hpp"
#include "dusk/frame_interpolation.h"
#include "dusk/gx_helper.h"
#include "dusk/logging.h"
#endif
class dDlst_2Dm_c {
public:
virtual void draw();
@@ -1432,8 +1433,14 @@ void dDlst_shadowSimple_c::set(cXyz* param_0, f32 param_1, f32 param_2, cXyz* pa
mDoMtx_stack_c::scaleM(param_2, 1.0f, param_2 * param_5);
cMtx_concat(j3dSys.getViewMtx(), mDoMtx_stack_c::get(), mMtx);
#ifdef TARGET_PC
dusk::frame_interp::record_final_mtx_raw(&mVolumeMtx, mVolumeMtx);
dusk::frame_interp::record_final_mtx_raw(&mMtx, mMtx);
const uint64_t shadow_tag_base = dusk::frame_interp::alloc_simple_shadow_pair_base();
if (shadow_tag_base != 0) {
dusk::frame_interp::record_final_mtx_raw_tagged(&mVolumeMtx, mVolumeMtx, shadow_tag_base);
dusk::frame_interp::record_final_mtx_raw_tagged(&mMtx, mMtx, shadow_tag_base + 1u);
} else {
dusk::frame_interp::record_final_mtx_raw(&mVolumeMtx, mVolumeMtx);
dusk::frame_interp::record_final_mtx_raw(&mMtx, mMtx);
}
#endif
mpTexObj = param_6;
}
+7
View File
@@ -423,6 +423,13 @@ void dMeter2_c::setLifeZero() {
void dMeter2_c::checkStatus() {
mStatus = 0;
#if TARGET_PC
if (dusk::getSettings().game.disableMainHUD) {
mStatus |= 0xF0000000;
}
#endif
field_0x12c = field_0x128;
field_0x128 = daPy_py_c::checkNowWolf();
+6 -1
View File
@@ -2289,7 +2289,8 @@ dMeter_drawHIO_c::dMeter_drawHIO_c() {
#if WIDESCREEN_SUPPORT
void dMeter_drawHIO_c::updateOnWide() {
#if TARGET_PC
g_drawHIO = {}; // this might be a bad idea
// Main HUD
g_drawHIO = {};
g_drawHIO.mMainHUDButtonsPosX = mDoGph_gInf_c::ScaleHUDXRight(g_drawHIO.mMainHUDButtonsPosX);
g_drawHIO.mRingHUDButtonsPosX = mDoGph_gInf_c::ScaleHUDXRight(g_drawHIO.mRingHUDButtonsPosX);
@@ -2301,6 +2302,10 @@ void dMeter_drawHIO_c::updateOnWide() {
g_drawHIO.mButtonCrossONPosX = mDoGph_gInf_c::ScaleHUDXLeft(g_drawHIO.mButtonCrossONPosX);
g_drawHIO.mLifeGaugePosX = mDoGph_gInf_c::ScaleHUDXLeft(g_drawHIO.mLifeGaugePosX);
g_drawHIO.mLanternMeterPosX = mDoGph_gInf_c::ScaleHUDXLeft(g_drawHIO.mLanternMeterPosX);
// River Canoe Minigame
g_drawHIO.mMiniGame.mCounterPosX[1] = mDoGph_gInf_c::ScaleHUDXRight(g_drawHIO.mMiniGame.mCounterPosX[1]);
g_drawHIO.mMiniGame.mIconPosX[1] = mDoGph_gInf_c::ScaleHUDXRight(g_drawHIO.mMiniGame.mIconPosX[1]);
#endif
}
+24 -1
View File
@@ -19,6 +19,10 @@
#include "JSystem/J2DGraph/J2DScreen.h"
#include "Z2AudioLib/Z2WolfHowlMgr.h"
#if TARGET_PC
#include "dusk/settings.h"
#endif
// POSIX already defines a macro with this name, but we know that this specific name is
// used in TP based on assertion messages. This redefinition is scoped to this TU which
// is unlikely to ever actually need the POSIX define, so we can just redefine it.
@@ -307,6 +311,17 @@ void dMsgScrnHowl_c::exec() {
mpButtonIcon[1]->setAlphaRate(field_0x1994 * alphaRate);
mpButtonText[0]->setAlphaRate(field_0x1998 * alphaRate);
mpButtonText[1]->setAlphaRate(field_0x1998 * alphaRate);
#if TARGET_PC
showCursor = true;
if (field_0x2798 == 0) {
if (mPlotTime != field_0x212c) {
field_0x212c = mPlotTime;
} else {
showCursor = false;
}
}
#endif
}
void dMsgScrnHowl_c::drawSelf() {
@@ -490,6 +505,7 @@ void dMsgScrnHowl_c::drawWave() {
grafContext->setScissor();
#endif
#if !TARGET_PC
bool bVar5 = true;
if (field_0x2798 == 0) {
if (mPlotTime != field_0x212c) {
@@ -498,7 +514,14 @@ void dMsgScrnHowl_c::drawWave() {
bVar5 = false;
}
}
if (bVar5) {
#endif
#if TARGET_PC
if (showCursor)
#else
if (bVar5)
#endif
{
for (int iVar10 = 0; iVar10 < field_0x2128 - 1; iVar10++) {
f32 local_54 = local_e0;
f32 local_c8 = field_0x180[sVar14];
+28 -10
View File
@@ -23,6 +23,10 @@
#include "m_Do/m_Do_lib.h"
#include <cstring>
#if TARGET_PC
#include "dusk/frame_interpolation.h"
#endif
static int dTimer_createStart2D(s32 param_0, u16 param_1);
int dTimer_c::_create() {
@@ -1336,29 +1340,44 @@ void dDlst_TimerScrnDraw_c::draw() {
f32 temp = (f32)g_drawHIO.mMiniGame.mGetInTextAlphaFrames +
((f32)g_drawHIO.mMiniGame.mGetInTextWaitFrames + 60.0f);
#if TARGET_PC
const u32 pending_ui_ticks = dusk::frame_interp::get_presentation_ui_advance_ticks();
#else
const u32 pending_ui_ticks = 1u;
#endif
for (int i = 0; i < 51; i++) {
if (m_getin_info[i].bck_frame > 0.0f && m_getin_info[i].bck_frame < temp) {
f32 var_f29 = 1.0f;
for (u32 tick = 0; tick < pending_ui_ticks; tick++) {
if (!(m_getin_info[i].bck_frame > 0.0f && m_getin_info[i].bck_frame < temp)) {
break;
}
if (m_getin_info[i].bck_frame < 60.0f) {
m_getin_info[i].bck_frame += g_drawHIO.mMiniGame.mGetInTextAnimSpeed;
if (m_getin_info[i].bck_frame > 60.0f) {
m_getin_info[i].bck_frame = 60.0f;
}
playBckAnimation(m_getin_info[i].bck_frame);
} else if (m_getin_info[i].bck_frame < g_drawHIO.mMiniGame.mGetInTextWaitFrames + 60.0f)
{
} else if (m_getin_info[i].bck_frame < g_drawHIO.mMiniGame.mGetInTextWaitFrames + 60.0f) {
m_getin_info[i].bck_frame++;
playBckAnimation(60.0f);
} else if (m_getin_info[i].bck_frame < temp) {
m_getin_info[i].bck_frame++;
playBckAnimation(60.0f);
}
}
if (m_getin_info[i].bck_frame > 0.0f && m_getin_info[i].bck_frame < temp) {
f32 var_f29 = 1.0f;
if (m_getin_info[i].bck_frame >= g_drawHIO.mMiniGame.mGetInTextWaitFrames + 60.0f &&
m_getin_info[i].bck_frame < temp) {
var_f29 = acc(g_drawHIO.mMiniGame.mGetInTextAlphaFrames,
temp - m_getin_info[i].bck_frame, 0);
}
if (m_getin_info[i].bck_frame < 60.0f) {
playBckAnimation(m_getin_info[i].bck_frame);
} else {
playBckAnimation(60.0f);
}
mpGetInParent->setAlphaRate(var_f29);
if (g_drawHIO.mMiniGame.mGetInTextLocation == 1) {
@@ -1388,8 +1407,7 @@ void dDlst_TimerScrnDraw_c::draw() {
m_getin_info[i].pikari_frame =
18.0f - g_drawHIO.mMiniGame.mGetInPikariAnimSpeed;
}
} else if (m_getin_info[i].bck_frame > g_drawHIO.mMiniGame.mStartPikariAppearFrames)
{
} else if (m_getin_info[i].bck_frame > g_drawHIO.mMiniGame.mStartPikariAppearFrames) {
m_getin_info[i].pikari_frame =
18.0f - g_drawHIO.mMiniGame.mStartPikariAnimSpeed;
}
+56 -8
View File
@@ -28,6 +28,7 @@ struct Data {
size_t child_index = 0;
Mtx matrix{};
const Mtx* dest = nullptr;
uint64_t stable_tag = 0;
};
struct Path;
@@ -46,6 +47,8 @@ struct Path {
std::vector<ChildBucket> children;
std::vector<OpBucket> ops;
std::vector<std::pair<Op, size_t>> items;
Label draw_scope{};
uint32_t simple_shadow_pair_seq = 0;
};
struct Recording {
@@ -57,6 +60,7 @@ struct MatrixValue {
};
using FinalMtxLookup = std::unordered_map<const Mtx*, const Data*>;
using FinalMtxLookupTagged = std::unordered_map<uint64_t, const Data*>;
bool s_initialized = false;
@@ -142,8 +146,9 @@ const OpBucket* find_op_bucket(const Path& path, Op op) {
return &*it;
}
void build_final_mtx_lookup(const Path& path, FinalMtxLookup& lookup) {
lookup.clear();
void build_final_mtx_lookups(const Path& path, FinalMtxLookup& dest_lookup, FinalMtxLookupTagged& tag_lookup) {
dest_lookup.clear();
tag_lookup.clear();
const OpBucket* bucket = find_op_bucket(path, Op::FinalMtx);
if (bucket == nullptr) {
@@ -151,10 +156,12 @@ void build_final_mtx_lookup(const Path& path, FinalMtxLookup& lookup) {
}
for (const Data& data : bucket->values) {
if (data.dest == nullptr) {
continue;
if (data.dest != nullptr) {
dest_lookup[data.dest] = &data;
}
if (data.stable_tag != 0) {
tag_lookup[data.stable_tag] = &data;
}
lookup[data.dest] = &data;
}
}
@@ -201,7 +208,8 @@ void store_replacement(const Data& old_data, const Data& new_data, float step) {
void interpolate_branch(const Path& old_path, const Path& new_path, float step) {
FinalMtxLookup old_final_mtx_lookup;
build_final_mtx_lookup(old_path, old_final_mtx_lookup);
FinalMtxLookupTagged old_final_mtx_lookup_tagged;
build_final_mtx_lookups(old_path, old_final_mtx_lookup, old_final_mtx_lookup_tagged);
for (const auto& item : new_path.items) {
const Op op = item.first;
@@ -230,7 +238,17 @@ void interpolate_branch(const Path& old_path, const Path& new_path, float step)
}
const Data* indexed_old_data = find_matching_data(old_path, op, index);
const Data* old_data = op == Op::FinalMtx ? find_matching_final_mtx(old_final_mtx_lookup, *new_data) : indexed_old_data;
const Data* old_data = nullptr;
if (op == Op::FinalMtx) {
if (new_data->stable_tag != 0) {
const auto it = old_final_mtx_lookup_tagged.find(new_data->stable_tag);
old_data = it != old_final_mtx_lookup_tagged.end() ? it->second : nullptr;
} else {
old_data = find_matching_final_mtx(old_final_mtx_lookup, *new_data);
}
} else {
old_data = indexed_old_data;
}
if (op == Op::FinalMtx) {
store_replacement(old_data != nullptr ? *old_data : *new_data, *new_data, step);
}
@@ -370,7 +388,9 @@ void open_child(const void* key, int32_t id) {
data.child_label = label;
data.child_index = siblings.size();
siblings.emplace_back(std::make_unique<Path>());
g_current_path.push_back(siblings.back().get());
Path* const child = siblings.back().get();
child->draw_scope = label;
g_current_path.push_back(child);
}
void close_child() {
@@ -388,6 +408,18 @@ void record_final_mtx_raw(const Mtx* dest, const Mtx src) {
Data& data = append_op(Op::FinalMtx);
data.dest = dest;
data.stable_tag = 0;
copy_matrix(src, data.matrix);
}
void record_final_mtx_raw_tagged(const Mtx* dest, const Mtx src, uint64_t stable_tag) {
if (!s_initialized || !g_recording || dest == nullptr) {
return;
}
Data& data = append_op(Op::FinalMtx);
data.dest = dest;
data.stable_tag = stable_tag;
copy_matrix(src, data.matrix);
}
@@ -501,4 +533,20 @@ bool build_star_view(Mtx o_view, Mtx o_cam_billboard_base, cXyz* o_anchor_eye, f
*o_fovy = s_star_prev.fovy + (s_star_curr.fovy - s_star_prev.fovy) * step;
return true;
}
uint64_t alloc_simple_shadow_pair_base() {
if (!s_initialized || !g_recording || g_current_path.size() <= 1) {
return 0;
}
Path* const scope = g_current_path.back();
const uint64_t h = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(scope->draw_scope.key));
const uint32_t lo = scope->simple_shadow_pair_seq;
scope->simple_shadow_pair_seq += 2;
uint64_t tag0 = (h << 17) ^ (static_cast<uint64_t>(lo) << 1u);
if (tag0 == 0) {
tag0 = 2;
}
return tag0;
}
} // namespace dusk::frame_interp
+7 -1
View File
@@ -40,9 +40,15 @@ namespace dusk {
void ImGuiTextCenter(std::string_view text) {
ImGui::NewLine();
float fontSize = ImGui::CalcTextSize(text.data(), text.data() + text.size()).x;
float fontSize = ImGui::CalcTextSize(
text.data(),
text.data() + text.size(),
false,
ImGui::GetWindowSize().x).x;
ImGui::SameLine(ImGui::GetWindowSize().x / 2 - fontSize + fontSize / 2);
ImGui::PushTextWrapPos(ImGui::GetWindowSize().x);
ImGuiStringViewText(text);
ImGui::PopTextWrapPos();
}
bool ImGuiButtonCenter(std::string_view text) {
+67 -65
View File
@@ -9,101 +9,117 @@ namespace dusk {
void ImGuiMenuEnhancements::draw() {
if (ImGui::BeginMenu("Enhancements")) {
if (ImGui::BeginMenu("Quality of Life")) {
config::ImGuiCheckbox("Quick Transform (R+Y)", getSettings().game.enableQuickTransform);
if (ImGui::BeginMenu("Gameplay")) {
ImGui::SeparatorText("Preferences");
config::ImGuiCheckbox("Mirror Mode", getSettings().game.enableMirrorMode);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Allows you to quickly transform between forms\n"
"without having to talk to Midna.");
ImGui::SetTooltip("Mirrors the world horizontally, matching the Wii version of the game.");
}
config::ImGuiCheckbox("Sun's Song (R+X)", getSettings().game.sunsSong);
config::ImGuiCheckbox("Disable Main HUD", getSettings().game.disableMainHUD);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Allows Wolf Link to howl and change the time of day.");
ImGui::SetTooltip("Disables the main HUD of the game.\n"
"Useful for recording or a more immersive experience!");
}
ImGui::SeparatorText("Difficulty");
config::ImGuiSliderInt("Damage Multiplier", getSettings().game.damageMultiplier, 1, 8, "x%d");
config::ImGuiCheckbox("Instant Death", getSettings().game.instantDeath);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Any hit will instantly kill you.");
}
config::ImGuiCheckbox("No Heart Drops", getSettings().game.noHeartDrops);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Hearts will never drop from enemies,\n"
"pots and various other places.");
}
ImGui::SeparatorText("Quality of Life");
config::ImGuiCheckbox("Bigger Wallets", getSettings().game.biggerWallets);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Wallet sizes are like in the HD version. (500, 1000, 2000)");
}
config::ImGuiCheckbox("No Rupee Returns", getSettings().game.noReturnRupees);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Always collect Rupees even if your Wallet is too full.");
}
config::ImGuiCheckbox("Disable Rupee Cutscenes", getSettings().game.disableRupeeCutscenes);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Rupees won't play cutscenes after you've collected them the first time.");
}
config::ImGuiCheckbox("No Sword Recoil", getSettings().game.noSwordRecoil);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Link won't recoil when his sword hits walls.");
}
config::ImGuiCheckbox("Faster Climbing", getSettings().game.fastClimbing);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Quicker climbing on ladders and vines like the HD version.");
}
config::ImGuiCheckbox("Faster Tears of Light", getSettings().game.fastTears);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Tears of Light dropped by Shadow Insects pop out faster like the HD version.");
}
config::ImGuiCheckbox("Instant Saves", getSettings().game.instantSaves);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Skip the delay when writing to the Memory Card.");
}
config::ImGuiCheckbox("No Climbing Miss Animation", getSettings().game.noMissClimbing);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Prevents Link from playing a struggle animation\n"
"when grabbing ledges or climbing on vines.");
}
config::ImGuiCheckbox("Faster Tears of Light", getSettings().game.fastTears);
config::ImGuiCheckbox("No Rupee Returns", getSettings().game.noReturnRupees);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Tears of Light dropped by Shadow Insects pop out faster like the HD version.");
ImGui::SetTooltip("Always collect Rupees even if your Wallet is too full.");
}
config::ImGuiCheckbox("Hide TV Settings Screen", getSettings().game.hideTvSettingsScreen);
config::ImGuiCheckbox("No Sword Recoil", getSettings().game.noSwordRecoil);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Hides the TV calibration screen shown when loading a save.");
ImGui::SetTooltip("Link won't recoil when his sword hits walls.");
}
config::ImGuiCheckbox("Skip TV Settings Screen", getSettings().game.hideTvSettingsScreen);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Skip the TV calibration screen shown when loading a save.");
}
config::ImGuiCheckbox("Skip Warning Screen", getSettings().game.skipWarningScreen);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Skips the warning screen shown when loading the game.");
ImGui::SetTooltip("Skip the warning screen shown when starting the game.");
}
config::ImGuiCheckbox("Instant Saves", getSettings().game.instantSaves);
config::ImGuiCheckbox("Sun's Song (R+X)", getSettings().game.sunsSong);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Skip the delay when writing to the Memory Card.");
ImGui::SetTooltip("Allows Wolf Link to howl and change the time of day.");
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Preferences")) {
config::ImGuiCheckbox("Mirror Mode", getSettings().game.enableMirrorMode);
config::ImGuiCheckbox("Quick Transform (R+Y)", getSettings().game.enableQuickTransform);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Mirrors the world, matching the Wii version of the game.");
ImGui::SetTooltip("Transform instantly by pressing R and Y simultaneously.");
}
config::ImGuiCheckbox("Invert Camera X Axis", getSettings().game.invertCameraXAxis);
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Graphics")) {
config::ImGuiSliderInt("Shadow Resolution", getSettings().game.shadowResolutionMultiplier, 1, 8, "x%d");
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Improves the shadow resolution, making them higher quality.");
}
config::ImGuiCheckbox("Unlock Framerate", getSettings().game.enableFrameInterpolation);
const bool frameInterpolationHovered = ImGui::IsItemHovered();
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.72f, 0.2f, 1.0f));
ImGui::TextUnformatted("[EXPERIMENTAL]");
ImGui::PopStyleColor();
if (frameInterpolationHovered || ImGui::IsItemHovered()) {
ImGui::SetTooltip("Uses inter-frame interpolation to enable higher frame rates.\nVisual artifacts, animation glitches, or instability may occur.");
}
config::ImGuiSliderInt("Shadow Resolution", getSettings().game.shadowResolutionMultiplier, 1, 8, "x%d");
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Improves the shadow resolution, making them higher quality.");
}
ImGui::EndMenu();
}
@@ -122,6 +138,10 @@ namespace dusk {
}
if (ImGui::BeginMenu("Input")) {
config::ImGuiCheckbox("Invert Camera X Axis", getSettings().game.invertCameraXAxis);
ImGui::SeparatorText("Gyro");
config::ImGuiCheckbox("Gyro Aim", getSettings().game.enableGyroAim);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Enables the gyroscope on supported controllers while aiming the\n"
@@ -133,9 +153,18 @@ namespace dusk {
config::ImGuiCheckbox("Invert Gyro Pitch", getSettings().game.gyroAimInvertPitch);
config::ImGuiCheckbox("Invert Gyro Yaw", getSettings().game.gyroAimInvertYaw);
ImGui::SeparatorText("Tools");
config::ImGuiCheckbox("Turbo Key", getSettings().game.enableTurboKeybind);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Hold TAB to increase game speed by up to 4x.");
}
ImGui::EndMenu();
}
ImGui::Separator();
if (ImGui::BeginMenu("Cheats")) {
config::ImGuiCheckbox("Fast Iron Boots", getSettings().game.enableFastIronBoots);
@@ -157,23 +186,6 @@ namespace dusk {
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Difficulty")) {
config::ImGuiSliderInt("Damage Multiplier", getSettings().game.damageMultiplier, 1, 8, "x%d");
config::ImGuiCheckbox("No Heart Drops", getSettings().game.noHeartDrops);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Hearts will never drop from enemies,\n"
"pots and various other places.");
}
config::ImGuiCheckbox("Instant Death", getSettings().game.instantDeath);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Any hit will instantly kill you.");
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Technical")) {
config::ImGuiCheckbox("Restore Wii 1.0 Glitches", getSettings().game.restoreWiiGlitches);
if (ImGui::IsItemHovered()) {
@@ -184,16 +196,6 @@ namespace dusk {
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Tools")) {
config::ImGuiCheckbox("Enable Turbo Key", getSettings().game.enableTurboKeybind);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Holding TAB will speed up the game.\n"
"This will not work with the \"Unlock Framerate\" enhancement.");
}
ImGui::EndMenu();
}
ImGui::EndMenu();
}
}
+38 -2
View File
@@ -7,6 +7,7 @@
#include "ImGuiConsole.hpp"
#include "dusk/main.h"
#include "dusk/settings.h"
#include "../iso_validate.hpp"
#include <SDL3/SDL_dialog.h>
#include <SDL3/SDL_error.h>
@@ -26,15 +27,42 @@ static constexpr std::array<SDL_DialogFileFilter, 2> skGameDiscFileFilters{{
{"All Files", "*"},
}};
static std::string ShowIsoInvalidError(const iso::ValidationError code) {
using namespace std::literals::string_literals;
switch (code) {
case iso::ValidationError::IOError:
return "Unknown IO error occurred"s;
case iso::ValidationError::InvalidImage:
return "Unable to interpret selected file as a disc image"s;
case iso::ValidationError::WrongGame:
return "Selected disc image is for a different game"s;
case iso::ValidationError::WrongVersion:
return "Selected disc image is for an unsupported version of the game. Only North American GameCube (NTSC/GZ2E01) is supported at this time."s;
case iso::ValidationError::ExecutableMismatch:
return "Selected disc image contains modified executable files."s;
default:
return "Unknown error"s;
}
}
void fileDialogCallback(void* userdata, const char* const* filelist, [[maybe_unused]] int filter) {
auto* self = static_cast<ImGuiPreLaunchWindow*>(userdata);
self->m_errorString.clear();
if (filelist != nullptr) {
if (filelist[0] == nullptr) {
// Cancelled
self->m_selectedIsoPath.clear();
} else {
self->m_selectedIsoPath = filelist[0];
getSettings().backend.isoPath.setValue(self->m_selectedIsoPath);
const auto path = filelist[0];
const auto ret = iso::validate(path);
if (ret != iso::ValidationError::Success) {
self->m_selectedIsoPath.clear();
self->m_errorString = std::move(ShowIsoInvalidError(ret));
return;
}
self->m_selectedIsoPath = path;
getSettings().backend.isoPath.setValue(path);
config::Save();
}
} else {
@@ -111,6 +139,10 @@ void ImGuiPreLaunchWindow::drawMainMenu() {
ImGui::PushFont(ImGuiEngine::fontLarge);
if (!isSelectedPathValid()) {
if (!m_errorString.empty()) {
ImGuiTextCenter(m_errorString);
}
if (ImGuiButtonCenter("Select disc image...")) {
SDL_ShowOpenFileDialog(&fileDialogCallback, this, aurora::window::get_sdl_window(),
skGameDiscFileFilters.data(), int(skGameDiscFileFilters.size()),
@@ -148,6 +180,10 @@ void ImGuiPreLaunchWindow::drawOptions() {
if (ImGui::BeginChild("OptionsChild", ImVec2(childWidth, endCursorY - cursorY),
ImGuiChildFlags_None, ImGuiWindowFlags_NoBackground))
{
if (!m_errorString.empty()) {
ImGuiTextCenter(m_errorString);
}
ImGui::InputText("Game ISO Path", &m_selectedIsoPath, ImGuiInputTextFlags_ReadOnly);
ImGui::SameLine();
if (ImGui::Button("Set")) {
+67 -13
View File
@@ -445,7 +445,7 @@ namespace dusk {
}
if (ImGui::BeginTabItem("Minigame")) {
//DrawFlagsTab();
drawMinigameTab();
ImGui::EndTabItem();
}
@@ -829,8 +829,8 @@ namespace dusk {
ImGuiBeginGroupPanel("Items", { 200, 100 });
for (int slot = 0; slot < 24; slot++) {
ImGui::Text("Slot %02d: ", slot);
ImGui::SameLine();
ImGui::Text("Slot %02d (%s): ", slot, itemMap.find(getSlotDefault(slot))->second.m_name.c_str());
ImGui::SameLine(240.0f);
if (ImGui::BeginCombo(fmt::format("##ItemComboBox{}", slot).c_str(), itemMap.find(item.mItems[slot])->second.m_name.c_str())) {
if (ImGui::Selectable("None")) {
dComIfGs_setItem(slot, dItemNo_NONE_e);
@@ -856,7 +856,27 @@ namespace dusk {
}
ImGuiEndGroupPanel();
dSv_player_item_record_c& itemRecord = dComIfGs_getSaveData()->getPlayer().getItemRecord();
dSv_player_item_max_c& itemMax = dComIfGs_getSaveData()->getPlayer().getItemMax();
ImGuiBeginGroupPanel("Item Max Capacities", { 200, 100 });
ImGui::InputScalar("Arrows Max", ImGuiDataType_U8, &itemMax.mItemMax[0]);
ImGui::InputScalar("Normal Bombs Max", ImGuiDataType_U8, &itemMax.mItemMax[1]);
ImGui::InputScalar("Water Bombs Max", ImGuiDataType_U8, &itemMax.mItemMax[2]);
ImGui::InputScalar("Bomblings Max", ImGuiDataType_U8, &itemMax.mItemMax[3]);
ImGuiEndGroupPanel();
ImGuiBeginGroupPanel("Item Amounts", { 200, 100 });
ImGui::InputScalar("Arrows Amount", ImGuiDataType_U8, &itemRecord.mArrowNum);
ImGui::InputScalar("Slingshot Amount", ImGuiDataType_U8, &itemRecord.mPachinkoNum);
ImGui::InputScalar("Bomb Bag 1 Amount", ImGuiDataType_U8, &itemRecord.mBombNum[0]);
ImGui::InputScalar("Bomb Bag 2 Amount", ImGuiDataType_U8, &itemRecord.mBombNum[1]);
ImGui::InputScalar("Bomb Bag 3 Amount", ImGuiDataType_U8, &itemRecord.mBombNum[2]);
ImGui::InputScalar("Bottle 1 Amount", ImGuiDataType_U8, &itemRecord.mBottleNum[0]);
ImGui::InputScalar("Bottle 2 Amount", ImGuiDataType_U8, &itemRecord.mBottleNum[1]);
ImGui::InputScalar("Bottle 3 Amount", ImGuiDataType_U8, &itemRecord.mBottleNum[2]);
ImGui::InputScalar("Bottle 4 Amount", ImGuiDataType_U8, &itemRecord.mBottleNum[3]);
ImGuiEndGroupPanel();
}
static inline void setItemFirstBit(u8 itemNo, bool owned) {
@@ -902,27 +922,42 @@ namespace dusk {
void ImGuiSaveEditor::drawCollectionTab() {
if (ImGui::TreeNode("Equipment")) {
if (ImGui::TreeNode("Swords")) {
static u8 sword_list[] = {
dItemNo_SWORD_e,
dItemNo_MASTER_SWORD_e,
dItemNo_WOOD_STICK_e,
dItemNo_LIGHT_SWORD_e,
};
for (int i = 0; i < 4; i++) {
bool got = dComIfGs_isCollectSword((u8)i) != 0;
bool got = dComIfGs_isItemFirstBit(sword_list[i]) != 0;
if (ImGui::Checkbox(fmt::format("{0}##sword_{1}", sSwordNames[i], i).c_str(), &got)) {
if (got) dComIfGs_setCollectSword((u8)i);
else dComIfGs_offCollectSword((u8)i);
if (got) dComIfGs_onItemFirstBit(sword_list[i]);
else dComIfGs_offItemFirstBit(sword_list[i]);
}
}
ImGui::TreePop();
}
if (ImGui::TreeNode("Shields")) {
static u8 shield_list[] = {
dItemNo_SHIELD_e,
dItemNo_WOOD_SHIELD_e,
dItemNo_HYLIA_SHIELD_e,
};
for (int i = 0; i < 3; i++) {
bool got = dComIfGs_isCollectShield((u8)i) != 0;
bool got = dComIfGs_isItemFirstBit(shield_list[i]) != 0;
if (ImGui::Checkbox(fmt::format("{0}##shield_{1}", sShieldNames[i], i).c_str(), &got)) {
if (got) dComIfGs_setCollectShield((u8)i);
else dComIfGs_offCollectShield((u8)i);
if (got) dComIfGs_onItemFirstBit(shield_list[i]);
else dComIfGs_offItemFirstBit(shield_list[i]);
}
}
ImGui::TreePop();
}
// TODO: the game checks if you're in ranch clothes, and if so won't display any other tunics in the menu
// find a way to deal with this later
if (ImGui::TreeNode("Tunics")) {
bool ordonClothes = dComIfGs_isItemFirstBit(dItemNo_WEAR_CASUAL_e) != 0;
if (ImGui::Checkbox("Ordon Clothes##tunic_ordon", &ordonClothes)) {
@@ -1235,7 +1270,7 @@ namespace dusk {
}
ImGuiEndGroupPanel();
ImVec2 cursor = ImGui::GetCursorPos();
ImVec2 start_cursor = ImGui::GetCursorPos();
ImGui::SameLine();
@@ -1245,13 +1280,17 @@ namespace dusk {
}
ImGuiEndGroupPanel();
ImGui::SetCursorPos(cursor);
ImVec2 cursor = ImGui::GetCursorPos();
ImGui::SetCursorPos(start_cursor);
ImGuiBeginGroupPanel("Item", { 100, 100 });
for (int j = 0; j < 1; j++) {
drawFlagList(fmt::format("##_item{}", j).c_str(), membit.mItem[j]);
}
ImGuiEndGroupPanel();
ImGui::SetCursorPos({ start_cursor.x, cursor.y });
}
void ImGuiSaveEditor::drawFlagsTab() {
@@ -1328,7 +1367,7 @@ namespace dusk {
dSv_event_c& event = dComIfGs_getSaveData()->mEvent;
for (int e = 0; e < 255; e++) {
ImGui::Text("%03d ", e);
ImGui::SameLine();
ImGui::SameLine(80.0f);
for (int i = 7; i >= 0; i--) {
bool flag = event.mEvent[e] & (1 << i);
if (ImGui::Checkbox(fmt::format("##event{0}{1}", e, i).c_str(), &flag)) {
@@ -1345,10 +1384,25 @@ namespace dusk {
}
}
void ImGuiSaveEditor::drawMinigameTab() {
dSv_MiniGame_c& minigame = dComIfGs_getSaveData()->getMiniGame();
InputScalarBE("STAR Game Time (Milliseconds)", ImGuiDataType_U32, &minigame.mHookGameTime);
InputScalarBE("Snowboard Race Time (Milliseconds)", ImGuiDataType_U32, &minigame.mRaceGameTime);
InputScalarBE("Fruit-Pop-Flight Score", ImGuiDataType_U32, &minigame.mBalloonScore);
}
void ImGuiSaveEditor::drawConfigTab() {
dSv_player_config_c& config = dComIfGs_getSaveData()->getPlayer().getConfig();
ImGui::Checkbox("Enable Vibration", (bool*)&config.mVibration);
if (ImGui::BeginCombo("Target Type", "Hold")) {
static const char* kTargetTypeNames[] = {"Hold", "Switch"};
if (ImGui::BeginCombo("Target Type", kTargetTypeNames[config.mAttentionType])) {
if (ImGui::Selectable("Hold")) {
config.mAttentionType = 0;
}
if (ImGui::Selectable("Switch")) {
config.mAttentionType = 1;
}
ImGui::EndCombo();
}
+1
View File
@@ -17,6 +17,7 @@ namespace dusk {
void drawInventoryTab();
void drawCollectionTab();
void drawFlagsTab();
void drawMinigameTab();
void drawConfigTab();
private:
+126
View File
@@ -0,0 +1,126 @@
#include "iso_validate.hpp"
#include <nod.h>
#include <span>
#include "SDL3/SDL_iostream.h"
namespace dusk::iso {
constexpr const char* TP_GAME_IDS[] = {
"GZ2E01", // GCN USA
"GZ2P01", // GCN PAL
"GZ2J01", // GCN JPN
"RZDE01", // Wii USA
"RZDP01", // Wii PAL
"RZDJ01", // Wii JPN
"RZDK01", // Wii KOR
};
constexpr const char* SUPPORTED_TP_GAME_IDS[] = {
"GZ2E01", // GCN USA
};
template <size_t N>
constexpr bool matches(const char (&id)[6], const char* const (&valid)[N]) {
for (auto elem : valid) {
if (strncmp(id, elem, 6) == 0) {
return true;
}
}
return false;
}
struct NodHandleWrapper {
NodHandle* handle;
NodHandleWrapper() : handle(nullptr) {
}
~NodHandleWrapper() {
if (handle != nullptr) {
nod_free(handle);
handle = nullptr;
}
}
};
static ValidationError convertNodError(NodResult result) {
switch (result) {
case NOD_RESULT_ERR_IO:
return ValidationError::IOError;
case NOD_RESULT_ERR_FORMAT:
return ValidationError::InvalidImage;
default:
return ValidationError::Unknown;
}
}
s64 StreamReadAt(void* user_data, u64 offset, void* out, size_t len) {
if (len == 0) {
return 0;
}
auto io = static_cast<SDL_IOStream*>(user_data);
auto ret = SDL_SeekIO(io, static_cast<s64>(offset), SDL_IO_SEEK_SET);
if (ret < 0) {
return -1;
}
auto read = SDL_ReadIO(io, out, len);
if (read == 0) {
if (SDL_GetIOStatus(io) == SDL_IO_STATUS_EOF) {
return 0;
}
return -1;
}
return static_cast<s64>(read);
}
s64 StreamLength(void* user_data) {
auto io = static_cast<SDL_IOStream*>(user_data);
return SDL_GetIOSize(io);
}
void StreamClose(void* user_data) {
auto io = static_cast<SDL_IOStream*>(user_data);
SDL_CloseIO(io);
}
ValidationError validate(const char* path) {
NodHandleWrapper disc;
const auto sdlStream = SDL_IOFromFile(path, "rb");
const NodDiscStream nod_stream {
.user_data = sdlStream,
.read_at = StreamReadAt,
.stream_len = StreamLength,
.close = StreamClose,
};
auto result = nod_disc_open_stream(&nod_stream, nullptr, &disc.handle);
if (disc.handle == nullptr || result != NOD_RESULT_OK) {
return convertNodError(result);
}
NodDiscHeader header{};
result = nod_disc_header(disc.handle, &header);
if (result != NOD_RESULT_OK) {
return convertNodError(result);
}
if (!matches(header.game_id, TP_GAME_IDS)) {
return ValidationError::WrongGame;
}
if (!matches(header.game_id, SUPPORTED_TP_GAME_IDS)) {
return ValidationError::WrongVersion;
}
return ValidationError::Success;
}
} // namespace dusk::iso
+18
View File
@@ -0,0 +1,18 @@
#ifndef DUSK_ISO_VALIDATE_HPP
#define DUSK_ISO_VALIDATE_HPP
namespace dusk::iso {
enum class ValidationError : u8 {
Success = 0,
IOError,
InvalidImage,
WrongGame,
WrongVersion,
ExecutableMismatch,
Unknown
};
ValidationError validate(const char* path);
}
#endif // DUSK_ISO_VALIDATE_HPP
+2
View File
@@ -40,6 +40,7 @@ UserSettings g_userSettings = {
// Preferences
.enableMirrorMode {"game.enableMirrorMode", false},
.invertCameraXAxis {"game.invertCameraXAxis", false},
.disableMainHUD {"game.disableMainHUD", false},
// Graphics
.bloomMode {"game.bloomMode", BloomMode::Classic},
@@ -117,6 +118,7 @@ void registerSettings() {
Register(g_userSettings.game.sunsSong);
Register(g_userSettings.game.enableMirrorMode);
Register(g_userSettings.game.invertCameraXAxis);
Register(g_userSettings.game.disableMainHUD);
Register(g_userSettings.game.bloomMode);
Register(g_userSettings.game.bloomMultiplier);
Register(g_userSettings.game.enableWaterRefraction);
+3 -4
View File
@@ -64,13 +64,12 @@ void fpcM_Management(fpcM_ManagementFunc i_preExecuteFn, fpcM_ManagementFunc i_p
}
#ifdef TARGET_PC
// Frame interpolation: call moved to m_Do_main
if (!dusk::getSettings().game.enableFrameInterpolation) {
// FRAME INTERP NOTE: Called in m_Do_main when interp is enabled
if (!dusk::getSettings().game.enableFrameInterpolation || dusk::getTransientSettings().skipFrameRateLimit)
#endif
{
cAPIGph_Painter();
#ifdef TARGET_PC
}
#endif
if (!dPa_control_c::isStatus(1)) {
fpcDt_Handler();
+2 -2
View File
@@ -2383,8 +2383,8 @@ void mDoExt_3DlineMat0_c::draw() {
int var_r26 = (field_0x14 << 1) & 0xFFFF;
for (int i = 0; i < field_0x10; i++) {
GXSETARRAY(GX_VA_POS, field_0x18->field_0x8[field_0x16], sizeof(cXyz) * var_r26, sizeof(cXyz), true);
GXSETARRAY(GX_VA_NRM, field_0x18->field_0x10[field_0x16], 3 * var_r26, 3, true);
GXSETARRAY(GX_VA_POS, var_r28->field_0x8[field_0x16], sizeof(cXyz) * var_r26, sizeof(cXyz), true);
GXSETARRAY(GX_VA_NRM, var_r28->field_0x10[field_0x16], 3 * var_r26, 3, true);
GXBegin(GX_TRIANGLESTRIP, GX_VTXFMT0, var_r26);
for (u16 j = 0; j < (u16)var_r26; j++) {
+5 -7
View File
@@ -2034,12 +2034,11 @@ int mDoGph_Painter() {
#endif
#ifdef TARGET_PC
for (u32 i = 0; i < pending_ui_ticks; ++i) {
for (u32 i = 0; i < pending_ui_ticks; ++i)
#endif
{
dComIfGp_particle_calcMenu();
#ifdef TARGET_PC
}
#endif
JFWDisplay::getManager()->setFader(mDoGph_gInf_c::getFader());
mDoGph_gInf_c::setClearColor(mDoGph_gInf_c::getBackColor());
@@ -2599,13 +2598,12 @@ int mDoGph_Painter() {
#endif
GXSetClipMode(GX_CLIP_ENABLE);
#ifdef TARGET_PC
for (u32 i = 0; i < pending_ui_ticks; ++i) {
#if TARGET_PC
for (u32 i = 0; i < pending_ui_ticks; ++i)
#endif
{
dDlst_list_c::calcWipe();
#ifdef TARGET_PC
}
#endif
j3dSys.reinitGX();
ortho.setOrtho(mDoGph_gInf_c::getMinXF(), mDoGph_gInf_c::getMinYF(),
+5 -9
View File
@@ -202,9 +202,8 @@ void main01(void) {
if (preLaunchUIWindowSize.width != 0)
mDoGph_gInf_c::setWindowSize(preLaunchUIWindowSize);
using clock = std::chrono::steady_clock;
constexpr double kSimStepSeconds = 1.0 / 30.0;
auto previous_time = clock::now();
auto previous_time = std::chrono::steady_clock::now();
double accumulator = kSimStepSeconds;
do {
@@ -229,7 +228,7 @@ void main01(void) {
eventsDone:;
auto current_time = clock::now();
auto current_time = std::chrono::steady_clock::now();
double frame_seconds = std::chrono::duration<double>(current_time - previous_time).count();
previous_time = current_time;
accumulator += frame_seconds;
@@ -242,9 +241,8 @@ void main01(void) {
continue;
}
if (dusk::getSettings().game.enableFrameInterpolation) {
if (dusk::getSettings().game.enableFrameInterpolation && !dusk::getTransientSettings().skipFrameRateLimit) {
dusk::frame_interp::notify_presentation_frame();
while (accumulator >= kSimStepSeconds) {
mDoCPd_c::read();
if (dusk::getSettings().game.enableGyroAim) {
@@ -254,13 +252,11 @@ void main01(void) {
mDoAud_Execute();
accumulator -= kSimStepSeconds;
}
float interp_alpha = static_cast<float>(accumulator / kSimStepSeconds);
dusk::frame_interp::interpolate(interp_alpha);
dusk::frame_interp::interpolate(static_cast<float>(accumulator / kSimStepSeconds));
cAPIGph_Painter();
} else {
accumulator = 0.0;
// Game Inputs
mDoCPd_c::read();
if (dusk::getSettings().game.enableGyroAim) {