diff options
23 files changed, 654 insertions, 161 deletions
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index d18ef6940..50846a854 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp @@ -17,7 +17,7 @@ namespace AudioCore { constexpr u32 STREAM_SAMPLE_RATE{48000}; constexpr u32 STREAM_NUM_CHANNELS{2}; - +using VoiceChannelHolder = std::array<VoiceResourceInformation*, 6>; class AudioRenderer::VoiceState { public: bool IsPlaying() const { @@ -37,9 +37,10 @@ public: } void SetWaveIndex(std::size_t index); - std::vector<s16> DequeueSamples(std::size_t sample_count, Core::Memory::Memory& memory); + std::vector<s16> DequeueSamples(std::size_t sample_count, Core::Memory::Memory& memory, + const VoiceChannelHolder& voice_resources); void UpdateState(); - void RefreshBuffer(Core::Memory::Memory& memory); + void RefreshBuffer(Core::Memory::Memory& memory, const VoiceChannelHolder& voice_resources); private: bool is_in_use{}; @@ -79,7 +80,7 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number) : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count), - effects(params.effect_count), memory{memory_} { + voice_resources(params.voice_count), effects(params.effect_count), memory{memory_} { behavior_info.SetUserRevision(params.revision); audio_out = std::make_unique<AudioCore::AudioOut>(); stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, @@ -127,6 +128,12 @@ ResultVal<std::vector<u8>> AudioRenderer::UpdateAudioRenderer(const std::vector< input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size, memory_pool_count * sizeof(MemoryPoolInfo)); + // Copy voice resources + const std::size_t voice_resource_offset{sizeof(UpdateDataHeader) + config.behavior_size + + config.memory_pools_size}; + std::memcpy(voice_resources.data(), input_params.data() + voice_resource_offset, + sizeof(VoiceResourceInformation) * voice_resources.size()); + // Copy VoiceInfo structs std::size_t voice_offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size + config.voice_resource_size}; @@ -220,14 +227,15 @@ void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) { is_refresh_pending = true; } -std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count, - Core::Memory::Memory& memory) { +std::vector<s16> AudioRenderer::VoiceState::DequeueSamples( + std::size_t sample_count, Core::Memory::Memory& memory, + const VoiceChannelHolder& voice_resources) { if (!IsPlaying()) { return {}; } if (is_refresh_pending) { - RefreshBuffer(memory); + RefreshBuffer(memory, voice_resources); } const std::size_t max_size{samples.size() - offset}; @@ -271,7 +279,8 @@ void AudioRenderer::VoiceState::UpdateState() { is_in_use = info.is_in_use; } -void AudioRenderer::VoiceState::RefreshBuffer(Core::Memory::Memory& memory) { +void AudioRenderer::VoiceState::RefreshBuffer(Core::Memory::Memory& memory, + const VoiceChannelHolder& voice_resources) { const auto wave_buffer_address = info.wave_buffer[wave_index].buffer_addr; const auto wave_buffer_size = info.wave_buffer[wave_index].buffer_sz; std::vector<s16> new_samples(wave_buffer_size / sizeof(s16)); @@ -296,17 +305,77 @@ void AudioRenderer::VoiceState::RefreshBuffer(Core::Memory::Memory& memory) { } switch (info.channel_count) { - case 1: + case 1: { // 1 channel is upsampled to 2 channel samples.resize(new_samples.size() * 2); + for (std::size_t index = 0; index < new_samples.size(); ++index) { - samples[index * 2] = new_samples[index]; - samples[index * 2 + 1] = new_samples[index]; + auto sample = static_cast<float>(new_samples[index]); + if (voice_resources[0]->in_use) { + sample *= voice_resources[0]->mix_volumes[0]; + } + + samples[index * 2] = static_cast<s16>(sample * info.volume); + samples[index * 2 + 1] = static_cast<s16>(sample * info.volume); } break; + } case 2: { // 2 channel is played as is samples = std::move(new_samples); + const std::size_t sample_count = (samples.size() / 2); + for (std::size_t index = 0; index < sample_count; ++index) { + const std::size_t index_l = index * 2; + const std::size_t index_r = index * 2 + 1; + + auto sample_l = static_cast<float>(samples[index_l]); + auto sample_r = static_cast<float>(samples[index_r]); + + if (voice_resources[0]->in_use) { + sample_l *= voice_resources[0]->mix_volumes[0]; + } + + if (voice_resources[1]->in_use) { + sample_r *= voice_resources[1]->mix_volumes[1]; + } + + samples[index_l] = static_cast<s16>(sample_l * info.volume); + samples[index_r] = static_cast<s16>(sample_r * info.volume); + } + break; + } + case 6: { + samples.resize((new_samples.size() / 6) * 2); + const std::size_t sample_count = samples.size() / 2; + + for (std::size_t index = 0; index < sample_count; ++index) { + auto FL = static_cast<float>(new_samples[index * 6]); + auto FR = static_cast<float>(new_samples[index * 6 + 1]); + auto FC = static_cast<float>(new_samples[index * 6 + 2]); + auto BL = static_cast<float>(new_samples[index * 6 + 4]); + auto BR = static_cast<float>(new_samples[index * 6 + 5]); + + if (voice_resources[0]->in_use) { + FL *= voice_resources[0]->mix_volumes[0]; + } + if (voice_resources[1]->in_use) { + FR *= voice_resources[1]->mix_volumes[1]; + } + if (voice_resources[2]->in_use) { + FC *= voice_resources[2]->mix_volumes[2]; + } + if (voice_resources[4]->in_use) { + BL *= voice_resources[4]->mix_volumes[4]; + } + if (voice_resources[5]->in_use) { + BR *= voice_resources[5]->mix_volumes[5]; + } + + samples[index * 2] = + static_cast<s16>((0.3694f * FL + 0.2612f * FC + 0.3694f * BL) * info.volume); + samples[index * 2 + 1] = + static_cast<s16>((0.3694f * FR + 0.2612f * FC + 0.3694f * BR) * info.volume); + } break; } default: @@ -352,11 +421,17 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { if (!voice.IsPlaying()) { continue; } + VoiceChannelHolder resources{}; + for (u32 channel = 0; channel < voice.GetInfo().channel_count; channel++) { + const auto channel_resource_id = voice.GetInfo().voice_channel_resource_ids[channel]; + resources[channel] = &voice_resources[channel_resource_id]; + } std::size_t offset{}; s64 samples_remaining{BUFFER_SIZE}; while (samples_remaining > 0) { - const std::vector<s16> samples{voice.DequeueSamples(samples_remaining, memory)}; + const std::vector<s16> samples{ + voice.DequeueSamples(samples_remaining, memory, resources)}; if (samples.empty()) { break; diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h index b42770fae..1f9114c07 100644 --- a/src/audio_core/audio_renderer.h +++ b/src/audio_core/audio_renderer.h @@ -9,6 +9,7 @@ #include <vector> #include "audio_core/behavior_info.h" +#include "audio_core/common.h" #include "audio_core/stream.h" #include "common/common_funcs.h" #include "common/common_types.h" @@ -116,6 +117,14 @@ struct WaveBuffer { }; static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size"); +struct VoiceResourceInformation { + s32_le id{}; + std::array<float_le, MAX_MIX_BUFFERS> mix_volumes{}; + bool in_use{}; + INSERT_PADDING_BYTES(11); +}; +static_assert(sizeof(VoiceResourceInformation) == 0x70, "VoiceResourceInformation has wrong size"); + struct VoiceInfo { u32_le id; u32_le node_id; @@ -244,6 +253,7 @@ private: AudioRendererParameter worker_params; std::shared_ptr<Kernel::WritableEvent> buffer_event; std::vector<VoiceState> voices; + std::vector<VoiceResourceInformation> voice_resources; std::vector<EffectState> effects; std::unique_ptr<AudioOut> audio_out; StreamPtr stream; diff --git a/src/audio_core/common.h b/src/audio_core/common.h index 98478b66b..7bb145c53 100644 --- a/src/audio_core/common.h +++ b/src/audio_core/common.h @@ -14,6 +14,7 @@ constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41}; } constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '8'); +constexpr std::size_t MAX_MIX_BUFFERS = 24; static constexpr u32 VersionFromRevision(u32_le rev) { // "REV7" -> 7 diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index d1ec8ff08..e6769a5f3 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -148,6 +148,8 @@ add_library(common STATIC thread.h thread_queue_list.h threadsafe_queue.h + time_zone.cpp + time_zone.h timer.cpp timer.h uint128.cpp diff --git a/src/common/time_zone.cpp b/src/common/time_zone.cpp new file mode 100644 index 000000000..ce239eb63 --- /dev/null +++ b/src/common/time_zone.cpp @@ -0,0 +1,49 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <chrono> +#include <iomanip> +#include <sstream> + +#include "common/logging/log.h" +#include "common/time_zone.h" + +namespace Common::TimeZone { + +std::string GetDefaultTimeZone() { + return "GMT"; +} + +static std::string GetOsTimeZoneOffset() { + const std::time_t t{std::time(nullptr)}; + const std::tm tm{*std::localtime(&t)}; + + std::stringstream ss; + ss << std::put_time(&tm, "%z"); // Get the current timezone offset, e.g. "-400", as a string + + return ss.str(); +} + +static int ConvertOsTimeZoneOffsetToInt(const std::string& timezone) { + try { + return std::stoi(timezone); + } catch (const std::invalid_argument&) { + LOG_CRITICAL(Common, "invalid_argument with {}!", timezone); + return 0; + } catch (const std::out_of_range&) { + LOG_CRITICAL(Common, "out_of_range with {}!", timezone); + return 0; + } +} + +std::chrono::seconds GetCurrentOffsetSeconds() { + const int offset{ConvertOsTimeZoneOffsetToInt(GetOsTimeZoneOffset())}; + + int seconds{(offset / 100) * 60 * 60}; // Convert hour component to seconds + seconds += (offset % 100) * 60; // Convert minute component to seconds + + return std::chrono::seconds{seconds}; +} + +} // namespace Common::TimeZone diff --git a/src/common/time_zone.h b/src/common/time_zone.h new file mode 100644 index 000000000..945daa09c --- /dev/null +++ b/src/common/time_zone.h @@ -0,0 +1,18 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <chrono> +#include <string> + +namespace Common::TimeZone { + +/// Gets the default timezone, i.e. "GMT" +std::string GetDefaultTimeZone(); + +/// Gets the offset of the current timezone (from the default), in seconds +std::chrono::seconds GetCurrentOffsetSeconds(); + +} // namespace Common::TimeZone diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp index 9d6c55865..b4dfe45e5 100644 --- a/src/core/hle/service/time/time_manager.cpp +++ b/src/core/hle/service/time/time_manager.cpp @@ -5,6 +5,7 @@ #include <chrono> #include <ctime> +#include "common/time_zone.h" #include "core/hle/service/time/ephemeral_network_system_clock_context_writer.h" #include "core/hle/service/time/local_system_clock_context_writer.h" #include "core/hle/service/time/network_system_clock_context_writer.h" @@ -21,8 +22,16 @@ static std::chrono::seconds GetSecondsSinceEpoch() { Settings::values.custom_rtc_differential; } +static s64 GetExternalTimeZoneOffset() { + // With "auto" timezone setting, we use the external system's timezone offset + if (Settings::GetTimeZoneString() == "auto") { + return Common::TimeZone::GetCurrentOffsetSeconds().count(); + } + return 0; +} + static s64 GetExternalRtcValue() { - return GetSecondsSinceEpoch().count(); + return GetSecondsSinceEpoch().count() + GetExternalTimeZoneOffset(); } TimeManager::TimeManager(Core::System& system) diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp index 78d4acd95..c070d6e97 100644 --- a/src/core/hle/service/time/time_zone_content_manager.cpp +++ b/src/core/hle/service/time/time_zone_content_manager.cpp @@ -5,6 +5,7 @@ #include <sstream> #include "common/logging/log.h" +#include "common/time_zone.h" #include "core/core.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/nca_metadata.h" @@ -14,6 +15,7 @@ #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/time/time_manager.h" #include "core/hle/service/time/time_zone_content_manager.h" +#include "core/settings.h" namespace Service::Time::TimeZone { @@ -68,10 +70,22 @@ static std::vector<std::string> BuildLocationNameCache(Core::System& system) { TimeZoneContentManager::TimeZoneContentManager(TimeManager& time_manager, Core::System& system) : system{system}, location_name_cache{BuildLocationNameCache(system)} { - if (FileSys::VirtualFile vfs_file; GetTimeZoneInfoFile("GMT", vfs_file) == RESULT_SUCCESS) { + + std::string location_name; + const auto timezone_setting = Settings::GetTimeZoneString(); + if (timezone_setting == "auto") { + location_name = Common::TimeZone::GetDefaultTimeZone(); + } else if (timezone_setting == "default") { + location_name = location_name; + } else { + location_name = timezone_setting; + } + + if (FileSys::VirtualFile vfs_file; + GetTimeZoneInfoFile(location_name, vfs_file) == RESULT_SUCCESS) { const auto time_point{ time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)}; - time_manager.SetupTimeZoneManager("GMT", time_point, location_name_cache.size(), {}, + time_manager.SetupTimeZoneManager(location_name, time_point, location_name_cache.size(), {}, vfs_file); } else { time_zone_manager.MarkAsInitialized(); @@ -114,6 +128,12 @@ ResultCode TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& locati vfs_file = zoneinfo_dir->GetFile(location_name); if (!vfs_file) { + LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"! Using default timezone.", + time_zone_binary_titleid, location_name); + vfs_file = zoneinfo_dir->GetFile(Common::TimeZone::GetDefaultTimeZone()); + } + + if (!vfs_file) { LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"!", time_zone_binary_titleid, location_name); return ERROR_TIME_NOT_FOUND; diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 2b0bdc4d3..da53cde05 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -63,6 +63,21 @@ const std::array<const char*, NumMouseButtons> mapping = {{ Values values = {}; +std::string GetTimeZoneString() { + static constexpr std::array<const char*, 46> timezones{{ + "auto", "default", "CET", "CST6CDT", "Cuba", "EET", "Egypt", "Eire", + "EST", "EST5EDT", "GB", "GB-Eire", "GMT", "GMT+0", "GMT-0", "GMT0", + "Greenwich", "Hongkong", "HST", "Iceland", "Iran", "Israel", "Jamaica", "Japan", + "Kwajalein", "Libya", "MET", "MST", "MST7MDT", "Navajo", "NZ", "NZ-CHAT", + "Poland", "Portugal", "PRC", "PST8PDT", "ROC", "ROK", "Singapore", "Turkey", + "UCT", "Universal", "UTC", "W-SU", "WET", "Zulu", + }}; + + ASSERT(Settings::values.time_zone_index < timezones.size()); + + return timezones[Settings::values.time_zone_index]; +} + void Apply() { GDBStub::SetServerPort(values.gdbstub_port); GDBStub::ToggleServer(values.use_gdbstub); @@ -87,6 +102,7 @@ void LogSettings() { LogSetting("System_CurrentUser", Settings::values.current_user); LogSetting("System_LanguageIndex", Settings::values.language_index); LogSetting("System_RegionIndex", Settings::values.region_index); + LogSetting("System_TimeZoneIndex", Settings::values.time_zone_index); LogSetting("Core_UseMultiCore", Settings::values.use_multi_core); LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor); LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit); diff --git a/src/core/settings.h b/src/core/settings.h index 163900f0b..c1266b341 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -394,6 +394,7 @@ struct Values { s32 current_user; s32 language_index; s32 region_index; + s32 time_zone_index; s32 sound_index; // Controls @@ -490,6 +491,9 @@ struct Values { bool IsGPULevelExtreme(); bool IsGPULevelHigh(); +std::string GetTimeZoneString(); + void Apply(); void LogSettings(); + } // namespace Settings diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 8dae754d4..e7cb87589 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -168,18 +168,22 @@ enum class Pred : u64 { }; enum class PredCondition : u64 { - LessThan = 1, - Equal = 2, - LessEqual = 3, - GreaterThan = 4, - NotEqual = 5, - GreaterEqual = 6, - LessThanWithNan = 9, - LessEqualWithNan = 11, - GreaterThanWithNan = 12, - NotEqualWithNan = 13, - GreaterEqualWithNan = 14, - // TODO(Subv): Other condition types + F = 0, // Always false + LT = 1, // Ordered less than + EQ = 2, // Ordered equal + LE = 3, // Ordered less than or equal + GT = 4, // Ordered greater than + NE = 5, // Ordered not equal + GE = 6, // Ordered greater than or equal + NUM = 7, // Ordered + NAN_ = 8, // Unordered + LTU = 9, // Unordered less than + EQU = 10, // Unordered equal + LEU = 11, // Unordered less than or equal + GTU = 12, // Unordered greater than + NEU = 13, // Unordered not equal + GEU = 14, // Unordered greater than or equal + T = 15, // Always true }; enum class PredOperation : u64 { diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 99fd4ae2c..960ebf1a1 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -1840,34 +1840,40 @@ private: Type::HalfFloat}; } - template <Type type> - Expression LogicalLessThan(Operation operation) { - return GenerateBinaryInfix(operation, "<", Type::Bool, type, type); - } - - template <Type type> - Expression LogicalEqual(Operation operation) { - return GenerateBinaryInfix(operation, "==", Type::Bool, type, type); - } - - template <Type type> - Expression LogicalLessEqual(Operation operation) { - return GenerateBinaryInfix(operation, "<=", Type::Bool, type, type); - } - - template <Type type> - Expression LogicalGreaterThan(Operation operation) { - return GenerateBinaryInfix(operation, ">", Type::Bool, type, type); + template <const std::string_view& op, Type type, bool unordered = false> + Expression Comparison(Operation operation) { + static_assert(!unordered || type == Type::Float); + + const Expression expr = GenerateBinaryInfix(operation, op, Type::Bool, type, type); + + if constexpr (op.compare("!=") == 0 && type == Type::Float && !unordered) { + // GLSL's operator!=(float, float) doesn't seem be ordered. This happens on both AMD's + // and Nvidia's proprietary stacks. Manually force an ordered comparison. + return {fmt::format("({} && !isnan({}) && !isnan({}))", expr.AsBool(), + VisitOperand(operation, 0).AsFloat(), + VisitOperand(operation, 1).AsFloat()), + Type::Bool}; + } + if constexpr (!unordered) { + return expr; + } + // Unordered comparisons are always true for NaN operands. + return {fmt::format("({} || isnan({}) || isnan({}))", expr.AsBool(), + VisitOperand(operation, 0).AsFloat(), + VisitOperand(operation, 1).AsFloat()), + Type::Bool}; } - template <Type type> - Expression LogicalNotEqual(Operation operation) { - return GenerateBinaryInfix(operation, "!=", Type::Bool, type, type); + Expression FOrdered(Operation operation) { + return {fmt::format("(!isnan({}) && !isnan({}))", VisitOperand(operation, 0).AsFloat(), + VisitOperand(operation, 1).AsFloat()), + Type::Bool}; } - template <Type type> - Expression LogicalGreaterEqual(Operation operation) { - return GenerateBinaryInfix(operation, ">=", Type::Bool, type, type); + Expression FUnordered(Operation operation) { + return {fmt::format("(isnan({}) || isnan({}))", VisitOperand(operation, 0).AsFloat(), + VisitOperand(operation, 1).AsFloat()), + Type::Bool}; } Expression LogicalAddCarry(Operation operation) { @@ -2324,6 +2330,13 @@ private: Func() = delete; ~Func() = delete; + static constexpr std::string_view LessThan = "<"; + static constexpr std::string_view Equal = "=="; + static constexpr std::string_view LessEqual = "<="; + static constexpr std::string_view GreaterThan = ">"; + static constexpr std::string_view NotEqual = "!="; + static constexpr std::string_view GreaterEqual = ">="; + static constexpr std::string_view Add = "Add"; static constexpr std::string_view Min = "Min"; static constexpr std::string_view Max = "Max"; @@ -2425,27 +2438,34 @@ private: &GLSLDecompiler::LogicalPick2, &GLSLDecompiler::LogicalAnd2, - &GLSLDecompiler::LogicalLessThan<Type::Float>, - &GLSLDecompiler::LogicalEqual<Type::Float>, - &GLSLDecompiler::LogicalLessEqual<Type::Float>, - &GLSLDecompiler::LogicalGreaterThan<Type::Float>, - &GLSLDecompiler::LogicalNotEqual<Type::Float>, - &GLSLDecompiler::LogicalGreaterEqual<Type::Float>, - &GLSLDecompiler::LogicalFIsNan, - - &GLSLDecompiler::LogicalLessThan<Type::Int>, - &GLSLDecompiler::LogicalEqual<Type::Int>, - &GLSLDecompiler::LogicalLessEqual<Type::Int>, - &GLSLDecompiler::LogicalGreaterThan<Type::Int>, - &GLSLDecompiler::LogicalNotEqual<Type::Int>, - &GLSLDecompiler::LogicalGreaterEqual<Type::Int>, - - &GLSLDecompiler::LogicalLessThan<Type::Uint>, - &GLSLDecompiler::LogicalEqual<Type::Uint>, - &GLSLDecompiler::LogicalLessEqual<Type::Uint>, - &GLSLDecompiler::LogicalGreaterThan<Type::Uint>, - &GLSLDecompiler::LogicalNotEqual<Type::Uint>, - &GLSLDecompiler::LogicalGreaterEqual<Type::Uint>, + &GLSLDecompiler::Comparison<Func::LessThan, Type::Float, false>, + &GLSLDecompiler::Comparison<Func::Equal, Type::Float, false>, + &GLSLDecompiler::Comparison<Func::LessEqual, Type::Float, false>, + &GLSLDecompiler::Comparison<Func::GreaterThan, Type::Float, false>, + &GLSLDecompiler::Comparison<Func::NotEqual, Type::Float, false>, + &GLSLDecompiler::Comparison<Func::GreaterEqual, Type::Float, false>, + &GLSLDecompiler::FOrdered, + &GLSLDecompiler::FUnordered, + &GLSLDecompiler::Comparison<Func::LessThan, Type::Float, true>, + &GLSLDecompiler::Comparison<Func::Equal, Type::Float, true>, + &GLSLDecompiler::Comparison<Func::LessEqual, Type::Float, true>, + &GLSLDecompiler::Comparison<Func::GreaterThan, Type::Float, true>, + &GLSLDecompiler::Comparison<Func::NotEqual, Type::Float, true>, + &GLSLDecompiler::Comparison<Func::GreaterEqual, Type::Float, true>, + + &GLSLDecompiler::Comparison<Func::LessThan, Type::Int>, + &GLSLDecompiler::Comparison<Func::Equal, Type::Int>, + &GLSLDecompiler::Comparison<Func::LessEqual, Type::Int>, + &GLSLDecompiler::Comparison<Func::GreaterThan, Type::Int>, + &GLSLDecompiler::Comparison<Func::NotEqual, Type::Int>, + &GLSLDecompiler::Comparison<Func::GreaterEqual, Type::Int>, + + &GLSLDecompiler::Comparison<Func::LessThan, Type::Uint>, + &GLSLDecompiler::Comparison<Func::Equal, Type::Uint>, + &GLSLDecompiler::Comparison<Func::LessEqual, Type::Uint>, + &GLSLDecompiler::Comparison<Func::GreaterThan, Type::Uint>, + &GLSLDecompiler::Comparison<Func::NotEqual, Type::Uint>, + &GLSLDecompiler::Comparison<Func::GreaterEqual, Type::Uint>, &GLSLDecompiler::LogicalAddCarry, diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 18678968c..167e20e91 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp @@ -1618,6 +1618,24 @@ private: return {}; } + Expression LogicalFOrdered(Operation operation) { + // Emulate SPIR-V's OpOrdered + const Id op_a = AsFloat(Visit(operation[0])); + const Id op_b = AsFloat(Visit(operation[1])); + const Id is_num_a = OpFOrdEqual(t_bool, op_a, op_a); + const Id is_num_b = OpFOrdEqual(t_bool, op_b, op_b); + return {OpLogicalAnd(t_bool, is_num_a, is_num_b), Type::Bool}; + } + + Expression LogicalFUnordered(Operation operation) { + // Emulate SPIR-V's OpUnordered + const Id op_a = AsFloat(Visit(operation[0])); + const Id op_b = AsFloat(Visit(operation[1])); + const Id is_nan_a = OpIsNan(t_bool, op_a); + const Id is_nan_b = OpIsNan(t_bool, op_b); + return {OpLogicalOr(t_bool, is_nan_a, is_nan_b), Type::Bool}; + } + Id GetTextureSampler(Operation operation) { const auto& meta = std::get<MetaTexture>(operation.GetMeta()); ASSERT(!meta.sampler.is_buffer); @@ -2511,7 +2529,14 @@ private: &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThan, Type::Bool, Type::Float>, &SPIRVDecompiler::Binary<&Module::OpFOrdNotEqual, Type::Bool, Type::Float>, &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThanEqual, Type::Bool, Type::Float>, - &SPIRVDecompiler::Unary<&Module::OpIsNan, Type::Bool, Type::Float>, + &SPIRVDecompiler::LogicalFOrdered, + &SPIRVDecompiler::LogicalFUnordered, + &SPIRVDecompiler::Binary<&Module::OpFUnordLessThan, Type::Bool, Type::Float>, + &SPIRVDecompiler::Binary<&Module::OpFUnordEqual, Type::Bool, Type::Float>, + &SPIRVDecompiler::Binary<&Module::OpFUnordLessThanEqual, Type::Bool, Type::Float>, + &SPIRVDecompiler::Binary<&Module::OpFUnordGreaterThan, Type::Bool, Type::Float>, + &SPIRVDecompiler::Binary<&Module::OpFUnordNotEqual, Type::Bool, Type::Float>, + &SPIRVDecompiler::Binary<&Module::OpFUnordGreaterThanEqual, Type::Bool, Type::Float>, &SPIRVDecompiler::Binary<&Module::OpSLessThan, Type::Bool, Type::Int>, &SPIRVDecompiler::Binary<&Module::OpIEqual, Type::Bool, Type::Int>, diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp index a75a5cc63..eeac328a6 100644 --- a/src/video_core/shader/decode.cpp +++ b/src/video_core/shader/decode.cpp @@ -255,7 +255,7 @@ void ShaderIR::InsertControlFlow(NodeBlock& bb, const ShaderBlock& block) { Node n = Operation(OperationCode::Branch, Immediate(branch_case.address)); Node op_b = Immediate(branch_case.cmp_value); Node condition = - GetPredicateComparisonInteger(Tegra::Shader::PredCondition::Equal, false, op_a, op_b); + GetPredicateComparisonInteger(Tegra::Shader::PredCondition::EQ, false, op_a, op_b); auto result = Conditional(condition, {n}); bb.push_back(result); global_code.push_back(result); diff --git a/src/video_core/shader/decode/xmad.cpp b/src/video_core/shader/decode/xmad.cpp index 6191ffba1..c83dc6615 100644 --- a/src/video_core/shader/decode/xmad.cpp +++ b/src/video_core/shader/decode/xmad.cpp @@ -97,19 +97,19 @@ u32 ShaderIR::DecodeXmad(NodeBlock& bb, u32 pc) { return SignedOperation(OperationCode::IAdd, is_signed_c, original_c, shifted_b); } case Tegra::Shader::XmadMode::CSfu: { - const Node comp_a = GetPredicateComparisonInteger(PredCondition::Equal, is_signed_a, - op_a, Immediate(0)); - const Node comp_b = GetPredicateComparisonInteger(PredCondition::Equal, is_signed_b, - op_b, Immediate(0)); + const Node comp_a = + GetPredicateComparisonInteger(PredCondition::EQ, is_signed_a, op_a, Immediate(0)); + const Node comp_b = + GetPredicateComparisonInteger(PredCondition::EQ, is_signed_b, op_b, Immediate(0)); const Node comp = Operation(OperationCode::LogicalOr, comp_a, comp_b); const Node comp_minus_a = GetPredicateComparisonInteger( - PredCondition::NotEqual, is_signed_a, + PredCondition::NE, is_signed_a, SignedOperation(OperationCode::IBitwiseAnd, is_signed_a, op_a, Immediate(0x80000000)), Immediate(0)); const Node comp_minus_b = GetPredicateComparisonInteger( - PredCondition::NotEqual, is_signed_b, + PredCondition::NE, is_signed_b, SignedOperation(OperationCode::IBitwiseAnd, is_signed_b, op_b, Immediate(0x80000000)), Immediate(0)); diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h index 601c822d2..f75b62240 100644 --- a/src/video_core/shader/node.h +++ b/src/video_core/shader/node.h @@ -110,13 +110,20 @@ enum class OperationCode { LogicalPick2, /// (bool2 pair, uint index) -> bool LogicalAnd2, /// (bool2 a) -> bool - LogicalFLessThan, /// (float a, float b) -> bool - LogicalFEqual, /// (float a, float b) -> bool - LogicalFLessEqual, /// (float a, float b) -> bool - LogicalFGreaterThan, /// (float a, float b) -> bool - LogicalFNotEqual, /// (float a, float b) -> bool - LogicalFGreaterEqual, /// (float a, float b) -> bool - LogicalFIsNan, /// (float a) -> bool + LogicalFOrdLessThan, /// (float a, float b) -> bool + LogicalFOrdEqual, /// (float a, float b) -> bool + LogicalFOrdLessEqual, /// (float a, float b) -> bool + LogicalFOrdGreaterThan, /// (float a, float b) -> bool + LogicalFOrdNotEqual, /// (float a, float b) -> bool + LogicalFOrdGreaterEqual, /// (float a, float b) -> bool + LogicalFOrdered, /// (float a, float b) -> bool + LogicalFUnordered, /// (float a, float b) -> bool + LogicalFUnordLessThan, /// (float a, float b) -> bool + LogicalFUnordEqual, /// (float a, float b) -> bool + LogicalFUnordLessEqual, /// (float a, float b) -> bool + LogicalFUnordGreaterThan, /// (float a, float b) -> bool + LogicalFUnordNotEqual, /// (float a, float b) -> bool + LogicalFUnordGreaterEqual, /// (float a, float b) -> bool LogicalILessThan, /// (int a, int b) -> bool LogicalIEqual, /// (int a, int b) -> bool diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp index 822674926..e322c3402 100644 --- a/src/video_core/shader/shader_ir.cpp +++ b/src/video_core/shader/shader_ir.cpp @@ -10,6 +10,7 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/node.h" #include "video_core/shader/node_helper.h" #include "video_core/shader/registry.h" #include "video_core/shader/shader_ir.h" @@ -243,56 +244,44 @@ Node ShaderIR::GetSaturatedHalfFloat(Node value, bool saturate) { } Node ShaderIR::GetPredicateComparisonFloat(PredCondition condition, Node op_a, Node op_b) { + if (condition == PredCondition::T) { + return GetPredicate(true); + } else if (condition == PredCondition::F) { + return GetPredicate(false); + } + static constexpr std::array comparison_table{ - std::pair{PredCondition::LessThan, OperationCode::LogicalFLessThan}, - std::pair{PredCondition::Equal, OperationCode::LogicalFEqual}, - std::pair{PredCondition::LessEqual, OperationCode::LogicalFLessEqual}, - std::pair{PredCondition::GreaterThan, OperationCode::LogicalFGreaterThan}, - std::pair{PredCondition::NotEqual, OperationCode::LogicalFNotEqual}, - std::pair{PredCondition::GreaterEqual, OperationCode::LogicalFGreaterEqual}, - std::pair{PredCondition::LessThanWithNan, OperationCode::LogicalFLessThan}, - std::pair{PredCondition::NotEqualWithNan, OperationCode::LogicalFNotEqual}, - std::pair{PredCondition::LessEqualWithNan, OperationCode::LogicalFLessEqual}, - std::pair{PredCondition::GreaterThanWithNan, OperationCode::LogicalFGreaterThan}, - std::pair{PredCondition::GreaterEqualWithNan, OperationCode::LogicalFGreaterEqual}, + OperationCode(0), + OperationCode::LogicalFOrdLessThan, // LT + OperationCode::LogicalFOrdEqual, // EQ + OperationCode::LogicalFOrdLessEqual, // LE + OperationCode::LogicalFOrdGreaterThan, // GT + OperationCode::LogicalFOrdNotEqual, // NE + OperationCode::LogicalFOrdGreaterEqual, // GE + OperationCode::LogicalFOrdered, // NUM + OperationCode::LogicalFUnordered, // NAN + OperationCode::LogicalFUnordLessThan, // LTU + OperationCode::LogicalFUnordEqual, // EQU + OperationCode::LogicalFUnordLessEqual, // LEU + OperationCode::LogicalFUnordGreaterThan, // GTU + OperationCode::LogicalFUnordNotEqual, // NEU + OperationCode::LogicalFUnordGreaterEqual, // GEU }; + const std::size_t index = static_cast<std::size_t>(condition); + ASSERT_MSG(index < std::size(comparison_table), "Invalid condition={}", index); - const auto comparison = - std::find_if(comparison_table.cbegin(), comparison_table.cend(), - [condition](const auto entry) { return condition == entry.first; }); - UNIMPLEMENTED_IF_MSG(comparison == comparison_table.cend(), - "Unknown predicate comparison operation"); - - Node predicate = Operation(comparison->second, NO_PRECISE, op_a, op_b); - - if (condition == PredCondition::LessThanWithNan || - condition == PredCondition::NotEqualWithNan || - condition == PredCondition::LessEqualWithNan || - condition == PredCondition::GreaterThanWithNan || - condition == PredCondition::GreaterEqualWithNan) { - predicate = Operation(OperationCode::LogicalOr, predicate, - Operation(OperationCode::LogicalFIsNan, op_a)); - predicate = Operation(OperationCode::LogicalOr, predicate, - Operation(OperationCode::LogicalFIsNan, op_b)); - } - - return predicate; + return Operation(comparison_table[index], op_a, op_b); } Node ShaderIR::GetPredicateComparisonInteger(PredCondition condition, bool is_signed, Node op_a, Node op_b) { static constexpr std::array comparison_table{ - std::pair{PredCondition::LessThan, OperationCode::LogicalILessThan}, - std::pair{PredCondition::Equal, OperationCode::LogicalIEqual}, - std::pair{PredCondition::LessEqual, OperationCode::LogicalILessEqual}, - std::pair{PredCondition::GreaterThan, OperationCode::LogicalIGreaterThan}, - std::pair{PredCondition::NotEqual, OperationCode::LogicalINotEqual}, - std::pair{PredCondition::GreaterEqual, OperationCode::LogicalIGreaterEqual}, - std::pair{PredCondition::LessThanWithNan, OperationCode::LogicalILessThan}, - std::pair{PredCondition::NotEqualWithNan, OperationCode::LogicalINotEqual}, - std::pair{PredCondition::LessEqualWithNan, OperationCode::LogicalILessEqual}, - std::pair{PredCondition::GreaterThanWithNan, OperationCode::LogicalIGreaterThan}, - std::pair{PredCondition::GreaterEqualWithNan, OperationCode::LogicalIGreaterEqual}, + std::pair{PredCondition::LT, OperationCode::LogicalILessThan}, + std::pair{PredCondition::EQ, OperationCode::LogicalIEqual}, + std::pair{PredCondition::LE, OperationCode::LogicalILessEqual}, + std::pair{PredCondition::GT, OperationCode::LogicalIGreaterThan}, + std::pair{PredCondition::NE, OperationCode::LogicalINotEqual}, + std::pair{PredCondition::GE, OperationCode::LogicalIGreaterEqual}, }; const auto comparison = @@ -301,32 +290,24 @@ Node ShaderIR::GetPredicateComparisonInteger(PredCondition condition, bool is_si UNIMPLEMENTED_IF_MSG(comparison == comparison_table.cend(), "Unknown predicate comparison operation"); - Node predicate = SignedOperation(comparison->second, is_signed, NO_PRECISE, std::move(op_a), - std::move(op_b)); - - UNIMPLEMENTED_IF_MSG(condition == PredCondition::LessThanWithNan || - condition == PredCondition::NotEqualWithNan || - condition == PredCondition::LessEqualWithNan || - condition == PredCondition::GreaterThanWithNan || - condition == PredCondition::GreaterEqualWithNan, - "NaN comparisons for integers are not implemented"); - return predicate; + return SignedOperation(comparison->second, is_signed, NO_PRECISE, std::move(op_a), + std::move(op_b)); } Node ShaderIR::GetPredicateComparisonHalf(Tegra::Shader::PredCondition condition, Node op_a, Node op_b) { static constexpr std::array comparison_table{ - std::pair{PredCondition::LessThan, OperationCode::Logical2HLessThan}, - std::pair{PredCondition::Equal, OperationCode::Logical2HEqual}, - std::pair{PredCondition::LessEqual, OperationCode::Logical2HLessEqual}, - std::pair{PredCondition::GreaterThan, OperationCode::Logical2HGreaterThan}, - std::pair{PredCondition::NotEqual, OperationCode::Logical2HNotEqual}, - std::pair{PredCondition::GreaterEqual, OperationCode::Logical2HGreaterEqual}, - std::pair{PredCondition::LessThanWithNan, OperationCode::Logical2HLessThanWithNan}, - std::pair{PredCondition::NotEqualWithNan, OperationCode::Logical2HNotEqualWithNan}, - std::pair{PredCondition::LessEqualWithNan, OperationCode::Logical2HLessEqualWithNan}, - std::pair{PredCondition::GreaterThanWithNan, OperationCode::Logical2HGreaterThanWithNan}, - std::pair{PredCondition::GreaterEqualWithNan, OperationCode::Logical2HGreaterEqualWithNan}, + std::pair{PredCondition::LT, OperationCode::Logical2HLessThan}, + std::pair{PredCondition::EQ, OperationCode::Logical2HEqual}, + std::pair{PredCondition::LE, OperationCode::Logical2HLessEqual}, + std::pair{PredCondition::GT, OperationCode::Logical2HGreaterThan}, + std::pair{PredCondition::NE, OperationCode::Logical2HNotEqual}, + std::pair{PredCondition::GE, OperationCode::Logical2HGreaterEqual}, + std::pair{PredCondition::LTU, OperationCode::Logical2HLessThanWithNan}, + std::pair{PredCondition::LEU, OperationCode::Logical2HLessEqualWithNan}, + std::pair{PredCondition::GTU, OperationCode::Logical2HGreaterThanWithNan}, + std::pair{PredCondition::NEU, OperationCode::Logical2HNotEqualWithNan}, + std::pair{PredCondition::GEU, OperationCode::Logical2HGreaterEqualWithNan}, }; const auto comparison = @@ -397,7 +378,7 @@ void ShaderIR::SetInternalFlagsFromFloat(NodeBlock& bb, Node value, bool sets_cc if (!sets_cc) { return; } - Node zerop = Operation(OperationCode::LogicalFEqual, std::move(value), Immediate(0.0f)); + Node zerop = Operation(OperationCode::LogicalFOrdEqual, std::move(value), Immediate(0.0f)); SetInternalFlag(bb, InternalFlag::Zero, std::move(zerop)); LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete"); } diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 75c6cf20b..27775701d 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -687,6 +687,8 @@ void Config::ReadSystemValues() { Settings::values.region_index = ReadSetting(QStringLiteral("region_index"), 1).toInt(); + Settings::values.time_zone_index = ReadSetting(QStringLiteral("time_zone_index"), 0).toInt(); + const auto rng_seed_enabled = ReadSetting(QStringLiteral("rng_seed_enabled"), false).toBool(); if (rng_seed_enabled) { Settings::values.rng_seed = ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong(); @@ -1126,6 +1128,7 @@ void Config::SaveSystemValues() { WriteSetting(QStringLiteral("current_user"), Settings::values.current_user, 0); WriteSetting(QStringLiteral("language_index"), Settings::values.language_index, 1); WriteSetting(QStringLiteral("region_index"), Settings::values.region_index, 1); + WriteSetting(QStringLiteral("time_zone_index"), Settings::values.time_zone_index, 0); WriteSetting(QStringLiteral("rng_seed_enabled"), Settings::values.rng_seed.has_value(), false); WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.value_or(0), 0); diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index f49cd4c8f..10315e7a6 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -57,6 +57,7 @@ void ConfigureSystem::SetConfiguration() { ui->combo_language->setCurrentIndex(Settings::values.language_index); ui->combo_region->setCurrentIndex(Settings::values.region_index); + ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index); ui->combo_sound->setCurrentIndex(Settings::values.sound_index); ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.has_value()); @@ -84,6 +85,7 @@ void ConfigureSystem::ApplyConfiguration() { Settings::values.language_index = ui->combo_language->currentIndex(); Settings::values.region_index = ui->combo_region->currentIndex(); + Settings::values.time_zone_index = ui->combo_time_zone->currentIndex(); Settings::values.sound_index = ui->combo_sound->currentIndex(); if (ui->rng_seed_checkbox->isChecked()) { diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h index d8fa2d2cc..26d42d5c5 100644 --- a/src/yuzu/configuration/configure_system.h +++ b/src/yuzu/configuration/configure_system.h @@ -37,5 +37,6 @@ private: int language_index = 0; int region_index = 0; + int time_zone_index = 0; int sound_index = 0; }; diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui index 4e2c7e76e..9c8cca6dc 100644 --- a/src/yuzu/configuration/configure_system.ui +++ b/src/yuzu/configuration/configure_system.ui @@ -22,14 +22,14 @@ <string>System Settings</string> </property> <layout class="QGridLayout" name="gridLayout"> - <item row="2" column="0"> + <item row="3" column="0"> <widget class="QLabel" name="label_sound"> <property name="text"> <string>Sound output mode</string> </property> </widget> </item> - <item row="3" column="0"> + <item row="4" column="0"> <widget class="QLabel" name="label_console_id"> <property name="text"> <string>Console ID:</string> @@ -174,14 +174,255 @@ </item> </widget> </item> - <item row="5" column="0"> + <item row="2" column="0"> + <widget class="QLabel" name="label_timezone"> + <property name="text"> + <string>Time Zone:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QComboBox" name="combo_time_zone"> + <item> + <property name="text"> + <string>Auto</string> + </property> + </item> + <item> + <property name="text"> + <string>Default</string> + </property> + </item> + <item> + <property name="text"> + <string>CET</string> + </property> + </item> + <item> + <property name="text"> + <string>CST6CDT</string> + </property> + </item> + <item> + <property name="text"> + <string>Cuba</string> + </property> + </item> + <item> + <property name="text"> + <string>EET</string> + </property> + </item> + <item> + <property name="text"> + <string>Egypt</string> + </property> + </item> + <item> + <property name="text"> + <string>Eire</string> + </property> + </item> + <item> + <property name="text"> + <string>EST</string> + </property> + </item> + <item> + <property name="text"> + <string>EST5EDT</string> + </property> + </item> + <item> + <property name="text"> + <string>GB</string> + </property> + </item> + <item> + <property name="text"> + <string>GB-Eire</string> + </property> + </item> + <item> + <property name="text"> + <string>GMT</string> + </property> + </item> + <item> + <property name="text"> + <string>GMT+0</string> + </property> + </item> + <item> + <property name="text"> + <string>GMT-0</string> + </property> + </item> + <item> + <property name="text"> + <string>GMT0</string> + </property> + </item> + <item> + <property name="text"> + <string>Greenwich</string> + </property> + </item> + <item> + <property name="text"> + <string>Hongkong</string> + </property> + </item> + <item> + <property name="text"> + <string>HST</string> + </property> + </item> + <item> + <property name="text"> + <string>Iceland</string> + </property> + </item> + <item> + <property name="text"> + <string>Iran</string> + </property> + </item> + <item> + <property name="text"> + <string>Israel</string> + </property> + </item> + <item> + <property name="text"> + <string>Jamaica</string> + </property> + </item> + <item> + <property name="text"> + <string>Japan</string> + </property> + </item> + <item> + <property name="text"> + <string>Kwajalein</string> + </property> + </item> + <item> + <property name="text"> + <string>Libya</string> + </property> + </item> + <item> + <property name="text"> + <string>MET</string> + </property> + </item> + <item> + <property name="text"> + <string>MST</string> + </property> + </item> + <item> + <property name="text"> + <string>MST7MDT</string> + </property> + </item> + <item> + <property name="text"> + <string>Navajo</string> + </property> + </item> + <item> + <property name="text"> + <string>NZ</string> + </property> + </item> + <item> + <property name="text"> + <string>NZ-CHAT</string> + </property> + </item> + <item> + <property name="text"> + <string>Poland</string> + </property> + </item> + <item> + <property name="text"> + <string>Portugal</string> + </property> + </item> + <item> + <property name="text"> + <string>PRC</string> + </property> + </item> + <item> + <property name="text"> + <string>PST8PDT</string> + </property> + </item> + <item> + <property name="text"> + <string>ROC</string> + </property> + </item> + <item> + <property name="text"> + <string>ROK</string> + </property> + </item> + <item> + <property name="text"> + <string>Singapore</string> + </property> + </item> + <item> + <property name="text"> + <string>Turkey</string> + </property> + </item> + <item> + <property name="text"> + <string>UCT</string> + </property> + </item> + <item> + <property name="text"> + <string>Universal</string> + </property> + </item> + <item> + <property name="text"> + <string>UTC</string> + </property> + </item> + <item> + <property name="text"> + <string>W-SU</string> + </property> + </item> + <item> + <property name="text"> + <string>WET</string> + </property> + </item> + <item> + <property name="text"> + <string>Zulu</string> + </property> + </item> + </widget> + </item> + <item row="6" column="0"> <widget class="QCheckBox" name="rng_seed_checkbox"> <property name="text"> <string>RNG Seed</string> </property> </widget> </item> - <item row="2" column="1"> + <item row="3" column="1"> <widget class="QComboBox" name="combo_sound"> <item> <property name="text"> @@ -207,7 +448,7 @@ </property> </widget> </item> - <item row="3" column="1"> + <item row="4" column="1"> <widget class="QPushButton" name="button_regenerate_console_id"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> @@ -223,14 +464,14 @@ </property> </widget> </item> - <item row="4" column="0"> + <item row="5" column="0"> <widget class="QCheckBox" name="custom_rtc_checkbox"> <property name="text"> <string>Custom RTC</string> </property> </widget> </item> - <item row="4" column="1"> + <item row="5" column="1"> <widget class="QDateTimeEdit" name="custom_rtc_edit"> <property name="minimumDate"> <date> @@ -244,7 +485,7 @@ </property> </widget> </item> - <item row="5" column="1"> + <item row="6" column="1"> <widget class="QLineEdit" name="rng_seed_edit"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 8476a5a16..2348e6e0d 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -367,6 +367,9 @@ void Config::ReadValues() { Settings::values.custom_rtc = std::nullopt; } + Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1); + Settings::values.time_zone_index = sdl2_config->GetInteger("System", "time_zone_index", 0); + // Core Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false); @@ -409,8 +412,6 @@ void Config::ReadValues() { Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto"); Settings::values.volume = static_cast<float>(sdl2_config->GetReal("Audio", "volume", 1)); - Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1); - // Miscellaneous Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false); diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 60b1a62fa..ae94b51c4 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -262,6 +262,10 @@ language_index = # -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan region_value = +# The system time zone that yuzu will use during emulation +# 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone +time_zone_index = + [Miscellaneous] # A filter which removes logs below a certain logging level. # Examples: *:Debug Kernel.SVC:Trace Service.*:Critical |