diff --git a/src/Features/LightLimitFix/Particle.cpp b/src/Features/LightLimitFix/Particle.cpp index b023ff8d21..7a3a61ef6e 100644 --- a/src/Features/LightLimitFix/Particle.cpp +++ b/src/Features/LightLimitFix/Particle.cpp @@ -526,7 +526,9 @@ void LightLimitFix::AddCachedParticleLights(eastl::vector& lightsData float distance = CalculateLightDistance(light.positionWS[0].data, light.radius); float dimmer = 0.0f; - if (distance < lightFadeStart || lightFadeEnd == 0.0f) + // lightFadeEnd <= lightFadeStart guards a zero/negative denominator below + // (equal engine globals would otherwise divide by zero → NaN into lighting). + if (distance < lightFadeStart || lightFadeEnd == 0.0f || lightFadeEnd <= lightFadeStart) dimmer = 1.0f; else if (distance <= lightFadeEnd) dimmer = 1.0f - ((distance - lightFadeStart) / (lightFadeEnd - lightFadeStart)); diff --git a/src/Features/LightLimitFix/ParticleLights.cpp b/src/Features/LightLimitFix/ParticleLights.cpp index df0cf38ac4..938bc0220e 100644 --- a/src/Features/LightLimitFix/ParticleLights.cpp +++ b/src/Features/LightLimitFix/ParticleLights.cpp @@ -31,6 +31,9 @@ void ParticleLights::GetConfigs() logger::info("[LLF] Loading particle lights configs"); auto configs = clib_util::distribution::get_configs("Data\\ParticleLights", "", ".ini"); + // get_configs order is filesystem-dependent; sort so the "first wins" + // duplicate policy below is deterministic across installs. + std::sort(configs.begin(), configs.end()); if (configs.empty()) { logger::warn("[LLF] No .ini files were found within the Data\\ParticleLights folder, aborting..."); @@ -78,6 +81,8 @@ void ParticleLights::GetConfigs() logger::info("[LLF] Loading particle lights gradients configs"); auto configs = clib_util::distribution::get_configs("Data\\ParticleLights\\Gradients", "", ".ini"); + // Deterministic "first wins" across installs (see above). + std::sort(configs.begin(), configs.end()); if (configs.empty()) { logger::warn("[LLF] No .ini files were found within the Data\\ParticleLights\\Gradients folder, aborting..."); @@ -112,7 +117,12 @@ void ParticleLights::GetConfigs() if (str.starts_with(prefix2)) str.remove_prefix(prefix2.size()); - bool matches = std::strspn(str.data(), cset.data()) == str.size(); + // Require exactly 6 (RRGGBB) or 8 (AARRGGBB) hex digits so malformed + // widths like "#1" / "#12345" fail closed instead of packing a garbage color. + // find_first_not_of is bounded to the view; strspn would rely on + // str.data() being NUL-terminated, which string_view doesn't guarantee. + bool matches = (str.size() == 6 || str.size() == 8) && + str.find_first_not_of(cset) == std::string_view::npos; if (matches) { try { diff --git a/src/Features/Upscaling.cpp b/src/Features/Upscaling.cpp index 7ca57363fb..6cdaf71bd4 100644 --- a/src/Features/Upscaling.cpp +++ b/src/Features/Upscaling.cpp @@ -536,11 +536,9 @@ void Upscaling::DrawSettings() if (ImGui::TreeNodeEx("Backend Diagnostics")) { // Streamline log level selection const char* logLevels[] = { "Off", "Default", "Verbose" }; - // Clamp before use: streamlineLogLevel is JSON-persisted and could be out - // of range (or a value that overflows the int cast) on a stale or - // hand-edited config; an unclamped value would index logLevels OOB. - int logLevelIdx = std::clamp(static_cast(settings.streamlineLogLevel), - 0, IM_ARRAYSIZE(logLevels) - 1); + // streamlineLogLevel is sanitized in LoadSettings (runs on every load, + // not gated on this node being expanded), so the stored value is in range. + int logLevelIdx = static_cast(settings.streamlineLogLevel); if (ImGui::Combo("Streamline Logging", &logLevelIdx, logLevels, IM_ARRAYSIZE(logLevels))) { settings.streamlineLogLevel = static_cast(logLevelIdx); } @@ -702,6 +700,10 @@ void Upscaling::LoadSettings(json& o_json) logger::warn("[Upscaling] Loaded presetDLSS {} out of range, resetting to 0 (Default)", settings.presetDLSS); settings.presetDLSS = 0; } + if (settings.streamlineLogLevel > 2) { // Off, Default, Verbose + logger::warn("[Upscaling] Loaded streamlineLogLevel {} out of range, clamping to 2", settings.streamlineLogLevel); + settings.streamlineLogLevel = 2; + } // Re-apply FoveatedRender's cross-feature clamp now that the JSON // re-assign above has overwritten anything it set during its own // LoadSettings (which fired before this block ran). Idempotent — no-op