From 4ee0d8ed4b91eb7469c9311644ea441105096544 Mon Sep 17 00:00:00 2001 From: qwertyquerty Date: Wed, 13 May 2026 07:56:16 -0700 Subject: [PATCH 1/3] 1.1.1 fixes (#1168) * fix keyboard npe * fix autosave NPE * hintTalkEvCamera UB * fix UB in f_pc_base logging * fix NPE in karg carry logic * fix link model dangling pointers * exponential audio slider and better audio default * fix speedrun mode defaullt layer restore issue --- include/dusk/audio/DuskAudioSystem.h | 11 +++++++++++ include/dusk/config_var.hpp | 7 +++++-- src/d/actor/d_a_alink_swindow.inc | 7 ++++++- src/d/actor/d_a_alink_wolf.inc | 6 ++++++ src/d/d_ev_camera.cpp | 4 ++++ src/dusk/autosave.cpp | 14 ++++++++++---- src/dusk/settings.cpp | 2 +- src/dusk/ui/overlay.cpp | 3 ++- src/dusk/ui/settings.cpp | 2 +- src/f_pc/f_pc_base.cpp | 11 ++++++++++- src/m_Do/m_Do_main.cpp | 2 +- 11 files changed, 57 insertions(+), 12 deletions(-) diff --git a/include/dusk/audio/DuskAudioSystem.h b/include/dusk/audio/DuskAudioSystem.h index 8cdd757c82..780187f9d2 100644 --- a/include/dusk/audio/DuskAudioSystem.h +++ b/include/dusk/audio/DuskAudioSystem.h @@ -1,8 +1,19 @@ #pragma once +#include #include namespace dusk::audio { + + // Converts a 0-1 volume to a linear amplitude multiplier. + // The curve is -4 dB per 10% step: 100% = 0 dB, 90% = -4 dB, ..., 0% = -inf dB + inline f32 MasterVolumeToLinear(f32 v) { + if (v <= 0.0f) { + return 0.0f; + } + return std::pow(10.0f, (v - 1.0f) * 2.0f); + } + /** * Initialize the audio system and start playing audio. */ diff --git a/include/dusk/config_var.hpp b/include/dusk/config_var.hpp index 59eed2199e..0bae27bfd3 100644 --- a/include/dusk/config_var.hpp +++ b/include/dusk/config_var.hpp @@ -175,6 +175,7 @@ class ConfigVar : public ConfigVarBase { T defaultValue; T value; T overrideValue; + ConfigVarLayer priorLayer = ConfigVarLayer::Default; public: /** @@ -265,6 +266,7 @@ public: void setSpeedrunValue(T newValue) { checkRegistered(); if (layer != ConfigVarLayer::Override) { + priorLayer = layer; overrideValue = std::move(newValue); layer = ConfigVarLayer::Speedrun; } @@ -282,7 +284,7 @@ public: checkRegistered(); if (layer == ConfigVarLayer::Speedrun) { overrideValue = {}; - layer = ConfigVarLayer::Value; + layer = priorLayer; } } @@ -293,7 +295,8 @@ public: */ [[nodiscard]] constexpr const T& getValueForSave() const noexcept { checkRegistered(); - return layer == ConfigVarLayer::Default ? defaultValue : value; + const ConfigVarLayer effectiveLayer = (layer == ConfigVarLayer::Speedrun) ? priorLayer : layer; + return effectiveLayer == ConfigVarLayer::Default ? defaultValue : value; } }; diff --git a/src/d/actor/d_a_alink_swindow.inc b/src/d/actor/d_a_alink_swindow.inc index 9016f14205..33f1fee37b 100644 --- a/src/d/actor/d_a_alink_swindow.inc +++ b/src/d/actor/d_a_alink_swindow.inc @@ -77,7 +77,12 @@ int daAlink_c::loadModelDVD() { mpWlMidnaHairModel = NULL; if (!checkNoResetFlg2(FLG2_UNK_280000)) { - dComIfG_resDelete(&mPhaseReq, mArcName); + if (!dComIfG_resDelete(&mPhaseReq, mArcName)) { +#if TARGET_PC + // resDelete no-ops if load was in-progress; force-unregister before freeAll + dComIfG_deleteObjectResMain(mArcName); +#endif + } cPhs_Reset(&mPhaseReq); mpArcHeap->freeAll(); diff --git a/src/d/actor/d_a_alink_wolf.inc b/src/d/actor/d_a_alink_wolf.inc index b2e60aea01..67fde3a7a7 100644 --- a/src/d/actor/d_a_alink_wolf.inc +++ b/src/d/actor/d_a_alink_wolf.inc @@ -8723,6 +8723,12 @@ int daAlink_c::procWolfCargoCarry() { return checkNextActionWolf(0); } +#if TARGET_PC + if (field_0x280c.getActor() == NULL) { + return checkNextActionWolf(0); + } +#endif + mDoMtx_stack_c::copy(((e_yc_class*)field_0x280c.getActor())->getLegR3Mtx()); mDoMtx_stack_c::transM(-9.0f, -7.0f, -30.0f); mDoMtx_stack_c::multVecZero(¤t.pos); diff --git a/src/d/d_ev_camera.cpp b/src/d/d_ev_camera.cpp index 442c8cc4af..736f9f7a82 100644 --- a/src/d/d_ev_camera.cpp +++ b/src/d/d_ev_camera.cpp @@ -3882,7 +3882,11 @@ bool dCamera_c::hintTalkEvCamera() { cSAngle acStack_1fc(20.0f); for (i = 0; i < 2; i++) { +#if AVOID_UB + for (j = 0; j < 10; j++) { +#else for (j = 0; j < 12; j++) { +#endif cSAngle acStack_200(local_b0[j] * fVar22); hintTalk->mDirection.U(acStack_1f8 + acStack_200); hintTalk->mDirection.V(((hintTalk->field_0x28.V() * acStack_200.Cos()) * 0.2f) + acStack_1fc); diff --git a/src/dusk/autosave.cpp b/src/dusk/autosave.cpp index 6fce913910..13fc2929eb 100644 --- a/src/dusk/autosave.cpp +++ b/src/dusk/autosave.cpp @@ -26,8 +26,12 @@ void updateAutoSave() { (AutoSaveFuncsProc[mAutoSaveProc])(); } -void writeAutoSave() { - int stageNo = dStage_stagInfo_GetSaveTbl(dComIfGp_getStageStagInfo()); +bool writeAutoSave() { + stage_stag_info_class* stagInfo = dComIfGp_getStageStagInfo(); + if (stagInfo == nullptr) { + return false; + } + int stageNo = dStage_stagInfo_GetSaveTbl(stagInfo); dComIfGs_putSave(stageNo); dComIfGs_setMemoryToCard(mSaveBuffer, dComIfGs_getDataNum()); @@ -40,6 +44,7 @@ void writeAutoSave() { } g_mDoMemCd_control.save(mSaveBuffer, sizeof(mSaveBuffer), 0); + return true; } void autoSaving() { @@ -48,8 +53,9 @@ void autoSaving() { if (cardState == 2) { mAutoSaveProc = 1; } else if (cardState == 1) { - writeAutoSave(); - mAutoSaveProc = 3; + if (writeAutoSave()) { + mAutoSaveProc = 3; + } } } } diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 5e3f73a366..dbe27db8aa 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -13,7 +13,7 @@ UserSettings g_userSettings = { }, .audio = { - .masterVolume {"audio.masterVolume", 80}, + .masterVolume {"audio.masterVolume", 60}, .mainMusicVolume {"audio.mainMusicVolume", 100}, .subMusicVolume {"audio.subMusicVolume", 100}, .soundEffectsVolume {"audio.soundEffectsVolume", 100}, diff --git a/src/dusk/ui/overlay.cpp b/src/dusk/ui/overlay.cpp index 5bcd21c401..c9b2b8614d 100644 --- a/src/dusk/ui/overlay.cpp +++ b/src/dusk/ui/overlay.cpp @@ -354,8 +354,9 @@ void Overlay::update() { } } + u32 count = 0; const bool showControllerWarning = PADGetIndexForPort(PAD_CHAN0) < 0 && - PADGetKeyButtonBindings(PAD_CHAN0, nullptr) == nullptr && + PADGetKeyButtonBindings(PAD_CHAN0, &count) == nullptr && dynamic_cast(top_document()) == nullptr && dynamic_cast(top_document()) == nullptr; if (showControllerWarning && mControllerWarning == nullptr) { diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index 2a26d5113a..c54b3784be 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -953,7 +953,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { [](int value) { getSettings().audio.masterVolume.setValue(value); config::Save(); - audio::SetMasterVolume(value / 100.f); + audio::SetMasterVolume(audio::MasterVolumeToLinear(value / 100.0f)); }, .isModified = [] { diff --git a/src/f_pc/f_pc_base.cpp b/src/f_pc/f_pc_base.cpp index 848153b836..7bb53ae634 100644 --- a/src/f_pc/f_pc_base.cpp +++ b/src/f_pc/f_pc_base.cpp @@ -136,8 +136,17 @@ base_process_class* fpcBs_Create(s16 i_profname, fpc_ProcID i_procID, void* i_ap u32 size; pprofile = (process_profile_definition*)fpcPf_Get(i_profname); + if (pprofile == NULL) { +#if TARGET_PC + DuskLog.debug("fpcBs_Create: profile not found for profname={}", i_profname); +#endif + return NULL; + } +#if TARGET_PC + const char* procName = getProcName(i_profname); DuskLog.debug("fpcBs_Create: pid={} profname={} ({}) profile={} procSize={} unkSize={}", - i_procID, getProcName(i_profname), i_profname, (void*)pprofile, pprofile->process_size, pprofile->unk_size); + i_procID, procName ? procName : "(unknown)", i_profname, (void*)pprofile, pprofile->process_size, pprofile->unk_size); +#endif size = pprofile->process_size + pprofile->unk_size; pprocess = (base_process_class*)cMl::memalignB(-4, size); diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index 8cfd235344..b892a1f706 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -585,7 +585,7 @@ int game_main(int argc, char* argv[]) { } VISetFrameBufferScale(dusk::getSettings().game.internalResolutionScale.getValue()); - dusk::audio::SetMasterVolume(dusk::getSettings().audio.masterVolume / 100.0f); + dusk::audio::SetMasterVolume(dusk::audio::MasterVolumeToLinear(dusk::getSettings().audio.masterVolume / 100.0f)); dusk::audio::SetEnableReverb(dusk::getSettings().audio.enableReverb); dusk::audio::EnableHrtf = dusk::getSettings().audio.enableHrtf; From 7a77d48954847330a5ab55f5c7f2e64cdd25d846 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Wed, 13 May 2026 09:08:41 -0600 Subject: [PATCH 2/3] Use legacy path if migration fails --- src/dusk/data.cpp | 66 ++++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/src/dusk/data.cpp b/src/dusk/data.cpp index 3e4ce15853..ce61965947 100644 --- a/src/dusk/data.cpp +++ b/src/dusk/data.cpp @@ -78,6 +78,7 @@ struct MigrationStats { std::optional sConfiguredDataPath; std::optional sActiveDescriptorPath; +std::optional sActivePrefPath; std::filesystem::path path_from_utf8(std::string_view value) { return std::filesystem::path{ @@ -86,19 +87,22 @@ std::filesystem::path path_from_utf8(std::string_view value) { }; } -std::filesystem::path get_legacy_path() { - if (std::string_view{LegacyAppName}.empty()) { +std::filesystem::path legacy_path_for_pref_path(const std::filesystem::path& prefPath) { + if (std::string_view{LegacyAppName}.empty() || prefPath.empty()) { return {}; } - char* prefPath = SDL_GetPrefPath(OrgName, LegacyAppName); - if (!prefPath) { - Log.fatal("Unable to get PrefPath: {}", SDL_GetError()); + auto normalizedPrefPath = prefPath; + if (normalizedPrefPath.filename().empty()) { + normalizedPrefPath = normalizedPrefPath.parent_path(); } - std::filesystem::path result{reinterpret_cast(prefPath)}; - SDL_free(prefPath); - return result; + const auto parentPath = normalizedPrefPath.parent_path(); + if (parentPath.empty()) { + return {}; + } + + return parentPath / LegacyAppName; } std::filesystem::path get_pref_path() { @@ -112,6 +116,13 @@ std::filesystem::path get_pref_path() { return result; } +std::filesystem::path active_pref_path() { + if (sActivePrefPath) { + return *sActivePrefPath; + } + return get_pref_path(); +} + std::filesystem::path base_path_relative(const std::filesystem::path& path) { const auto* basePath = SDL_GetBasePath(); if (!basePath) { @@ -265,12 +276,12 @@ std::filesystem::path absolute_path(const std::filesystem::path& path) { return absolute.lexically_normal(); } -void rename_legacy_pref_path( +std::filesystem::path rename_legacy_pref_path( const std::filesystem::path& legacyPath, const std::filesystem::path& prefPath) { if (legacyPath.empty() || prefPath.empty() || normalized_path(legacyPath) == normalized_path(prefPath)) { - return; + return prefPath; } std::error_code ec; @@ -279,14 +290,14 @@ void rename_legacy_pref_path( Log.warn("Failed to inspect legacy data directory '{}': {}", io::fs_path_to_string(legacyPath), ec.message()); } - return; + return prefPath; } const bool prefExists = std::filesystem::exists(prefPath, ec); if (ec) { Log.warn("Failed to inspect data directory '{}': {}", io::fs_path_to_string(prefPath), ec.message()); - return; + return prefPath; } if (prefExists) { if (!std::filesystem::is_directory(prefPath, ec) || @@ -299,14 +310,14 @@ void rename_legacy_pref_path( Log.info("Skipping legacy data directory rename because '{}' is not empty", io::fs_path_to_string(prefPath)); } - return; + return prefPath; } std::filesystem::remove(prefPath, ec); if (ec) { Log.warn("Failed to remove empty data directory '{}' before legacy rename: {}", io::fs_path_to_string(prefPath), ec.message()); - return; + return prefPath; } } @@ -314,11 +325,18 @@ void rename_legacy_pref_path( if (ec) { Log.warn("Failed to rename legacy data directory '{}' to '{}': {}", io::fs_path_to_string(legacyPath), io::fs_path_to_string(prefPath), ec.message()); - return; + ec.clear(); + if (!std::filesystem::exists(prefPath, ec) && !ec) { + Log.info("Using legacy data directory '{}' because the new data directory is absent", + io::fs_path_to_string(legacyPath)); + return legacyPath; + } + return prefPath; } Log.info("Renamed legacy data directory '{}' to '{}'", io::fs_path_to_string(legacyPath), io::fs_path_to_string(prefPath)); + return prefPath; } bool is_same_or_inside(const std::filesystem::path& root, const std::filesystem::path& path) { @@ -394,7 +412,7 @@ std::filesystem::path current_data_path() { if (!ConfigPath.empty()) { return ConfigPath; } - const auto prefPath = get_pref_path(); + const auto prefPath = active_pref_path(); const auto descriptor = read_location_descriptor(prefPath); if (descriptor) { sActiveDescriptorPath = descriptor->path; @@ -460,7 +478,7 @@ bool write_location_descriptor(LocationMode mode, const std::filesystem::path& t json["previousPath"] = io::fs_path_to_string(descriptor.previousPath); } - const auto prefPath = get_pref_path(); + const auto prefPath = active_pref_path(); for (const auto& path : descriptor_write_paths(prefPath)) { if (write_descriptor_json(path, json)) { sActiveDescriptorPath = path; @@ -1013,12 +1031,12 @@ bool set_portable_data_path() { } bool reset_data_path() { - const auto prefPath = get_pref_path(); + const auto prefPath = active_pref_path(); return write_location_descriptor(LocationMode::Default, default_data_path(prefPath)); } bool is_default_data_path() { - const auto prefPath = get_pref_path(); + const auto prefPath = active_pref_path(); return normalized_path(configured_data_path()) == normalized_path(default_data_path(prefPath)); } @@ -1027,7 +1045,7 @@ std::filesystem::path configured_data_path() { return *sConfiguredDataPath; } - const auto prefPath = get_pref_path(); + const auto prefPath = active_pref_path(); const auto descriptor = read_location_descriptor(prefPath); if (descriptor) { sActiveDescriptorPath = descriptor->path; @@ -1041,7 +1059,7 @@ std::filesystem::path cache_path() { if (!CachePath.empty()) { return CachePath; } - return get_pref_path(); + return active_pref_path(); } bool is_data_path_restart_pending() { @@ -1053,8 +1071,10 @@ bool is_data_path_restart_pending() { } Paths initialize_data() { - const auto prefPath = get_pref_path(); - rename_legacy_pref_path(get_legacy_path(), prefPath); + const auto preferredPrefPath = get_pref_path(); + const auto prefPath = + rename_legacy_pref_path(legacy_path_for_pref_path(preferredPrefPath), preferredPrefPath); + sActivePrefPath = prefPath; const auto descriptor = read_location_descriptor(prefPath); if (descriptor) { From 6026b4bb9b4b097efeff3417b37a446cf1b8be2c Mon Sep 17 00:00:00 2001 From: JaxonWasTaken Date: Wed, 13 May 2026 22:34:14 +0200 Subject: [PATCH 3/3] Apply rebrand to bloom setting value name (#1211) The display name for BloomSetting::Dusk was unchanged in the rebranding process, and still shows up as "Dusk" in the settings menu. Rename it to "Dusklight" to bring it in line with the rebrand. --- src/dusk/ui/graphics_tuner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dusk/ui/graphics_tuner.cpp b/src/dusk/ui/graphics_tuner.cpp index 4f68d4a078..440d9f312a 100644 --- a/src/dusk/ui/graphics_tuner.cpp +++ b/src/dusk/ui/graphics_tuner.cpp @@ -184,7 +184,7 @@ Rml::String format_graphics_setting_value(GraphicsOption option, int value) { case BloomMode::Classic: return "Classic"; case BloomMode::Dusk: - return "Dusk"; + return "Dusklight"; } break; case GraphicsOption::BloomMultiplier: