Compare commits

...

32 Commits

Author SHA1 Message Date
GodKratos
60b184377e add UUID validation check
Co-authored-by: VolcaEM <63682805+VolcaEM@users.noreply.github.com>
2020-05-24 01:13:49 +12:00
Godkratos
7341257fc4 Validate uuid and fix returns 2020-05-18 11:10:12 +12:00
Godkratos
d011f89f15 Validate username before returning 2020-05-18 10:37:25 +12:00
Godkratos
de1ef273b3 Clang formatting 2020-05-18 01:01:40 +12:00
Godkratos
c2522f3e43 Move new method into anonymous namespace 2020-05-18 00:55:32 +12:00
Godkratos
d8b83aa8f5 Add button to show and update current user profile on status bar 2020-05-18 00:08:41 +12:00
bunnei
65010607b7 Merge pull request #3665 from bunnei/device-save
FS: Improve emulation of device saves
2020-05-16 12:39:58 -04:00
bunnei
3c378a31b5 Merge pull request #3945 from ogniK5377/nvflinger-pixformat
nv_flinger: Use enum for pixel format instead of u32
2020-05-16 02:00:50 -04:00
bunnei
74d5c0ed2f Merge pull request #3944 from ogniK5377/dma_mget
DmaPusher: Remove dead code in step
2020-05-16 02:00:14 -04:00
David Marcec
6f0360690b nv_flinger: Use enum for pixel format instead of u32 2020-05-16 13:47:55 +10:00
David Marcec
4b9504028d DmaPusher: Remove dead code in step 2020-05-16 12:42:27 +10:00
bunnei
55c0dd1cb3 Merge pull request #3942 from ReinUsesLisp/flush-and-invalidate
vk_rasterizer: Match OpenGL's FlushAndInvalidate behavior
2020-05-15 21:34:07 -04:00
ReinUsesLisp
7a27b7f3a3 vk_rasterizer: Match OpenGL's FlushAndInvalidate behavior
Match OpenGL's behavior. This can fix or simplify bisecting issues on
Vulkan.
2020-05-15 20:40:08 -03:00
Morph
b73f678ee8 frontend: Set minimum window size to 640x360 instead of 1280x720 (#3413) 2020-05-15 22:22:27 +02:00
bunnei
024c84d2db Merge pull request #3927 from jroweboy/fix-bug
Frontend: Remove tracking for context wrapper
2020-05-14 00:07:38 -04:00
bunnei
0e2ded049d Merge pull request #3757 from ogniK5377/better-voice-mixing
audio_renderer: Better voice mixing and 6 channel downmixing
2020-05-13 22:46:41 -04:00
bunnei
670a7f51e8 Merge pull request #3909 from bunnei/timezone
Improve time zone support
2020-05-13 21:41:45 -04:00
bunnei
b1a1bd12ca Merge pull request #3899 from ReinUsesLisp/float-comparisons
shader_ir: Add separate instructions for ordered and unordered comparisons and fix NE on GLSL
2020-05-13 09:51:14 -04:00
bunnei
bba54e1880 time_zone: Use std::chrono::seconds for strong typing. 2020-05-12 18:44:07 -04:00
James Rowe
1585981eec Frontend: Remove tracking for context wrapper 2020-05-11 23:50:03 -06:00
bunnei
3c8cd62b0d hle: service: time_zone_manager: Use current time zone setting. 2020-05-11 17:55:25 -04:00
bunnei
33441fa728 common: Add module to get the current time zone. 2020-05-11 17:51:26 -04:00
bunnei
988e42a3f5 core: settings: Add a setting for time zone. 2020-05-11 17:50:07 -04:00
David Marcec
16c0373adc fix logic error & scale sample volume based on voice volume 2020-05-11 12:56:15 -04:00
David Marcec
c4e7ec7a99 pass by const ref instead 2020-05-11 12:56:15 -04:00
David Marcec
9de860a419 audio_renderer: Better voice mixing and 6 channel downmixing
Supersedes #3738 and #3321
2020-05-11 12:56:15 -04:00
bunnei
47b97b9577 service: fsp_srv: Stub implementation of OpenMultiCommitManager. 2020-05-11 12:54:30 -04:00
bunnei
551c61bf27 yuzu: game_list: Fix 'Open Save Data Location' for device saves. 2020-05-11 12:54:30 -04:00
bunnei
ab9ddab0a2 file_sys: savefata_factory: Update to support DeviceSaveData. 2020-05-11 12:54:30 -04:00
bunnei
9c065c013e file_sys: control_metadata: Expose device_save_data_size. 2020-05-11 12:54:30 -04:00
ReinUsesLisp
8b329ddcc9 gl_shader_decompiler: Properly emulate NaN behaviour on NE
"Not equal" operators on GLSL seem to behave as unordered when we expect
an ordered comparison.

Manually emulate this checking for LGE values (numbers, not-NaNs).
2020-05-10 02:59:33 -03:00
ReinUsesLisp
4e57f9d5cf shader_ir: Separate float-point comparisons in ordered and unordered
This allows us to use native SPIR-V instructions without having to
manually check for NAN.
2020-05-09 04:55:15 -03:00
42 changed files with 877 additions and 220 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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

49
src/common/time_zone.cpp Normal file
View File

@@ -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

18
src/common/time_zone.h Normal file
View File

@@ -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

View File

@@ -95,6 +95,10 @@ u32 NACP::GetSupportedLanguages() const {
return raw.supported_languages;
}
u64 NACP::GetDeviceSaveDataSize() const {
return raw.device_save_data_size;
}
std::vector<u8> NACP::GetRawBytes() const {
std::vector<u8> out(sizeof(RawNACP));
std::memcpy(out.data(), &raw, sizeof(RawNACP));

View File

@@ -113,6 +113,7 @@ public:
u32 GetSupportedLanguages() const;
std::vector<u8> GetRawBytes() const;
bool GetUserAccountSwitchLock() const;
u64 GetDeviceSaveDataSize() const;
private:
RawNACP raw{};

View File

@@ -57,7 +57,8 @@ void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) {
bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataDescriptor& desc) {
return desc.type == SaveDataType::CacheStorage || desc.type == SaveDataType::TemporaryStorage ||
(space == SaveDataSpaceId::NandUser && ///< Normal Save Data -- Current Title & User
desc.type == SaveDataType::SaveData && desc.title_id == 0 && desc.save_id == 0);
(desc.type == SaveDataType::SaveData || desc.type == SaveDataType::DeviceSaveData) &&
desc.title_id == 0 && desc.save_id == 0);
}
} // Anonymous namespace
@@ -139,8 +140,10 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
u128 user_id, u64 save_id) {
// According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
// be interpreted as the title id of the current process.
if (type == SaveDataType::SaveData && title_id == 0) {
title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) {
if (title_id == 0) {
title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
}
}
std::string out = GetSaveDataSpaceIdPath(space);

View File

@@ -46,7 +46,7 @@ private:
EmuWindow::EmuWindow() {
// TODO: Find a better place to set this.
config.min_client_area_size =
std::make_pair(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
std::make_pair(Layout::MinimumSize::Width, Layout::MinimumSize::Height);
active_config = config;
touch_state = std::make_shared<TouchState>();
Input::RegisterFactory<Input::TouchDevice>("emu_window", touch_state);

View File

@@ -8,6 +8,11 @@
namespace Layout {
namespace MinimumSize {
constexpr u32 Width = 640;
constexpr u32 Height = 360;
} // namespace MinimumSize
namespace ScreenUndocked {
constexpr u32 Width = 1280;
constexpr u32 Height = 720;

View File

@@ -767,7 +767,7 @@ FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter)
{1014, nullptr, "OutputMultiProgramTagAccessLog"},
{1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
{1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"},
{1200, nullptr, "OpenMultiCommitManager"},
{1200, &FSP_SRV::OpenMultiCommitManager, "OpenMultiCommitManager"},
{1300, nullptr, "OpenBisWiper"},
};
// clang-format on
@@ -988,4 +988,40 @@ void FSP_SRV::GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx) {
rb.Push(access_log_program_index);
}
class IMultiCommitManager final : public ServiceFramework<IMultiCommitManager> {
public:
explicit IMultiCommitManager() : ServiceFramework("IMultiCommitManager") {
static const FunctionInfo functions[] = {
{1, &IMultiCommitManager::Add, "Add"},
{2, &IMultiCommitManager::Commit, "Commit"},
};
RegisterHandlers(functions);
}
private:
FileSys::VirtualFile backend;
void Add(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Commit(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
};
void FSP_SRV::OpenMultiCommitManager(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IMultiCommitManager>(std::make_shared<IMultiCommitManager>());
}
} // namespace Service::FileSystem

View File

@@ -50,6 +50,7 @@ private:
void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx);
void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx);
void OpenMultiCommitManager(Kernel::HLERequestContext& ctx);
FileSystemController& fsc;

View File

@@ -138,9 +138,7 @@ u32 BufferQueue::Query(QueryType type) {
switch (type) {
case QueryType::NativeWindowFormat:
// TODO(Subv): Use an enum for this
static constexpr u32 FormatABGR8 = 1;
return FormatABGR8;
return static_cast<u32>(PixelFormat::RGBA8888);
}
UNIMPLEMENTED();

View File

@@ -66,6 +66,16 @@ public:
Rotate270 = 0x07,
};
enum class PixelFormat : u32 {
RGBA8888 = 1,
RGBX8888 = 2,
RGB888 = 3,
RGB565 = 4,
BGRA8888 = 5,
RGBA5551 = 6,
RRGBA4444 = 7,
};
struct Buffer {
enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 };

View File

@@ -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)

View File

@@ -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();
@@ -113,6 +127,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);

View File

@@ -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);

View File

@@ -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

View File

@@ -54,9 +54,7 @@ bool DmaPusher::Step() {
return true;
});
const CommandListHeader command_list_header{command_list[dma_pushbuffer_subindex++]};
GPUVAddr dma_get = command_list_header.addr;
GPUVAddr dma_put = dma_get + command_list_header.size * sizeof(u32);
bool non_main = command_list_header.is_non_main;
const GPUVAddr dma_get = command_list_header.addr;
if (dma_pushbuffer_subindex >= command_list.size()) {
// We've gone through the current list, remove it from the queue
@@ -133,11 +131,6 @@ bool DmaPusher::Step() {
index++;
}
if (!non_main) {
// TODO (degasus): This is dead code, as dma_mget is never read.
dma_mget = dma_put;
}
return true;
}

View File

@@ -102,7 +102,6 @@ private:
DmaState dma_state{};
bool dma_increment_once{};
GPUVAddr dma_mget{}; ///< main pushbuffer last read address
bool ib_enable{true}; ///< IB mode enabled
std::array<Tegra::Engines::EngineInterface*, max_subchannels> subchannels{};

View File

@@ -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 {

View File

@@ -1840,34 +1840,40 @@ private:
Type::HalfFloat};
}
template <Type type>
Expression LogicalLessThan(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 LogicalEqual(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 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 <Type type>
Expression LogicalNotEqual(Operation operation) {
return GenerateBinaryInfix(operation, "!=", Type::Bool, type, type);
}
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::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::LogicalLessThan<Type::Int>,
&GLSLDecompiler::LogicalEqual<Type::Int>,
&GLSLDecompiler::LogicalLessEqual<Type::Int>,
&GLSLDecompiler::LogicalGreaterThan<Type::Int>,
&GLSLDecompiler::LogicalNotEqual<Type::Int>,
&GLSLDecompiler::LogicalGreaterEqual<Type::Int>,
&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::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::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,

View File

@@ -569,7 +569,9 @@ void RasterizerVulkan::ReleaseFences() {
}
void RasterizerVulkan::FlushAndInvalidateRegion(VAddr addr, u64 size) {
FlushRegion(addr, size);
if (Settings::IsGPULevelExtreme()) {
FlushRegion(addr, size);
}
InvalidateRegion(addr, size);
}

View File

@@ -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>,

View File

@@ -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);

View File

@@ -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));

View File

@@ -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

View File

@@ -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) {
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},
};
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));
if (condition == PredCondition::T) {
return GetPredicate(true);
} else if (condition == PredCondition::F) {
return GetPredicate(false);
}
return predicate;
static constexpr std::array comparison_table{
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);
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");
}

View File

@@ -150,18 +150,19 @@ public:
}
void MakeCurrent() override {
if (is_current) {
return;
// We can't track the current state of the underlying context in this wrapper class because
// Qt may make the underlying context not current for one reason or another. In particular,
// the WebBrowser uses GL, so it seems to conflict if we aren't careful.
// Instead of always just making the context current (which does not have any caching to
// check if the underlying context is already current) we can check for the current context
// in the thread local data by calling `currentContext()` and checking if its ours.
if (QOpenGLContext::currentContext() != context.get()) {
context->makeCurrent(surface);
}
is_current = context->makeCurrent(surface);
}
void DoneCurrent() override {
if (!is_current) {
return;
}
context->doneCurrent();
is_current = false;
}
QOpenGLContext* GetShareContext() {
@@ -178,7 +179,6 @@ private:
std::unique_ptr<QOpenGLContext> context;
std::unique_ptr<QOffscreenSurface> offscreen_surface{};
QSurface* surface;
bool is_current = false;
};
class DummyContext : public Core::Frontend::GraphicsContext {};

View File

@@ -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);

View File

@@ -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()) {

View File

@@ -37,5 +37,6 @@ private:
int language_index = 0;
int region_index = 0;
int time_zone_index = 0;
int sound_index = 0;
};

View File

@@ -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">

View File

@@ -488,11 +488,11 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string pat
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0);
connect(open_save_location, &QAction::triggered, [this, program_id]() {
emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData);
connect(open_save_location, &QAction::triggered, [this, program_id, path]() {
emit OpenFolderRequested(GameListOpenTarget::SaveData, path);
});
connect(open_lfs_location, &QAction::triggered, [this, program_id]() {
emit OpenFolderRequested(program_id, GameListOpenTarget::ModData);
connect(open_lfs_location, &QAction::triggered, [this, program_id, path]() {
emit OpenFolderRequested(GameListOpenTarget::ModData, path);
});
connect(open_transferable_shader_cache, &QAction::triggered,
[this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); });

View File

@@ -73,7 +73,7 @@ public:
signals:
void GameChosen(QString game_path);
void ShouldCancelWorker();
void OpenFolderRequested(u64 program_id, GameListOpenTarget target);
void OpenFolderRequested(GameListOpenTarget target, const std::string& game_path);
void OpenTransferableShaderCacheRequested(u64 program_id);
void DumpRomFSRequested(u64 program_id, const std::string& game_path);
void CopyTIDRequested(u64 program_id);

View File

@@ -19,6 +19,7 @@
#include <QTime>
#include <QtConcurrent/QtConcurrentRun>
#include "common/logging/log.h"
#include "core/frontend/framebuffer_layout.h"
#include "core/loader/loader.h"
#include "ui_loading_screen.h"
#include "video_core/rasterizer_interface.h"
@@ -61,7 +62,7 @@ LoadingScreen::LoadingScreen(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::LoadingScreen>()),
previous_stage(VideoCore::LoadCallbackStage::Complete) {
ui->setupUi(this);
setMinimumSize(1280, 720);
setMinimumSize(Layout::MinimumSize::Width, Layout::MinimumSize::Height);
// Create a fade out effect to hide this loading screen widget.
// When fading opacity, it will fade to the parent widgets background color, which is why we

View File

@@ -135,6 +135,28 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
}
#endif
namespace {
QString GetAccountUsername() {
const QString nouser = QString::fromStdString("No User");
Service::Account::ProfileManager manager;
const auto current_user = manager.GetUser(Settings::values.current_user);
if (!current_user.has_value() || (current_user == Common::UUID{})) {
return nouser;
}
Service::Account::ProfileBase profile;
if (!manager.GetProfileBase(*current_user, profile)) {
return nouser;
}
const auto text = Common::StringFromFixedZeroTerminatedBuffer(
reinterpret_cast<const char*>(profile.username.data()), profile.username.size());
return text.empty() ? nouser : QString::fromStdString(text);
}
} // Anonymous namespace
constexpr int default_mouse_timeout = 2500;
constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
@@ -493,6 +515,48 @@ void GMainWindow::InitializeWidgets() {
statusBar()->addPermanentWidget(label);
}
// Setup Profile button
profile_status_button = new QPushButton();
profile_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
profile_status_button->setCheckable(true);
profile_status_button->setChecked(true);
profile_status_button->setFocusPolicy(Qt::NoFocus);
const auto username = GetAccountUsername();
profile_status_button->setText(username);
connect(profile_status_button, &QPushButton::clicked, [=] {
profile_status_button->setChecked(true);
if (emulation_running) {
return;
}
// User save data
const auto select_profile = [this] {
QtProfileSelectionDialog dialog(this);
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
dialog.setWindowModality(Qt::WindowModal);
if (dialog.exec() == QDialog::Rejected) {
return -1;
}
return dialog.GetIndex();
};
const auto index = select_profile();
if (index == -1) {
return;
}
Settings::values.current_user = index;
Settings::Apply();
const auto username = GetAccountUsername();
profile_status_button->setText(username);
});
statusBar()->insertPermanentWidget(0, profile_status_button);
// Setup Dock button
dock_status_button = new QPushButton();
dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
@@ -724,13 +788,13 @@ void GMainWindow::InitializeHotkeys() {
}
void GMainWindow::SetDefaultUIGeometry() {
// geometry: 55% of the window contents are in the upper screen half, 45% in the lower half
// geometry: 53% of the window contents are in the upper screen half, 47% in the lower half
const QRect screenRect = QApplication::desktop()->screenGeometry(this);
const int w = screenRect.width() * 2 / 3;
const int h = screenRect.height() / 2;
const int h = screenRect.height() * 2 / 3;
const int x = (screenRect.x() + screenRect.width()) / 2 - w / 2;
const int y = (screenRect.y() + screenRect.height()) / 2 - h * 55 / 100;
const int y = (screenRect.y() + screenRect.height()) / 2 - h * 53 / 100;
setGeometry(x, y, w, h);
}
@@ -831,6 +895,7 @@ void GMainWindow::ConnectMenuEvents() {
&GMainWindow::OnDisplayTitleBars);
connect(ui.action_Show_Filter_Bar, &QAction::triggered, this, &GMainWindow::OnToggleFilterBar);
connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible);
connect(ui.action_Reset_Window_Size, &QAction::triggered, this, &GMainWindow::ResetWindowSize);
// Fullscreen
ui.action_Fullscreen->setShortcut(
@@ -1154,40 +1219,62 @@ void GMainWindow::OnGameListLoadFile(QString game_path) {
BootGame(game_path);
}
void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target) {
void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path) {
std::string path;
QString open_target;
const auto v_file = Core::GetGameFileFromPath(vfs, game_path);
const auto loader = Loader::GetLoader(v_file);
FileSys::NACP control{};
u64 program_id{};
loader->ReadControlData(control);
loader->ReadProgramId(program_id);
const bool has_user_save{control.GetDefaultNormalSaveSize() > 0};
const bool has_device_save{control.GetDeviceSaveDataSize() > 0};
ASSERT_MSG(has_user_save != has_device_save, "Game uses both user and device savedata?");
switch (target) {
case GameListOpenTarget::SaveData: {
open_target = tr("Save Data");
const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
ASSERT(program_id != 0);
const auto select_profile = [this] {
QtProfileSelectionDialog dialog(this);
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
dialog.setWindowModality(Qt::WindowModal);
if (has_user_save) {
// User save data
const auto select_profile = [this] {
QtProfileSelectionDialog dialog(this);
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
dialog.setWindowModality(Qt::WindowModal);
if (dialog.exec() == QDialog::Rejected) {
return -1;
if (dialog.exec() == QDialog::Rejected) {
return -1;
}
return dialog.GetIndex();
};
const auto index = select_profile();
if (index == -1) {
return;
}
return dialog.GetIndex();
};
const auto index = select_profile();
if (index == -1) {
return;
Service::Account::ProfileManager manager;
const auto user_id = manager.GetUser(static_cast<std::size_t>(index));
ASSERT(user_id);
path = nand_dir + FileSys::SaveDataFactory::GetFullPath(
FileSys::SaveDataSpaceId::NandUser,
FileSys::SaveDataType::SaveData, program_id, user_id->uuid, 0);
} else {
// Device save data
path = nand_dir + FileSys::SaveDataFactory::GetFullPath(
FileSys::SaveDataSpaceId::NandUser,
FileSys::SaveDataType::SaveData, program_id, {}, 0);
}
Service::Account::ProfileManager manager;
const auto user_id = manager.GetUser(static_cast<std::size_t>(index));
ASSERT(user_id);
path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser,
FileSys::SaveDataType::SaveData,
program_id, user_id->uuid, 0);
if (!FileUtil::Exists(path)) {
FileUtil::CreateFullPath(path);
FileUtil::CreateDir(path);
@@ -1829,6 +1916,20 @@ void GMainWindow::ToggleWindowMode() {
}
}
void GMainWindow::ResetWindowSize() {
const auto aspect_ratio = Layout::EmulationAspectRatio(
static_cast<Layout::AspectRatio>(Settings::values.aspect_ratio),
static_cast<float>(Layout::ScreenUndocked::Height) / Layout::ScreenUndocked::Width);
if (!ui.action_Single_Window_Mode->isChecked()) {
render_window->resize(Layout::ScreenUndocked::Height / aspect_ratio,
Layout::ScreenUndocked::Height);
} else {
resize(Layout::ScreenUndocked::Height / aspect_ratio,
Layout::ScreenUndocked::Height + menuBar()->height() +
(ui.action_Show_Status_Bar->isChecked() ? statusBar()->height() : 0));
}
}
void GMainWindow::OnConfigure() {
const auto old_theme = UISettings::values.theme;
const bool old_discord_presence = UISettings::values.enable_discord_presence;
@@ -1865,6 +1966,8 @@ void GMainWindow::OnConfigure() {
ui.centralwidget->setMouseTracking(false);
}
const auto username = GetAccountUsername();
profile_status_button->setText(username);
dock_status_button->setChecked(Settings::values.use_docked_mode);
async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation);
#ifdef HAS_VULKAN

View File

@@ -183,7 +183,7 @@ private slots:
void OnMenuReportCompatibility();
/// Called whenever a user selects a game in the game list widget.
void OnGameListLoadFile(QString game_path);
void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target);
void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path);
void OnTransferableShaderCacheOpenFile(u64 program_id);
void OnGameListDumpRomFS(u64 program_id, const std::string& game_path);
void OnGameListCopyTID(u64 program_id);
@@ -208,6 +208,7 @@ private slots:
void ShowFullscreen();
void HideFullscreen();
void ToggleWindowMode();
void ResetWindowSize();
void OnCaptureScreenshot();
void OnCoreError(Core::System::ResultStatus, std::string);
void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
@@ -232,6 +233,7 @@ private:
QLabel* emu_speed_label = nullptr;
QLabel* game_fps_label = nullptr;
QLabel* emu_frametime_label = nullptr;
QPushButton* profile_status_button = nullptr;
QPushButton* async_status_button = nullptr;
QPushButton* renderer_status_button = nullptr;
QPushButton* dock_status_button = nullptr;

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1081</width>
<height>730</height>
<width>1280</width>
<height>720</height>
</rect>
</property>
<property name="windowTitle">
@@ -44,7 +44,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>1081</width>
<width>1280</width>
<height>21</height>
</rect>
</property>
@@ -96,6 +96,7 @@
<addaction name="action_Display_Dock_Widget_Headers"/>
<addaction name="action_Show_Filter_Bar"/>
<addaction name="action_Show_Status_Bar"/>
<addaction name="action_Reset_Window_Size"/>
<addaction name="separator"/>
<addaction name="menu_View_Debugging"/>
</widget>
@@ -215,6 +216,11 @@
<string>Show Status Bar</string>
</property>
</action>
<action name="action_Reset_Window_Size">
<property name="text">
<string>Reset Window Size</string>
</property>
</action>
<action name="action_Fullscreen">
<property name="checkable">
<bool>true</bool>

View File

@@ -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);

View File

@@ -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