Merge branch 'master' of https://github.com/yuzu-emu/yuzu into u/kemathe/GPU-puller
This commit is contained in:
@@ -22,8 +22,7 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
|
|||||||
return Stream::Format::Multi51Channel16;
|
return Stream::Format::Multi51Channel16;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_CRITICAL(Audio, "Unimplemented num_channels={}", num_channels);
|
UNIMPLEMENTED_MSG("Unimplemented num_channels={}", num_channels);
|
||||||
UNREACHABLE();
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -260,8 +260,7 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
LOG_CRITICAL(Audio, "Unimplemented sample_format={}", info.sample_format);
|
UNIMPLEMENTED_MSG("Unimplemented sample_format={}", info.sample_format);
|
||||||
UNREACHABLE();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,8 +279,7 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
LOG_CRITICAL(Audio, "Unimplemented channel_count={}", info.channel_count);
|
UNIMPLEMENTED_MSG("Unimplemented channel_count={}", info.channel_count);
|
||||||
UNREACHABLE();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,7 @@ u32 Stream::GetNumChannels() const {
|
|||||||
case Format::Multi51Channel16:
|
case Format::Multi51Channel16:
|
||||||
return 6;
|
return 6;
|
||||||
}
|
}
|
||||||
LOG_CRITICAL(Audio, "Unimplemented format={}", static_cast<u32>(format));
|
UNIMPLEMENTED_MSG("Unimplemented format={}", static_cast<u32>(format));
|
||||||
UNREACHABLE();
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +48,7 @@ void Stream::Play() {
|
|||||||
|
|
||||||
void Stream::Stop() {
|
void Stream::Stop() {
|
||||||
state = State::Stopped;
|
state = State::Stopped;
|
||||||
ASSERT_MSG(false, "Unimplemented");
|
UNIMPLEMENTED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream::State Stream::GetState() const {
|
Stream::State Stream::GetState() const {
|
||||||
@@ -120,7 +119,7 @@ bool Stream::QueueBuffer(BufferPtr&& buffer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Stream::ContainsBuffer(Buffer::Tag tag) const {
|
bool Stream::ContainsBuffer(Buffer::Tag tag) const {
|
||||||
ASSERT_MSG(false, "Unimplemented");
|
UNIMPLEMENTED();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ add_library(common STATIC
|
|||||||
detached_tasks.cpp
|
detached_tasks.cpp
|
||||||
detached_tasks.h
|
detached_tasks.h
|
||||||
bit_field.h
|
bit_field.h
|
||||||
|
bit_util.h
|
||||||
cityhash.cpp
|
cityhash.cpp
|
||||||
cityhash.h
|
cityhash.h
|
||||||
color.h
|
color.h
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
// Copyright 2018 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <climits>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <intrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
/// Gets the size of a specified type T in bits.
|
||||||
|
template <typename T>
|
||||||
|
constexpr std::size_t BitSize() {
|
||||||
|
return sizeof(T) * CHAR_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
inline u32 CountLeadingZeroes32(u32 value) {
|
||||||
|
unsigned long leading_zero = 0;
|
||||||
|
|
||||||
|
if (_BitScanReverse(&leading_zero, value) != 0) {
|
||||||
|
return 31 - leading_zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline u64 CountLeadingZeroes64(u64 value) {
|
||||||
|
unsigned long leading_zero = 0;
|
||||||
|
|
||||||
|
if (_BitScanReverse64(&leading_zero, value) != 0) {
|
||||||
|
return 63 - leading_zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 64;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
inline u32 CountLeadingZeroes32(u32 value) {
|
||||||
|
if (value == 0) {
|
||||||
|
return 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
return __builtin_clz(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline u64 CountLeadingZeroes64(u64 value) {
|
||||||
|
if (value == 0) {
|
||||||
|
return 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
return __builtin_clzll(value);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} // namespace Common
|
||||||
@@ -12,7 +12,7 @@ template <typename T>
|
|||||||
class Quaternion {
|
class Quaternion {
|
||||||
public:
|
public:
|
||||||
Math::Vec3<T> xyz;
|
Math::Vec3<T> xyz;
|
||||||
T w;
|
T w{};
|
||||||
|
|
||||||
Quaternion<decltype(-T{})> Inverse() const {
|
Quaternion<decltype(-T{})> Inverse() const {
|
||||||
return {-xyz, w};
|
return {-xyz, w};
|
||||||
|
|||||||
@@ -83,6 +83,8 @@ add_library(core STATIC
|
|||||||
file_sys/vfs_vector.h
|
file_sys/vfs_vector.h
|
||||||
file_sys/xts_archive.cpp
|
file_sys/xts_archive.cpp
|
||||||
file_sys/xts_archive.h
|
file_sys/xts_archive.h
|
||||||
|
frontend/applets/profile_select.cpp
|
||||||
|
frontend/applets/profile_select.h
|
||||||
frontend/applets/software_keyboard.cpp
|
frontend/applets/software_keyboard.cpp
|
||||||
frontend/applets/software_keyboard.h
|
frontend/applets/software_keyboard.h
|
||||||
frontend/emu_window.cpp
|
frontend/emu_window.cpp
|
||||||
@@ -113,6 +115,8 @@ add_library(core STATIC
|
|||||||
hle/kernel/object.h
|
hle/kernel/object.h
|
||||||
hle/kernel/process.cpp
|
hle/kernel/process.cpp
|
||||||
hle/kernel/process.h
|
hle/kernel/process.h
|
||||||
|
hle/kernel/process_capability.cpp
|
||||||
|
hle/kernel/process_capability.h
|
||||||
hle/kernel/readable_event.cpp
|
hle/kernel/readable_event.cpp
|
||||||
hle/kernel/readable_event.h
|
hle/kernel/readable_event.h
|
||||||
hle/kernel/resource_limit.cpp
|
hle/kernel/resource_limit.cpp
|
||||||
@@ -162,6 +166,8 @@ add_library(core STATIC
|
|||||||
hle/service/am/applet_oe.h
|
hle/service/am/applet_oe.h
|
||||||
hle/service/am/applets/applets.cpp
|
hle/service/am/applets/applets.cpp
|
||||||
hle/service/am/applets/applets.h
|
hle/service/am/applets/applets.h
|
||||||
|
hle/service/am/applets/profile_select.cpp
|
||||||
|
hle/service/am/applets/profile_select.h
|
||||||
hle/service/am/applets/software_keyboard.cpp
|
hle/service/am/applets/software_keyboard.cpp
|
||||||
hle/service/am/applets/software_keyboard.h
|
hle/service/am/applets/software_keyboard.h
|
||||||
hle/service/am/applets/stub_applet.cpp
|
hle/service/am/applets/stub_applet.cpp
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
|
|||||||
config.tpidr_el0 = &cb->tpidr_el0;
|
config.tpidr_el0 = &cb->tpidr_el0;
|
||||||
config.dczid_el0 = 4;
|
config.dczid_el0 = 4;
|
||||||
config.ctr_el0 = 0x8444c004;
|
config.ctr_el0 = 0x8444c004;
|
||||||
|
config.cntfrq_el0 = 19200000; // Value from fusee.
|
||||||
|
|
||||||
// Unpredictable instructions
|
// Unpredictable instructions
|
||||||
config.define_unpredictable_behaviour = true;
|
config.define_unpredictable_behaviour = true;
|
||||||
|
|||||||
@@ -99,6 +99,8 @@ struct System::Impl {
|
|||||||
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
|
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
|
||||||
|
|
||||||
/// Create default implementations of applets if one is not provided.
|
/// Create default implementations of applets if one is not provided.
|
||||||
|
if (profile_selector == nullptr)
|
||||||
|
profile_selector = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
|
||||||
if (software_keyboard == nullptr)
|
if (software_keyboard == nullptr)
|
||||||
software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
|
software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
|
||||||
|
|
||||||
@@ -229,6 +231,7 @@ struct System::Impl {
|
|||||||
bool is_powered_on = false;
|
bool is_powered_on = false;
|
||||||
|
|
||||||
/// Frontend applets
|
/// Frontend applets
|
||||||
|
std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector;
|
||||||
std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
|
std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
|
||||||
|
|
||||||
/// Service manager
|
/// Service manager
|
||||||
@@ -424,6 +427,14 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
|
|||||||
return impl->virtual_filesystem;
|
return impl->virtual_filesystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void System::SetProfileSelector(std::unique_ptr<Core::Frontend::ProfileSelectApplet> applet) {
|
||||||
|
impl->profile_selector = std::move(applet);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Core::Frontend::ProfileSelectApplet& System::GetProfileSelector() const {
|
||||||
|
return *impl->profile_selector;
|
||||||
|
}
|
||||||
|
|
||||||
void System::SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet) {
|
void System::SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet) {
|
||||||
impl->software_keyboard = std::move(applet);
|
impl->software_keyboard = std::move(applet);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/file_sys/vfs_types.h"
|
#include "core/file_sys/vfs_types.h"
|
||||||
#include "core/hle/kernel/object.h"
|
#include "core/hle/kernel/object.h"
|
||||||
|
#include "frontend/applets/profile_select.h"
|
||||||
|
|
||||||
namespace Core::Frontend {
|
namespace Core::Frontend {
|
||||||
class EmuWindow;
|
class EmuWindow;
|
||||||
@@ -241,6 +242,10 @@ public:
|
|||||||
|
|
||||||
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
|
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
|
||||||
|
|
||||||
|
void SetProfileSelector(std::unique_ptr<Core::Frontend::ProfileSelectApplet> applet);
|
||||||
|
|
||||||
|
const Core::Frontend::ProfileSelectApplet& GetProfileSelector() const;
|
||||||
|
|
||||||
void SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet);
|
void SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet);
|
||||||
|
|
||||||
const Core::Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const;
|
const Core::Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const;
|
||||||
|
|||||||
@@ -36,18 +36,20 @@ std::string LanguageEntry::GetDeveloperName() const {
|
|||||||
developer_name.size());
|
developer_name.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
|
NACP::NACP() = default;
|
||||||
file->ReadObject(raw.get());
|
|
||||||
|
NACP::NACP(VirtualFile file) {
|
||||||
|
file->ReadObject(&raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
NACP::~NACP() = default;
|
NACP::~NACP() = default;
|
||||||
|
|
||||||
const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
|
const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
|
||||||
if (language != Language::Default) {
|
if (language != Language::Default) {
|
||||||
return raw->language_entries.at(static_cast<u8>(language));
|
return raw.language_entries.at(static_cast<u8>(language));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& language_entry : raw->language_entries) {
|
for (const auto& language_entry : raw.language_entries) {
|
||||||
if (!language_entry.GetApplicationName().empty())
|
if (!language_entry.GetApplicationName().empty())
|
||||||
return language_entry;
|
return language_entry;
|
||||||
}
|
}
|
||||||
@@ -65,21 +67,29 @@ std::string NACP::GetDeveloperName(Language language) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u64 NACP::GetTitleId() const {
|
u64 NACP::GetTitleId() const {
|
||||||
return raw->title_id;
|
return raw.title_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 NACP::GetDLCBaseTitleId() const {
|
u64 NACP::GetDLCBaseTitleId() const {
|
||||||
return raw->dlc_base_title_id;
|
return raw.dlc_base_title_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string NACP::GetVersionString() const {
|
std::string NACP::GetVersionString() const {
|
||||||
return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(),
|
return Common::StringFromFixedZeroTerminatedBuffer(raw.version_string.data(),
|
||||||
raw->version_string.size());
|
raw.version_string.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 NACP::GetDefaultNormalSaveSize() const {
|
||||||
|
return raw.normal_save_data_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 NACP::GetDefaultJournalSaveSize() const {
|
||||||
|
return raw.journal_sava_data_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u8> NACP::GetRawBytes() const {
|
std::vector<u8> NACP::GetRawBytes() const {
|
||||||
std::vector<u8> out(sizeof(RawNACP));
|
std::vector<u8> out(sizeof(RawNACP));
|
||||||
std::memcpy(out.data(), raw.get(), sizeof(RawNACP));
|
std::memcpy(out.data(), &raw, sizeof(RawNACP));
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
} // namespace FileSys
|
} // namespace FileSys
|
||||||
|
|||||||
@@ -28,17 +28,30 @@ static_assert(sizeof(LanguageEntry) == 0x300, "LanguageEntry has incorrect size.
|
|||||||
// The raw file format of a NACP file.
|
// The raw file format of a NACP file.
|
||||||
struct RawNACP {
|
struct RawNACP {
|
||||||
std::array<LanguageEntry, 16> language_entries;
|
std::array<LanguageEntry, 16> language_entries;
|
||||||
INSERT_PADDING_BYTES(0x38);
|
std::array<u8, 0x25> isbn;
|
||||||
|
u8 startup_user_account;
|
||||||
|
INSERT_PADDING_BYTES(2);
|
||||||
|
u32_le application_attribute;
|
||||||
|
u32_le supported_languages;
|
||||||
|
u32_le parental_control;
|
||||||
|
bool screenshot_enabled;
|
||||||
|
u8 video_capture_mode;
|
||||||
|
bool data_loss_confirmation;
|
||||||
|
INSERT_PADDING_BYTES(1);
|
||||||
u64_le title_id;
|
u64_le title_id;
|
||||||
INSERT_PADDING_BYTES(0x20);
|
std::array<u8, 0x20> rating_age;
|
||||||
std::array<char, 0x10> version_string;
|
std::array<char, 0x10> version_string;
|
||||||
u64_le dlc_base_title_id;
|
u64_le dlc_base_title_id;
|
||||||
u64_le title_id_2;
|
u64_le title_id_2;
|
||||||
INSERT_PADDING_BYTES(0x28);
|
u64_le normal_save_data_size;
|
||||||
|
u64_le journal_sava_data_size;
|
||||||
|
INSERT_PADDING_BYTES(0x18);
|
||||||
u64_le product_code;
|
u64_le product_code;
|
||||||
u64_le title_id_3;
|
std::array<u64_le, 0x8> local_communication;
|
||||||
std::array<u64_le, 0x7> title_id_array;
|
u8 logo_type;
|
||||||
INSERT_PADDING_BYTES(0x8);
|
u8 logo_handling;
|
||||||
|
bool runtime_add_on_content_install;
|
||||||
|
INSERT_PADDING_BYTES(5);
|
||||||
u64_le title_id_update;
|
u64_le title_id_update;
|
||||||
std::array<u8, 0x40> bcat_passphrase;
|
std::array<u8, 0x40> bcat_passphrase;
|
||||||
INSERT_PADDING_BYTES(0xEC0);
|
INSERT_PADDING_BYTES(0xEC0);
|
||||||
@@ -72,6 +85,7 @@ extern const std::array<const char*, 15> LANGUAGE_NAMES;
|
|||||||
// These store application name, dev name, title id, and other miscellaneous data.
|
// These store application name, dev name, title id, and other miscellaneous data.
|
||||||
class NACP {
|
class NACP {
|
||||||
public:
|
public:
|
||||||
|
explicit NACP();
|
||||||
explicit NACP(VirtualFile file);
|
explicit NACP(VirtualFile file);
|
||||||
~NACP();
|
~NACP();
|
||||||
|
|
||||||
@@ -81,10 +95,12 @@ public:
|
|||||||
u64 GetTitleId() const;
|
u64 GetTitleId() const;
|
||||||
u64 GetDLCBaseTitleId() const;
|
u64 GetDLCBaseTitleId() const;
|
||||||
std::string GetVersionString() const;
|
std::string GetVersionString() const;
|
||||||
|
u64 GetDefaultNormalSaveSize() const;
|
||||||
|
u64 GetDefaultJournalSaveSize() const;
|
||||||
std::vector<u8> GetRawBytes() const;
|
std::vector<u8> GetRawBytes() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<RawNACP> raw;
|
RawNACP raw{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace FileSys
|
} // namespace FileSys
|
||||||
|
|||||||
@@ -40,6 +40,13 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
|
|||||||
if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset))
|
if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset))
|
||||||
return Loader::ResultStatus::ErrorBadFileAccessHeader;
|
return Loader::ResultStatus::ErrorBadFileAccessHeader;
|
||||||
|
|
||||||
|
aci_kernel_capabilities.resize(aci_header.kac_size / sizeof(u32));
|
||||||
|
const u64 read_size = aci_header.kac_size;
|
||||||
|
const u64 read_offset = npdm_header.aci_offset + aci_header.kac_offset;
|
||||||
|
if (file->ReadBytes(aci_kernel_capabilities.data(), read_size, read_offset) != read_size) {
|
||||||
|
return Loader::ResultStatus::ErrorBadKernelCapabilityDescriptors;
|
||||||
|
}
|
||||||
|
|
||||||
return Loader::ResultStatus::Success;
|
return Loader::ResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,6 +78,10 @@ u64 ProgramMetadata::GetFilesystemPermissions() const {
|
|||||||
return aci_file_access.permissions;
|
return aci_file_access.permissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const {
|
||||||
|
return aci_kernel_capabilities;
|
||||||
|
}
|
||||||
|
|
||||||
void ProgramMetadata::Print() const {
|
void ProgramMetadata::Print() const {
|
||||||
LOG_DEBUG(Service_FS, "Magic: {:.4}", npdm_header.magic.data());
|
LOG_DEBUG(Service_FS, "Magic: {:.4}", npdm_header.magic.data());
|
||||||
LOG_DEBUG(Service_FS, "Main thread priority: 0x{:02X}", npdm_header.main_thread_priority);
|
LOG_DEBUG(Service_FS, "Main thread priority: 0x{:02X}", npdm_header.main_thread_priority);
|
||||||
@@ -81,16 +92,20 @@ void ProgramMetadata::Print() const {
|
|||||||
LOG_DEBUG(Service_FS, " > 64-bit instructions: {}",
|
LOG_DEBUG(Service_FS, " > 64-bit instructions: {}",
|
||||||
npdm_header.has_64_bit_instructions ? "YES" : "NO");
|
npdm_header.has_64_bit_instructions ? "YES" : "NO");
|
||||||
|
|
||||||
auto address_space = "Unknown";
|
const char* address_space = "Unknown";
|
||||||
switch (npdm_header.address_space_type) {
|
switch (npdm_header.address_space_type) {
|
||||||
case ProgramAddressSpaceType::Is36Bit:
|
case ProgramAddressSpaceType::Is36Bit:
|
||||||
|
address_space = "64-bit (36-bit address space)";
|
||||||
|
break;
|
||||||
case ProgramAddressSpaceType::Is39Bit:
|
case ProgramAddressSpaceType::Is39Bit:
|
||||||
address_space = "64-bit";
|
address_space = "64-bit (39-bit address space)";
|
||||||
break;
|
break;
|
||||||
case ProgramAddressSpaceType::Is32Bit:
|
case ProgramAddressSpaceType::Is32Bit:
|
||||||
case ProgramAddressSpaceType::Is32BitNoMap:
|
|
||||||
address_space = "32-bit";
|
address_space = "32-bit";
|
||||||
break;
|
break;
|
||||||
|
case ProgramAddressSpaceType::Is32BitNoMap:
|
||||||
|
address_space = "32-bit (no map region)";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DEBUG(Service_FS, " > Address space: {}\n", address_space);
|
LOG_DEBUG(Service_FS, " > Address space: {}\n", address_space);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
#include "common/bit_field.h"
|
#include "common/bit_field.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
@@ -38,6 +39,8 @@ enum class ProgramFilePermission : u64 {
|
|||||||
*/
|
*/
|
||||||
class ProgramMetadata {
|
class ProgramMetadata {
|
||||||
public:
|
public:
|
||||||
|
using KernelCapabilityDescriptors = std::vector<u32>;
|
||||||
|
|
||||||
ProgramMetadata();
|
ProgramMetadata();
|
||||||
~ProgramMetadata();
|
~ProgramMetadata();
|
||||||
|
|
||||||
@@ -50,6 +53,7 @@ public:
|
|||||||
u32 GetMainThreadStackSize() const;
|
u32 GetMainThreadStackSize() const;
|
||||||
u64 GetTitleID() const;
|
u64 GetTitleID() const;
|
||||||
u64 GetFilesystemPermissions() const;
|
u64 GetFilesystemPermissions() const;
|
||||||
|
const KernelCapabilityDescriptors& GetKernelCapabilities() const;
|
||||||
|
|
||||||
void Print() const;
|
void Print() const;
|
||||||
|
|
||||||
@@ -154,6 +158,8 @@ private:
|
|||||||
|
|
||||||
FileAccessControl acid_file_access;
|
FileAccessControl acid_file_access;
|
||||||
FileAccessHeader aci_file_access;
|
FileAccessHeader aci_file_access;
|
||||||
|
|
||||||
|
KernelCapabilityDescriptors aci_kernel_capabilities;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace FileSys
|
} // namespace FileSys
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
|
||||||
|
|
||||||
std::string SaveDataDescriptor::DebugInfo() const {
|
std::string SaveDataDescriptor::DebugInfo() const {
|
||||||
return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}]",
|
return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}]",
|
||||||
static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id);
|
static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id);
|
||||||
@@ -128,7 +130,36 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
|
|||||||
return fmt::format("{}save/cache/{:016X}", out, title_id);
|
return fmt::format("{}save/cache/{:016X}", out, title_id);
|
||||||
default:
|
default:
|
||||||
ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type));
|
ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type));
|
||||||
|
return fmt::format("{}save/unknown_{:X}/{:016X}", out, static_cast<u8>(type), title_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
|
||||||
|
u128 user_id) const {
|
||||||
|
const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
|
||||||
|
const auto dir = GetOrCreateDirectoryRelative(this->dir, path);
|
||||||
|
|
||||||
|
const auto size_file = dir->GetFile(SAVE_DATA_SIZE_FILENAME);
|
||||||
|
if (size_file == nullptr || size_file->GetSize() < sizeof(SaveDataSize))
|
||||||
|
return {0, 0};
|
||||||
|
|
||||||
|
SaveDataSize out;
|
||||||
|
if (size_file->ReadObject(&out) != sizeof(SaveDataSize))
|
||||||
|
return {0, 0};
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
|
||||||
|
SaveDataSize new_value) {
|
||||||
|
const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
|
||||||
|
const auto dir = GetOrCreateDirectoryRelative(this->dir, path);
|
||||||
|
|
||||||
|
const auto size_file = dir->CreateFile(SAVE_DATA_SIZE_FILENAME);
|
||||||
|
if (size_file == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
size_file->Resize(sizeof(SaveDataSize));
|
||||||
|
size_file->WriteObject(new_value);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace FileSys
|
} // namespace FileSys
|
||||||
|
|||||||
@@ -46,6 +46,11 @@ struct SaveDataDescriptor {
|
|||||||
};
|
};
|
||||||
static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorrect size.");
|
static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorrect size.");
|
||||||
|
|
||||||
|
struct SaveDataSize {
|
||||||
|
u64 normal;
|
||||||
|
u64 journal;
|
||||||
|
};
|
||||||
|
|
||||||
/// File system interface to the SaveData archive
|
/// File system interface to the SaveData archive
|
||||||
class SaveDataFactory {
|
class SaveDataFactory {
|
||||||
public:
|
public:
|
||||||
@@ -60,6 +65,9 @@ public:
|
|||||||
static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
|
static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
|
||||||
u128 user_id, u64 save_id);
|
u128 user_id, u64 save_id);
|
||||||
|
|
||||||
|
SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const;
|
||||||
|
void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, SaveDataSize new_value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VirtualDir dir;
|
VirtualDir dir;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ public:
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
std::size_t WriteArray(const T* data, std::size_t number_elements, std::size_t offset = 0) {
|
std::size_t WriteArray(const T* data, std::size_t number_elements, std::size_t offset = 0) {
|
||||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||||
return Write(data, number_elements * sizeof(T), offset);
|
return Write(reinterpret_cast<const u8*>(data), number_elements * sizeof(T), offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Writes size bytes starting at memory location data to offset in file.
|
// Writes size bytes starting at memory location data to offset in file.
|
||||||
@@ -166,7 +166,7 @@ public:
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
std::size_t WriteObject(const T& data, std::size_t offset = 0) {
|
std::size_t WriteObject(const T& data, std::size_t offset = 0) {
|
||||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||||
return Write(&data, sizeof(T), offset);
|
return Write(reinterpret_cast<const u8*>(&data), sizeof(T), offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renames the file to name. Returns whether or not the operation was successsful.
|
// Renames the file to name. Returns whether or not the operation was successsful.
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// Copyright 2018 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "core/frontend/applets/profile_select.h"
|
||||||
|
#include "core/settings.h"
|
||||||
|
|
||||||
|
namespace Core::Frontend {
|
||||||
|
|
||||||
|
ProfileSelectApplet::~ProfileSelectApplet() = default;
|
||||||
|
|
||||||
|
void DefaultProfileSelectApplet::SelectProfile(
|
||||||
|
std::function<void(std::optional<Service::Account::UUID>)> callback) const {
|
||||||
|
Service::Account::ProfileManager manager;
|
||||||
|
callback(manager.GetUser(Settings::values.current_user).value_or(Service::Account::UUID{}));
|
||||||
|
LOG_INFO(Service_ACC, "called, selecting current user instead of prompting...");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Core::Frontend
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
// Copyright 2018 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <optional>
|
||||||
|
#include "core/hle/service/acc/profile_manager.h"
|
||||||
|
|
||||||
|
namespace Core::Frontend {
|
||||||
|
|
||||||
|
class ProfileSelectApplet {
|
||||||
|
public:
|
||||||
|
virtual ~ProfileSelectApplet();
|
||||||
|
|
||||||
|
virtual void SelectProfile(
|
||||||
|
std::function<void(std::optional<Service::Account::UUID>)> callback) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DefaultProfileSelectApplet final : public ProfileSelectApplet {
|
||||||
|
public:
|
||||||
|
void SelectProfile(
|
||||||
|
std::function<void(std::optional<Service::Account::UUID>)> callback) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Core::Frontend
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "core/frontend/framebuffer_layout.h"
|
#include "core/frontend/framebuffer_layout.h"
|
||||||
|
#include "core/settings.h"
|
||||||
|
|
||||||
namespace Layout {
|
namespace Layout {
|
||||||
|
|
||||||
@@ -42,4 +43,18 @@ FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FramebufferLayout FrameLayoutFromResolutionScale(u16 res_scale) {
|
||||||
|
int width, height;
|
||||||
|
|
||||||
|
if (Settings::values.use_docked_mode) {
|
||||||
|
width = ScreenDocked::WidthDocked * res_scale;
|
||||||
|
height = ScreenDocked::HeightDocked * res_scale;
|
||||||
|
} else {
|
||||||
|
width = ScreenUndocked::Width * res_scale;
|
||||||
|
height = ScreenUndocked::Height * res_scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefaultFrameLayout(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Layout
|
} // namespace Layout
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
namespace Layout {
|
namespace Layout {
|
||||||
|
|
||||||
enum ScreenUndocked : unsigned { Width = 1280, Height = 720 };
|
enum ScreenUndocked : unsigned { Width = 1280, Height = 720 };
|
||||||
|
enum ScreenDocked : unsigned { WidthDocked = 1920, HeightDocked = 1080 };
|
||||||
|
|
||||||
/// Describes the layout of the window framebuffer
|
/// Describes the layout of the window framebuffer
|
||||||
struct FramebufferLayout {
|
struct FramebufferLayout {
|
||||||
@@ -34,4 +35,10 @@ struct FramebufferLayout {
|
|||||||
*/
|
*/
|
||||||
FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height);
|
FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience method to get frame layout by resolution scale
|
||||||
|
* @param res_scale resolution scale factor
|
||||||
|
*/
|
||||||
|
FramebufferLayout FrameLayoutFromResolutionScale(u16 res_scale);
|
||||||
|
|
||||||
} // namespace Layout
|
} // namespace Layout
|
||||||
|
|||||||
@@ -201,11 +201,11 @@ void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) {
|
|||||||
modules.push_back(std::move(module));
|
modules.push_back(std::move(module));
|
||||||
}
|
}
|
||||||
|
|
||||||
static Kernel::Thread* FindThreadById(int id) {
|
static Kernel::Thread* FindThreadById(s64 id) {
|
||||||
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
|
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
|
||||||
const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
|
const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
|
||||||
for (auto& thread : threads) {
|
for (auto& thread : threads) {
|
||||||
if (thread->GetThreadID() == static_cast<u32>(id)) {
|
if (thread->GetThreadID() == static_cast<u64>(id)) {
|
||||||
current_core = core;
|
current_core = core;
|
||||||
return thread.get();
|
return thread.get();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ namespace Kernel {
|
|||||||
// Confirmed Switch kernel error codes
|
// Confirmed Switch kernel error codes
|
||||||
|
|
||||||
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
|
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
|
||||||
|
constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14};
|
||||||
constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
|
constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
|
||||||
constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
|
constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
|
||||||
constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
|
constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
|
||||||
@@ -27,9 +28,10 @@ constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118};
|
|||||||
constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119};
|
constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119};
|
||||||
constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120};
|
constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120};
|
||||||
constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121};
|
constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121};
|
||||||
constexpr ResultCode ERR_ALREADY_REGISTERED{ErrorModule::Kernel, 122};
|
constexpr ResultCode ERR_BUSY{ErrorModule::Kernel, 122};
|
||||||
constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123};
|
constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123};
|
||||||
constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125};
|
constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125};
|
||||||
|
constexpr ResultCode ERR_RESERVED_VALUE{ErrorModule::Kernel, 126};
|
||||||
constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132};
|
constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132};
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ enum KernelHandle : Handle {
|
|||||||
*/
|
*/
|
||||||
class HandleTable final : NonCopyable {
|
class HandleTable final : NonCopyable {
|
||||||
public:
|
public:
|
||||||
|
/// This is the maximum limit of handles allowed per process in Horizon
|
||||||
|
static constexpr std::size_t MAX_COUNT = 1024;
|
||||||
|
|
||||||
HandleTable();
|
HandleTable();
|
||||||
~HandleTable();
|
~HandleTable();
|
||||||
|
|
||||||
@@ -91,9 +94,6 @@ public:
|
|||||||
void Clear();
|
void Clear();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// This is the maximum limit of handles allowed per process in Horizon
|
|
||||||
static constexpr std::size_t MAX_COUNT = 1024;
|
|
||||||
|
|
||||||
/// Stores the Object referenced by the handle or null if the slot is empty.
|
/// Stores the Object referenced by the handle or null if the slot is empty.
|
||||||
std::array<SharedPtr<Object>, MAX_COUNT> objects;
|
std::array<SharedPtr<Object>, MAX_COUNT> objects;
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ struct KernelCore::Impl {
|
|||||||
|
|
||||||
void Shutdown() {
|
void Shutdown() {
|
||||||
next_object_id = 0;
|
next_object_id = 0;
|
||||||
next_process_id = 10;
|
next_process_id = Process::ProcessIDMin;
|
||||||
next_thread_id = 1;
|
next_thread_id = 1;
|
||||||
|
|
||||||
process_list.clear();
|
process_list.clear();
|
||||||
@@ -153,10 +153,8 @@ struct KernelCore::Impl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::atomic<u32> next_object_id{0};
|
std::atomic<u32> next_object_id{0};
|
||||||
// TODO(Subv): Start the process ids from 10 for now, as lower PIDs are
|
std::atomic<u64> next_process_id{Process::ProcessIDMin};
|
||||||
// reserved for low-level services
|
std::atomic<u64> next_thread_id{1};
|
||||||
std::atomic<u32> next_process_id{10};
|
|
||||||
std::atomic<u32> next_thread_id{1};
|
|
||||||
|
|
||||||
// Lists all processes that exist in the current session.
|
// Lists all processes that exist in the current session.
|
||||||
std::vector<SharedPtr<Process>> process_list;
|
std::vector<SharedPtr<Process>> process_list;
|
||||||
@@ -242,11 +240,11 @@ u32 KernelCore::CreateNewObjectID() {
|
|||||||
return impl->next_object_id++;
|
return impl->next_object_id++;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 KernelCore::CreateNewThreadID() {
|
u64 KernelCore::CreateNewThreadID() {
|
||||||
return impl->next_thread_id++;
|
return impl->next_thread_id++;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 KernelCore::CreateNewProcessID() {
|
u64 KernelCore::CreateNewProcessID() {
|
||||||
return impl->next_process_id++;
|
return impl->next_process_id++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,10 +88,10 @@ private:
|
|||||||
u32 CreateNewObjectID();
|
u32 CreateNewObjectID();
|
||||||
|
|
||||||
/// Creates a new process ID, incrementing the internal process ID counter;
|
/// Creates a new process ID, incrementing the internal process ID counter;
|
||||||
u32 CreateNewProcessID();
|
u64 CreateNewProcessID();
|
||||||
|
|
||||||
/// Creates a new thread ID, incrementing the internal thread ID counter.
|
/// Creates a new thread ID, incrementing the internal thread ID counter.
|
||||||
u32 CreateNewThreadID();
|
u64 CreateNewThreadID();
|
||||||
|
|
||||||
/// Creates a timer callback handle for the given timer.
|
/// Creates a timer callback handle for the given timer.
|
||||||
ResultVal<Handle> CreateTimerCallbackHandle(const SharedPtr<Timer>& timer);
|
ResultVal<Handle> CreateTimerCallbackHandle(const SharedPtr<Timer>& timer);
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ bool Object::IsWaitable() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|||||||
@@ -28,13 +28,11 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
|
|||||||
SharedPtr<Process> process(new Process(kernel));
|
SharedPtr<Process> process(new Process(kernel));
|
||||||
|
|
||||||
process->name = std::move(name);
|
process->name = std::move(name);
|
||||||
process->flags.raw = 0;
|
|
||||||
process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
|
|
||||||
process->resource_limit = kernel.GetSystemResourceLimit();
|
process->resource_limit = kernel.GetSystemResourceLimit();
|
||||||
process->status = ProcessStatus::Created;
|
process->status = ProcessStatus::Created;
|
||||||
process->program_id = 0;
|
process->program_id = 0;
|
||||||
process->process_id = kernel.CreateNewProcessID();
|
process->process_id = kernel.CreateNewProcessID();
|
||||||
process->svc_access_mask.set();
|
process->capabilities.InitializeForMetadatalessProcess();
|
||||||
|
|
||||||
std::mt19937 rng(Settings::values.rng_seed.value_or(0));
|
std::mt19937 rng(Settings::values.rng_seed.value_or(0));
|
||||||
std::uniform_int_distribution<u64> distribution;
|
std::uniform_int_distribution<u64> distribution;
|
||||||
@@ -64,83 +62,15 @@ ResultCode Process::ClearSignalState() {
|
|||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
|
ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
|
||||||
program_id = metadata.GetTitleID();
|
program_id = metadata.GetTitleID();
|
||||||
ideal_processor = metadata.GetMainThreadCore();
|
ideal_processor = metadata.GetMainThreadCore();
|
||||||
is_64bit_process = metadata.Is64BitProgram();
|
is_64bit_process = metadata.Is64BitProgram();
|
||||||
|
|
||||||
vm_manager.Reset(metadata.GetAddressSpaceType());
|
vm_manager.Reset(metadata.GetAddressSpaceType());
|
||||||
}
|
|
||||||
|
|
||||||
void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) {
|
const auto& caps = metadata.GetKernelCapabilities();
|
||||||
for (std::size_t i = 0; i < len; ++i) {
|
return capabilities.InitializeForUserProcess(caps.data(), caps.size(), vm_manager);
|
||||||
u32 descriptor = kernel_caps[i];
|
|
||||||
u32 type = descriptor >> 20;
|
|
||||||
|
|
||||||
if (descriptor == 0xFFFFFFFF) {
|
|
||||||
// Unused descriptor entry
|
|
||||||
continue;
|
|
||||||
} else if ((type & 0xF00) == 0xE00) { // 0x0FFF
|
|
||||||
// Allowed interrupts list
|
|
||||||
LOG_WARNING(Loader, "ExHeader allowed interrupts list ignored");
|
|
||||||
} else if ((type & 0xF80) == 0xF00) { // 0x07FF
|
|
||||||
// Allowed syscalls mask
|
|
||||||
unsigned int index = ((descriptor >> 24) & 7) * 24;
|
|
||||||
u32 bits = descriptor & 0xFFFFFF;
|
|
||||||
|
|
||||||
while (bits && index < svc_access_mask.size()) {
|
|
||||||
svc_access_mask.set(index, bits & 1);
|
|
||||||
++index;
|
|
||||||
bits >>= 1;
|
|
||||||
}
|
|
||||||
} else if ((type & 0xFF0) == 0xFE0) { // 0x00FF
|
|
||||||
// Handle table size
|
|
||||||
handle_table_size = descriptor & 0x3FF;
|
|
||||||
} else if ((type & 0xFF8) == 0xFF0) { // 0x007F
|
|
||||||
// Misc. flags
|
|
||||||
flags.raw = descriptor & 0xFFFF;
|
|
||||||
} else if ((type & 0xFFE) == 0xFF8) { // 0x001F
|
|
||||||
// Mapped memory range
|
|
||||||
if (i + 1 >= len || ((kernel_caps[i + 1] >> 20) & 0xFFE) != 0xFF8) {
|
|
||||||
LOG_WARNING(Loader, "Incomplete exheader memory range descriptor ignored.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
u32 end_desc = kernel_caps[i + 1];
|
|
||||||
++i; // Skip over the second descriptor on the next iteration
|
|
||||||
|
|
||||||
AddressMapping mapping;
|
|
||||||
mapping.address = descriptor << 12;
|
|
||||||
VAddr end_address = end_desc << 12;
|
|
||||||
|
|
||||||
if (mapping.address < end_address) {
|
|
||||||
mapping.size = end_address - mapping.address;
|
|
||||||
} else {
|
|
||||||
mapping.size = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
mapping.read_only = (descriptor & (1 << 20)) != 0;
|
|
||||||
mapping.unk_flag = (end_desc & (1 << 20)) != 0;
|
|
||||||
|
|
||||||
address_mappings.push_back(mapping);
|
|
||||||
} else if ((type & 0xFFF) == 0xFFE) { // 0x000F
|
|
||||||
// Mapped memory page
|
|
||||||
AddressMapping mapping;
|
|
||||||
mapping.address = descriptor << 12;
|
|
||||||
mapping.size = Memory::PAGE_SIZE;
|
|
||||||
mapping.read_only = false;
|
|
||||||
mapping.unk_flag = false;
|
|
||||||
|
|
||||||
address_mappings.push_back(mapping);
|
|
||||||
} else if ((type & 0xFE0) == 0xFC0) { // 0x01FF
|
|
||||||
// Kernel version
|
|
||||||
kernel_version = descriptor & 0xFFFF;
|
|
||||||
|
|
||||||
int minor = kernel_version & 0xFF;
|
|
||||||
int major = (kernel_version >> 8) & 0xFF;
|
|
||||||
LOG_INFO(Loader, "ExHeader kernel version: {}.{}", major, minor);
|
|
||||||
} else {
|
|
||||||
LOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x{:08X}", descriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
|
void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
|
||||||
@@ -268,22 +198,6 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
|
|||||||
Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
|
Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
|
|
||||||
return vm_manager.HeapAllocate(target, size, perms);
|
|
||||||
}
|
|
||||||
|
|
||||||
ResultCode Process::HeapFree(VAddr target, u32 size) {
|
|
||||||
return vm_manager.HeapFree(target, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
|
|
||||||
return vm_manager.MirrorMemory(dst_addr, src_addr, size, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) {
|
|
||||||
return vm_manager.UnmapRange(dst_addr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
Kernel::Process::Process(KernelCore& kernel) : WaitObject{kernel} {}
|
Kernel::Process::Process(KernelCore& kernel) : WaitObject{kernel} {}
|
||||||
Kernel::Process::~Process() {}
|
Kernel::Process::~Process() {}
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,9 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <boost/container/static_vector.hpp>
|
#include <boost/container/static_vector.hpp>
|
||||||
#include "common/bit_field.h"
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/hle/kernel/handle_table.h"
|
#include "core/hle/kernel/handle_table.h"
|
||||||
|
#include "core/hle/kernel/process_capability.h"
|
||||||
#include "core/hle/kernel/thread.h"
|
#include "core/hle/kernel/thread.h"
|
||||||
#include "core/hle/kernel/vm_manager.h"
|
#include "core/hle/kernel/vm_manager.h"
|
||||||
#include "core/hle/kernel/wait_object.h"
|
#include "core/hle/kernel/wait_object.h"
|
||||||
@@ -42,24 +42,6 @@ enum class MemoryRegion : u16 {
|
|||||||
BASE = 3,
|
BASE = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
union ProcessFlags {
|
|
||||||
u16 raw;
|
|
||||||
|
|
||||||
BitField<0, 1, u16>
|
|
||||||
allow_debug; ///< Allows other processes to attach to and debug this process.
|
|
||||||
BitField<1, 1, u16> force_debug; ///< Allows this process to attach to processes even if they
|
|
||||||
/// don't have allow_debug set.
|
|
||||||
BitField<2, 1, u16> allow_nonalphanum;
|
|
||||||
BitField<3, 1, u16> shared_page_writable; ///< Shared page is mapped with write permissions.
|
|
||||||
BitField<4, 1, u16> privileged_priority; ///< Can use priority levels higher than 24.
|
|
||||||
BitField<5, 1, u16> allow_main_args;
|
|
||||||
BitField<6, 1, u16> shared_device_mem;
|
|
||||||
BitField<7, 1, u16> runnable_on_sleep;
|
|
||||||
BitField<8, 4, MemoryRegion>
|
|
||||||
memory_region; ///< Default region for memory allocations for this process
|
|
||||||
BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000).
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates the status of a Process instance.
|
* Indicates the status of a Process instance.
|
||||||
*
|
*
|
||||||
@@ -120,6 +102,18 @@ struct CodeSet final {
|
|||||||
|
|
||||||
class Process final : public WaitObject {
|
class Process final : public WaitObject {
|
||||||
public:
|
public:
|
||||||
|
enum : u64 {
|
||||||
|
/// Lowest allowed process ID for a kernel initial process.
|
||||||
|
InitialKIPIDMin = 1,
|
||||||
|
/// Highest allowed process ID for a kernel initial process.
|
||||||
|
InitialKIPIDMax = 80,
|
||||||
|
|
||||||
|
/// Lowest allowed process ID for a userland process.
|
||||||
|
ProcessIDMin = 81,
|
||||||
|
/// Highest allowed process ID for a userland process.
|
||||||
|
ProcessIDMax = 0xFFFFFFFFFFFFFFFF,
|
||||||
|
};
|
||||||
|
|
||||||
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
|
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
|
||||||
|
|
||||||
static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name);
|
static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name);
|
||||||
@@ -162,7 +156,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the unique ID that identifies this particular process.
|
/// Gets the unique ID that identifies this particular process.
|
||||||
u32 GetProcessID() const {
|
u64 GetProcessID() const {
|
||||||
return process_id;
|
return process_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,13 +174,13 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the bitmask of allowed CPUs that this process' threads can run on.
|
/// Gets the bitmask of allowed CPUs that this process' threads can run on.
|
||||||
u32 GetAllowedProcessorMask() const {
|
u64 GetAllowedProcessorMask() const {
|
||||||
return allowed_processor_mask;
|
return capabilities.GetCoreMask();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the bitmask of allowed thread priorities.
|
/// Gets the bitmask of allowed thread priorities.
|
||||||
u32 GetAllowedThreadPriorityMask() const {
|
u64 GetAllowedThreadPriorityMask() const {
|
||||||
return allowed_thread_priority_mask;
|
return capabilities.GetPriorityMask();
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 IsVirtualMemoryEnabled() const {
|
u32 IsVirtualMemoryEnabled() const {
|
||||||
@@ -227,15 +221,12 @@ public:
|
|||||||
* Loads process-specifics configuration info with metadata provided
|
* Loads process-specifics configuration info with metadata provided
|
||||||
* by an executable.
|
* by an executable.
|
||||||
*
|
*
|
||||||
* @param metadata The provided metadata to load process specific info.
|
* @param metadata The provided metadata to load process specific info from.
|
||||||
|
*
|
||||||
|
* @returns RESULT_SUCCESS if all relevant metadata was able to be
|
||||||
|
* loaded and parsed. Otherwise, an error code is returned.
|
||||||
*/
|
*/
|
||||||
void LoadFromMetadata(const FileSys::ProgramMetadata& metadata);
|
ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata);
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them
|
|
||||||
* to this process.
|
|
||||||
*/
|
|
||||||
void ParseKernelCaps(const u32* kernel_caps, std::size_t len);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies address space changes and launches the process main thread.
|
* Applies address space changes and launches the process main thread.
|
||||||
@@ -251,7 +242,7 @@ public:
|
|||||||
void LoadModule(CodeSet module_, VAddr base_addr);
|
void LoadModule(CodeSet module_, VAddr base_addr);
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Memory Management
|
// Thread-local storage management
|
||||||
|
|
||||||
// Marks the next available region as used and returns the address of the slot.
|
// Marks the next available region as used and returns the address of the slot.
|
||||||
VAddr MarkNextAvailableTLSSlotAsUsed(Thread& thread);
|
VAddr MarkNextAvailableTLSSlotAsUsed(Thread& thread);
|
||||||
@@ -259,13 +250,6 @@ public:
|
|||||||
// Frees a used TLS slot identified by the given address
|
// Frees a used TLS slot identified by the given address
|
||||||
void FreeTLSSlot(VAddr tls_address);
|
void FreeTLSSlot(VAddr tls_address);
|
||||||
|
|
||||||
ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
|
|
||||||
ResultCode HeapFree(VAddr target, u32 size);
|
|
||||||
|
|
||||||
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state);
|
|
||||||
|
|
||||||
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit Process(KernelCore& kernel);
|
explicit Process(KernelCore& kernel);
|
||||||
~Process() override;
|
~Process() override;
|
||||||
@@ -288,30 +272,16 @@ private:
|
|||||||
ProcessStatus status;
|
ProcessStatus status;
|
||||||
|
|
||||||
/// The ID of this process
|
/// The ID of this process
|
||||||
u32 process_id = 0;
|
u64 process_id = 0;
|
||||||
|
|
||||||
/// Title ID corresponding to the process
|
/// Title ID corresponding to the process
|
||||||
u64 program_id;
|
u64 program_id = 0;
|
||||||
|
|
||||||
/// Resource limit descriptor for this process
|
/// Resource limit descriptor for this process
|
||||||
SharedPtr<ResourceLimit> resource_limit;
|
SharedPtr<ResourceLimit> resource_limit;
|
||||||
|
|
||||||
/// The process may only call SVCs which have the corresponding bit set.
|
|
||||||
std::bitset<0x80> svc_access_mask;
|
|
||||||
/// Maximum size of the handle table for the process.
|
|
||||||
u32 handle_table_size = 0x200;
|
|
||||||
/// Special memory ranges mapped into this processes address space. This is used to give
|
|
||||||
/// processes access to specific I/O regions and device memory.
|
|
||||||
boost::container::static_vector<AddressMapping, 8> address_mappings;
|
|
||||||
ProcessFlags flags;
|
|
||||||
/// Kernel compatibility version for this process
|
|
||||||
u16 kernel_version = 0;
|
|
||||||
/// The default CPU for this process, threads are scheduled on this cpu by default.
|
/// The default CPU for this process, threads are scheduled on this cpu by default.
|
||||||
u8 ideal_processor = 0;
|
u8 ideal_processor = 0;
|
||||||
/// Bitmask of allowed CPUs that this process' threads can run on. TODO(Subv): Actually parse
|
|
||||||
/// this value from the process header.
|
|
||||||
u32 allowed_processor_mask = THREADPROCESSORID_DEFAULT_MASK;
|
|
||||||
u32 allowed_thread_priority_mask = 0xFFFFFFFF;
|
|
||||||
u32 is_virtual_address_memory_enabled = 0;
|
u32 is_virtual_address_memory_enabled = 0;
|
||||||
|
|
||||||
/// The Thread Local Storage area is allocated as processes create threads,
|
/// The Thread Local Storage area is allocated as processes create threads,
|
||||||
@@ -321,6 +291,9 @@ private:
|
|||||||
/// This vector will grow as more pages are allocated for new threads.
|
/// This vector will grow as more pages are allocated for new threads.
|
||||||
std::vector<std::bitset<8>> tls_slots;
|
std::vector<std::bitset<8>> tls_slots;
|
||||||
|
|
||||||
|
/// Contains the parsed process capability descriptors.
|
||||||
|
ProcessCapabilities capabilities;
|
||||||
|
|
||||||
/// Whether or not this process is AArch64, or AArch32.
|
/// Whether or not this process is AArch64, or AArch32.
|
||||||
/// By default, we currently assume this is true, unless otherwise
|
/// By default, we currently assume this is true, unless otherwise
|
||||||
/// specified by metadata provided to the process during loading.
|
/// specified by metadata provided to the process during loading.
|
||||||
|
|||||||
@@ -0,0 +1,355 @@
|
|||||||
|
// Copyright 2018 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "common/bit_util.h"
|
||||||
|
#include "core/hle/kernel/errors.h"
|
||||||
|
#include "core/hle/kernel/handle_table.h"
|
||||||
|
#include "core/hle/kernel/process_capability.h"
|
||||||
|
#include "core/hle/kernel/vm_manager.h"
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
|
||||||
|
// Shift offsets for kernel capability types.
|
||||||
|
enum : u32 {
|
||||||
|
CapabilityOffset_PriorityAndCoreNum = 3,
|
||||||
|
CapabilityOffset_Syscall = 4,
|
||||||
|
CapabilityOffset_MapPhysical = 6,
|
||||||
|
CapabilityOffset_MapIO = 7,
|
||||||
|
CapabilityOffset_Interrupt = 11,
|
||||||
|
CapabilityOffset_ProgramType = 13,
|
||||||
|
CapabilityOffset_KernelVersion = 14,
|
||||||
|
CapabilityOffset_HandleTableSize = 15,
|
||||||
|
CapabilityOffset_Debug = 16,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Combined mask of all parameters that may be initialized only once.
|
||||||
|
constexpr u32 InitializeOnceMask = (1U << CapabilityOffset_PriorityAndCoreNum) |
|
||||||
|
(1U << CapabilityOffset_ProgramType) |
|
||||||
|
(1U << CapabilityOffset_KernelVersion) |
|
||||||
|
(1U << CapabilityOffset_HandleTableSize) |
|
||||||
|
(1U << CapabilityOffset_Debug);
|
||||||
|
|
||||||
|
// Packed kernel version indicating 10.4.0
|
||||||
|
constexpr u32 PackedKernelVersion = 0x520000;
|
||||||
|
|
||||||
|
// Indicates possible types of capabilities that can be specified.
|
||||||
|
enum class CapabilityType : u32 {
|
||||||
|
Unset = 0U,
|
||||||
|
PriorityAndCoreNum = (1U << CapabilityOffset_PriorityAndCoreNum) - 1,
|
||||||
|
Syscall = (1U << CapabilityOffset_Syscall) - 1,
|
||||||
|
MapPhysical = (1U << CapabilityOffset_MapPhysical) - 1,
|
||||||
|
MapIO = (1U << CapabilityOffset_MapIO) - 1,
|
||||||
|
Interrupt = (1U << CapabilityOffset_Interrupt) - 1,
|
||||||
|
ProgramType = (1U << CapabilityOffset_ProgramType) - 1,
|
||||||
|
KernelVersion = (1U << CapabilityOffset_KernelVersion) - 1,
|
||||||
|
HandleTableSize = (1U << CapabilityOffset_HandleTableSize) - 1,
|
||||||
|
Debug = (1U << CapabilityOffset_Debug) - 1,
|
||||||
|
Ignorable = 0xFFFFFFFFU,
|
||||||
|
};
|
||||||
|
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
constexpr CapabilityType GetCapabilityType(u32 value) {
|
||||||
|
return static_cast<CapabilityType>((~value & (value + 1)) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetFlagBitOffset(CapabilityType type) {
|
||||||
|
const auto value = static_cast<u32>(type);
|
||||||
|
return static_cast<u32>(Common::BitSize<u32>() - Common::CountLeadingZeroes32(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // Anonymous namespace
|
||||||
|
|
||||||
|
ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities,
|
||||||
|
std::size_t num_capabilities,
|
||||||
|
VMManager& vm_manager) {
|
||||||
|
Clear();
|
||||||
|
|
||||||
|
// Allow all cores and priorities.
|
||||||
|
core_mask = 0xF;
|
||||||
|
priority_mask = 0xFFFFFFFFFFFFFFFF;
|
||||||
|
kernel_version = PackedKernelVersion;
|
||||||
|
|
||||||
|
return ParseCapabilities(capabilities, num_capabilities, vm_manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode ProcessCapabilities::InitializeForUserProcess(const u32* capabilities,
|
||||||
|
std::size_t num_capabilities,
|
||||||
|
VMManager& vm_manager) {
|
||||||
|
Clear();
|
||||||
|
|
||||||
|
return ParseCapabilities(capabilities, num_capabilities, vm_manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessCapabilities::InitializeForMetadatalessProcess() {
|
||||||
|
// Allow all cores and priorities
|
||||||
|
core_mask = 0xF;
|
||||||
|
priority_mask = 0xFFFFFFFFFFFFFFFF;
|
||||||
|
kernel_version = PackedKernelVersion;
|
||||||
|
|
||||||
|
// Allow all system calls and interrupts.
|
||||||
|
svc_capabilities.set();
|
||||||
|
interrupt_capabilities.set();
|
||||||
|
|
||||||
|
// Allow using the maximum possible amount of handles
|
||||||
|
handle_table_size = static_cast<u32>(HandleTable::MAX_COUNT);
|
||||||
|
|
||||||
|
// Allow all debugging capabilities.
|
||||||
|
is_debuggable = true;
|
||||||
|
can_force_debug = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities,
|
||||||
|
std::size_t num_capabilities,
|
||||||
|
VMManager& vm_manager) {
|
||||||
|
u32 set_flags = 0;
|
||||||
|
u32 set_svc_bits = 0;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < num_capabilities; ++i) {
|
||||||
|
const u32 descriptor = capabilities[i];
|
||||||
|
const auto type = GetCapabilityType(descriptor);
|
||||||
|
|
||||||
|
if (type == CapabilityType::MapPhysical) {
|
||||||
|
i++;
|
||||||
|
|
||||||
|
// The MapPhysical type uses two descriptor flags for its parameters.
|
||||||
|
// If there's only one, then there's a problem.
|
||||||
|
if (i >= num_capabilities) {
|
||||||
|
return ERR_INVALID_COMBINATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto size_flags = capabilities[i];
|
||||||
|
if (GetCapabilityType(size_flags) != CapabilityType::MapPhysical) {
|
||||||
|
return ERR_INVALID_COMBINATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto result = HandleMapPhysicalFlags(descriptor, size_flags, vm_manager);
|
||||||
|
if (result.IsError()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const auto result =
|
||||||
|
ParseSingleFlagCapability(set_flags, set_svc_bits, descriptor, vm_manager);
|
||||||
|
if (result.IsError()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits,
|
||||||
|
u32 flag, VMManager& vm_manager) {
|
||||||
|
const auto type = GetCapabilityType(flag);
|
||||||
|
|
||||||
|
if (type == CapabilityType::Unset) {
|
||||||
|
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bail early on ignorable entries, as one would expect,
|
||||||
|
// ignorable descriptors can be ignored.
|
||||||
|
if (type == CapabilityType::Ignorable) {
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that the give flag hasn't already been initialized before.
|
||||||
|
// If it has been, then bail.
|
||||||
|
const u32 flag_length = GetFlagBitOffset(type);
|
||||||
|
const u32 set_flag = 1U << flag_length;
|
||||||
|
if ((set_flag & set_flags & InitializeOnceMask) != 0) {
|
||||||
|
return ERR_INVALID_COMBINATION;
|
||||||
|
}
|
||||||
|
set_flags |= set_flag;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case CapabilityType::PriorityAndCoreNum:
|
||||||
|
return HandlePriorityCoreNumFlags(flag);
|
||||||
|
case CapabilityType::Syscall:
|
||||||
|
return HandleSyscallFlags(set_svc_bits, flag);
|
||||||
|
case CapabilityType::MapIO:
|
||||||
|
return HandleMapIOFlags(flag, vm_manager);
|
||||||
|
case CapabilityType::Interrupt:
|
||||||
|
return HandleInterruptFlags(flag);
|
||||||
|
case CapabilityType::ProgramType:
|
||||||
|
return HandleProgramTypeFlags(flag);
|
||||||
|
case CapabilityType::KernelVersion:
|
||||||
|
return HandleKernelVersionFlags(flag);
|
||||||
|
case CapabilityType::HandleTableSize:
|
||||||
|
return HandleHandleTableFlags(flag);
|
||||||
|
case CapabilityType::Debug:
|
||||||
|
return HandleDebugFlags(flag);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessCapabilities::Clear() {
|
||||||
|
svc_capabilities.reset();
|
||||||
|
interrupt_capabilities.reset();
|
||||||
|
|
||||||
|
core_mask = 0;
|
||||||
|
priority_mask = 0;
|
||||||
|
|
||||||
|
handle_table_size = 0;
|
||||||
|
kernel_version = 0;
|
||||||
|
|
||||||
|
program_type = ProgramType::SysModule;
|
||||||
|
|
||||||
|
is_debuggable = false;
|
||||||
|
can_force_debug = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
|
||||||
|
if (priority_mask != 0 || core_mask != 0) {
|
||||||
|
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 core_num_min = (flags >> 16) & 0xFF;
|
||||||
|
const u32 core_num_max = (flags >> 24) & 0xFF;
|
||||||
|
if (core_num_min > core_num_max) {
|
||||||
|
return ERR_INVALID_COMBINATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 priority_min = (flags >> 10) & 0x3F;
|
||||||
|
const u32 priority_max = (flags >> 4) & 0x3F;
|
||||||
|
if (priority_min > priority_max) {
|
||||||
|
return ERR_INVALID_COMBINATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The switch only has 4 usable cores.
|
||||||
|
if (core_num_max >= 4) {
|
||||||
|
return ERR_INVALID_PROCESSOR_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto make_mask = [](u64 min, u64 max) {
|
||||||
|
const u64 range = max - min + 1;
|
||||||
|
const u64 mask = (1ULL << range) - 1;
|
||||||
|
|
||||||
|
return mask << min;
|
||||||
|
};
|
||||||
|
|
||||||
|
core_mask = make_mask(core_num_min, core_num_max);
|
||||||
|
priority_mask = make_mask(priority_min, priority_max);
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) {
|
||||||
|
const u32 index = flags >> 29;
|
||||||
|
const u32 svc_bit = 1U << index;
|
||||||
|
|
||||||
|
// If we've already set this svc before, bail.
|
||||||
|
if ((set_svc_bits & svc_bit) != 0) {
|
||||||
|
return ERR_INVALID_COMBINATION;
|
||||||
|
}
|
||||||
|
set_svc_bits |= svc_bit;
|
||||||
|
|
||||||
|
const u32 svc_mask = (flags >> 5) & 0xFFFFFF;
|
||||||
|
for (u32 i = 0; i < 24; ++i) {
|
||||||
|
const u32 svc_number = index * 24 + i;
|
||||||
|
|
||||||
|
if ((svc_mask & (1U << i)) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (svc_number >= svc_capabilities.size()) {
|
||||||
|
return ERR_OUT_OF_RANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
svc_capabilities[svc_number] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags,
|
||||||
|
VMManager& vm_manager) {
|
||||||
|
// TODO(Lioncache): Implement once the memory manager can handle this.
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, VMManager& vm_manager) {
|
||||||
|
// TODO(Lioncache): Implement once the memory manager can handle this.
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) {
|
||||||
|
constexpr u32 interrupt_ignore_value = 0x3FF;
|
||||||
|
const u32 interrupt0 = (flags >> 12) & 0x3FF;
|
||||||
|
const u32 interrupt1 = (flags >> 22) & 0x3FF;
|
||||||
|
|
||||||
|
for (u32 interrupt : {interrupt0, interrupt1}) {
|
||||||
|
if (interrupt == interrupt_ignore_value) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE:
|
||||||
|
// This should be checking a generic interrupt controller value
|
||||||
|
// as part of the calculation, however, given we don't currently
|
||||||
|
// emulate that, it's sufficient to mark every interrupt as defined.
|
||||||
|
|
||||||
|
if (interrupt >= interrupt_capabilities.size()) {
|
||||||
|
return ERR_OUT_OF_RANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
interrupt_capabilities[interrupt] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) {
|
||||||
|
const u32 reserved = flags >> 17;
|
||||||
|
if (reserved != 0) {
|
||||||
|
return ERR_RESERVED_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
program_type = static_cast<ProgramType>((flags >> 14) & 0b111);
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) {
|
||||||
|
// Yes, the internal member variable is checked in the actual kernel here.
|
||||||
|
// This might look odd for options that are only allowed to be initialized
|
||||||
|
// just once, however the kernel has a separate initialization function for
|
||||||
|
// kernel processes and userland processes. The kernel variant sets this
|
||||||
|
// member variable ahead of time.
|
||||||
|
|
||||||
|
const u32 major_version = kernel_version >> 19;
|
||||||
|
|
||||||
|
if (major_version != 0 || flags < 0x80000) {
|
||||||
|
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel_version = flags;
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
|
||||||
|
const u32 reserved = flags >> 26;
|
||||||
|
if (reserved != 0) {
|
||||||
|
return ERR_RESERVED_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_table_size = (flags >> 16) & 0x3FF;
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode ProcessCapabilities::HandleDebugFlags(u32 flags) {
|
||||||
|
const u32 reserved = flags >> 19;
|
||||||
|
if (reserved != 0) {
|
||||||
|
return ERR_RESERVED_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_debuggable = (flags & 0x20000) != 0;
|
||||||
|
can_force_debug = (flags & 0x40000) != 0;
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Kernel
|
||||||
@@ -0,0 +1,264 @@
|
|||||||
|
// Copyright 2018 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <bitset>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
union ResultCode;
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
class VMManager;
|
||||||
|
|
||||||
|
/// The possible types of programs that may be indicated
|
||||||
|
/// by the program type capability descriptor.
|
||||||
|
enum class ProgramType {
|
||||||
|
SysModule,
|
||||||
|
Application,
|
||||||
|
Applet,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Handles kernel capability descriptors that are provided by
|
||||||
|
/// application metadata. These descriptors provide information
|
||||||
|
/// that alters certain parameters for kernel process instance
|
||||||
|
/// that will run said application (or applet).
|
||||||
|
///
|
||||||
|
/// Capabilities are a sequence of flag descriptors, that indicate various
|
||||||
|
/// configurations and constraints for a particular process.
|
||||||
|
///
|
||||||
|
/// Flag types are indicated by a sequence of set low bits. E.g. the
|
||||||
|
/// types are indicated with the low bits as follows (where x indicates "don't care"):
|
||||||
|
///
|
||||||
|
/// - Priority and core mask : 0bxxxxxxxxxxxx0111
|
||||||
|
/// - Allowed service call mask: 0bxxxxxxxxxxx01111
|
||||||
|
/// - Map physical memory : 0bxxxxxxxxx0111111
|
||||||
|
/// - Map IO memory : 0bxxxxxxxx01111111
|
||||||
|
/// - Interrupts : 0bxxxx011111111111
|
||||||
|
/// - Application type : 0bxx01111111111111
|
||||||
|
/// - Kernel version : 0bx011111111111111
|
||||||
|
/// - Handle table size : 0b0111111111111111
|
||||||
|
/// - Debugger flags : 0b1111111111111111
|
||||||
|
///
|
||||||
|
/// These are essentially a bit offset subtracted by 1 to create a mask.
|
||||||
|
/// e.g. The first entry in the above list is simply bit 3 (value 8 -> 0b1000)
|
||||||
|
/// subtracted by one (7 -> 0b0111)
|
||||||
|
///
|
||||||
|
/// An example of a bit layout (using the map physical layout):
|
||||||
|
/// <example>
|
||||||
|
/// The MapPhysical type indicates a sequence entry pair of:
|
||||||
|
///
|
||||||
|
/// [initial, memory_flags], where:
|
||||||
|
///
|
||||||
|
/// initial:
|
||||||
|
/// bits:
|
||||||
|
/// 7-24: Starting page to map memory at.
|
||||||
|
/// 25 : Indicates if the memory should be mapped as read only.
|
||||||
|
///
|
||||||
|
/// memory_flags:
|
||||||
|
/// bits:
|
||||||
|
/// 7-20 : Number of pages to map
|
||||||
|
/// 21-25: Seems to be reserved (still checked against though)
|
||||||
|
/// 26 : Whether or not the memory being mapped is IO memory, or physical memory
|
||||||
|
/// </example>
|
||||||
|
///
|
||||||
|
class ProcessCapabilities {
|
||||||
|
public:
|
||||||
|
using InterruptCapabilities = std::bitset<1024>;
|
||||||
|
using SyscallCapabilities = std::bitset<128>;
|
||||||
|
|
||||||
|
ProcessCapabilities() = default;
|
||||||
|
ProcessCapabilities(const ProcessCapabilities&) = delete;
|
||||||
|
ProcessCapabilities(ProcessCapabilities&&) = default;
|
||||||
|
|
||||||
|
ProcessCapabilities& operator=(const ProcessCapabilities&) = delete;
|
||||||
|
ProcessCapabilities& operator=(ProcessCapabilities&&) = default;
|
||||||
|
|
||||||
|
/// Initializes this process capabilities instance for a kernel process.
|
||||||
|
///
|
||||||
|
/// @param capabilities The capabilities to parse
|
||||||
|
/// @param num_capabilities The number of capabilities to parse.
|
||||||
|
/// @param vm_manager The memory manager to use for handling any mapping-related
|
||||||
|
/// operations (such as mapping IO memory, etc).
|
||||||
|
///
|
||||||
|
/// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized,
|
||||||
|
/// otherwise, an error code upon failure.
|
||||||
|
///
|
||||||
|
ResultCode InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities,
|
||||||
|
VMManager& vm_manager);
|
||||||
|
|
||||||
|
/// Initializes this process capabilities instance for a userland process.
|
||||||
|
///
|
||||||
|
/// @param capabilities The capabilities to parse.
|
||||||
|
/// @param num_capabilities The total number of capabilities to parse.
|
||||||
|
/// @param vm_manager The memory manager to use for handling any mapping-related
|
||||||
|
/// operations (such as mapping IO memory, etc).
|
||||||
|
///
|
||||||
|
/// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized,
|
||||||
|
/// otherwise, an error code upon failure.
|
||||||
|
///
|
||||||
|
ResultCode InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities,
|
||||||
|
VMManager& vm_manager);
|
||||||
|
|
||||||
|
/// Initializes this process capabilities instance for a process that does not
|
||||||
|
/// have any metadata to parse.
|
||||||
|
///
|
||||||
|
/// This is necessary, as we allow running raw executables, and the internal
|
||||||
|
/// kernel process capabilities also determine what CPU cores the process is
|
||||||
|
/// allowed to run on, and what priorities are allowed for threads. It also
|
||||||
|
/// determines the max handle table size, what the program type is, whether or
|
||||||
|
/// not the process can be debugged, or whether it's possible for a process to
|
||||||
|
/// forcibly debug another process.
|
||||||
|
///
|
||||||
|
/// Given the above, this essentially enables all capabilities across the board
|
||||||
|
/// for the process. It allows the process to:
|
||||||
|
///
|
||||||
|
/// - Run on any core
|
||||||
|
/// - Use any thread priority
|
||||||
|
/// - Use the maximum amount of handles a process is allowed to.
|
||||||
|
/// - Be debuggable
|
||||||
|
/// - Forcibly debug other processes.
|
||||||
|
///
|
||||||
|
/// Note that this is not a behavior that the kernel allows a process to do via
|
||||||
|
/// a single function like this. This is yuzu-specific behavior to handle
|
||||||
|
/// executables with no capability descriptors whatsoever to derive behavior from.
|
||||||
|
/// It being yuzu-specific is why this is also not the default behavior and not
|
||||||
|
/// done by default in the constructor.
|
||||||
|
///
|
||||||
|
void InitializeForMetadatalessProcess();
|
||||||
|
|
||||||
|
/// Gets the allowable core mask
|
||||||
|
u64 GetCoreMask() const {
|
||||||
|
return core_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the allowable priority mask
|
||||||
|
u64 GetPriorityMask() const {
|
||||||
|
return priority_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the SVC access permission bits
|
||||||
|
const SyscallCapabilities& GetServiceCapabilities() const {
|
||||||
|
return svc_capabilities;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the valid interrupt bits.
|
||||||
|
const InterruptCapabilities& GetInterruptCapabilities() const {
|
||||||
|
return interrupt_capabilities;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the program type for this process.
|
||||||
|
ProgramType GetProgramType() const {
|
||||||
|
return program_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the number of total allowable handles for the process' handle table.
|
||||||
|
u32 GetHandleTableSize() const {
|
||||||
|
return handle_table_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the kernel version value.
|
||||||
|
u32 GetKernelVersion() const {
|
||||||
|
return kernel_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether or not this process can be debugged.
|
||||||
|
bool IsDebuggable() const {
|
||||||
|
return is_debuggable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether or not this process can forcibly debug another
|
||||||
|
/// process, even if that process is not considered debuggable.
|
||||||
|
bool CanForceDebug() const {
|
||||||
|
return can_force_debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Attempts to parse a given sequence of capability descriptors.
|
||||||
|
///
|
||||||
|
/// @param capabilities The sequence of capability descriptors to parse.
|
||||||
|
/// @param num_capabilities The number of descriptors within the given sequence.
|
||||||
|
/// @param vm_manager The memory manager that will perform any memory
|
||||||
|
/// mapping if necessary.
|
||||||
|
///
|
||||||
|
/// @return RESULT_SUCCESS if no errors occur, otherwise an error code.
|
||||||
|
///
|
||||||
|
ResultCode ParseCapabilities(const u32* capabilities, std::size_t num_capabilities,
|
||||||
|
VMManager& vm_manager);
|
||||||
|
|
||||||
|
/// Attempts to parse a capability descriptor that is only represented by a
|
||||||
|
/// single flag set.
|
||||||
|
///
|
||||||
|
/// @param set_flags Running set of flags that are used to catch
|
||||||
|
/// flags being initialized more than once when they shouldn't be.
|
||||||
|
/// @param set_svc_bits Running set of bits representing the allowed supervisor calls mask.
|
||||||
|
/// @param flag The flag to attempt to parse.
|
||||||
|
/// @param vm_manager The memory manager that will perform any memory
|
||||||
|
/// mapping if necessary.
|
||||||
|
///
|
||||||
|
/// @return RESULT_SUCCESS if no errors occurred, otherwise an error code.
|
||||||
|
///
|
||||||
|
ResultCode ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag,
|
||||||
|
VMManager& vm_manager);
|
||||||
|
|
||||||
|
/// Clears the internal state of this process capability instance. Necessary,
|
||||||
|
/// to have a sane starting point due to us allowing running executables without
|
||||||
|
/// configuration metadata. We assume a process is not going to have metadata,
|
||||||
|
/// and if it turns out that the process does, in fact, have metadata, then
|
||||||
|
/// we attempt to parse it. Thus, we need this to reset data members back to
|
||||||
|
/// a good state.
|
||||||
|
///
|
||||||
|
/// DO NOT ever make this a public member function. This isn't an invariant
|
||||||
|
/// anything external should depend upon (and if anything comes to rely on it,
|
||||||
|
/// you should immediately be questioning the design of that thing, not this
|
||||||
|
/// class. If the kernel itself can run without depending on behavior like that,
|
||||||
|
/// then so can yuzu).
|
||||||
|
///
|
||||||
|
void Clear();
|
||||||
|
|
||||||
|
/// Handles flags related to the priority and core number capability flags.
|
||||||
|
ResultCode HandlePriorityCoreNumFlags(u32 flags);
|
||||||
|
|
||||||
|
/// Handles flags related to determining the allowable SVC mask.
|
||||||
|
ResultCode HandleSyscallFlags(u32& set_svc_bits, u32 flags);
|
||||||
|
|
||||||
|
/// Handles flags related to mapping physical memory pages.
|
||||||
|
ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, VMManager& vm_manager);
|
||||||
|
|
||||||
|
/// Handles flags related to mapping IO pages.
|
||||||
|
ResultCode HandleMapIOFlags(u32 flags, VMManager& vm_manager);
|
||||||
|
|
||||||
|
/// Handles flags related to the interrupt capability flags.
|
||||||
|
ResultCode HandleInterruptFlags(u32 flags);
|
||||||
|
|
||||||
|
/// Handles flags related to the program type.
|
||||||
|
ResultCode HandleProgramTypeFlags(u32 flags);
|
||||||
|
|
||||||
|
/// Handles flags related to the handle table size.
|
||||||
|
ResultCode HandleHandleTableFlags(u32 flags);
|
||||||
|
|
||||||
|
/// Handles flags related to the kernel version capability flags.
|
||||||
|
ResultCode HandleKernelVersionFlags(u32 flags);
|
||||||
|
|
||||||
|
/// Handles flags related to debug-specific capabilities.
|
||||||
|
ResultCode HandleDebugFlags(u32 flags);
|
||||||
|
|
||||||
|
SyscallCapabilities svc_capabilities;
|
||||||
|
InterruptCapabilities interrupt_capabilities;
|
||||||
|
|
||||||
|
u64 core_mask = 0;
|
||||||
|
u64 priority_mask = 0;
|
||||||
|
|
||||||
|
u32 handle_table_size = 0;
|
||||||
|
u32 kernel_version = 0;
|
||||||
|
|
||||||
|
ProgramType program_type = ProgramType::SysModule;
|
||||||
|
|
||||||
|
bool is_debuggable = false;
|
||||||
|
bool can_force_debug = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Kernel
|
||||||
+122
-34
@@ -190,10 +190,16 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
|
|||||||
return ERR_INVALID_SIZE;
|
return ERR_INVALID_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& process = *Core::CurrentProcess();
|
auto& vm_manager = Core::CurrentProcess()->VMManager();
|
||||||
const VAddr heap_base = process.VMManager().GetHeapRegionBaseAddress();
|
const VAddr heap_base = vm_manager.GetHeapRegionBaseAddress();
|
||||||
CASCADE_RESULT(*heap_addr,
|
const auto alloc_result =
|
||||||
process.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite));
|
vm_manager.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite);
|
||||||
|
|
||||||
|
if (alloc_result.Failed()) {
|
||||||
|
return alloc_result.Code();
|
||||||
|
}
|
||||||
|
|
||||||
|
*heap_addr = *alloc_result;
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,11 +260,52 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
|
|||||||
return vm_manager.ReprotectRange(addr, size, converted_permissions);
|
return vm_manager.ReprotectRange(addr, size, converted_permissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) {
|
static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attribute) {
|
||||||
LOG_WARNING(Kernel_SVC,
|
LOG_DEBUG(Kernel_SVC,
|
||||||
"(STUBBED) called, addr=0x{:X}, size=0x{:X}, state0=0x{:X}, state1=0x{:X}", addr,
|
"called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
|
||||||
size, state0, state1);
|
size, mask, attribute);
|
||||||
return RESULT_SUCCESS;
|
|
||||||
|
if (!Common::Is4KBAligned(address)) {
|
||||||
|
LOG_ERROR(Kernel_SVC, "Address not page aligned (0x{:016X})", address);
|
||||||
|
return ERR_INVALID_ADDRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size == 0 || !Common::Is4KBAligned(size)) {
|
||||||
|
LOG_ERROR(Kernel_SVC, "Invalid size (0x{:X}). Size must be non-zero and page aligned.",
|
||||||
|
size);
|
||||||
|
return ERR_INVALID_ADDRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsValidAddressRange(address, size)) {
|
||||||
|
LOG_ERROR(Kernel_SVC, "Address range overflowed (Address: 0x{:016X}, Size: 0x{:016X})",
|
||||||
|
address, size);
|
||||||
|
return ERR_INVALID_ADDRESS_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto mem_attribute = static_cast<MemoryAttribute>(attribute);
|
||||||
|
const auto mem_mask = static_cast<MemoryAttribute>(mask);
|
||||||
|
const auto attribute_with_mask = mem_attribute | mem_mask;
|
||||||
|
|
||||||
|
if (attribute_with_mask != mem_mask) {
|
||||||
|
LOG_ERROR(Kernel_SVC,
|
||||||
|
"Memory attribute doesn't match the given mask (Attribute: 0x{:X}, Mask: {:X}",
|
||||||
|
attribute, mask);
|
||||||
|
return ERR_INVALID_COMBINATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((attribute_with_mask | MemoryAttribute::Uncached) != MemoryAttribute::Uncached) {
|
||||||
|
LOG_ERROR(Kernel_SVC, "Specified attribute isn't equal to MemoryAttributeUncached (8).");
|
||||||
|
return ERR_INVALID_COMBINATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& vm_manager = Core::CurrentProcess()->VMManager();
|
||||||
|
if (!IsInsideAddressSpace(vm_manager, address, size)) {
|
||||||
|
LOG_ERROR(Kernel_SVC,
|
||||||
|
"Given address (0x{:016X}) is outside the bounds of the address space.", address);
|
||||||
|
return ERR_INVALID_ADDRESS_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vm_manager.SetMemoryAttribute(address, size, mem_mask, mem_attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Maps a memory range into a different range.
|
/// Maps a memory range into a different range.
|
||||||
@@ -266,15 +313,14 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
|
|||||||
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
|
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
|
||||||
src_addr, size);
|
src_addr, size);
|
||||||
|
|
||||||
auto* const current_process = Core::CurrentProcess();
|
auto& vm_manager = Core::CurrentProcess()->VMManager();
|
||||||
const auto& vm_manager = current_process->VMManager();
|
|
||||||
|
|
||||||
const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
|
const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
|
||||||
if (result != RESULT_SUCCESS) {
|
|
||||||
|
if (result.IsError()) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return current_process->MirrorMemory(dst_addr, src_addr, size, MemoryState::Stack);
|
return vm_manager.MirrorMemory(dst_addr, src_addr, size, MemoryState::Stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unmaps a region that was previously mapped with svcMapMemory
|
/// Unmaps a region that was previously mapped with svcMapMemory
|
||||||
@@ -282,15 +328,14 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
|
|||||||
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
|
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
|
||||||
src_addr, size);
|
src_addr, size);
|
||||||
|
|
||||||
auto* const current_process = Core::CurrentProcess();
|
auto& vm_manager = Core::CurrentProcess()->VMManager();
|
||||||
const auto& vm_manager = current_process->VMManager();
|
|
||||||
|
|
||||||
const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
|
const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
|
||||||
if (result != RESULT_SUCCESS) {
|
|
||||||
|
if (result.IsError()) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return current_process->UnmapMemory(dst_addr, src_addr, size);
|
return vm_manager.UnmapRange(dst_addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Connect to an OS service given the port name, returns the handle to the port to out
|
/// Connect to an OS service given the port name, returns the handle to the port to out
|
||||||
@@ -350,7 +395,7 @@ static ResultCode SendSyncRequest(Handle handle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the ID for the specified thread.
|
/// Get the ID for the specified thread.
|
||||||
static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
|
static ResultCode GetThreadId(u64* thread_id, Handle thread_handle) {
|
||||||
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
|
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
|
||||||
|
|
||||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||||
@@ -364,20 +409,33 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
|
|||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the ID of the specified process
|
/// Gets the ID of the specified process or a specified thread's owning process.
|
||||||
static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
|
static ResultCode GetProcessId(u64* process_id, Handle handle) {
|
||||||
LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle);
|
LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
|
||||||
|
|
||||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||||
const SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
|
const SharedPtr<Process> process = handle_table.Get<Process>(handle);
|
||||||
if (!process) {
|
if (process) {
|
||||||
LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
|
*process_id = process->GetProcessID();
|
||||||
process_handle);
|
return RESULT_SUCCESS;
|
||||||
return ERR_INVALID_HANDLE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*process_id = process->GetProcessID();
|
const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle);
|
||||||
return RESULT_SUCCESS;
|
if (thread) {
|
||||||
|
const Process* const owner_process = thread->GetOwnerProcess();
|
||||||
|
if (!owner_process) {
|
||||||
|
LOG_ERROR(Kernel_SVC, "Non-existent owning process encountered.");
|
||||||
|
return ERR_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*process_id = owner_process->GetProcessID();
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: This should also handle debug objects before returning.
|
||||||
|
|
||||||
|
LOG_ERROR(Kernel_SVC, "Handle does not exist, handle=0x{:08X}", handle);
|
||||||
|
return ERR_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Default thread wakeup callback for WaitSynchronization
|
/// Default thread wakeup callback for WaitSynchronization
|
||||||
@@ -878,8 +936,35 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the thread activity
|
/// Sets the thread activity
|
||||||
static ResultCode SetThreadActivity(Handle handle, u32 unknown) {
|
static ResultCode SetThreadActivity(Handle handle, u32 activity) {
|
||||||
LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, unknown=0x{:08X}", handle, unknown);
|
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity);
|
||||||
|
if (activity > static_cast<u32>(ThreadActivity::Paused)) {
|
||||||
|
return ERR_INVALID_ENUM_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* current_process = Core::CurrentProcess();
|
||||||
|
const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
|
||||||
|
if (!thread) {
|
||||||
|
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
|
||||||
|
return ERR_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thread->GetOwnerProcess() != current_process) {
|
||||||
|
LOG_ERROR(Kernel_SVC,
|
||||||
|
"The current process does not own the current thread, thread_handle={:08X} "
|
||||||
|
"thread_pid={}, "
|
||||||
|
"current_process_pid={}",
|
||||||
|
handle, thread->GetOwnerProcess()->GetProcessID(),
|
||||||
|
current_process->GetProcessID());
|
||||||
|
return ERR_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thread == GetCurrentThread()) {
|
||||||
|
LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
|
||||||
|
return ERR_BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread->SetActivity(static_cast<ThreadActivity>(activity));
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -906,7 +991,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
|
|||||||
|
|
||||||
if (thread == GetCurrentThread()) {
|
if (thread == GetCurrentThread()) {
|
||||||
LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
|
LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
|
||||||
return ERR_ALREADY_REGISTERED;
|
return ERR_BUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::ARM_Interface::ThreadContext ctx = thread->GetContext();
|
Core::ARM_Interface::ThreadContext ctx = thread->GetContext();
|
||||||
@@ -1191,7 +1276,10 @@ static ResultCode StartThread(Handle thread_handle) {
|
|||||||
ASSERT(thread->GetStatus() == ThreadStatus::Dormant);
|
ASSERT(thread->GetStatus() == ThreadStatus::Dormant);
|
||||||
|
|
||||||
thread->ResumeFromWait();
|
thread->ResumeFromWait();
|
||||||
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
|
|
||||||
|
if (thread->GetStatus() == ThreadStatus::Ready) {
|
||||||
|
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
|
||||||
|
}
|
||||||
|
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,15 @@ void SvcWrap() {
|
|||||||
template <ResultCode func(u32*, u64)>
|
template <ResultCode func(u32*, u64)>
|
||||||
void SvcWrap() {
|
void SvcWrap() {
|
||||||
u32 param_1 = 0;
|
u32 param_1 = 0;
|
||||||
u32 retval = func(¶m_1, Param(1)).raw;
|
const u32 retval = func(¶m_1, Param(1)).raw;
|
||||||
|
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||||
|
FuncReturn(retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <ResultCode func(u64*, u32)>
|
||||||
|
void SvcWrap() {
|
||||||
|
u64 param_1 = 0;
|
||||||
|
const u32 retval = func(¶m_1, static_cast<u32>(Param(1))).raw;
|
||||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||||
FuncReturn(retval);
|
FuncReturn(retval);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ void Thread::Stop() {
|
|||||||
|
|
||||||
// Clean up thread from ready queue
|
// Clean up thread from ready queue
|
||||||
// This is only needed when the thread is terminated forcefully (SVC TerminateProcess)
|
// This is only needed when the thread is terminated forcefully (SVC TerminateProcess)
|
||||||
if (status == ThreadStatus::Ready) {
|
if (status == ThreadStatus::Ready || status == ThreadStatus::Paused) {
|
||||||
scheduler->UnscheduleThread(this, current_priority);
|
scheduler->UnscheduleThread(this, current_priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,6 +140,11 @@ void Thread::ResumeFromWait() {
|
|||||||
|
|
||||||
wakeup_callback = nullptr;
|
wakeup_callback = nullptr;
|
||||||
|
|
||||||
|
if (activity == ThreadActivity::Paused) {
|
||||||
|
status = ThreadStatus::Paused;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
status = ThreadStatus::Ready;
|
status = ThreadStatus::Ready;
|
||||||
|
|
||||||
ChangeScheduler();
|
ChangeScheduler();
|
||||||
@@ -158,6 +163,9 @@ static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAdd
|
|||||||
context.cpu_registers[0] = arg;
|
context.cpu_registers[0] = arg;
|
||||||
context.pc = entry_point;
|
context.pc = entry_point;
|
||||||
context.sp = stack_top;
|
context.sp = stack_top;
|
||||||
|
// TODO(merry): Perform a hardware test to determine the below value.
|
||||||
|
// AHP = 0, DN = 1, FTZ = 1, RMode = Round towards zero
|
||||||
|
context.fpcr = 0x03C00000;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point,
|
ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point,
|
||||||
@@ -388,6 +396,23 @@ bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> t
|
|||||||
return wakeup_callback(reason, std::move(thread), std::move(object), index);
|
return wakeup_callback(reason, std::move(thread), std::move(object), index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Thread::SetActivity(ThreadActivity value) {
|
||||||
|
activity = value;
|
||||||
|
|
||||||
|
if (value == ThreadActivity::Paused) {
|
||||||
|
// Set status if not waiting
|
||||||
|
if (status == ThreadStatus::Ready) {
|
||||||
|
status = ThreadStatus::Paused;
|
||||||
|
} else if (status == ThreadStatus::Running) {
|
||||||
|
status = ThreadStatus::Paused;
|
||||||
|
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
|
||||||
|
}
|
||||||
|
} else if (status == ThreadStatus::Paused) {
|
||||||
|
// Ready to reschedule
|
||||||
|
ResumeFromWait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ enum ThreadProcessorId : s32 {
|
|||||||
enum class ThreadStatus {
|
enum class ThreadStatus {
|
||||||
Running, ///< Currently running
|
Running, ///< Currently running
|
||||||
Ready, ///< Ready to run
|
Ready, ///< Ready to run
|
||||||
|
Paused, ///< Paused by SetThreadActivity or debug
|
||||||
WaitHLEEvent, ///< Waiting for hle event to finish
|
WaitHLEEvent, ///< Waiting for hle event to finish
|
||||||
WaitSleep, ///< Waiting due to a SleepThread SVC
|
WaitSleep, ///< Waiting due to a SleepThread SVC
|
||||||
WaitIPC, ///< Waiting for the reply from an IPC request
|
WaitIPC, ///< Waiting for the reply from an IPC request
|
||||||
@@ -61,6 +62,11 @@ enum class ThreadWakeupReason {
|
|||||||
Timeout // The thread was woken up due to a wait timeout.
|
Timeout // The thread was woken up due to a wait timeout.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class ThreadActivity : u32 {
|
||||||
|
Normal = 0,
|
||||||
|
Paused = 1,
|
||||||
|
};
|
||||||
|
|
||||||
class Thread final : public WaitObject {
|
class Thread final : public WaitObject {
|
||||||
public:
|
public:
|
||||||
using TLSMemory = std::vector<u8>;
|
using TLSMemory = std::vector<u8>;
|
||||||
@@ -151,7 +157,7 @@ public:
|
|||||||
* Gets the thread's thread ID
|
* Gets the thread's thread ID
|
||||||
* @return The thread's ID
|
* @return The thread's ID
|
||||||
*/
|
*/
|
||||||
u32 GetThreadID() const {
|
u64 GetThreadID() const {
|
||||||
return thread_id;
|
return thread_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,6 +377,12 @@ public:
|
|||||||
return affinity_mask;
|
return affinity_mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThreadActivity GetActivity() const {
|
||||||
|
return activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetActivity(ThreadActivity value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit Thread(KernelCore& kernel);
|
explicit Thread(KernelCore& kernel);
|
||||||
~Thread() override;
|
~Thread() override;
|
||||||
@@ -379,7 +391,7 @@ private:
|
|||||||
|
|
||||||
Core::ARM_Interface::ThreadContext context{};
|
Core::ARM_Interface::ThreadContext context{};
|
||||||
|
|
||||||
u32 thread_id = 0;
|
u64 thread_id = 0;
|
||||||
|
|
||||||
ThreadStatus status = ThreadStatus::Dormant;
|
ThreadStatus status = ThreadStatus::Dormant;
|
||||||
|
|
||||||
@@ -439,6 +451,8 @@ private:
|
|||||||
TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>();
|
TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>();
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
||||||
|
ThreadActivity activity = ThreadActivity::Normal;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ static const char* GetMemoryStateName(MemoryState state) {
|
|||||||
|
|
||||||
bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
|
bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
|
||||||
ASSERT(base + size == next.base);
|
ASSERT(base + size == next.base);
|
||||||
if (permissions != next.permissions || meminfo_state != next.meminfo_state ||
|
if (permissions != next.permissions || state != next.state || attribute != next.attribute ||
|
||||||
type != next.type) {
|
type != next.type) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -115,7 +115,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
|
|||||||
|
|
||||||
final_vma.type = VMAType::AllocatedMemoryBlock;
|
final_vma.type = VMAType::AllocatedMemoryBlock;
|
||||||
final_vma.permissions = VMAPermission::ReadWrite;
|
final_vma.permissions = VMAPermission::ReadWrite;
|
||||||
final_vma.meminfo_state = state;
|
final_vma.state = state;
|
||||||
final_vma.backing_block = std::move(block);
|
final_vma.backing_block = std::move(block);
|
||||||
final_vma.offset = offset;
|
final_vma.offset = offset;
|
||||||
UpdatePageTableForVMA(final_vma);
|
UpdatePageTableForVMA(final_vma);
|
||||||
@@ -140,7 +140,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
|
|||||||
|
|
||||||
final_vma.type = VMAType::BackingMemory;
|
final_vma.type = VMAType::BackingMemory;
|
||||||
final_vma.permissions = VMAPermission::ReadWrite;
|
final_vma.permissions = VMAPermission::ReadWrite;
|
||||||
final_vma.meminfo_state = state;
|
final_vma.state = state;
|
||||||
final_vma.backing_memory = memory;
|
final_vma.backing_memory = memory;
|
||||||
UpdatePageTableForVMA(final_vma);
|
UpdatePageTableForVMA(final_vma);
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u6
|
|||||||
|
|
||||||
final_vma.type = VMAType::MMIO;
|
final_vma.type = VMAType::MMIO;
|
||||||
final_vma.permissions = VMAPermission::ReadWrite;
|
final_vma.permissions = VMAPermission::ReadWrite;
|
||||||
final_vma.meminfo_state = state;
|
final_vma.state = state;
|
||||||
final_vma.paddr = paddr;
|
final_vma.paddr = paddr;
|
||||||
final_vma.mmio_handler = std::move(mmio_handler);
|
final_vma.mmio_handler = std::move(mmio_handler);
|
||||||
UpdatePageTableForVMA(final_vma);
|
UpdatePageTableForVMA(final_vma);
|
||||||
@@ -189,7 +189,8 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) {
|
|||||||
VirtualMemoryArea& vma = vma_handle->second;
|
VirtualMemoryArea& vma = vma_handle->second;
|
||||||
vma.type = VMAType::Free;
|
vma.type = VMAType::Free;
|
||||||
vma.permissions = VMAPermission::None;
|
vma.permissions = VMAPermission::None;
|
||||||
vma.meminfo_state = MemoryState::Unmapped;
|
vma.state = MemoryState::Unmapped;
|
||||||
|
vma.attribute = MemoryAttribute::None;
|
||||||
|
|
||||||
vma.backing_block = nullptr;
|
vma.backing_block = nullptr;
|
||||||
vma.offset = 0;
|
vma.offset = 0;
|
||||||
@@ -308,9 +309,10 @@ MemoryInfo VMManager::QueryMemory(VAddr address) const {
|
|||||||
|
|
||||||
if (IsValidHandle(vma)) {
|
if (IsValidHandle(vma)) {
|
||||||
memory_info.base_address = vma->second.base;
|
memory_info.base_address = vma->second.base;
|
||||||
|
memory_info.attributes = ToSvcMemoryAttribute(vma->second.attribute);
|
||||||
memory_info.permission = static_cast<u32>(vma->second.permissions);
|
memory_info.permission = static_cast<u32>(vma->second.permissions);
|
||||||
memory_info.size = vma->second.size;
|
memory_info.size = vma->second.size;
|
||||||
memory_info.state = ToSvcMemoryState(vma->second.meminfo_state);
|
memory_info.state = ToSvcMemoryState(vma->second.state);
|
||||||
} else {
|
} else {
|
||||||
memory_info.base_address = address_space_end;
|
memory_info.base_address = address_space_end;
|
||||||
memory_info.permission = static_cast<u32>(VMAPermission::None);
|
memory_info.permission = static_cast<u32>(VMAPermission::None);
|
||||||
@@ -321,6 +323,34 @@ MemoryInfo VMManager::QueryMemory(VAddr address) const {
|
|||||||
return memory_info;
|
return memory_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResultCode VMManager::SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask,
|
||||||
|
MemoryAttribute attribute) {
|
||||||
|
constexpr auto ignore_mask = MemoryAttribute::Uncached | MemoryAttribute::DeviceMapped;
|
||||||
|
constexpr auto attribute_mask = ~ignore_mask;
|
||||||
|
|
||||||
|
const auto result = CheckRangeState(
|
||||||
|
address, size, MemoryState::FlagUncached, MemoryState::FlagUncached, VMAPermission::None,
|
||||||
|
VMAPermission::None, attribute_mask, MemoryAttribute::None, ignore_mask);
|
||||||
|
|
||||||
|
if (result.Failed()) {
|
||||||
|
return result.Code();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto [prev_state, prev_permissions, prev_attributes] = *result;
|
||||||
|
const auto new_attribute = (prev_attributes & ~mask) | (mask & attribute);
|
||||||
|
|
||||||
|
const auto carve_result = CarveVMARange(address, size);
|
||||||
|
if (carve_result.Failed()) {
|
||||||
|
return carve_result.Code();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto vma_iter = *carve_result;
|
||||||
|
vma_iter->second.attribute = new_attribute;
|
||||||
|
|
||||||
|
MergeAdjacent(vma_iter);
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
|
ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
|
||||||
const auto vma = FindVMA(src_addr);
|
const auto vma = FindVMA(src_addr);
|
||||||
|
|
||||||
@@ -364,7 +394,7 @@ void VMManager::LogLayout() const {
|
|||||||
(u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-',
|
(u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-',
|
||||||
(u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-',
|
(u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-',
|
||||||
(u8)vma.permissions & (u8)VMAPermission::Execute ? 'X' : '-',
|
(u8)vma.permissions & (u8)VMAPermission::Execute ? 'X' : '-',
|
||||||
GetMemoryStateName(vma.meminfo_state));
|
GetMemoryStateName(vma.state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -591,6 +621,66 @@ void VMManager::ClearPageTable() {
|
|||||||
Memory::PageType::Unmapped);
|
Memory::PageType::Unmapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, MemoryState state_mask,
|
||||||
|
MemoryState state, VMAPermission permission_mask,
|
||||||
|
VMAPermission permissions,
|
||||||
|
MemoryAttribute attribute_mask,
|
||||||
|
MemoryAttribute attribute,
|
||||||
|
MemoryAttribute ignore_mask) const {
|
||||||
|
auto iter = FindVMA(address);
|
||||||
|
|
||||||
|
// If we don't have a valid VMA handle at this point, then it means this is
|
||||||
|
// being called with an address outside of the address space, which is definitely
|
||||||
|
// indicative of a bug, as this function only operates on mapped memory regions.
|
||||||
|
DEBUG_ASSERT(IsValidHandle(iter));
|
||||||
|
|
||||||
|
const VAddr end_address = address + size - 1;
|
||||||
|
const MemoryAttribute initial_attributes = iter->second.attribute;
|
||||||
|
const VMAPermission initial_permissions = iter->second.permissions;
|
||||||
|
const MemoryState initial_state = iter->second.state;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// The iterator should be valid throughout the traversal. Hitting the end of
|
||||||
|
// the mapped VMA regions is unquestionably indicative of a bug.
|
||||||
|
DEBUG_ASSERT(IsValidHandle(iter));
|
||||||
|
|
||||||
|
const auto& vma = iter->second;
|
||||||
|
|
||||||
|
if (vma.state != initial_state) {
|
||||||
|
return ERR_INVALID_ADDRESS_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((vma.state & state_mask) != state) {
|
||||||
|
return ERR_INVALID_ADDRESS_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vma.permissions != initial_permissions) {
|
||||||
|
return ERR_INVALID_ADDRESS_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((vma.permissions & permission_mask) != permissions) {
|
||||||
|
return ERR_INVALID_ADDRESS_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((vma.attribute | ignore_mask) != (initial_attributes | ignore_mask)) {
|
||||||
|
return ERR_INVALID_ADDRESS_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((vma.attribute & attribute_mask) != attribute) {
|
||||||
|
return ERR_INVALID_ADDRESS_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end_address <= vma.EndAddress()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MakeResult(
|
||||||
|
std::make_tuple(initial_state, initial_permissions, initial_attributes & ~ignore_mask));
|
||||||
|
}
|
||||||
|
|
||||||
u64 VMManager::GetTotalMemoryUsage() const {
|
u64 VMManager::GetTotalMemoryUsage() const {
|
||||||
LOG_WARNING(Kernel, "(STUBBED) called");
|
LOG_WARNING(Kernel, "(STUBBED) called");
|
||||||
return 0xF8000000;
|
return 0xF8000000;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <tuple>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
@@ -43,6 +44,88 @@ enum class VMAPermission : u8 {
|
|||||||
ReadWriteExecute = Read | Write | Execute,
|
ReadWriteExecute = Read | Write | Execute,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr VMAPermission operator|(VMAPermission lhs, VMAPermission rhs) {
|
||||||
|
return static_cast<VMAPermission>(u32(lhs) | u32(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr VMAPermission operator&(VMAPermission lhs, VMAPermission rhs) {
|
||||||
|
return static_cast<VMAPermission>(u32(lhs) & u32(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr VMAPermission operator^(VMAPermission lhs, VMAPermission rhs) {
|
||||||
|
return static_cast<VMAPermission>(u32(lhs) ^ u32(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr VMAPermission operator~(VMAPermission permission) {
|
||||||
|
return static_cast<VMAPermission>(~u32(permission));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr VMAPermission& operator|=(VMAPermission& lhs, VMAPermission rhs) {
|
||||||
|
lhs = lhs | rhs;
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr VMAPermission& operator&=(VMAPermission& lhs, VMAPermission rhs) {
|
||||||
|
lhs = lhs & rhs;
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr VMAPermission& operator^=(VMAPermission& lhs, VMAPermission rhs) {
|
||||||
|
lhs = lhs ^ rhs;
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attribute flags that can be applied to a VMA
|
||||||
|
enum class MemoryAttribute : u32 {
|
||||||
|
Mask = 0xFF,
|
||||||
|
|
||||||
|
/// No particular qualities
|
||||||
|
None = 0,
|
||||||
|
/// Memory locked/borrowed for use. e.g. This would be used by transfer memory.
|
||||||
|
Locked = 1,
|
||||||
|
/// Memory locked for use by IPC-related internals.
|
||||||
|
LockedForIPC = 2,
|
||||||
|
/// Mapped as part of the device address space.
|
||||||
|
DeviceMapped = 4,
|
||||||
|
/// Uncached memory
|
||||||
|
Uncached = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr MemoryAttribute operator|(MemoryAttribute lhs, MemoryAttribute rhs) {
|
||||||
|
return static_cast<MemoryAttribute>(u32(lhs) | u32(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr MemoryAttribute operator&(MemoryAttribute lhs, MemoryAttribute rhs) {
|
||||||
|
return static_cast<MemoryAttribute>(u32(lhs) & u32(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr MemoryAttribute operator^(MemoryAttribute lhs, MemoryAttribute rhs) {
|
||||||
|
return static_cast<MemoryAttribute>(u32(lhs) ^ u32(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr MemoryAttribute operator~(MemoryAttribute attribute) {
|
||||||
|
return static_cast<MemoryAttribute>(~u32(attribute));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr MemoryAttribute& operator|=(MemoryAttribute& lhs, MemoryAttribute rhs) {
|
||||||
|
lhs = lhs | rhs;
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr MemoryAttribute& operator&=(MemoryAttribute& lhs, MemoryAttribute rhs) {
|
||||||
|
lhs = lhs & rhs;
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr MemoryAttribute& operator^=(MemoryAttribute& lhs, MemoryAttribute rhs) {
|
||||||
|
lhs = lhs ^ rhs;
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u32 ToSvcMemoryAttribute(MemoryAttribute attribute) {
|
||||||
|
return static_cast<u32>(attribute & MemoryAttribute::Mask);
|
||||||
|
}
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
/// Represents memory states and any relevant flags, as used by the kernel.
|
/// Represents memory states and any relevant flags, as used by the kernel.
|
||||||
/// svcQueryMemory interprets these by masking away all but the first eight
|
/// svcQueryMemory interprets these by masking away all but the first eight
|
||||||
@@ -174,6 +257,16 @@ struct PageInfo {
|
|||||||
* also backed by a single host memory allocation.
|
* also backed by a single host memory allocation.
|
||||||
*/
|
*/
|
||||||
struct VirtualMemoryArea {
|
struct VirtualMemoryArea {
|
||||||
|
/// Gets the starting (base) address of this VMA.
|
||||||
|
VAddr StartAddress() const {
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the ending address of this VMA.
|
||||||
|
VAddr EndAddress() const {
|
||||||
|
return base + size - 1;
|
||||||
|
}
|
||||||
|
|
||||||
/// Virtual base address of the region.
|
/// Virtual base address of the region.
|
||||||
VAddr base = 0;
|
VAddr base = 0;
|
||||||
/// Size of the region.
|
/// Size of the region.
|
||||||
@@ -181,8 +274,8 @@ struct VirtualMemoryArea {
|
|||||||
|
|
||||||
VMAType type = VMAType::Free;
|
VMAType type = VMAType::Free;
|
||||||
VMAPermission permissions = VMAPermission::None;
|
VMAPermission permissions = VMAPermission::None;
|
||||||
/// Tag returned by svcQueryMemory. Not otherwise used.
|
MemoryState state = MemoryState::Unmapped;
|
||||||
MemoryState meminfo_state = MemoryState::Unmapped;
|
MemoryAttribute attribute = MemoryAttribute::None;
|
||||||
|
|
||||||
// Settings for type = AllocatedMemoryBlock
|
// Settings for type = AllocatedMemoryBlock
|
||||||
/// Memory block backing this VMA.
|
/// Memory block backing this VMA.
|
||||||
@@ -299,6 +392,19 @@ public:
|
|||||||
///
|
///
|
||||||
MemoryInfo QueryMemory(VAddr address) const;
|
MemoryInfo QueryMemory(VAddr address) const;
|
||||||
|
|
||||||
|
/// Sets an attribute across the given address range.
|
||||||
|
///
|
||||||
|
/// @param address The starting address
|
||||||
|
/// @param size The size of the range to set the attribute on.
|
||||||
|
/// @param mask The attribute mask
|
||||||
|
/// @param attribute The attribute to set across the given address range
|
||||||
|
///
|
||||||
|
/// @returns RESULT_SUCCESS if successful
|
||||||
|
/// @returns ERR_INVALID_ADDRESS_STATE if the attribute could not be set.
|
||||||
|
///
|
||||||
|
ResultCode SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask,
|
||||||
|
MemoryAttribute attribute);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scans all VMAs and updates the page table range of any that use the given vector as backing
|
* Scans all VMAs and updates the page table range of any that use the given vector as backing
|
||||||
* memory. This should be called after any operation that causes reallocation of the vector.
|
* memory. This should be called after any operation that causes reallocation of the vector.
|
||||||
@@ -435,6 +541,35 @@ private:
|
|||||||
/// Clears out the page table
|
/// Clears out the page table
|
||||||
void ClearPageTable();
|
void ClearPageTable();
|
||||||
|
|
||||||
|
using CheckResults = ResultVal<std::tuple<MemoryState, VMAPermission, MemoryAttribute>>;
|
||||||
|
|
||||||
|
/// Checks if an address range adheres to the specified states provided.
|
||||||
|
///
|
||||||
|
/// @param address The starting address of the address range.
|
||||||
|
/// @param size The size of the address range.
|
||||||
|
/// @param state_mask The memory state mask.
|
||||||
|
/// @param state The state to compare the individual VMA states against,
|
||||||
|
/// which is done in the form of: (vma.state & state_mask) != state.
|
||||||
|
/// @param permission_mask The memory permissions mask.
|
||||||
|
/// @param permissions The permission to compare the individual VMA permissions against,
|
||||||
|
/// which is done in the form of:
|
||||||
|
/// (vma.permission & permission_mask) != permission.
|
||||||
|
/// @param attribute_mask The memory attribute mask.
|
||||||
|
/// @param attribute The memory attributes to compare the individual VMA attributes
|
||||||
|
/// against, which is done in the form of:
|
||||||
|
/// (vma.attributes & attribute_mask) != attribute.
|
||||||
|
/// @param ignore_mask The memory attributes to ignore during the check.
|
||||||
|
///
|
||||||
|
/// @returns If successful, returns a tuple containing the memory attributes
|
||||||
|
/// (with ignored bits specified by ignore_mask unset), memory permissions, and
|
||||||
|
/// memory state across the memory range.
|
||||||
|
/// @returns If not successful, returns ERR_INVALID_ADDRESS_STATE.
|
||||||
|
///
|
||||||
|
CheckResults CheckRangeState(VAddr address, u64 size, MemoryState state_mask, MemoryState state,
|
||||||
|
VMAPermission permission_mask, VMAPermission permissions,
|
||||||
|
MemoryAttribute attribute_mask, MemoryAttribute attribute,
|
||||||
|
MemoryAttribute ignore_mask) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A map covering the entirety of the managed address space, keyed by the `base` field of each
|
* A map covering the entirety of the managed address space, keyed by the `base` field of each
|
||||||
* VMA. It must always be modified by splitting or merging VMAs, so that the invariant
|
* VMA. It must always be modified by splitting or merging VMAs, so that the invariant
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <stack>
|
#include <stack>
|
||||||
#include "audio_core/audio_renderer.h"
|
#include "audio_core/audio_renderer.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
#include "core/file_sys/savedata_factory.h"
|
||||||
#include "core/hle/ipc_helpers.h"
|
#include "core/hle/ipc_helpers.h"
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
#include "core/hle/kernel/process.h"
|
#include "core/hle/kernel/process.h"
|
||||||
@@ -19,6 +20,7 @@
|
|||||||
#include "core/hle/service/am/applet_ae.h"
|
#include "core/hle/service/am/applet_ae.h"
|
||||||
#include "core/hle/service/am/applet_oe.h"
|
#include "core/hle/service/am/applet_oe.h"
|
||||||
#include "core/hle/service/am/applets/applets.h"
|
#include "core/hle/service/am/applets/applets.h"
|
||||||
|
#include "core/hle/service/am/applets/profile_select.h"
|
||||||
#include "core/hle/service/am/applets/software_keyboard.h"
|
#include "core/hle/service/am/applets/software_keyboard.h"
|
||||||
#include "core/hle/service/am/applets/stub_applet.h"
|
#include "core/hle/service/am/applets/stub_applet.h"
|
||||||
#include "core/hle/service/am/idle.h"
|
#include "core/hle/service/am/idle.h"
|
||||||
@@ -39,6 +41,7 @@ constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2};
|
|||||||
constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
|
constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
|
||||||
|
|
||||||
enum class AppletId : u32 {
|
enum class AppletId : u32 {
|
||||||
|
ProfileSelect = 0x10,
|
||||||
SoftwareKeyboard = 0x11,
|
SoftwareKeyboard = 0x11,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -71,10 +74,13 @@ IWindowController::IWindowController() : ServiceFramework("IWindowController") {
|
|||||||
IWindowController::~IWindowController() = default;
|
IWindowController::~IWindowController() = default;
|
||||||
|
|
||||||
void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) {
|
void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
const u64 process_id = Core::System::GetInstance().Kernel().CurrentProcess()->GetProcessID();
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_AM, "called. Process ID=0x{:016X}", process_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.Push<u64>(0);
|
rb.Push<u64>(process_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx) {
|
void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx) {
|
||||||
@@ -565,7 +571,6 @@ private:
|
|||||||
void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) {
|
void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_AM, "called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
|
|
||||||
applet->GetBroker().SignalStateChanged();
|
|
||||||
const auto event = applet->GetBroker().GetStateChangedEvent();
|
const auto event = applet->GetBroker().GetStateChangedEvent();
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||||
@@ -773,6 +778,8 @@ ILibraryAppletCreator::~ILibraryAppletCreator() = default;
|
|||||||
|
|
||||||
static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) {
|
static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) {
|
||||||
switch (id) {
|
switch (id) {
|
||||||
|
case AppletId::ProfileSelect:
|
||||||
|
return std::make_shared<Applets::ProfileSelect>();
|
||||||
case AppletId::SoftwareKeyboard:
|
case AppletId::SoftwareKeyboard:
|
||||||
return std::make_shared<Applets::SoftwareKeyboard>();
|
return std::make_shared<Applets::SoftwareKeyboard>();
|
||||||
default:
|
default:
|
||||||
@@ -859,8 +866,8 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
|
|||||||
{22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
|
{22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
|
||||||
{23, &IApplicationFunctions::GetDisplayVersion, "GetDisplayVersion"},
|
{23, &IApplicationFunctions::GetDisplayVersion, "GetDisplayVersion"},
|
||||||
{24, nullptr, "GetLaunchStorageInfoForDebug"},
|
{24, nullptr, "GetLaunchStorageInfoForDebug"},
|
||||||
{25, nullptr, "ExtendSaveData"},
|
{25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"},
|
||||||
{26, nullptr, "GetSaveDataSize"},
|
{26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"},
|
||||||
{30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"},
|
{30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"},
|
||||||
{31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"},
|
{31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"},
|
||||||
{32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
|
{32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
|
||||||
@@ -1037,6 +1044,48 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
|
|||||||
rb.Push<u64>(0);
|
rb.Push<u64>(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto type{rp.PopRaw<FileSys::SaveDataType>()};
|
||||||
|
rp.Skip(1, false);
|
||||||
|
const auto user_id{rp.PopRaw<u128>()};
|
||||||
|
const auto new_normal_size{rp.PopRaw<u64>()};
|
||||||
|
const auto new_journal_size{rp.PopRaw<u64>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_AM,
|
||||||
|
"called with type={:02X}, user_id={:016X}{:016X}, new_normal={:016X}, "
|
||||||
|
"new_journal={:016X}",
|
||||||
|
static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
|
||||||
|
|
||||||
|
FileSystem::WriteSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id,
|
||||||
|
{new_normal_size, new_journal_size});
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
|
||||||
|
// The following value is used upon failure to help the system recover.
|
||||||
|
// Since we always succeed, this should be 0.
|
||||||
|
rb.Push<u64>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto type{rp.PopRaw<FileSys::SaveDataType>()};
|
||||||
|
rp.Skip(1, false);
|
||||||
|
const auto user_id{rp.PopRaw<u128>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type),
|
||||||
|
user_id[1], user_id[0]);
|
||||||
|
|
||||||
|
const auto size =
|
||||||
|
FileSystem::ReadSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 6};
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
rb.Push(size.normal);
|
||||||
|
rb.Push(size.journal);
|
||||||
|
}
|
||||||
|
|
||||||
void InstallInterfaces(SM::ServiceManager& service_manager,
|
void InstallInterfaces(SM::ServiceManager& service_manager,
|
||||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
|
std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
|
||||||
auto message_queue = std::make_shared<AppletMessageQueue>();
|
auto message_queue = std::make_shared<AppletMessageQueue>();
|
||||||
|
|||||||
@@ -206,6 +206,8 @@ private:
|
|||||||
void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx);
|
void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx);
|
||||||
void NotifyRunning(Kernel::HLERequestContext& ctx);
|
void NotifyRunning(Kernel::HLERequestContext& ctx);
|
||||||
void GetPseudoDeviceId(Kernel::HLERequestContext& ctx);
|
void GetPseudoDeviceId(Kernel::HLERequestContext& ctx);
|
||||||
|
void ExtendSaveData(Kernel::HLERequestContext& ctx);
|
||||||
|
void GetSaveDataSize(Kernel::HLERequestContext& ctx);
|
||||||
void BeginBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
|
void BeginBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
|
||||||
void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
|
void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
|
||||||
void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
|
void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
|
||||||
|
|||||||
@@ -16,11 +16,11 @@ namespace Service::AM::Applets {
|
|||||||
AppletDataBroker::AppletDataBroker() {
|
AppletDataBroker::AppletDataBroker() {
|
||||||
auto& kernel = Core::System::GetInstance().Kernel();
|
auto& kernel = Core::System::GetInstance().Kernel();
|
||||||
state_changed_event = Kernel::WritableEvent::CreateEventPair(
|
state_changed_event = Kernel::WritableEvent::CreateEventPair(
|
||||||
kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:StateChangedEvent");
|
kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:StateChangedEvent");
|
||||||
pop_out_data_event = Kernel::WritableEvent::CreateEventPair(
|
pop_out_data_event = Kernel::WritableEvent::CreateEventPair(
|
||||||
kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:PopDataOutEvent");
|
kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:PopDataOutEvent");
|
||||||
pop_interactive_out_data_event = Kernel::WritableEvent::CreateEventPair(
|
pop_interactive_out_data_event = Kernel::WritableEvent::CreateEventPair(
|
||||||
kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
|
kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
|
||||||
}
|
}
|
||||||
|
|
||||||
AppletDataBroker::~AppletDataBroker() = default;
|
AppletDataBroker::~AppletDataBroker() = default;
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
// Copyright 2018 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/frontend/applets/software_keyboard.h"
|
||||||
|
#include "core/hle/service/am/am.h"
|
||||||
|
#include "core/hle/service/am/applets/profile_select.h"
|
||||||
|
|
||||||
|
namespace Service::AM::Applets {
|
||||||
|
|
||||||
|
constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1};
|
||||||
|
|
||||||
|
ProfileSelect::ProfileSelect() = default;
|
||||||
|
ProfileSelect::~ProfileSelect() = default;
|
||||||
|
|
||||||
|
void ProfileSelect::Initialize() {
|
||||||
|
complete = false;
|
||||||
|
status = RESULT_SUCCESS;
|
||||||
|
final_data.clear();
|
||||||
|
|
||||||
|
Applet::Initialize();
|
||||||
|
|
||||||
|
const auto user_config_storage = broker.PopNormalDataToApplet();
|
||||||
|
ASSERT(user_config_storage != nullptr);
|
||||||
|
const auto& user_config = user_config_storage->GetData();
|
||||||
|
|
||||||
|
ASSERT(user_config.size() >= sizeof(UserSelectionConfig));
|
||||||
|
std::memcpy(&config, user_config.data(), sizeof(UserSelectionConfig));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProfileSelect::TransactionComplete() const {
|
||||||
|
return complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode ProfileSelect::GetStatus() const {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfileSelect::ExecuteInteractive() {
|
||||||
|
UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfileSelect::Execute() {
|
||||||
|
if (complete) {
|
||||||
|
broker.PushNormalDataFromApplet(IStorage{final_data});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& frontend{Core::System::GetInstance().GetProfileSelector()};
|
||||||
|
|
||||||
|
frontend.SelectProfile([this](std::optional<Account::UUID> uuid) { SelectionComplete(uuid); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfileSelect::SelectionComplete(std::optional<Account::UUID> uuid) {
|
||||||
|
UserSelectionOutput output{};
|
||||||
|
|
||||||
|
if (uuid.has_value() && uuid->uuid != Account::INVALID_UUID) {
|
||||||
|
output.result = 0;
|
||||||
|
output.uuid_selected = uuid->uuid;
|
||||||
|
} else {
|
||||||
|
status = ERR_USER_CANCELLED_SELECTION;
|
||||||
|
output.result = ERR_USER_CANCELLED_SELECTION.raw;
|
||||||
|
output.uuid_selected = Account::INVALID_UUID;
|
||||||
|
}
|
||||||
|
|
||||||
|
final_data = std::vector<u8>(sizeof(UserSelectionOutput));
|
||||||
|
std::memcpy(final_data.data(), &output, final_data.size());
|
||||||
|
broker.PushNormalDataFromApplet(IStorage{final_data});
|
||||||
|
broker.SignalStateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::AM::Applets
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
// Copyright 2018 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "common/common_funcs.h"
|
||||||
|
#include "core/hle/service/acc/profile_manager.h"
|
||||||
|
#include "core/hle/service/am/applets/applets.h"
|
||||||
|
|
||||||
|
namespace Service::AM::Applets {
|
||||||
|
|
||||||
|
struct UserSelectionConfig {
|
||||||
|
// TODO(DarkLordZach): RE this structure
|
||||||
|
// It seems to be flags and the like that determine the UI of the applet on the switch... from
|
||||||
|
// my research this is safe to ignore for now.
|
||||||
|
INSERT_PADDING_BYTES(0xA0);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(UserSelectionConfig) == 0xA0, "UserSelectionConfig has incorrect size.");
|
||||||
|
|
||||||
|
struct UserSelectionOutput {
|
||||||
|
u64 result;
|
||||||
|
u128 uuid_selected;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has incorrect size.");
|
||||||
|
|
||||||
|
class ProfileSelect final : public Applet {
|
||||||
|
public:
|
||||||
|
ProfileSelect();
|
||||||
|
~ProfileSelect() override;
|
||||||
|
|
||||||
|
void Initialize() override;
|
||||||
|
|
||||||
|
bool TransactionComplete() const override;
|
||||||
|
ResultCode GetStatus() const override;
|
||||||
|
void ExecuteInteractive() override;
|
||||||
|
void Execute() override;
|
||||||
|
|
||||||
|
void SelectionComplete(std::optional<Account::UUID> uuid);
|
||||||
|
|
||||||
|
private:
|
||||||
|
UserSelectionConfig config;
|
||||||
|
bool complete = false;
|
||||||
|
ResultCode status = RESULT_SUCCESS;
|
||||||
|
std::vector<u8> final_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Service::AM::Applets
|
||||||
@@ -146,11 +146,10 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
|
|||||||
|
|
||||||
if (complete) {
|
if (complete) {
|
||||||
broker.PushNormalDataFromApplet(IStorage{output_main});
|
broker.PushNormalDataFromApplet(IStorage{output_main});
|
||||||
|
broker.SignalStateChanged();
|
||||||
} else {
|
} else {
|
||||||
broker.PushInteractiveDataFromApplet(IStorage{output_sub});
|
broker.PushInteractiveDataFromApplet(IStorage{output_sub});
|
||||||
}
|
}
|
||||||
|
|
||||||
broker.SignalStateChanged();
|
|
||||||
} else {
|
} else {
|
||||||
output_main[0] = 1;
|
output_main[0] = 1;
|
||||||
complete = true;
|
complete = true;
|
||||||
|
|||||||
@@ -8,18 +8,23 @@
|
|||||||
#include "common/file_util.h"
|
#include "common/file_util.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/file_sys/bis_factory.h"
|
#include "core/file_sys/bis_factory.h"
|
||||||
|
#include "core/file_sys/control_metadata.h"
|
||||||
#include "core/file_sys/errors.h"
|
#include "core/file_sys/errors.h"
|
||||||
#include "core/file_sys/mode.h"
|
#include "core/file_sys/mode.h"
|
||||||
|
#include "core/file_sys/partition_filesystem.h"
|
||||||
|
#include "core/file_sys/patch_manager.h"
|
||||||
#include "core/file_sys/registered_cache.h"
|
#include "core/file_sys/registered_cache.h"
|
||||||
#include "core/file_sys/romfs_factory.h"
|
#include "core/file_sys/romfs_factory.h"
|
||||||
#include "core/file_sys/savedata_factory.h"
|
#include "core/file_sys/savedata_factory.h"
|
||||||
#include "core/file_sys/sdmc_factory.h"
|
#include "core/file_sys/sdmc_factory.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs.h"
|
||||||
#include "core/file_sys/vfs_offset.h"
|
#include "core/file_sys/vfs_offset.h"
|
||||||
|
#include "core/hle/kernel/process.h"
|
||||||
#include "core/hle/service/filesystem/filesystem.h"
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
#include "core/hle/service/filesystem/fsp_ldr.h"
|
#include "core/hle/service/filesystem/fsp_ldr.h"
|
||||||
#include "core/hle/service/filesystem/fsp_pr.h"
|
#include "core/hle/service/filesystem/fsp_pr.h"
|
||||||
#include "core/hle/service/filesystem/fsp_srv.h"
|
#include "core/hle/service/filesystem/fsp_srv.h"
|
||||||
|
#include "core/loader/loader.h"
|
||||||
|
|
||||||
namespace Service::FileSystem {
|
namespace Service::FileSystem {
|
||||||
|
|
||||||
@@ -28,6 +33,10 @@ namespace Service::FileSystem {
|
|||||||
// TODO(DarkLordZach): Eventually make this configurable in settings.
|
// TODO(DarkLordZach): Eventually make this configurable in settings.
|
||||||
constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
|
constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
|
||||||
|
|
||||||
|
// A default size for normal/journal save data size if application control metadata cannot be found.
|
||||||
|
// This should be large enough to satisfy even the most extreme requirements (~4.2GB)
|
||||||
|
constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000;
|
||||||
|
|
||||||
static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
|
static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
|
||||||
std::string_view dir_name_) {
|
std::string_view dir_name_) {
|
||||||
std::string dir_name(FileUtil::SanitizePath(dir_name_));
|
std::string dir_name(FileUtil::SanitizePath(dir_name_));
|
||||||
@@ -341,6 +350,44 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
|
|||||||
return sdmc_factory->Open();
|
return sdmc_factory->Open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id) {
|
||||||
|
if (save_data_factory == nullptr) {
|
||||||
|
return {0, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto value = save_data_factory->ReadSaveDataSize(type, title_id, user_id);
|
||||||
|
|
||||||
|
if (value.normal == 0 && value.journal == 0) {
|
||||||
|
FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE};
|
||||||
|
|
||||||
|
FileSys::NACP nacp;
|
||||||
|
const auto res = Core::System::GetInstance().GetAppLoader().ReadControlData(nacp);
|
||||||
|
|
||||||
|
if (res != Loader::ResultStatus::Success) {
|
||||||
|
FileSys::PatchManager pm{Core::CurrentProcess()->GetTitleID()};
|
||||||
|
auto [nacp_unique, discard] = pm.GetControlMetadata();
|
||||||
|
|
||||||
|
if (nacp_unique != nullptr) {
|
||||||
|
new_size = {nacp_unique->GetDefaultNormalSaveSize(),
|
||||||
|
nacp_unique->GetDefaultJournalSaveSize()};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
new_size = {nacp.GetDefaultNormalSaveSize(), nacp.GetDefaultJournalSaveSize()};
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteSaveDataSize(type, title_id, user_id, new_size);
|
||||||
|
return new_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
|
||||||
|
FileSys::SaveDataSize new_value) {
|
||||||
|
if (save_data_factory != nullptr)
|
||||||
|
save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value);
|
||||||
|
}
|
||||||
|
|
||||||
FileSys::RegisteredCacheUnion GetUnionContents() {
|
FileSys::RegisteredCacheUnion GetUnionContents() {
|
||||||
return FileSys::RegisteredCacheUnion{
|
return FileSys::RegisteredCacheUnion{
|
||||||
{GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}};
|
{GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}};
|
||||||
|
|||||||
@@ -21,9 +21,11 @@ class SDMCFactory;
|
|||||||
enum class ContentRecordType : u8;
|
enum class ContentRecordType : u8;
|
||||||
enum class Mode : u32;
|
enum class Mode : u32;
|
||||||
enum class SaveDataSpaceId : u8;
|
enum class SaveDataSpaceId : u8;
|
||||||
|
enum class SaveDataType : u8;
|
||||||
enum class StorageId : u8;
|
enum class StorageId : u8;
|
||||||
|
|
||||||
struct SaveDataDescriptor;
|
struct SaveDataDescriptor;
|
||||||
|
struct SaveDataSize;
|
||||||
} // namespace FileSys
|
} // namespace FileSys
|
||||||
|
|
||||||
namespace Service {
|
namespace Service {
|
||||||
@@ -48,6 +50,10 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
|
|||||||
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
|
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
|
||||||
ResultVal<FileSys::VirtualDir> OpenSDMC();
|
ResultVal<FileSys::VirtualDir> OpenSDMC();
|
||||||
|
|
||||||
|
FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id);
|
||||||
|
void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
|
||||||
|
FileSys::SaveDataSize new_value);
|
||||||
|
|
||||||
FileSys::RegisteredCacheUnion GetUnionContents();
|
FileSys::RegisteredCacheUnion GetUnionContents();
|
||||||
|
|
||||||
FileSys::RegisteredCache* GetSystemNANDContents();
|
FileSys::RegisteredCache* GetSystemNANDContents();
|
||||||
|
|||||||
@@ -339,52 +339,6 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
|
|||||||
npad.pokeball_states.npad[npad.pokeball_states.common.last_entry_index];
|
npad.pokeball_states.npad[npad.pokeball_states.common.last_entry_index];
|
||||||
auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
|
auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
|
||||||
|
|
||||||
if (hold_type == NpadHoldType::Horizontal) {
|
|
||||||
ControllerPadState state{};
|
|
||||||
AnalogPosition temp_lstick_entry{};
|
|
||||||
AnalogPosition temp_rstick_entry{};
|
|
||||||
if (controller_type == NPadControllerType::JoyLeft) {
|
|
||||||
state.d_down.Assign(pad_state.pad_states.d_left.Value());
|
|
||||||
state.d_left.Assign(pad_state.pad_states.d_up.Value());
|
|
||||||
state.d_right.Assign(pad_state.pad_states.d_down.Value());
|
|
||||||
state.d_up.Assign(pad_state.pad_states.d_right.Value());
|
|
||||||
state.l.Assign(pad_state.pad_states.l.Value() |
|
|
||||||
pad_state.pad_states.left_sl.Value());
|
|
||||||
state.r.Assign(pad_state.pad_states.r.Value() |
|
|
||||||
pad_state.pad_states.left_sr.Value());
|
|
||||||
|
|
||||||
state.zl.Assign(pad_state.pad_states.zl.Value());
|
|
||||||
state.plus.Assign(pad_state.pad_states.minus.Value());
|
|
||||||
|
|
||||||
temp_lstick_entry = pad_state.l_stick;
|
|
||||||
temp_rstick_entry = pad_state.r_stick;
|
|
||||||
std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
|
|
||||||
std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
|
|
||||||
temp_lstick_entry.y *= -1;
|
|
||||||
} else if (controller_type == NPadControllerType::JoyRight) {
|
|
||||||
state.x.Assign(pad_state.pad_states.a.Value());
|
|
||||||
state.a.Assign(pad_state.pad_states.b.Value());
|
|
||||||
state.b.Assign(pad_state.pad_states.y.Value());
|
|
||||||
state.y.Assign(pad_state.pad_states.b.Value());
|
|
||||||
|
|
||||||
state.l.Assign(pad_state.pad_states.l.Value() |
|
|
||||||
pad_state.pad_states.right_sl.Value());
|
|
||||||
state.r.Assign(pad_state.pad_states.r.Value() |
|
|
||||||
pad_state.pad_states.right_sr.Value());
|
|
||||||
state.zr.Assign(pad_state.pad_states.zr.Value());
|
|
||||||
state.plus.Assign(pad_state.pad_states.plus.Value());
|
|
||||||
|
|
||||||
temp_lstick_entry = pad_state.l_stick;
|
|
||||||
temp_rstick_entry = pad_state.r_stick;
|
|
||||||
std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
|
|
||||||
std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
|
|
||||||
temp_rstick_entry.x *= -1;
|
|
||||||
}
|
|
||||||
pad_state.pad_states.raw = state.raw;
|
|
||||||
pad_state.l_stick = temp_lstick_entry;
|
|
||||||
pad_state.r_stick = temp_rstick_entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
libnx_entry.connection_status.raw = 0;
|
libnx_entry.connection_status.raw = 0;
|
||||||
|
|
||||||
switch (controller_type) {
|
switch (controller_type) {
|
||||||
|
|||||||
@@ -306,7 +306,10 @@ private:
|
|||||||
std::shared_ptr<IAppletResource> applet_resource;
|
std::shared_ptr<IAppletResource> applet_resource;
|
||||||
|
|
||||||
void CreateAppletResource(Kernel::HLERequestContext& ctx) {
|
void CreateAppletResource(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_HID, "called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||||
|
|
||||||
if (applet_resource == nullptr) {
|
if (applet_resource == nullptr) {
|
||||||
applet_resource = std::make_shared<IAppletResource>();
|
applet_resource = std::make_shared<IAppletResource>();
|
||||||
@@ -318,7 +321,12 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ActivateXpad(Kernel::HLERequestContext& ctx) {
|
void ActivateXpad(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_HID, "called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto basic_xpad_id{rp.Pop<u32>()};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}",
|
||||||
|
basic_xpad_id, applet_resource_user_id);
|
||||||
|
|
||||||
applet_resource->ActivateController(HidController::XPad);
|
applet_resource->ActivateController(HidController::XPad);
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
@@ -326,7 +334,10 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ActivateDebugPad(Kernel::HLERequestContext& ctx) {
|
void ActivateDebugPad(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_HID, "called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||||
|
|
||||||
applet_resource->ActivateController(HidController::DebugPad);
|
applet_resource->ActivateController(HidController::DebugPad);
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
@@ -334,7 +345,10 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ActivateTouchScreen(Kernel::HLERequestContext& ctx) {
|
void ActivateTouchScreen(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_HID, "called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||||
|
|
||||||
applet_resource->ActivateController(HidController::Touchscreen);
|
applet_resource->ActivateController(HidController::Touchscreen);
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
@@ -342,7 +356,10 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ActivateMouse(Kernel::HLERequestContext& ctx) {
|
void ActivateMouse(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_HID, "called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||||
|
|
||||||
applet_resource->ActivateController(HidController::Mouse);
|
applet_resource->ActivateController(HidController::Mouse);
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
@@ -350,7 +367,10 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ActivateKeyboard(Kernel::HLERequestContext& ctx) {
|
void ActivateKeyboard(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_HID, "called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||||
|
|
||||||
applet_resource->ActivateController(HidController::Keyboard);
|
applet_resource->ActivateController(HidController::Keyboard);
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
@@ -358,7 +378,12 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ActivateGesture(Kernel::HLERequestContext& ctx) {
|
void ActivateGesture(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_HID, "called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto unknown{rp.Pop<u32>()};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown,
|
||||||
|
applet_resource_user_id);
|
||||||
|
|
||||||
applet_resource->ActivateController(HidController::Gesture);
|
applet_resource->ActivateController(HidController::Gesture);
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
@@ -367,7 +392,12 @@ private:
|
|||||||
|
|
||||||
void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
|
void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
|
||||||
// Should have no effect with how our npad sets up the data
|
// Should have no effect with how our npad sets up the data
|
||||||
LOG_DEBUG(Service_HID, "called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto unknown{rp.Pop<u32>()};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown,
|
||||||
|
applet_resource_user_id);
|
||||||
|
|
||||||
applet_resource->ActivateController(HidController::NPad);
|
applet_resource->ActivateController(HidController::NPad);
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
@@ -376,22 +406,37 @@ private:
|
|||||||
|
|
||||||
void StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
void StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
auto handle = rp.PopRaw<u32>();
|
const auto handle{rp.Pop<u32>()};
|
||||||
LOG_WARNING(Service_HID, "(STUBBED) called with handle={}", handle);
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle,
|
||||||
|
applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
|
void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto handle{rp.Pop<u32>()};
|
||||||
|
const auto drift_mode{rp.Pop<u32>()};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_WARNING(Service_HID,
|
||||||
|
"(STUBBED) called, handle={}, drift_mode={}, applet_resource_user_id={}",
|
||||||
|
handle, drift_mode, applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
|
void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto handle{rp.Pop<u32>()};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle,
|
||||||
|
applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
@@ -401,8 +446,9 @@ private:
|
|||||||
|
|
||||||
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
|
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
auto supported_styleset = rp.PopRaw<u32>();
|
const auto supported_styleset{rp.Pop<u32>()};
|
||||||
LOG_DEBUG(Service_HID, "called with supported_styleset={}", supported_styleset);
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, supported_styleset={}", supported_styleset);
|
||||||
|
|
||||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||||
.SetSupportedStyleSet({supported_styleset});
|
.SetSupportedStyleSet({supported_styleset});
|
||||||
@@ -412,7 +458,10 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
|
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_HID, "called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||||
|
|
||||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||||
|
|
||||||
@@ -422,7 +471,10 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
|
void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_HID, "called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||||
|
|
||||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||||
.SetSupportedNPadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
|
.SetSupportedNPadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
|
||||||
@@ -431,7 +483,10 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ActivateNpad(Kernel::HLERequestContext& ctx) {
|
void ActivateNpad(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_HID, "called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
@@ -440,8 +495,12 @@ private:
|
|||||||
|
|
||||||
void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
|
void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
auto npad_id = rp.PopRaw<u32>();
|
const auto npad_id{rp.Pop<u32>()};
|
||||||
LOG_DEBUG(Service_HID, "called with npad_id={}", npad_id);
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
const auto unknown{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}",
|
||||||
|
npad_id, applet_resource_user_id, unknown);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
@@ -451,8 +510,11 @@ private:
|
|||||||
|
|
||||||
void DisconnectNpad(Kernel::HLERequestContext& ctx) {
|
void DisconnectNpad(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
auto npad_id = rp.PopRaw<u32>();
|
const auto npad_id{rp.Pop<u32>()};
|
||||||
LOG_DEBUG(Service_HID, "called with npad_id={}", npad_id);
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id,
|
||||||
|
applet_resource_user_id);
|
||||||
|
|
||||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||||
.DisconnectNPad(npad_id);
|
.DisconnectNPad(npad_id);
|
||||||
@@ -462,8 +524,9 @@ private:
|
|||||||
|
|
||||||
void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
|
void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
auto npad_id = rp.PopRaw<u32>();
|
const auto npad_id{rp.Pop<u32>()};
|
||||||
LOG_DEBUG(Service_HID, "called with npad_id={}", npad_id);
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
@@ -474,8 +537,11 @@ private:
|
|||||||
|
|
||||||
void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
|
void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto hold_type = rp.PopRaw<u64>();
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
LOG_DEBUG(Service_HID, "called with hold_type={}", hold_type);
|
const auto hold_type{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}",
|
||||||
|
applet_resource_user_id, hold_type);
|
||||||
|
|
||||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||||
controller.SetHoldType(Controller_NPad::NpadHoldType{hold_type});
|
controller.SetHoldType(Controller_NPad::NpadHoldType{hold_type});
|
||||||
@@ -485,7 +551,10 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
|
void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_HID, "called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||||
|
|
||||||
const auto& controller =
|
const auto& controller =
|
||||||
applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||||
@@ -496,15 +565,21 @@ private:
|
|||||||
|
|
||||||
void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
|
void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
auto npad_id = rp.PopRaw<u32>();
|
const auto npad_id{rp.Pop<u32>()};
|
||||||
LOG_WARNING(Service_HID, "(STUBBED) called with npad_id={}", npad_id);
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
|
||||||
|
npad_id, applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
|
void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_HID, "called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||||
|
|
||||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||||
.SetVibrationEnabled(true);
|
.SetVibrationEnabled(true);
|
||||||
@@ -523,9 +598,12 @@ private:
|
|||||||
|
|
||||||
void SendVibrationValue(Kernel::HLERequestContext& ctx) {
|
void SendVibrationValue(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto controller_id = rp.PopRaw<u32>();
|
const auto controller_id{rp.Pop<u32>()};
|
||||||
const auto vibration_values = rp.PopRaw<Controller_NPad::Vibration>();
|
const auto vibration_values{rp.PopRaw<Controller_NPad::Vibration>()};
|
||||||
LOG_DEBUG(Service_HID, "called with controller_id={}", controller_id);
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}",
|
||||||
|
controller_id, applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
@@ -535,7 +613,10 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SendVibrationValues(Kernel::HLERequestContext& ctx) {
|
void SendVibrationValues(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_HID, "called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||||
|
|
||||||
const auto controllers = ctx.ReadBuffer(0);
|
const auto controllers = ctx.ReadBuffer(0);
|
||||||
const auto vibrations = ctx.ReadBuffer(1);
|
const auto vibrations = ctx.ReadBuffer(1);
|
||||||
@@ -557,7 +638,12 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
|
void GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_HID, "called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto controller_id{rp.Pop<u32>()};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}",
|
||||||
|
controller_id, applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 6};
|
IPC::ResponseBuilder rb{ctx, 6};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
@@ -568,8 +654,11 @@ private:
|
|||||||
|
|
||||||
void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
|
void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto npad_id = rp.PopRaw<u32>();
|
const auto npad_id{rp.Pop<u32>()};
|
||||||
LOG_DEBUG(Service_HID, "called with npad_id={}", npad_id);
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id,
|
||||||
|
applet_resource_user_id);
|
||||||
|
|
||||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||||
controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual);
|
controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual);
|
||||||
@@ -579,7 +668,14 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
|
void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto unknown_1{rp.Pop<u32>()};
|
||||||
|
const auto unknown_2{rp.Pop<u32>()};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_WARNING(Service_HID,
|
||||||
|
"(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
|
||||||
|
unknown_1, unknown_2, applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
@@ -587,8 +683,11 @@ private:
|
|||||||
|
|
||||||
void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
|
void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
auto mode = rp.PopRaw<u32>();
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
LOG_WARNING(Service_HID, "(STUBBED) called with mode={}", mode);
|
const auto mode{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, mode={}",
|
||||||
|
applet_resource_user_id, mode);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
@@ -612,35 +711,55 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
|
||||||
|
applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto handle{rp.Pop<u32>()};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle,
|
||||||
|
applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
void StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto handle{rp.Pop<u32>()};
|
||||||
|
|
||||||
|
LOG_WARNING(Service_HID, "(STUBBED) called, handle={}", handle);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) {
|
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
const auto unknown{rp.Pop<u32>()};
|
||||||
|
|
||||||
|
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, unknown={}",
|
||||||
|
applet_resource_user_id, unknown);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx) {
|
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto unknown{rp.Pop<u32>()};
|
||||||
|
|
||||||
|
LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}", unknown);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
|||||||
@@ -318,14 +318,18 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(process->MirrorMemory(*map_address, nro_addr, nro_size,
|
ASSERT(vm_manager
|
||||||
Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
|
.MirrorMemory(*map_address, nro_addr, nro_size,
|
||||||
ASSERT(process->UnmapMemory(nro_addr, 0, nro_size) == RESULT_SUCCESS);
|
Kernel::MemoryState::ModuleCodeStatic)
|
||||||
|
.IsSuccess());
|
||||||
|
ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess());
|
||||||
|
|
||||||
if (bss_size > 0) {
|
if (bss_size > 0) {
|
||||||
ASSERT(process->MirrorMemory(*map_address + nro_size, bss_addr, bss_size,
|
ASSERT(vm_manager
|
||||||
Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
|
.MirrorMemory(*map_address + nro_size, bss_addr, bss_size,
|
||||||
ASSERT(process->UnmapMemory(bss_addr, 0, bss_size) == RESULT_SUCCESS);
|
Kernel::MemoryState::ModuleCodeStatic)
|
||||||
|
.IsSuccess());
|
||||||
|
ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess());
|
||||||
}
|
}
|
||||||
|
|
||||||
vm_manager.ReprotectRange(*map_address, header.text_size,
|
vm_manager.ReprotectRange(*map_address, header.text_size,
|
||||||
@@ -380,13 +384,14 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* process = Core::CurrentProcess();
|
auto& vm_manager = Core::CurrentProcess()->VMManager();
|
||||||
auto& vm_manager = process->VMManager();
|
|
||||||
const auto& nro_size = iter->second.size;
|
const auto& nro_size = iter->second.size;
|
||||||
|
|
||||||
ASSERT(process->MirrorMemory(heap_addr, mapped_addr, nro_size,
|
ASSERT(vm_manager
|
||||||
Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
|
.MirrorMemory(heap_addr, mapped_addr, nro_size,
|
||||||
ASSERT(process->UnmapMemory(mapped_addr, 0, nro_size) == RESULT_SUCCESS);
|
Kernel::MemoryState::ModuleCodeStatic)
|
||||||
|
.IsSuccess());
|
||||||
|
ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess());
|
||||||
|
|
||||||
Core::System::GetInstance().InvalidateCpuInstructionCaches();
|
Core::System::GetInstance().InvalidateCpuInstructionCaches();
|
||||||
|
|
||||||
|
|||||||
@@ -317,8 +317,8 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool has_attached_handle{};
|
bool has_attached_handle{};
|
||||||
const u64 device_handle{Common::MakeMagic('Y', 'U', 'Z', 'U')};
|
const u64 device_handle{0}; // Npad device 1
|
||||||
const u32 npad_id{0}; // Player 1 controller
|
const u32 npad_id{0}; // Player 1 controller
|
||||||
State state{State::NonInitialized};
|
State state{State::NonInitialized};
|
||||||
DeviceState device_state{DeviceState::Initialized};
|
DeviceState device_state{DeviceState::Initialized};
|
||||||
Kernel::EventPair deactivate_event;
|
Kernel::EventPair deactivate_event;
|
||||||
|
|||||||
@@ -145,12 +145,13 @@ void SM::RegisterService(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
const std::string name(name_buf.begin(), end);
|
const std::string name(name_buf.begin(), end);
|
||||||
|
|
||||||
const auto unk_bool = static_cast<bool>(rp.PopRaw<u32>());
|
const auto is_light = static_cast<bool>(rp.PopRaw<u32>());
|
||||||
const auto session_count = rp.PopRaw<u32>();
|
const auto max_session_count = rp.PopRaw<u32>();
|
||||||
|
|
||||||
LOG_DEBUG(Service_SM, "called with unk_bool={}", unk_bool);
|
LOG_DEBUG(Service_SM, "called with name={}, max_session_count={}, is_light={}", name,
|
||||||
|
max_session_count, is_light);
|
||||||
|
|
||||||
auto handle = service_manager->RegisterService(name, session_count);
|
auto handle = service_manager->RegisterService(name, max_session_count);
|
||||||
if (handle.Failed()) {
|
if (handle.Failed()) {
|
||||||
LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}",
|
LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}",
|
||||||
handle.Code().raw);
|
handle.Code().raw);
|
||||||
|
|||||||
@@ -129,7 +129,10 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
|
|||||||
return ResultStatus::Error32BitISA;
|
return ResultStatus::Error32BitISA;
|
||||||
}
|
}
|
||||||
|
|
||||||
process.LoadFromMetadata(metadata);
|
if (process.LoadFromMetadata(metadata).IsError()) {
|
||||||
|
return ResultStatus::ErrorUnableToParseKernelMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
const FileSys::PatchManager pm(metadata.GetTitleID());
|
const FileSys::PatchManager pm(metadata.GetTitleID());
|
||||||
|
|
||||||
// Load NSO modules
|
// Load NSO modules
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ std::string GetFileTypeString(FileType type) {
|
|||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr std::array<const char*, 60> RESULT_MESSAGES{
|
constexpr std::array<const char*, 62> RESULT_MESSAGES{
|
||||||
"The operation completed successfully.",
|
"The operation completed successfully.",
|
||||||
"The loader requested to load is already loaded.",
|
"The loader requested to load is already loaded.",
|
||||||
"The operation is not implemented.",
|
"The operation is not implemented.",
|
||||||
@@ -103,6 +103,7 @@ constexpr std::array<const char*, 60> RESULT_MESSAGES{
|
|||||||
"The NPDM has a bad ACI header,",
|
"The NPDM has a bad ACI header,",
|
||||||
"The NPDM file has a bad file access control.",
|
"The NPDM file has a bad file access control.",
|
||||||
"The NPDM has a bad file access header.",
|
"The NPDM has a bad file access header.",
|
||||||
|
"The NPDM has bad kernel capability descriptors.",
|
||||||
"The PFS/HFS partition has a bad header.",
|
"The PFS/HFS partition has a bad header.",
|
||||||
"The PFS/HFS partition has incorrect size as determined by the header.",
|
"The PFS/HFS partition has incorrect size as determined by the header.",
|
||||||
"The NCA file has a bad header.",
|
"The NCA file has a bad header.",
|
||||||
@@ -125,6 +126,7 @@ constexpr std::array<const char*, 60> RESULT_MESSAGES{
|
|||||||
"The file could not be found or does not exist.",
|
"The file could not be found or does not exist.",
|
||||||
"The game is missing a program metadata file (main.npdm).",
|
"The game is missing a program metadata file (main.npdm).",
|
||||||
"The game uses the currently-unimplemented 32-bit architecture.",
|
"The game uses the currently-unimplemented 32-bit architecture.",
|
||||||
|
"Unable to completely parse the kernel metadata when loading the emulated process",
|
||||||
"The RomFS could not be found.",
|
"The RomFS could not be found.",
|
||||||
"The ELF file has incorrect size as determined by the header.",
|
"The ELF file has incorrect size as determined by the header.",
|
||||||
"There was a general error loading the NRO into emulated memory.",
|
"There was a general error loading the NRO into emulated memory.",
|
||||||
|
|||||||
@@ -15,6 +15,10 @@
|
|||||||
#include "core/file_sys/control_metadata.h"
|
#include "core/file_sys/control_metadata.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
class NACP;
|
||||||
|
} // namespace FileSys
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
struct AddressMapping;
|
struct AddressMapping;
|
||||||
class Process;
|
class Process;
|
||||||
@@ -67,6 +71,7 @@ enum class ResultStatus : u16 {
|
|||||||
ErrorBadACIHeader,
|
ErrorBadACIHeader,
|
||||||
ErrorBadFileAccessControl,
|
ErrorBadFileAccessControl,
|
||||||
ErrorBadFileAccessHeader,
|
ErrorBadFileAccessHeader,
|
||||||
|
ErrorBadKernelCapabilityDescriptors,
|
||||||
ErrorBadPFSHeader,
|
ErrorBadPFSHeader,
|
||||||
ErrorIncorrectPFSFileSize,
|
ErrorIncorrectPFSFileSize,
|
||||||
ErrorBadNCAHeader,
|
ErrorBadNCAHeader,
|
||||||
@@ -89,6 +94,7 @@ enum class ResultStatus : u16 {
|
|||||||
ErrorNullFile,
|
ErrorNullFile,
|
||||||
ErrorMissingNPDM,
|
ErrorMissingNPDM,
|
||||||
Error32BitISA,
|
Error32BitISA,
|
||||||
|
ErrorUnableToParseKernelMetadata,
|
||||||
ErrorNoRomFS,
|
ErrorNoRomFS,
|
||||||
ErrorIncorrectELFFileSize,
|
ErrorIncorrectELFFileSize,
|
||||||
ErrorLoadingNRO,
|
ErrorLoadingNRO,
|
||||||
@@ -245,11 +251,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the developer of the application
|
* Get the control data (CNMT) of the application
|
||||||
* @param developer Reference to store the application developer into
|
* @param control Reference to store the application control data into
|
||||||
* @return ResultStatus result of function
|
* @return ResultStatus result of function
|
||||||
*/
|
*/
|
||||||
virtual ResultStatus ReadDeveloper(std::string& developer) {
|
virtual ResultStatus ReadControlData(FileSys::NACP& control) {
|
||||||
return ResultStatus::ErrorNotImplemented;
|
return ResultStatus::ErrorNotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -152,10 +152,10 @@ ResultStatus AppLoader_NSP::ReadTitle(std::string& title) {
|
|||||||
return ResultStatus::Success;
|
return ResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultStatus AppLoader_NSP::ReadDeveloper(std::string& developer) {
|
ResultStatus AppLoader_NSP::ReadControlData(FileSys::NACP& nacp) {
|
||||||
if (nacp_file == nullptr)
|
if (nacp_file == nullptr)
|
||||||
return ResultStatus::ErrorNoControl;
|
return ResultStatus::ErrorNoControl;
|
||||||
developer = nacp_file->GetDeveloperName();
|
nacp = *nacp_file;
|
||||||
return ResultStatus::Success;
|
return ResultStatus::Success;
|
||||||
}
|
}
|
||||||
} // namespace Loader
|
} // namespace Loader
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public:
|
|||||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||||
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
|
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
|
||||||
ResultStatus ReadTitle(std::string& title) override;
|
ResultStatus ReadTitle(std::string& title) override;
|
||||||
ResultStatus ReadDeveloper(std::string& developer) override;
|
ResultStatus ReadControlData(FileSys::NACP& nacp) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<FileSys::NSP> nsp;
|
std::unique_ptr<FileSys::NSP> nsp;
|
||||||
|
|||||||
@@ -121,10 +121,11 @@ ResultStatus AppLoader_XCI::ReadTitle(std::string& title) {
|
|||||||
return ResultStatus::Success;
|
return ResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultStatus AppLoader_XCI::ReadDeveloper(std::string& developer) {
|
ResultStatus AppLoader_XCI::ReadControlData(FileSys::NACP& control) {
|
||||||
if (nacp_file == nullptr)
|
if (nacp_file == nullptr)
|
||||||
return ResultStatus::ErrorNoControl;
|
return ResultStatus::ErrorNoControl;
|
||||||
developer = nacp_file->GetDeveloperName();
|
control = *nacp_file;
|
||||||
return ResultStatus::Success;
|
return ResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Loader
|
} // namespace Loader
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public:
|
|||||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||||
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
|
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
|
||||||
ResultStatus ReadTitle(std::string& title) override;
|
ResultStatus ReadTitle(std::string& title) override;
|
||||||
ResultStatus ReadDeveloper(std::string& developer) override;
|
ResultStatus ReadControlData(FileSys::NACP& control) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<FileSys::XCI> xci;
|
std::unique_ptr<FileSys::XCI> xci;
|
||||||
|
|||||||
@@ -187,6 +187,7 @@ T Read(const VAddr vaddr) {
|
|||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ public:
|
|||||||
return 3;
|
return 3;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -871,6 +872,7 @@ public:
|
|||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
GPUVAddr StartAddress() const {
|
GPUVAddr StartAddress() const {
|
||||||
|
|||||||
@@ -575,7 +575,7 @@ union Instruction {
|
|||||||
|
|
||||||
union {
|
union {
|
||||||
BitField<39, 2, u64> tab5cb8_2;
|
BitField<39, 2, u64> tab5cb8_2;
|
||||||
BitField<41, 3, u64> tab5c68_1;
|
BitField<41, 3, u64> postfactor;
|
||||||
BitField<44, 2, u64> tab5c68_0;
|
BitField<44, 2, u64> tab5c68_0;
|
||||||
BitField<48, 1, u64> negate_b;
|
BitField<48, 1, u64> negate_b;
|
||||||
} fmul;
|
} fmul;
|
||||||
@@ -609,7 +609,7 @@ union Instruction {
|
|||||||
|
|
||||||
BitField<31, 1, u64> negate_b;
|
BitField<31, 1, u64> negate_b;
|
||||||
BitField<30, 1, u64> abs_b;
|
BitField<30, 1, u64> abs_b;
|
||||||
BitField<47, 2, HalfType> type_b;
|
BitField<28, 2, HalfType> type_b;
|
||||||
|
|
||||||
BitField<35, 2, HalfType> type_c;
|
BitField<35, 2, HalfType> type_c;
|
||||||
} alu_half;
|
} alu_half;
|
||||||
@@ -1049,7 +1049,7 @@ union Instruction {
|
|||||||
BitField<49, 1, u64> nodep_flag;
|
BitField<49, 1, u64> nodep_flag;
|
||||||
BitField<50, 3, u64> component_mask_selector;
|
BitField<50, 3, u64> component_mask_selector;
|
||||||
BitField<53, 4, u64> texture_info;
|
BitField<53, 4, u64> texture_info;
|
||||||
BitField<60, 1, u64> fp32_flag;
|
BitField<59, 1, u64> fp32_flag;
|
||||||
|
|
||||||
TextureType GetTextureType() const {
|
TextureType GetTextureType() const {
|
||||||
// The TEXS instruction has a weird encoding for the texture type.
|
// The TEXS instruction has a weird encoding for the texture type.
|
||||||
@@ -1065,6 +1065,7 @@ union Instruction {
|
|||||||
LOG_CRITICAL(HW_GPU, "Unhandled texture_info: {}",
|
LOG_CRITICAL(HW_GPU, "Unhandled texture_info: {}",
|
||||||
static_cast<u32>(texture_info.Value()));
|
static_cast<u32>(texture_info.Value()));
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
return TextureType::Texture1D;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureProcessMode GetTextureProcessMode() const {
|
TextureProcessMode GetTextureProcessMode() const {
|
||||||
@@ -1145,6 +1146,7 @@ union Instruction {
|
|||||||
LOG_CRITICAL(HW_GPU, "Unhandled texture_info: {}",
|
LOG_CRITICAL(HW_GPU, "Unhandled texture_info: {}",
|
||||||
static_cast<u32>(texture_info.Value()));
|
static_cast<u32>(texture_info.Value()));
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
return TextureType::Texture1D;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureProcessMode GetTextureProcessMode() const {
|
TextureProcessMode GetTextureProcessMode() const {
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
|
|||||||
return 1;
|
return 1;
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast<u32>(format));
|
UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast<u32>(format));
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,6 +122,7 @@ u32 DepthFormatBytesPerPixel(DepthFormat format) {
|
|||||||
return 2;
|
return 2;
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented Depth format {}", static_cast<u32>(format));
|
UNIMPLEMENTED_MSG("Unimplemented Depth format {}", static_cast<u32>(format));
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -171,6 +171,7 @@ u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b)
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented ALU operation {}", static_cast<u32>(operation));
|
UNIMPLEMENTED_MSG("Unimplemented ALU operation {}", static_cast<u32>(operation));
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,6 +269,7 @@ bool MacroInterpreter::EvaluateBranchCondition(BranchCondition cond, u32 value)
|
|||||||
return value != 0;
|
return value != 0;
|
||||||
}
|
}
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Tegra
|
} // namespace Tegra
|
||||||
|
|||||||
@@ -66,8 +66,6 @@ static constexpr ConversionArray morton_to_linear_fns = {
|
|||||||
MortonCopy<true, PixelFormat::BC6H_UF16>,
|
MortonCopy<true, PixelFormat::BC6H_UF16>,
|
||||||
MortonCopy<true, PixelFormat::BC6H_SF16>,
|
MortonCopy<true, PixelFormat::BC6H_SF16>,
|
||||||
MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
|
MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
|
||||||
MortonCopy<true, PixelFormat::G8R8U>,
|
|
||||||
MortonCopy<true, PixelFormat::G8R8S>,
|
|
||||||
MortonCopy<true, PixelFormat::BGRA8>,
|
MortonCopy<true, PixelFormat::BGRA8>,
|
||||||
MortonCopy<true, PixelFormat::RGBA32F>,
|
MortonCopy<true, PixelFormat::RGBA32F>,
|
||||||
MortonCopy<true, PixelFormat::RG32F>,
|
MortonCopy<true, PixelFormat::RG32F>,
|
||||||
@@ -138,8 +136,6 @@ static constexpr ConversionArray linear_to_morton_fns = {
|
|||||||
MortonCopy<false, PixelFormat::BC6H_SF16>,
|
MortonCopy<false, PixelFormat::BC6H_SF16>,
|
||||||
// TODO(Subv): Swizzling ASTC formats are not supported
|
// TODO(Subv): Swizzling ASTC formats are not supported
|
||||||
nullptr,
|
nullptr,
|
||||||
MortonCopy<false, PixelFormat::G8R8U>,
|
|
||||||
MortonCopy<false, PixelFormat::G8R8S>,
|
|
||||||
MortonCopy<false, PixelFormat::BGRA8>,
|
MortonCopy<false, PixelFormat::BGRA8>,
|
||||||
MortonCopy<false, PixelFormat::RGBA32F>,
|
MortonCopy<false, PixelFormat::RGBA32F>,
|
||||||
MortonCopy<false, PixelFormat::RG32F>,
|
MortonCopy<false, PixelFormat::RG32F>,
|
||||||
@@ -192,6 +188,7 @@ static MortonCopyFn GetSwizzleFunction(MortonSwizzleMode mode, Surface::PixelFor
|
|||||||
return linear_to_morton_fns[static_cast<std::size_t>(format)];
|
return linear_to_morton_fns[static_cast<std::size_t>(format)];
|
||||||
}
|
}
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
return morton_to_linear_fns[static_cast<std::size_t>(format)];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 8x8 Z-Order coordinate from 2D coordinates
|
/// 8x8 Z-Order coordinate from 2D coordinates
|
||||||
|
|||||||
@@ -27,4 +27,16 @@ void RendererBase::UpdateCurrentFramebufferLayout() {
|
|||||||
render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height);
|
render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RendererBase::RequestScreenshot(void* data, std::function<void()> callback,
|
||||||
|
const Layout::FramebufferLayout& layout) {
|
||||||
|
if (renderer_settings.screenshot_requested) {
|
||||||
|
LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
renderer_settings.screenshot_bits = data;
|
||||||
|
renderer_settings.screenshot_complete_callback = std::move(callback);
|
||||||
|
renderer_settings.screenshot_framebuffer_layout = layout;
|
||||||
|
renderer_settings.screenshot_requested = true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace VideoCore
|
} // namespace VideoCore
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "core/frontend/emu_window.h"
|
||||||
#include "video_core/gpu.h"
|
#include "video_core/gpu.h"
|
||||||
#include "video_core/rasterizer_interface.h"
|
#include "video_core/rasterizer_interface.h"
|
||||||
|
|
||||||
@@ -21,6 +22,12 @@ namespace VideoCore {
|
|||||||
struct RendererSettings {
|
struct RendererSettings {
|
||||||
std::atomic_bool use_framelimiter{false};
|
std::atomic_bool use_framelimiter{false};
|
||||||
std::atomic_bool set_background_color{false};
|
std::atomic_bool set_background_color{false};
|
||||||
|
|
||||||
|
// Screenshot
|
||||||
|
std::atomic<bool> screenshot_requested{false};
|
||||||
|
void* screenshot_bits;
|
||||||
|
std::function<void()> screenshot_complete_callback;
|
||||||
|
Layout::FramebufferLayout screenshot_framebuffer_layout;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RendererBase : NonCopyable {
|
class RendererBase : NonCopyable {
|
||||||
@@ -57,9 +64,29 @@ public:
|
|||||||
return *rasterizer;
|
return *rasterizer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Core::Frontend::EmuWindow& GetRenderWindow() {
|
||||||
|
return render_window;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Core::Frontend::EmuWindow& GetRenderWindow() const {
|
||||||
|
return render_window;
|
||||||
|
}
|
||||||
|
|
||||||
|
RendererSettings& Settings() {
|
||||||
|
return renderer_settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RendererSettings& Settings() const {
|
||||||
|
return renderer_settings;
|
||||||
|
}
|
||||||
|
|
||||||
/// Refreshes the settings common to all renderers
|
/// Refreshes the settings common to all renderers
|
||||||
void RefreshBaseSettings();
|
void RefreshBaseSettings();
|
||||||
|
|
||||||
|
/// Request a screenshot of the next frame
|
||||||
|
void RequestScreenshot(void* data, std::function<void()> callback,
|
||||||
|
const Layout::FramebufferLayout& layout);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle.
|
Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle.
|
||||||
std::unique_ptr<RasterizerInterface> rasterizer;
|
std::unique_ptr<RasterizerInterface> rasterizer;
|
||||||
|
|||||||
@@ -101,8 +101,18 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
|
|||||||
params.srgb_conversion = config.tic.IsSrgbConversionEnabled();
|
params.srgb_conversion = config.tic.IsSrgbConversionEnabled();
|
||||||
params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(),
|
params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(),
|
||||||
params.srgb_conversion);
|
params.srgb_conversion);
|
||||||
|
|
||||||
|
if (params.pixel_format == PixelFormat::R16U && config.tsc.depth_compare_enabled) {
|
||||||
|
// Some titles create a 'R16U' (normalized 16-bit) texture with depth_compare enabled,
|
||||||
|
// then attempt to sample from it via a shadow sampler. Convert format to Z16 (which also
|
||||||
|
// causes GetFormatType to properly return 'Depth' below).
|
||||||
|
params.pixel_format = PixelFormat::Z16;
|
||||||
|
}
|
||||||
|
|
||||||
params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
|
params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
|
||||||
params.type = GetFormatType(params.pixel_format);
|
params.type = GetFormatType(params.pixel_format);
|
||||||
|
UNIMPLEMENTED_IF(params.type == SurfaceType::ColorTexture && config.tsc.depth_compare_enabled);
|
||||||
|
|
||||||
params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
|
params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
|
||||||
params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
|
params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
|
||||||
params.unaligned_height = config.tic.Height();
|
params.unaligned_height = config.tic.Height();
|
||||||
@@ -257,7 +267,7 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex
|
|||||||
{GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // R8UI
|
{GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // R8UI
|
||||||
{GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, ComponentType::Float, false}, // RGBA16F
|
{GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, ComponentType::Float, false}, // RGBA16F
|
||||||
{GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT, ComponentType::UNorm, false}, // RGBA16U
|
{GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT, ComponentType::UNorm, false}, // RGBA16U
|
||||||
{GL_RGBA16UI, GL_RGBA, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // RGBA16UI
|
{GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // RGBA16UI
|
||||||
{GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, ComponentType::Float,
|
{GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, ComponentType::Float,
|
||||||
false}, // R11FG11FB10F
|
false}, // R11FG11FB10F
|
||||||
{GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RGBA32UI
|
{GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RGBA32UI
|
||||||
@@ -278,8 +288,6 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex
|
|||||||
{GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float,
|
{GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float,
|
||||||
true}, // BC6H_SF16
|
true}, // BC6H_SF16
|
||||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4
|
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4
|
||||||
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8U
|
|
||||||
{GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // G8R8S
|
|
||||||
{GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // BGRA8
|
{GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // BGRA8
|
||||||
{GL_RGBA32F, GL_RGBA, GL_FLOAT, ComponentType::Float, false}, // RGBA32F
|
{GL_RGBA32F, GL_RGBA, GL_FLOAT, ComponentType::Float, false}, // RGBA32F
|
||||||
{GL_RG32F, GL_RG, GL_FLOAT, ComponentType::Float, false}, // RG32F
|
{GL_RG32F, GL_RG, GL_FLOAT, ComponentType::Float, false}, // RG32F
|
||||||
@@ -610,18 +618,6 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height, bo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
|
|
||||||
constexpr auto bpp{GetBytesPerPixel(PixelFormat::G8R8U)};
|
|
||||||
for (std::size_t y = 0; y < height; ++y) {
|
|
||||||
for (std::size_t x = 0; x < width; ++x) {
|
|
||||||
const std::size_t offset{bpp * (y * width + x)};
|
|
||||||
const u8 temp{data[offset]};
|
|
||||||
data[offset] = data[offset + 1];
|
|
||||||
data[offset + 1] = temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to perform software conversion (as needed) when loading a buffer from Switch
|
* Helper function to perform software conversion (as needed) when loading a buffer from Switch
|
||||||
* memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or with
|
* memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or with
|
||||||
@@ -654,12 +650,6 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
|
|||||||
// Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24.
|
// Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24.
|
||||||
ConvertS8Z24ToZ24S8(data, width, height, false);
|
ConvertS8Z24ToZ24S8(data, width, height, false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PixelFormat::G8R8U:
|
|
||||||
case PixelFormat::G8R8S:
|
|
||||||
// Convert the G8R8 color format to R8G8, as OpenGL does not support G8R8.
|
|
||||||
ConvertG8R8ToR8G8(data, width, height);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -671,8 +661,6 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
|
|||||||
static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelFormat pixel_format,
|
static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelFormat pixel_format,
|
||||||
u32 width, u32 height) {
|
u32 width, u32 height) {
|
||||||
switch (pixel_format) {
|
switch (pixel_format) {
|
||||||
case PixelFormat::G8R8U:
|
|
||||||
case PixelFormat::G8R8S:
|
|
||||||
case PixelFormat::ASTC_2D_4X4:
|
case PixelFormat::ASTC_2D_4X4:
|
||||||
case PixelFormat::ASTC_2D_8X8:
|
case PixelFormat::ASTC_2D_8X8:
|
||||||
case PixelFormat::ASTC_2D_4X4_SRGB:
|
case PixelFormat::ASTC_2D_4X4_SRGB:
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ public:
|
|||||||
6, "ShaderTrianglesAdjacency");
|
6, "ShaderTrianglesAdjacency");
|
||||||
default:
|
default:
|
||||||
UNREACHABLE_MSG("Unknown primitive mode.");
|
UNREACHABLE_MSG("Unknown primitive mode.");
|
||||||
|
return LazyGeometryProgram(geometry_programs.points, "points", 1, "ShaderPoints");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -347,6 +347,15 @@ public:
|
|||||||
BuildInputList();
|
BuildInputList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetConditionalCodesFromExpression(const std::string& expresion) {
|
||||||
|
SetInternalFlag(InternalFlag::ZeroFlag, "(" + expresion + ") == 0");
|
||||||
|
LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetConditionalCodesFromRegister(const Register& reg, u64 dest_elem = 0) {
|
||||||
|
SetConditionalCodesFromExpression(GetRegister(reg, static_cast<u32>(dest_elem)));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns code that does an integer size conversion for the specified size.
|
* Returns code that does an integer size conversion for the specified size.
|
||||||
* @param value Value to perform integer size conversion on.
|
* @param value Value to perform integer size conversion on.
|
||||||
@@ -364,6 +373,7 @@ public:
|
|||||||
return value;
|
return value;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE_MSG("Unimplemented conversion size: {}", static_cast<u32>(size));
|
UNREACHABLE_MSG("Unimplemented conversion size: {}", static_cast<u32>(size));
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,14 +410,24 @@ public:
|
|||||||
* @param dest_num_components Number of components in the destination.
|
* @param dest_num_components Number of components in the destination.
|
||||||
* @param value_num_components Number of components in the value.
|
* @param value_num_components Number of components in the value.
|
||||||
* @param is_saturated Optional, when True, saturates the provided value.
|
* @param is_saturated Optional, when True, saturates the provided value.
|
||||||
|
* @param sets_cc Optional, when True, sets the corresponding values to the implemented
|
||||||
|
* condition flags.
|
||||||
* @param dest_elem Optional, the destination element to use for the operation.
|
* @param dest_elem Optional, the destination element to use for the operation.
|
||||||
*/
|
*/
|
||||||
void SetRegisterToFloat(const Register& reg, u64 elem, const std::string& value,
|
void SetRegisterToFloat(const Register& reg, u64 elem, const std::string& value,
|
||||||
u64 dest_num_components, u64 value_num_components,
|
u64 dest_num_components, u64 value_num_components,
|
||||||
bool is_saturated = false, u64 dest_elem = 0, bool precise = false) {
|
bool is_saturated = false, bool sets_cc = false, u64 dest_elem = 0,
|
||||||
|
bool precise = false) {
|
||||||
SetRegister(reg, elem, is_saturated ? "clamp(" + value + ", 0.0, 1.0)" : value,
|
const std::string clamped_value = is_saturated ? "clamp(" + value + ", 0.0, 1.0)" : value;
|
||||||
dest_num_components, value_num_components, dest_elem, precise);
|
SetRegister(reg, elem, clamped_value, dest_num_components, value_num_components, dest_elem,
|
||||||
|
precise);
|
||||||
|
if (sets_cc) {
|
||||||
|
if (reg == Register::ZeroIndex) {
|
||||||
|
SetConditionalCodesFromExpression(clamped_value);
|
||||||
|
} else {
|
||||||
|
SetConditionalCodesFromRegister(reg, dest_elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -418,25 +438,29 @@ public:
|
|||||||
* @param dest_num_components Number of components in the destination.
|
* @param dest_num_components Number of components in the destination.
|
||||||
* @param value_num_components Number of components in the value.
|
* @param value_num_components Number of components in the value.
|
||||||
* @param is_saturated Optional, when True, saturates the provided value.
|
* @param is_saturated Optional, when True, saturates the provided value.
|
||||||
|
* @param sets_cc Optional, when True, sets the corresponding values to the implemented
|
||||||
|
* condition flags.
|
||||||
* @param dest_elem Optional, the destination element to use for the operation.
|
* @param dest_elem Optional, the destination element to use for the operation.
|
||||||
* @param size Register size to use for conversion instructions.
|
* @param size Register size to use for conversion instructions.
|
||||||
*/
|
*/
|
||||||
void SetRegisterToInteger(const Register& reg, bool is_signed, u64 elem,
|
void SetRegisterToInteger(const Register& reg, bool is_signed, u64 elem,
|
||||||
const std::string& value, u64 dest_num_components,
|
const std::string& value, u64 dest_num_components,
|
||||||
u64 value_num_components, bool is_saturated = false,
|
u64 value_num_components, bool is_saturated = false,
|
||||||
u64 dest_elem = 0, Register::Size size = Register::Size::Word,
|
bool sets_cc = false, u64 dest_elem = 0,
|
||||||
bool sets_cc = false) {
|
Register::Size size = Register::Size::Word) {
|
||||||
UNIMPLEMENTED_IF(is_saturated);
|
UNIMPLEMENTED_IF(is_saturated);
|
||||||
|
const std::string final_value = ConvertIntegerSize(value, size);
|
||||||
const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"};
|
const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"};
|
||||||
|
|
||||||
SetRegister(reg, elem, func + '(' + ConvertIntegerSize(value, size) + ')',
|
SetRegister(reg, elem, func + '(' + final_value + ')', dest_num_components,
|
||||||
dest_num_components, value_num_components, dest_elem, false);
|
value_num_components, dest_elem, false);
|
||||||
|
|
||||||
if (sets_cc) {
|
if (sets_cc) {
|
||||||
const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )";
|
if (reg == Register::ZeroIndex) {
|
||||||
SetInternalFlag(InternalFlag::ZeroFlag, zero_condition);
|
SetConditionalCodesFromExpression(final_value);
|
||||||
LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete.");
|
} else {
|
||||||
|
SetConditionalCodesFromRegister(reg, dest_elem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,10 +493,10 @@ public:
|
|||||||
// pack. I couldn't test this on hardware but it shouldn't really matter since most
|
// pack. I couldn't test this on hardware but it shouldn't really matter since most
|
||||||
// of the time when a Mrg_* flag is used both components will be mirrored. That
|
// of the time when a Mrg_* flag is used both components will be mirrored. That
|
||||||
// being said, it deserves a test.
|
// being said, it deserves a test.
|
||||||
return "((" + GetRegisterAsInteger(reg, 0, false) +
|
return "uintBitsToFloat((" + GetRegisterAsInteger(reg, 0, false) +
|
||||||
" & 0xffff0000) | (packHalf2x16(" + value + ") & 0x0000ffff))";
|
" & 0xffff0000) | (packHalf2x16(" + value + ") & 0x0000ffff))";
|
||||||
case Tegra::Shader::HalfMerge::Mrg_H1:
|
case Tegra::Shader::HalfMerge::Mrg_H1:
|
||||||
return "((" + GetRegisterAsInteger(reg, 0, false) +
|
return "uintBitsToFloat((" + GetRegisterAsInteger(reg, 0, false) +
|
||||||
" & 0x0000ffff) | (packHalf2x16(" + value + ") & 0xffff0000))";
|
" & 0x0000ffff) | (packHalf2x16(" + value + ") & 0xffff0000))";
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
@@ -626,6 +650,7 @@ public:
|
|||||||
return "floatBitsToInt(" + value + ')';
|
return "floatBitsToInt(" + value + ')';
|
||||||
} else {
|
} else {
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1273,7 +1298,7 @@ private:
|
|||||||
void WriteLogicOperation(Register dest, LogicOperation logic_op, const std::string& op_a,
|
void WriteLogicOperation(Register dest, LogicOperation logic_op, const std::string& op_a,
|
||||||
const std::string& op_b,
|
const std::string& op_b,
|
||||||
Tegra::Shader::PredicateResultMode predicate_mode,
|
Tegra::Shader::PredicateResultMode predicate_mode,
|
||||||
Tegra::Shader::Pred predicate) {
|
Tegra::Shader::Pred predicate, const bool set_cc) {
|
||||||
std::string result{};
|
std::string result{};
|
||||||
switch (logic_op) {
|
switch (logic_op) {
|
||||||
case LogicOperation::And: {
|
case LogicOperation::And: {
|
||||||
@@ -1297,7 +1322,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dest != Tegra::Shader::Register::ZeroIndex) {
|
if (dest != Tegra::Shader::Register::ZeroIndex) {
|
||||||
regs.SetRegisterToInteger(dest, true, 0, result, 1, 1);
|
regs.SetRegisterToInteger(dest, true, 0, result, 1, 1, false, set_cc);
|
||||||
}
|
}
|
||||||
|
|
||||||
using Tegra::Shader::PredicateResultMode;
|
using Tegra::Shader::PredicateResultMode;
|
||||||
@@ -1317,7 +1342,8 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WriteLop3Instruction(Register dest, const std::string& op_a, const std::string& op_b,
|
void WriteLop3Instruction(Register dest, const std::string& op_a, const std::string& op_b,
|
||||||
const std::string& op_c, const std::string& imm_lut) {
|
const std::string& op_c, const std::string& imm_lut,
|
||||||
|
const bool set_cc) {
|
||||||
if (dest == Tegra::Shader::Register::ZeroIndex) {
|
if (dest == Tegra::Shader::Register::ZeroIndex) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1340,7 +1366,7 @@ private:
|
|||||||
|
|
||||||
result += ')';
|
result += ')';
|
||||||
|
|
||||||
regs.SetRegisterToInteger(dest, true, 0, result, 1, 1);
|
regs.SetRegisterToInteger(dest, true, 0, result, 1, 1, false, set_cc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteTexsInstructionFloat(const Instruction& instr, const std::string& texture) {
|
void WriteTexsInstructionFloat(const Instruction& instr, const std::string& texture) {
|
||||||
@@ -1355,12 +1381,12 @@ private:
|
|||||||
|
|
||||||
if (written_components < 2) {
|
if (written_components < 2) {
|
||||||
// Write the first two swizzle components to gpr0 and gpr0+1
|
// Write the first two swizzle components to gpr0 and gpr0+1
|
||||||
regs.SetRegisterToFloat(instr.gpr0, component, texture, 1, 4, false,
|
regs.SetRegisterToFloat(instr.gpr0, component, texture, 1, 4, false, false,
|
||||||
written_components % 2);
|
written_components % 2);
|
||||||
} else {
|
} else {
|
||||||
ASSERT(instr.texs.HasTwoDestinations());
|
ASSERT(instr.texs.HasTwoDestinations());
|
||||||
// Write the rest of the swizzle components to gpr28 and gpr28+1
|
// Write the rest of the swizzle components to gpr28 and gpr28+1
|
||||||
regs.SetRegisterToFloat(instr.gpr28, component, texture, 1, 4, false,
|
regs.SetRegisterToFloat(instr.gpr28, component, texture, 1, 4, false, false,
|
||||||
written_components % 2);
|
written_components % 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1591,23 +1617,21 @@ private:
|
|||||||
process_mode == Tegra::Shader::TextureProcessMode::LL ||
|
process_mode == Tegra::Shader::TextureProcessMode::LL ||
|
||||||
process_mode == Tegra::Shader::TextureProcessMode::LLA;
|
process_mode == Tegra::Shader::TextureProcessMode::LLA;
|
||||||
|
|
||||||
|
// LOD selection (either via bias or explicit textureLod) not supported in GL for
|
||||||
|
// sampler2DArrayShadow and samplerCubeArrayShadow.
|
||||||
const bool gl_lod_supported = !(
|
const bool gl_lod_supported = !(
|
||||||
(texture_type == Tegra::Shader::TextureType::Texture2D && is_array && depth_compare) ||
|
(texture_type == Tegra::Shader::TextureType::Texture2D && is_array && depth_compare) ||
|
||||||
(texture_type == Tegra::Shader::TextureType::TextureCube && !is_array &&
|
(texture_type == Tegra::Shader::TextureType::TextureCube && is_array && depth_compare));
|
||||||
depth_compare));
|
|
||||||
|
|
||||||
const std::string read_method = lod_needed && gl_lod_supported ? "textureLod(" : "texture(";
|
const std::string read_method = lod_needed && gl_lod_supported ? "textureLod(" : "texture(";
|
||||||
std::string texture = read_method + sampler + ", coord";
|
std::string texture = read_method + sampler + ", coord";
|
||||||
|
|
||||||
if (process_mode != Tegra::Shader::TextureProcessMode::None) {
|
UNIMPLEMENTED_IF(process_mode != Tegra::Shader::TextureProcessMode::None &&
|
||||||
|
!gl_lod_supported);
|
||||||
|
|
||||||
|
if (process_mode != Tegra::Shader::TextureProcessMode::None && gl_lod_supported) {
|
||||||
if (process_mode == Tegra::Shader::TextureProcessMode::LZ) {
|
if (process_mode == Tegra::Shader::TextureProcessMode::LZ) {
|
||||||
if (gl_lod_supported) {
|
texture += ", 0.0";
|
||||||
texture += ", 0";
|
|
||||||
} else {
|
|
||||||
// Lod 0 is emulated by a big negative bias
|
|
||||||
// in scenarios that are not supported by glsl
|
|
||||||
texture += ", -1000";
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// If present, lod or bias are always stored in the register indexed by the
|
// If present, lod or bias are always stored in the register indexed by the
|
||||||
// gpr20
|
// gpr20
|
||||||
@@ -1645,15 +1669,15 @@ private:
|
|||||||
if (depth_compare && !is_array && texture_type == Tegra::Shader::TextureType::Texture1D) {
|
if (depth_compare && !is_array && texture_type == Tegra::Shader::TextureType::Texture1D) {
|
||||||
coord += ",0.0";
|
coord += ",0.0";
|
||||||
}
|
}
|
||||||
|
if (is_array) {
|
||||||
|
coord += ',' + regs.GetRegisterAsInteger(array_register);
|
||||||
|
}
|
||||||
if (depth_compare) {
|
if (depth_compare) {
|
||||||
// Depth is always stored in the register signaled by gpr20
|
// Depth is always stored in the register signaled by gpr20
|
||||||
// or in the next register if lod or bias are used
|
// or in the next register if lod or bias are used
|
||||||
const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0);
|
const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0);
|
||||||
coord += ',' + regs.GetRegisterAsFloat(depth_register);
|
coord += ',' + regs.GetRegisterAsFloat(depth_register);
|
||||||
}
|
}
|
||||||
if (is_array) {
|
|
||||||
coord += ',' + regs.GetRegisterAsInteger(array_register);
|
|
||||||
}
|
|
||||||
coord += ");";
|
coord += ");";
|
||||||
return std::make_pair(
|
return std::make_pair(
|
||||||
coord, GetTextureCode(instr, texture_type, process_mode, depth_compare, is_array, 0));
|
coord, GetTextureCode(instr, texture_type, process_mode, depth_compare, is_array, 0));
|
||||||
@@ -1686,15 +1710,15 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_array) {
|
||||||
|
coord += ',' + regs.GetRegisterAsInteger(array_register);
|
||||||
|
}
|
||||||
if (depth_compare) {
|
if (depth_compare) {
|
||||||
// Depth is always stored in the register signaled by gpr20
|
// Depth is always stored in the register signaled by gpr20
|
||||||
// or in the next register if lod or bias are used
|
// or in the next register if lod or bias are used
|
||||||
const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0);
|
const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0);
|
||||||
coord += ',' + regs.GetRegisterAsFloat(depth_register);
|
coord += ',' + regs.GetRegisterAsFloat(depth_register);
|
||||||
}
|
}
|
||||||
if (is_array) {
|
|
||||||
coord += ',' + regs.GetRegisterAsInteger(array_register);
|
|
||||||
}
|
|
||||||
coord += ");";
|
coord += ");";
|
||||||
|
|
||||||
return std::make_pair(coord,
|
return std::make_pair(coord,
|
||||||
@@ -1755,7 +1779,7 @@ private:
|
|||||||
instr.tlds.GetTextureProcessMode() == Tegra::Shader::TextureProcessMode::LL;
|
instr.tlds.GetTextureProcessMode() == Tegra::Shader::TextureProcessMode::LL;
|
||||||
|
|
||||||
constexpr std::array<const char*, 4> coord_container{
|
constexpr std::array<const char*, 4> coord_container{
|
||||||
{"", "int coord = (", "ivec2 coord = ivec2(", "ivec3 coord = ivec3("}};
|
{"", "int coords = (", "ivec2 coords = ivec2(", "ivec3 coords = ivec3("}};
|
||||||
|
|
||||||
std::string coord = coord_container[total_coord_count];
|
std::string coord = coord_container[total_coord_count];
|
||||||
|
|
||||||
@@ -1867,33 +1891,44 @@ private:
|
|||||||
UNIMPLEMENTED_IF_MSG(instr.fmul.tab5cb8_2 != 0,
|
UNIMPLEMENTED_IF_MSG(instr.fmul.tab5cb8_2 != 0,
|
||||||
"FMUL tab5cb8_2({}) is not implemented",
|
"FMUL tab5cb8_2({}) is not implemented",
|
||||||
instr.fmul.tab5cb8_2.Value());
|
instr.fmul.tab5cb8_2.Value());
|
||||||
UNIMPLEMENTED_IF_MSG(instr.fmul.tab5c68_1 != 0,
|
|
||||||
"FMUL tab5cb8_1({}) is not implemented",
|
|
||||||
instr.fmul.tab5c68_1.Value());
|
|
||||||
UNIMPLEMENTED_IF_MSG(
|
UNIMPLEMENTED_IF_MSG(
|
||||||
instr.fmul.tab5c68_0 != 1, "FMUL tab5cb8_0({}) is not implemented",
|
instr.fmul.tab5c68_0 != 1, "FMUL tab5cb8_0({}) is not implemented",
|
||||||
instr.fmul.tab5c68_0
|
instr.fmul.tab5c68_0
|
||||||
.Value()); // SMO typical sends 1 here which seems to be the default
|
.Value()); // SMO typical sends 1 here which seems to be the default
|
||||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
|
||||||
"Condition codes generation in FMUL is not implemented");
|
|
||||||
|
|
||||||
op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b);
|
op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b);
|
||||||
|
|
||||||
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1,
|
std::string postfactor_op;
|
||||||
instr.alu.saturate_d, 0, true);
|
if (instr.fmul.postfactor != 0) {
|
||||||
|
s8 postfactor = static_cast<s8>(instr.fmul.postfactor);
|
||||||
|
|
||||||
|
// postfactor encoded as 3-bit 1's complement in instruction,
|
||||||
|
// interpreted with below logic.
|
||||||
|
if (postfactor >= 4) {
|
||||||
|
postfactor = 7 - postfactor;
|
||||||
|
} else {
|
||||||
|
postfactor = 0 - postfactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (postfactor > 0) {
|
||||||
|
postfactor_op = " * " + std::to_string(1 << postfactor);
|
||||||
|
} else {
|
||||||
|
postfactor_op = " / " + std::to_string(1 << -postfactor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b + postfactor_op, 1, 1,
|
||||||
|
instr.alu.saturate_d, instr.generates_cc, 0, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Id::FADD_C:
|
case OpCode::Id::FADD_C:
|
||||||
case OpCode::Id::FADD_R:
|
case OpCode::Id::FADD_R:
|
||||||
case OpCode::Id::FADD_IMM: {
|
case OpCode::Id::FADD_IMM: {
|
||||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
|
||||||
"Condition codes generation in FADD is not implemented");
|
|
||||||
|
|
||||||
op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a);
|
op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a);
|
||||||
op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b);
|
op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b);
|
||||||
|
|
||||||
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1,
|
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1,
|
||||||
instr.alu.saturate_d, 0, true);
|
instr.alu.saturate_d, instr.generates_cc, 0, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Id::MUFU: {
|
case OpCode::Id::MUFU: {
|
||||||
@@ -1901,31 +1936,31 @@ private:
|
|||||||
switch (instr.sub_op) {
|
switch (instr.sub_op) {
|
||||||
case SubOp::Cos:
|
case SubOp::Cos:
|
||||||
regs.SetRegisterToFloat(instr.gpr0, 0, "cos(" + op_a + ')', 1, 1,
|
regs.SetRegisterToFloat(instr.gpr0, 0, "cos(" + op_a + ')', 1, 1,
|
||||||
instr.alu.saturate_d, 0, true);
|
instr.alu.saturate_d, false, 0, true);
|
||||||
break;
|
break;
|
||||||
case SubOp::Sin:
|
case SubOp::Sin:
|
||||||
regs.SetRegisterToFloat(instr.gpr0, 0, "sin(" + op_a + ')', 1, 1,
|
regs.SetRegisterToFloat(instr.gpr0, 0, "sin(" + op_a + ')', 1, 1,
|
||||||
instr.alu.saturate_d, 0, true);
|
instr.alu.saturate_d, false, 0, true);
|
||||||
break;
|
break;
|
||||||
case SubOp::Ex2:
|
case SubOp::Ex2:
|
||||||
regs.SetRegisterToFloat(instr.gpr0, 0, "exp2(" + op_a + ')', 1, 1,
|
regs.SetRegisterToFloat(instr.gpr0, 0, "exp2(" + op_a + ')', 1, 1,
|
||||||
instr.alu.saturate_d, 0, true);
|
instr.alu.saturate_d, false, 0, true);
|
||||||
break;
|
break;
|
||||||
case SubOp::Lg2:
|
case SubOp::Lg2:
|
||||||
regs.SetRegisterToFloat(instr.gpr0, 0, "log2(" + op_a + ')', 1, 1,
|
regs.SetRegisterToFloat(instr.gpr0, 0, "log2(" + op_a + ')', 1, 1,
|
||||||
instr.alu.saturate_d, 0, true);
|
instr.alu.saturate_d, false, 0, true);
|
||||||
break;
|
break;
|
||||||
case SubOp::Rcp:
|
case SubOp::Rcp:
|
||||||
regs.SetRegisterToFloat(instr.gpr0, 0, "1.0 / " + op_a, 1, 1,
|
regs.SetRegisterToFloat(instr.gpr0, 0, "1.0 / " + op_a, 1, 1,
|
||||||
instr.alu.saturate_d, 0, true);
|
instr.alu.saturate_d, false, 0, true);
|
||||||
break;
|
break;
|
||||||
case SubOp::Rsq:
|
case SubOp::Rsq:
|
||||||
regs.SetRegisterToFloat(instr.gpr0, 0, "inversesqrt(" + op_a + ')', 1, 1,
|
regs.SetRegisterToFloat(instr.gpr0, 0, "inversesqrt(" + op_a + ')', 1, 1,
|
||||||
instr.alu.saturate_d, 0, true);
|
instr.alu.saturate_d, false, 0, true);
|
||||||
break;
|
break;
|
||||||
case SubOp::Sqrt:
|
case SubOp::Sqrt:
|
||||||
regs.SetRegisterToFloat(instr.gpr0, 0, "sqrt(" + op_a + ')', 1, 1,
|
regs.SetRegisterToFloat(instr.gpr0, 0, "sqrt(" + op_a + ')', 1, 1,
|
||||||
instr.alu.saturate_d, 0, true);
|
instr.alu.saturate_d, false, 0, true);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unhandled MUFU sub op={0:x}",
|
UNIMPLEMENTED_MSG("Unhandled MUFU sub op={0:x}",
|
||||||
@@ -1936,8 +1971,9 @@ private:
|
|||||||
case OpCode::Id::FMNMX_C:
|
case OpCode::Id::FMNMX_C:
|
||||||
case OpCode::Id::FMNMX_R:
|
case OpCode::Id::FMNMX_R:
|
||||||
case OpCode::Id::FMNMX_IMM: {
|
case OpCode::Id::FMNMX_IMM: {
|
||||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
UNIMPLEMENTED_IF_MSG(
|
||||||
"Condition codes generation in FMNMX is not implemented");
|
instr.generates_cc,
|
||||||
|
"Condition codes generation in FMNMX is partially implemented");
|
||||||
|
|
||||||
op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a);
|
op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a);
|
||||||
op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b);
|
op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b);
|
||||||
@@ -1948,7 +1984,7 @@ private:
|
|||||||
regs.SetRegisterToFloat(instr.gpr0, 0,
|
regs.SetRegisterToFloat(instr.gpr0, 0,
|
||||||
'(' + condition + ") ? min(" + parameters + ") : max(" +
|
'(' + condition + ") ? min(" + parameters + ") : max(" +
|
||||||
parameters + ')',
|
parameters + ')',
|
||||||
1, 1, false, 0, true);
|
1, 1, false, instr.generates_cc, 0, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Id::RRO_C:
|
case OpCode::Id::RRO_C:
|
||||||
@@ -1973,18 +2009,16 @@ private:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Id::FMUL32_IMM: {
|
case OpCode::Id::FMUL32_IMM: {
|
||||||
UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
|
regs.SetRegisterToFloat(
|
||||||
"Condition codes generation in FMUL32 is not implemented");
|
instr.gpr0, 0,
|
||||||
|
regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1,
|
||||||
regs.SetRegisterToFloat(instr.gpr0, 0,
|
instr.fmul32.saturate, instr.op_32.generates_cc, 0, true);
|
||||||
regs.GetRegisterAsFloat(instr.gpr8) + " * " +
|
|
||||||
GetImmediate32(instr),
|
|
||||||
1, 1, instr.fmul32.saturate, 0, true);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Id::FADD32I: {
|
case OpCode::Id::FADD32I: {
|
||||||
UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
|
UNIMPLEMENTED_IF_MSG(
|
||||||
"Condition codes generation in FADD32I is not implemented");
|
instr.op_32.generates_cc,
|
||||||
|
"Condition codes generation in FADD32I is partially implemented");
|
||||||
|
|
||||||
std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
|
std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
|
||||||
std::string op_b = GetImmediate32(instr);
|
std::string op_b = GetImmediate32(instr);
|
||||||
@@ -2005,7 +2039,8 @@ private:
|
|||||||
op_b = "-(" + op_b + ')';
|
op_b = "-(" + op_b + ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, false, 0, true);
|
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, false,
|
||||||
|
instr.op_32.generates_cc, 0, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2019,16 +2054,14 @@ private:
|
|||||||
|
|
||||||
switch (opcode->get().GetId()) {
|
switch (opcode->get().GetId()) {
|
||||||
case OpCode::Id::BFE_IMM: {
|
case OpCode::Id::BFE_IMM: {
|
||||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
|
||||||
"Condition codes generation in BFE is not implemented");
|
|
||||||
|
|
||||||
std::string inner_shift =
|
std::string inner_shift =
|
||||||
'(' + op_a + " << " + std::to_string(instr.bfe.GetLeftShiftValue()) + ')';
|
'(' + op_a + " << " + std::to_string(instr.bfe.GetLeftShiftValue()) + ')';
|
||||||
std::string outer_shift =
|
std::string outer_shift =
|
||||||
'(' + inner_shift + " >> " +
|
'(' + inner_shift + " >> " +
|
||||||
std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')';
|
std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')';
|
||||||
|
|
||||||
regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1);
|
regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1, false,
|
||||||
|
instr.generates_cc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
@@ -2039,8 +2072,6 @@ private:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Type::Bfi: {
|
case OpCode::Type::Bfi: {
|
||||||
UNIMPLEMENTED_IF(instr.generates_cc);
|
|
||||||
|
|
||||||
const auto [base, packed_shift] = [&]() -> std::tuple<std::string, std::string> {
|
const auto [base, packed_shift] = [&]() -> std::tuple<std::string, std::string> {
|
||||||
switch (opcode->get().GetId()) {
|
switch (opcode->get().GetId()) {
|
||||||
case OpCode::Id::BFI_IMM_R:
|
case OpCode::Id::BFI_IMM_R:
|
||||||
@@ -2048,14 +2079,17 @@ private:
|
|||||||
std::to_string(instr.alu.GetSignedImm20_20())};
|
std::to_string(instr.alu.GetSignedImm20_20())};
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
return {regs.GetRegisterAsInteger(instr.gpr39, 0, false),
|
||||||
|
std::to_string(instr.alu.GetSignedImm20_20())};
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
const std::string offset = '(' + packed_shift + " & 0xff)";
|
const std::string offset = '(' + packed_shift + " & 0xff)";
|
||||||
const std::string bits = "((" + packed_shift + " >> 8) & 0xff)";
|
const std::string bits = "((" + packed_shift + " >> 8) & 0xff)";
|
||||||
const std::string insert = regs.GetRegisterAsInteger(instr.gpr8, 0, false);
|
const std::string insert = regs.GetRegisterAsInteger(instr.gpr8, 0, false);
|
||||||
regs.SetRegisterToInteger(
|
regs.SetRegisterToInteger(instr.gpr0, false, 0,
|
||||||
instr.gpr0, false, 0,
|
"bitfieldInsert(" + base + ", " + insert + ", " + offset +
|
||||||
"bitfieldInsert(" + base + ", " + insert + ", " + offset + ", " + bits + ')', 1, 1);
|
", " + bits + ')',
|
||||||
|
1, 1, false, instr.generates_cc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Type::Shift: {
|
case OpCode::Type::Shift: {
|
||||||
@@ -2077,9 +2111,6 @@ private:
|
|||||||
case OpCode::Id::SHR_C:
|
case OpCode::Id::SHR_C:
|
||||||
case OpCode::Id::SHR_R:
|
case OpCode::Id::SHR_R:
|
||||||
case OpCode::Id::SHR_IMM: {
|
case OpCode::Id::SHR_IMM: {
|
||||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
|
||||||
"Condition codes generation in SHR is not implemented");
|
|
||||||
|
|
||||||
if (!instr.shift.is_signed) {
|
if (!instr.shift.is_signed) {
|
||||||
// Logical shift right
|
// Logical shift right
|
||||||
op_a = "uint(" + op_a + ')';
|
op_a = "uint(" + op_a + ')';
|
||||||
@@ -2087,7 +2118,7 @@ private:
|
|||||||
|
|
||||||
// Cast to int is superfluous for arithmetic shift, it's only for a logical shift
|
// Cast to int is superfluous for arithmetic shift, it's only for a logical shift
|
||||||
regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')',
|
regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')',
|
||||||
1, 1);
|
1, 1, false, instr.generates_cc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Id::SHL_C:
|
case OpCode::Id::SHL_C:
|
||||||
@@ -2095,7 +2126,8 @@ private:
|
|||||||
case OpCode::Id::SHL_IMM:
|
case OpCode::Id::SHL_IMM:
|
||||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
||||||
"Condition codes generation in SHL is not implemented");
|
"Condition codes generation in SHL is not implemented");
|
||||||
regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1);
|
regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1, false,
|
||||||
|
instr.generates_cc);
|
||||||
break;
|
break;
|
||||||
default: {
|
default: {
|
||||||
UNIMPLEMENTED_MSG("Unhandled shift instruction: {}", opcode->get().GetName());
|
UNIMPLEMENTED_MSG("Unhandled shift instruction: {}", opcode->get().GetName());
|
||||||
@@ -2109,18 +2141,17 @@ private:
|
|||||||
|
|
||||||
switch (opcode->get().GetId()) {
|
switch (opcode->get().GetId()) {
|
||||||
case OpCode::Id::IADD32I:
|
case OpCode::Id::IADD32I:
|
||||||
UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
|
UNIMPLEMENTED_IF_MSG(
|
||||||
"Condition codes generation in IADD32I is not implemented");
|
instr.op_32.generates_cc,
|
||||||
|
"Condition codes generation in IADD32I is partially implemented");
|
||||||
|
|
||||||
if (instr.iadd32i.negate_a)
|
if (instr.iadd32i.negate_a)
|
||||||
op_a = "-(" + op_a + ')';
|
op_a = "-(" + op_a + ')';
|
||||||
|
|
||||||
regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
|
regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
|
||||||
instr.iadd32i.saturate != 0);
|
instr.iadd32i.saturate, instr.op_32.generates_cc);
|
||||||
break;
|
break;
|
||||||
case OpCode::Id::LOP32I: {
|
case OpCode::Id::LOP32I: {
|
||||||
UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
|
|
||||||
"Condition codes generation in LOP32I is not implemented");
|
|
||||||
|
|
||||||
if (instr.alu.lop32i.invert_a)
|
if (instr.alu.lop32i.invert_a)
|
||||||
op_a = "~(" + op_a + ')';
|
op_a = "~(" + op_a + ')';
|
||||||
@@ -2130,7 +2161,7 @@ private:
|
|||||||
|
|
||||||
WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b,
|
WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b,
|
||||||
Tegra::Shader::PredicateResultMode::None,
|
Tegra::Shader::PredicateResultMode::None,
|
||||||
Tegra::Shader::Pred::UnusedIndex);
|
Tegra::Shader::Pred::UnusedIndex, instr.op_32.generates_cc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
@@ -2159,7 +2190,7 @@ private:
|
|||||||
case OpCode::Id::IADD_R:
|
case OpCode::Id::IADD_R:
|
||||||
case OpCode::Id::IADD_IMM: {
|
case OpCode::Id::IADD_IMM: {
|
||||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
||||||
"Condition codes generation in IADD is not implemented");
|
"Condition codes generation in IADD is partially implemented");
|
||||||
|
|
||||||
if (instr.alu_integer.negate_a)
|
if (instr.alu_integer.negate_a)
|
||||||
op_a = "-(" + op_a + ')';
|
op_a = "-(" + op_a + ')';
|
||||||
@@ -2168,14 +2199,15 @@ private:
|
|||||||
op_b = "-(" + op_b + ')';
|
op_b = "-(" + op_b + ')';
|
||||||
|
|
||||||
regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
|
regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
|
||||||
instr.alu.saturate_d);
|
instr.alu.saturate_d, instr.generates_cc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Id::IADD3_C:
|
case OpCode::Id::IADD3_C:
|
||||||
case OpCode::Id::IADD3_R:
|
case OpCode::Id::IADD3_R:
|
||||||
case OpCode::Id::IADD3_IMM: {
|
case OpCode::Id::IADD3_IMM: {
|
||||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
UNIMPLEMENTED_IF_MSG(
|
||||||
"Condition codes generation in IADD3 is not implemented");
|
instr.generates_cc,
|
||||||
|
"Condition codes generation in IADD3 is partially implemented");
|
||||||
|
|
||||||
std::string op_c = regs.GetRegisterAsInteger(instr.gpr39);
|
std::string op_c = regs.GetRegisterAsInteger(instr.gpr39);
|
||||||
|
|
||||||
@@ -2231,14 +2263,16 @@ private:
|
|||||||
result = '(' + op_a + " + " + op_b + " + " + op_c + ')';
|
result = '(' + op_a + " + " + op_b + " + " + op_c + ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1);
|
regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1, false,
|
||||||
|
instr.generates_cc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Id::ISCADD_C:
|
case OpCode::Id::ISCADD_C:
|
||||||
case OpCode::Id::ISCADD_R:
|
case OpCode::Id::ISCADD_R:
|
||||||
case OpCode::Id::ISCADD_IMM: {
|
case OpCode::Id::ISCADD_IMM: {
|
||||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
UNIMPLEMENTED_IF_MSG(
|
||||||
"Condition codes generation in ISCADD is not implemented");
|
instr.generates_cc,
|
||||||
|
"Condition codes generation in ISCADD is partially implemented");
|
||||||
|
|
||||||
if (instr.alu_integer.negate_a)
|
if (instr.alu_integer.negate_a)
|
||||||
op_a = "-(" + op_a + ')';
|
op_a = "-(" + op_a + ')';
|
||||||
@@ -2249,7 +2283,8 @@ private:
|
|||||||
const std::string shift = std::to_string(instr.alu_integer.shift_amount.Value());
|
const std::string shift = std::to_string(instr.alu_integer.shift_amount.Value());
|
||||||
|
|
||||||
regs.SetRegisterToInteger(instr.gpr0, true, 0,
|
regs.SetRegisterToInteger(instr.gpr0, true, 0,
|
||||||
"((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1);
|
"((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1,
|
||||||
|
false, instr.generates_cc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Id::POPC_C:
|
case OpCode::Id::POPC_C:
|
||||||
@@ -2273,8 +2308,6 @@ private:
|
|||||||
case OpCode::Id::LOP_C:
|
case OpCode::Id::LOP_C:
|
||||||
case OpCode::Id::LOP_R:
|
case OpCode::Id::LOP_R:
|
||||||
case OpCode::Id::LOP_IMM: {
|
case OpCode::Id::LOP_IMM: {
|
||||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
|
||||||
"Condition codes generation in LOP is not implemented");
|
|
||||||
|
|
||||||
if (instr.alu.lop.invert_a)
|
if (instr.alu.lop.invert_a)
|
||||||
op_a = "~(" + op_a + ')';
|
op_a = "~(" + op_a + ')';
|
||||||
@@ -2283,15 +2316,13 @@ private:
|
|||||||
op_b = "~(" + op_b + ')';
|
op_b = "~(" + op_b + ')';
|
||||||
|
|
||||||
WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b,
|
WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b,
|
||||||
instr.alu.lop.pred_result_mode, instr.alu.lop.pred48);
|
instr.alu.lop.pred_result_mode, instr.alu.lop.pred48,
|
||||||
|
instr.generates_cc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Id::LOP3_C:
|
case OpCode::Id::LOP3_C:
|
||||||
case OpCode::Id::LOP3_R:
|
case OpCode::Id::LOP3_R:
|
||||||
case OpCode::Id::LOP3_IMM: {
|
case OpCode::Id::LOP3_IMM: {
|
||||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
|
||||||
"Condition codes generation in LOP3 is not implemented");
|
|
||||||
|
|
||||||
const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39);
|
const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39);
|
||||||
std::string lut;
|
std::string lut;
|
||||||
|
|
||||||
@@ -2301,15 +2332,16 @@ private:
|
|||||||
lut = '(' + std::to_string(instr.alu.lop3.GetImmLut48()) + ')';
|
lut = '(' + std::to_string(instr.alu.lop3.GetImmLut48()) + ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut);
|
WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut, instr.generates_cc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Id::IMNMX_C:
|
case OpCode::Id::IMNMX_C:
|
||||||
case OpCode::Id::IMNMX_R:
|
case OpCode::Id::IMNMX_R:
|
||||||
case OpCode::Id::IMNMX_IMM: {
|
case OpCode::Id::IMNMX_IMM: {
|
||||||
UNIMPLEMENTED_IF(instr.imnmx.exchange != Tegra::Shader::IMinMaxExchange::None);
|
UNIMPLEMENTED_IF(instr.imnmx.exchange != Tegra::Shader::IMinMaxExchange::None);
|
||||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
UNIMPLEMENTED_IF_MSG(
|
||||||
"Condition codes generation in IMNMX is not implemented");
|
instr.generates_cc,
|
||||||
|
"Condition codes generation in IMNMX is partially implemented");
|
||||||
|
|
||||||
const std::string condition =
|
const std::string condition =
|
||||||
GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0);
|
GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0);
|
||||||
@@ -2317,7 +2349,7 @@ private:
|
|||||||
regs.SetRegisterToInteger(instr.gpr0, instr.imnmx.is_signed, 0,
|
regs.SetRegisterToInteger(instr.gpr0, instr.imnmx.is_signed, 0,
|
||||||
'(' + condition + ") ? min(" + parameters + ") : max(" +
|
'(' + condition + ") ? min(" + parameters + ") : max(" +
|
||||||
parameters + ')',
|
parameters + ')',
|
||||||
1, 1);
|
1, 1, false, instr.generates_cc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Id::LEA_R2:
|
case OpCode::Id::LEA_R2:
|
||||||
@@ -2378,7 +2410,8 @@ private:
|
|||||||
UNIMPLEMENTED_IF_MSG(instr.lea.pred48 != static_cast<u64>(Pred::UnusedIndex),
|
UNIMPLEMENTED_IF_MSG(instr.lea.pred48 != static_cast<u64>(Pred::UnusedIndex),
|
||||||
"Unhandled LEA Predicate");
|
"Unhandled LEA Predicate");
|
||||||
const std::string value = '(' + op_a + " + (" + op_b + "*(1 << " + op_c + ")))";
|
const std::string value = '(' + op_a + " + (" + op_b + "*(1 << " + op_c + ")))";
|
||||||
regs.SetRegisterToInteger(instr.gpr0, true, 0, value, 1, 1);
|
regs.SetRegisterToInteger(instr.gpr0, true, 0, value, 1, 1, false,
|
||||||
|
instr.generates_cc);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2483,7 +2516,7 @@ private:
|
|||||||
UNIMPLEMENTED_IF_MSG(instr.ffma.tab5980_1 != 0, "FFMA tab5980_1({}) not implemented",
|
UNIMPLEMENTED_IF_MSG(instr.ffma.tab5980_1 != 0, "FFMA tab5980_1({}) not implemented",
|
||||||
instr.ffma.tab5980_1.Value());
|
instr.ffma.tab5980_1.Value());
|
||||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
||||||
"Condition codes generation in FFMA is not implemented");
|
"Condition codes generation in FFMA is partially implemented");
|
||||||
|
|
||||||
switch (opcode->get().GetId()) {
|
switch (opcode->get().GetId()) {
|
||||||
case OpCode::Id::FFMA_CR: {
|
case OpCode::Id::FFMA_CR: {
|
||||||
@@ -2514,7 +2547,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
regs.SetRegisterToFloat(instr.gpr0, 0, "fma(" + op_a + ", " + op_b + ", " + op_c + ')',
|
regs.SetRegisterToFloat(instr.gpr0, 0, "fma(" + op_a + ", " + op_b + ", " + op_c + ')',
|
||||||
1, 1, instr.alu.saturate_d, 0, true);
|
1, 1, instr.alu.saturate_d, instr.generates_cc, 0, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Type::Hfma2: {
|
case OpCode::Type::Hfma2: {
|
||||||
@@ -2585,16 +2618,14 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
|
regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
|
||||||
1, instr.alu.saturate_d, 0, instr.conversion.dest_size,
|
1, instr.alu.saturate_d, instr.generates_cc, 0,
|
||||||
instr.generates_cc.Value() != 0);
|
instr.conversion.dest_size);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Id::I2F_R:
|
case OpCode::Id::I2F_R:
|
||||||
case OpCode::Id::I2F_C: {
|
case OpCode::Id::I2F_C: {
|
||||||
UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word);
|
UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word);
|
||||||
UNIMPLEMENTED_IF(instr.conversion.selector);
|
UNIMPLEMENTED_IF(instr.conversion.selector);
|
||||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
|
||||||
"Condition codes generation in I2F is not implemented");
|
|
||||||
std::string op_a;
|
std::string op_a;
|
||||||
|
|
||||||
if (instr.is_b_gpr) {
|
if (instr.is_b_gpr) {
|
||||||
@@ -2617,14 +2648,12 @@ private:
|
|||||||
op_a = "-(" + op_a + ')';
|
op_a = "-(" + op_a + ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1);
|
regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, false, instr.generates_cc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Id::F2F_R: {
|
case OpCode::Id::F2F_R: {
|
||||||
UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word);
|
UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word);
|
||||||
UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word);
|
UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word);
|
||||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
|
||||||
"Condition codes generation in F2F is not implemented");
|
|
||||||
std::string op_a = regs.GetRegisterAsFloat(instr.gpr20);
|
std::string op_a = regs.GetRegisterAsFloat(instr.gpr20);
|
||||||
|
|
||||||
if (instr.conversion.abs_a) {
|
if (instr.conversion.abs_a) {
|
||||||
@@ -2656,14 +2685,13 @@ private:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d);
|
regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d,
|
||||||
|
instr.generates_cc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Id::F2I_R:
|
case OpCode::Id::F2I_R:
|
||||||
case OpCode::Id::F2I_C: {
|
case OpCode::Id::F2I_C: {
|
||||||
UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word);
|
UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word);
|
||||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
|
||||||
"Condition codes generation in F2I is not implemented");
|
|
||||||
std::string op_a{};
|
std::string op_a{};
|
||||||
|
|
||||||
if (instr.is_b_gpr) {
|
if (instr.is_b_gpr) {
|
||||||
@@ -2706,7 +2734,8 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
|
regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
|
||||||
1, false, 0, instr.conversion.dest_size);
|
1, false, instr.generates_cc, 0,
|
||||||
|
instr.conversion.dest_size);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
@@ -2869,7 +2898,7 @@ private:
|
|||||||
shader.AddLine(coord);
|
shader.AddLine(coord);
|
||||||
|
|
||||||
if (depth_compare) {
|
if (depth_compare) {
|
||||||
regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false);
|
regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1);
|
||||||
} else {
|
} else {
|
||||||
shader.AddLine("vec4 texture_tmp = " + texture + ';');
|
shader.AddLine("vec4 texture_tmp = " + texture + ';');
|
||||||
std::size_t dest_elem{};
|
std::size_t dest_elem{};
|
||||||
@@ -2878,7 +2907,7 @@ private:
|
|||||||
// Skip disabled components
|
// Skip disabled components
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false,
|
regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false, false,
|
||||||
dest_elem);
|
dest_elem);
|
||||||
++dest_elem;
|
++dest_elem;
|
||||||
}
|
}
|
||||||
@@ -2964,7 +2993,7 @@ private:
|
|||||||
// Skip disabled components
|
// Skip disabled components
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false,
|
regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false, false,
|
||||||
dest_elem);
|
dest_elem);
|
||||||
++dest_elem;
|
++dest_elem;
|
||||||
}
|
}
|
||||||
@@ -3213,7 +3242,7 @@ private:
|
|||||||
}
|
}
|
||||||
case OpCode::Type::PredicateSetRegister: {
|
case OpCode::Type::PredicateSetRegister: {
|
||||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
||||||
"Condition codes generation in PSET is not implemented");
|
"Condition codes generation in PSET is partially implemented");
|
||||||
|
|
||||||
const std::string op_a =
|
const std::string op_a =
|
||||||
GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0);
|
GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0);
|
||||||
@@ -3230,10 +3259,11 @@ private:
|
|||||||
const std::string result = '(' + predicate + ") " + combiner + " (" + second_pred + ')';
|
const std::string result = '(' + predicate + ") " + combiner + " (" + second_pred + ')';
|
||||||
if (instr.pset.bf == 0) {
|
if (instr.pset.bf == 0) {
|
||||||
const std::string value = '(' + result + ") ? 0xFFFFFFFF : 0";
|
const std::string value = '(' + result + ") ? 0xFFFFFFFF : 0";
|
||||||
regs.SetRegisterToInteger(instr.gpr0, false, 0, value, 1, 1);
|
regs.SetRegisterToInteger(instr.gpr0, false, 0, value, 1, 1, false,
|
||||||
|
instr.generates_cc);
|
||||||
} else {
|
} else {
|
||||||
const std::string value = '(' + result + ") ? 1.0 : 0.0";
|
const std::string value = '(' + result + ") ? 1.0 : 0.0";
|
||||||
regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1);
|
regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1, false, instr.generates_cc);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -3298,6 +3328,7 @@ private:
|
|||||||
return std::to_string(instr.r2p.immediate_mask);
|
return std::to_string(instr.r2p.immediate_mask);
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
return std::to_string(instr.r2p.immediate_mask);
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
const std::string mask = '(' + regs.GetRegisterAsInteger(instr.gpr8, 0, false) +
|
const std::string mask = '(' + regs.GetRegisterAsInteger(instr.gpr8, 0, false) +
|
||||||
@@ -3349,14 +3380,11 @@ private:
|
|||||||
") " + combiner + " (" + second_pred + "))";
|
") " + combiner + " (" + second_pred + "))";
|
||||||
|
|
||||||
if (instr.fset.bf) {
|
if (instr.fset.bf) {
|
||||||
regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1);
|
regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1, false,
|
||||||
|
instr.generates_cc);
|
||||||
} else {
|
} else {
|
||||||
regs.SetRegisterToInteger(instr.gpr0, false, 0, predicate + " ? 0xFFFFFFFF : 0", 1,
|
regs.SetRegisterToInteger(instr.gpr0, false, 0, predicate + " ? 0xFFFFFFFF : 0", 1,
|
||||||
1);
|
1, false, instr.generates_cc);
|
||||||
}
|
|
||||||
if (instr.generates_cc.Value() != 0) {
|
|
||||||
regs.SetInternalFlag(InternalFlag::ZeroFlag, predicate);
|
|
||||||
LOG_WARNING(HW_GPU, "FSET Condition Code is incomplete");
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -3443,7 +3471,7 @@ private:
|
|||||||
UNIMPLEMENTED_IF(instr.xmad.sign_a);
|
UNIMPLEMENTED_IF(instr.xmad.sign_a);
|
||||||
UNIMPLEMENTED_IF(instr.xmad.sign_b);
|
UNIMPLEMENTED_IF(instr.xmad.sign_b);
|
||||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
||||||
"Condition codes generation in XMAD is not implemented");
|
"Condition codes generation in XMAD is partially implemented");
|
||||||
|
|
||||||
std::string op_a{regs.GetRegisterAsInteger(instr.gpr8, 0, instr.xmad.sign_a)};
|
std::string op_a{regs.GetRegisterAsInteger(instr.gpr8, 0, instr.xmad.sign_a)};
|
||||||
std::string op_b;
|
std::string op_b;
|
||||||
@@ -3529,7 +3557,8 @@ private:
|
|||||||
sum = "((" + sum + " & 0xFFFF) | (" + src2 + "<< 16))";
|
sum = "((" + sum + " & 0xFFFF) | (" + src2 + "<< 16))";
|
||||||
}
|
}
|
||||||
|
|
||||||
regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1);
|
regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1, false,
|
||||||
|
instr.generates_cc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
@@ -3733,8 +3762,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1,
|
regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1,
|
||||||
instr.vmad.saturate == 1, 0, Register::Size::Word,
|
instr.vmad.saturate, instr.vmad.cc);
|
||||||
instr.vmad.cc);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Id::VSETP: {
|
case OpCode::Id::VSETP: {
|
||||||
@@ -3761,7 +3789,10 @@ private:
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: { UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName()); }
|
default: {
|
||||||
|
UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName());
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -138,7 +138,12 @@ void RendererOpenGL::SwapBuffers(
|
|||||||
|
|
||||||
// Load the framebuffer from memory, draw it to the screen, and swap buffers
|
// Load the framebuffer from memory, draw it to the screen, and swap buffers
|
||||||
LoadFBToScreenInfo(*framebuffer);
|
LoadFBToScreenInfo(*framebuffer);
|
||||||
DrawScreen();
|
|
||||||
|
if (renderer_settings.screenshot_requested)
|
||||||
|
CaptureScreenshot();
|
||||||
|
|
||||||
|
DrawScreen(render_window.GetFramebufferLayout());
|
||||||
|
|
||||||
render_window.SwapBuffers();
|
render_window.SwapBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,14 +388,13 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
|
|||||||
/**
|
/**
|
||||||
* Draws the emulated screens to the emulator window.
|
* Draws the emulated screens to the emulator window.
|
||||||
*/
|
*/
|
||||||
void RendererOpenGL::DrawScreen() {
|
void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
|
||||||
if (renderer_settings.set_background_color) {
|
if (renderer_settings.set_background_color) {
|
||||||
// Update background color before drawing
|
// Update background color before drawing
|
||||||
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
||||||
0.0f);
|
0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& layout = render_window.GetFramebufferLayout();
|
|
||||||
const auto& screen = layout.screen;
|
const auto& screen = layout.screen;
|
||||||
|
|
||||||
glViewport(0, 0, layout.width, layout.height);
|
glViewport(0, 0, layout.width, layout.height);
|
||||||
@@ -414,6 +418,37 @@ void RendererOpenGL::DrawScreen() {
|
|||||||
/// Updates the framerate
|
/// Updates the framerate
|
||||||
void RendererOpenGL::UpdateFramerate() {}
|
void RendererOpenGL::UpdateFramerate() {}
|
||||||
|
|
||||||
|
void RendererOpenGL::CaptureScreenshot() {
|
||||||
|
// Draw the current frame to the screenshot framebuffer
|
||||||
|
screenshot_framebuffer.Create();
|
||||||
|
GLuint old_read_fb = state.draw.read_framebuffer;
|
||||||
|
GLuint old_draw_fb = state.draw.draw_framebuffer;
|
||||||
|
state.draw.read_framebuffer = state.draw.draw_framebuffer = screenshot_framebuffer.handle;
|
||||||
|
state.Apply();
|
||||||
|
|
||||||
|
Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
|
||||||
|
|
||||||
|
GLuint renderbuffer;
|
||||||
|
glGenRenderbuffers(1, &renderbuffer);
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
|
||||||
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB8, layout.width, layout.height);
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
|
||||||
|
|
||||||
|
DrawScreen(layout);
|
||||||
|
|
||||||
|
glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
||||||
|
renderer_settings.screenshot_bits);
|
||||||
|
|
||||||
|
screenshot_framebuffer.Release();
|
||||||
|
state.draw.read_framebuffer = old_read_fb;
|
||||||
|
state.draw.draw_framebuffer = old_draw_fb;
|
||||||
|
state.Apply();
|
||||||
|
glDeleteRenderbuffers(1, &renderbuffer);
|
||||||
|
|
||||||
|
renderer_settings.screenshot_complete_callback();
|
||||||
|
renderer_settings.screenshot_requested = false;
|
||||||
|
}
|
||||||
|
|
||||||
static const char* GetSource(GLenum source) {
|
static const char* GetSource(GLenum source) {
|
||||||
#define RET(s) \
|
#define RET(s) \
|
||||||
case GL_DEBUG_SOURCE_##s: \
|
case GL_DEBUG_SOURCE_##s: \
|
||||||
@@ -427,6 +462,7 @@ static const char* GetSource(GLenum source) {
|
|||||||
RET(OTHER);
|
RET(OTHER);
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
return "Unknown source";
|
||||||
}
|
}
|
||||||
#undef RET
|
#undef RET
|
||||||
}
|
}
|
||||||
@@ -445,6 +481,7 @@ static const char* GetType(GLenum type) {
|
|||||||
RET(MARKER);
|
RET(MARKER);
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
return "Unknown type";
|
||||||
}
|
}
|
||||||
#undef RET
|
#undef RET
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ namespace Core::Frontend {
|
|||||||
class EmuWindow;
|
class EmuWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Layout {
|
||||||
|
struct FramebufferLayout;
|
||||||
|
}
|
||||||
|
|
||||||
namespace OpenGL {
|
namespace OpenGL {
|
||||||
|
|
||||||
/// Structure used for storing information about the textures for the Switch screen
|
/// Structure used for storing information about the textures for the Switch screen
|
||||||
@@ -66,10 +70,12 @@ private:
|
|||||||
|
|
||||||
void ConfigureFramebufferTexture(TextureInfo& texture,
|
void ConfigureFramebufferTexture(TextureInfo& texture,
|
||||||
const Tegra::FramebufferConfig& framebuffer);
|
const Tegra::FramebufferConfig& framebuffer);
|
||||||
void DrawScreen();
|
void DrawScreen(const Layout::FramebufferLayout& layout);
|
||||||
void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h);
|
void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h);
|
||||||
void UpdateFramerate();
|
void UpdateFramerate();
|
||||||
|
|
||||||
|
void CaptureScreenshot();
|
||||||
|
|
||||||
// Loads framebuffer from emulated memory into the display information structure
|
// Loads framebuffer from emulated memory into the display information structure
|
||||||
void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer);
|
void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer);
|
||||||
// Fills active OpenGL texture with the given RGBA color.
|
// Fills active OpenGL texture with the given RGBA color.
|
||||||
@@ -82,6 +88,7 @@ private:
|
|||||||
OGLVertexArray vertex_array;
|
OGLVertexArray vertex_array;
|
||||||
OGLBuffer vertex_buffer;
|
OGLBuffer vertex_buffer;
|
||||||
OGLProgram shader;
|
OGLProgram shader;
|
||||||
|
OGLFramebuffer screenshot_framebuffer;
|
||||||
|
|
||||||
/// Display information for Switch screen
|
/// Display information for Switch screen
|
||||||
ScreenInfo screen_info;
|
ScreenInfo screen_info;
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) {
|
|||||||
default:
|
default:
|
||||||
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
return PixelFormat::S8Z24;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,6 +142,7 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format)
|
|||||||
default:
|
default:
|
||||||
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
return PixelFormat::RGBA8_SRGB;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,11 +196,14 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
|
|||||||
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
|
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
case Tegra::Texture::TextureFormat::G8R8:
|
case Tegra::Texture::TextureFormat::G8R8:
|
||||||
|
// TextureFormat::G8R8 is actually ordered red then green, as such we can use
|
||||||
|
// PixelFormat::RG8U and PixelFormat::RG8S. This was tested with The Legend of Zelda: Breath
|
||||||
|
// of the Wild, which uses this format to render the hearts on the UI.
|
||||||
switch (component_type) {
|
switch (component_type) {
|
||||||
case Tegra::Texture::ComponentType::UNORM:
|
case Tegra::Texture::ComponentType::UNORM:
|
||||||
return PixelFormat::G8R8U;
|
return PixelFormat::RG8U;
|
||||||
case Tegra::Texture::ComponentType::SNORM:
|
case Tegra::Texture::ComponentType::SNORM:
|
||||||
return PixelFormat::G8R8S;
|
return PixelFormat::RG8S;
|
||||||
}
|
}
|
||||||
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
|
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
@@ -327,6 +332,7 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
|
|||||||
LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}", static_cast<u32>(format),
|
LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}", static_cast<u32>(format),
|
||||||
static_cast<u32>(component_type));
|
static_cast<u32>(component_type));
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
return PixelFormat::ABGR8U;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,6 +352,7 @@ ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) {
|
|||||||
default:
|
default:
|
||||||
LOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast<u32>(type));
|
LOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast<u32>(type));
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
return ComponentType::UNorm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,6 +400,7 @@ ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format) {
|
|||||||
default:
|
default:
|
||||||
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
return ComponentType::UNorm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -403,6 +411,7 @@ PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat
|
|||||||
default:
|
default:
|
||||||
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
return PixelFormat::ABGR8U;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,6 +427,7 @@ ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format) {
|
|||||||
default:
|
default:
|
||||||
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
return ComponentType::UNorm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+41
-51
@@ -38,57 +38,55 @@ enum class PixelFormat {
|
|||||||
BC6H_UF16 = 20,
|
BC6H_UF16 = 20,
|
||||||
BC6H_SF16 = 21,
|
BC6H_SF16 = 21,
|
||||||
ASTC_2D_4X4 = 22,
|
ASTC_2D_4X4 = 22,
|
||||||
G8R8U = 23,
|
BGRA8 = 23,
|
||||||
G8R8S = 24,
|
RGBA32F = 24,
|
||||||
BGRA8 = 25,
|
RG32F = 25,
|
||||||
RGBA32F = 26,
|
R32F = 26,
|
||||||
RG32F = 27,
|
R16F = 27,
|
||||||
R32F = 28,
|
R16U = 28,
|
||||||
R16F = 29,
|
R16S = 29,
|
||||||
R16U = 30,
|
R16UI = 30,
|
||||||
R16S = 31,
|
R16I = 31,
|
||||||
R16UI = 32,
|
RG16 = 32,
|
||||||
R16I = 33,
|
RG16F = 33,
|
||||||
RG16 = 34,
|
RG16UI = 34,
|
||||||
RG16F = 35,
|
RG16I = 35,
|
||||||
RG16UI = 36,
|
RG16S = 36,
|
||||||
RG16I = 37,
|
RGB32F = 37,
|
||||||
RG16S = 38,
|
RGBA8_SRGB = 38,
|
||||||
RGB32F = 39,
|
RG8U = 39,
|
||||||
RGBA8_SRGB = 40,
|
RG8S = 40,
|
||||||
RG8U = 41,
|
RG32UI = 41,
|
||||||
RG8S = 42,
|
R32UI = 42,
|
||||||
RG32UI = 43,
|
ASTC_2D_8X8 = 43,
|
||||||
R32UI = 44,
|
ASTC_2D_8X5 = 44,
|
||||||
ASTC_2D_8X8 = 45,
|
ASTC_2D_5X4 = 45,
|
||||||
ASTC_2D_8X5 = 46,
|
BGRA8_SRGB = 46,
|
||||||
ASTC_2D_5X4 = 47,
|
DXT1_SRGB = 47,
|
||||||
BGRA8_SRGB = 48,
|
DXT23_SRGB = 48,
|
||||||
DXT1_SRGB = 49,
|
DXT45_SRGB = 49,
|
||||||
DXT23_SRGB = 50,
|
BC7U_SRGB = 50,
|
||||||
DXT45_SRGB = 51,
|
ASTC_2D_4X4_SRGB = 51,
|
||||||
BC7U_SRGB = 52,
|
ASTC_2D_8X8_SRGB = 52,
|
||||||
ASTC_2D_4X4_SRGB = 53,
|
ASTC_2D_8X5_SRGB = 53,
|
||||||
ASTC_2D_8X8_SRGB = 54,
|
ASTC_2D_5X4_SRGB = 54,
|
||||||
ASTC_2D_8X5_SRGB = 55,
|
ASTC_2D_5X5 = 55,
|
||||||
ASTC_2D_5X4_SRGB = 56,
|
ASTC_2D_5X5_SRGB = 56,
|
||||||
ASTC_2D_5X5 = 57,
|
ASTC_2D_10X8 = 57,
|
||||||
ASTC_2D_5X5_SRGB = 58,
|
ASTC_2D_10X8_SRGB = 58,
|
||||||
ASTC_2D_10X8 = 59,
|
|
||||||
ASTC_2D_10X8_SRGB = 60,
|
|
||||||
|
|
||||||
MaxColorFormat,
|
MaxColorFormat,
|
||||||
|
|
||||||
// Depth formats
|
// Depth formats
|
||||||
Z32F = 61,
|
Z32F = 59,
|
||||||
Z16 = 62,
|
Z16 = 60,
|
||||||
|
|
||||||
MaxDepthFormat,
|
MaxDepthFormat,
|
||||||
|
|
||||||
// DepthStencil formats
|
// DepthStencil formats
|
||||||
Z24S8 = 63,
|
Z24S8 = 61,
|
||||||
S8Z24 = 64,
|
S8Z24 = 62,
|
||||||
Z32FS8 = 65,
|
Z32FS8 = 63,
|
||||||
|
|
||||||
MaxDepthStencilFormat,
|
MaxDepthStencilFormat,
|
||||||
|
|
||||||
@@ -149,8 +147,6 @@ constexpr std::array<u32, MaxPixelFormat> compression_factor_table = {{
|
|||||||
4, // BC6H_UF16
|
4, // BC6H_UF16
|
||||||
4, // BC6H_SF16
|
4, // BC6H_SF16
|
||||||
4, // ASTC_2D_4X4
|
4, // ASTC_2D_4X4
|
||||||
1, // G8R8U
|
|
||||||
1, // G8R8S
|
|
||||||
1, // BGRA8
|
1, // BGRA8
|
||||||
1, // RGBA32F
|
1, // RGBA32F
|
||||||
1, // RG32F
|
1, // RG32F
|
||||||
@@ -232,8 +228,6 @@ constexpr std::array<u32, MaxPixelFormat> block_width_table = {{
|
|||||||
4, // BC6H_UF16
|
4, // BC6H_UF16
|
||||||
4, // BC6H_SF16
|
4, // BC6H_SF16
|
||||||
4, // ASTC_2D_4X4
|
4, // ASTC_2D_4X4
|
||||||
1, // G8R8U
|
|
||||||
1, // G8R8S
|
|
||||||
1, // BGRA8
|
1, // BGRA8
|
||||||
1, // RGBA32F
|
1, // RGBA32F
|
||||||
1, // RG32F
|
1, // RG32F
|
||||||
@@ -309,8 +303,6 @@ constexpr std::array<u32, MaxPixelFormat> block_height_table = {{
|
|||||||
4, // BC6H_UF16
|
4, // BC6H_UF16
|
||||||
4, // BC6H_SF16
|
4, // BC6H_SF16
|
||||||
4, // ASTC_2D_4X4
|
4, // ASTC_2D_4X4
|
||||||
1, // G8R8U
|
|
||||||
1, // G8R8S
|
|
||||||
1, // BGRA8
|
1, // BGRA8
|
||||||
1, // RGBA32F
|
1, // RGBA32F
|
||||||
1, // RG32F
|
1, // RG32F
|
||||||
@@ -386,8 +378,6 @@ constexpr std::array<u32, MaxPixelFormat> bpp_table = {{
|
|||||||
128, // BC6H_UF16
|
128, // BC6H_UF16
|
||||||
128, // BC6H_SF16
|
128, // BC6H_SF16
|
||||||
128, // ASTC_2D_4X4
|
128, // ASTC_2D_4X4
|
||||||
16, // G8R8U
|
|
||||||
16, // G8R8S
|
|
||||||
32, // BGRA8
|
32, // BGRA8
|
||||||
128, // RGBA32F
|
128, // RGBA32F
|
||||||
64, // RG32F
|
64, // RG32F
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ u32 BytesPerPixel(TextureFormat format) {
|
|||||||
return 8;
|
return 8;
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Format not implemented");
|
UNIMPLEMENTED_MSG("Format not implemented");
|
||||||
break;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/settings.h"
|
||||||
#include "video_core/renderer_base.h"
|
#include "video_core/renderer_base.h"
|
||||||
#include "video_core/renderer_opengl/renderer_opengl.h"
|
#include "video_core/renderer_opengl/renderer_opengl.h"
|
||||||
#include "video_core/video_core.h"
|
#include "video_core/video_core.h"
|
||||||
@@ -13,4 +15,10 @@ std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_wind
|
|||||||
return std::make_unique<OpenGL::RendererOpenGL>(emu_window);
|
return std::make_unique<OpenGL::RendererOpenGL>(emu_window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u16 GetResolutionScaleFactor(const RendererBase& renderer) {
|
||||||
|
return !Settings::values.resolution_factor
|
||||||
|
? renderer.GetRenderWindow().GetFramebufferLayout().GetScalingRatio()
|
||||||
|
: Settings::values.resolution_factor;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace VideoCore
|
} // namespace VideoCore
|
||||||
|
|||||||
@@ -22,4 +22,6 @@ class RendererBase;
|
|||||||
*/
|
*/
|
||||||
std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window);
|
std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window);
|
||||||
|
|
||||||
|
u16 GetResolutionScaleFactor(const RendererBase& renderer);
|
||||||
|
|
||||||
} // namespace VideoCore
|
} // namespace VideoCore
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ add_executable(yuzu
|
|||||||
Info.plist
|
Info.plist
|
||||||
about_dialog.cpp
|
about_dialog.cpp
|
||||||
about_dialog.h
|
about_dialog.h
|
||||||
|
applets/profile_select.cpp
|
||||||
|
applets/profile_select.h
|
||||||
applets/software_keyboard.cpp
|
applets/software_keyboard.cpp
|
||||||
applets/software_keyboard.h
|
applets/software_keyboard.h
|
||||||
bootmanager.cpp
|
bootmanager.cpp
|
||||||
@@ -31,6 +33,8 @@ add_executable(yuzu
|
|||||||
configuration/configure_input.h
|
configuration/configure_input.h
|
||||||
configuration/configure_input_player.cpp
|
configuration/configure_input_player.cpp
|
||||||
configuration/configure_input_player.h
|
configuration/configure_input_player.h
|
||||||
|
configuration/configure_input_simple.cpp
|
||||||
|
configuration/configure_input_simple.h
|
||||||
configuration/configure_mouse_advanced.cpp
|
configuration/configure_mouse_advanced.cpp
|
||||||
configuration/configure_mouse_advanced.h
|
configuration/configure_mouse_advanced.h
|
||||||
configuration/configure_system.cpp
|
configuration/configure_system.cpp
|
||||||
@@ -87,6 +91,7 @@ set(UIS
|
|||||||
configuration/configure_graphics.ui
|
configuration/configure_graphics.ui
|
||||||
configuration/configure_input.ui
|
configuration/configure_input.ui
|
||||||
configuration/configure_input_player.ui
|
configuration/configure_input_player.ui
|
||||||
|
configuration/configure_input_simple.ui
|
||||||
configuration/configure_mouse_advanced.ui
|
configuration/configure_mouse_advanced.ui
|
||||||
configuration/configure_per_general.ui
|
configuration/configure_per_general.ui
|
||||||
configuration/configure_system.ui
|
configuration/configure_system.ui
|
||||||
|
|||||||
@@ -0,0 +1,168 @@
|
|||||||
|
// Copyright 2018 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QScrollArea>
|
||||||
|
#include <QStandardItemModel>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include "common/file_util.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
|
#include "core/hle/lock.h"
|
||||||
|
#include "yuzu/applets/profile_select.h"
|
||||||
|
#include "yuzu/main.h"
|
||||||
|
|
||||||
|
// Same backup JPEG used by acc IProfile::GetImage if no jpeg found
|
||||||
|
constexpr std::array<u8, 107> backup_jpeg{
|
||||||
|
0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
|
||||||
|
0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05,
|
||||||
|
0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e,
|
||||||
|
0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13,
|
||||||
|
0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01,
|
||||||
|
0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08,
|
||||||
|
0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
|
||||||
|
};
|
||||||
|
|
||||||
|
QString FormatUserEntryText(const QString& username, Service::Account::UUID uuid) {
|
||||||
|
return QtProfileSelectionDialog::tr(
|
||||||
|
"%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. "
|
||||||
|
"00112233-4455-6677-8899-AABBCCDDEEFF))")
|
||||||
|
.arg(username, QString::fromStdString(uuid.FormatSwitch()));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString GetImagePath(Service::Account::UUID uuid) {
|
||||||
|
const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
|
||||||
|
"/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
|
||||||
|
return QString::fromStdString(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap GetIcon(Service::Account::UUID uuid) {
|
||||||
|
QPixmap icon{GetImagePath(uuid)};
|
||||||
|
|
||||||
|
if (!icon) {
|
||||||
|
icon.fill(Qt::black);
|
||||||
|
icon.loadFromData(backup_jpeg.data(), static_cast<u32>(backup_jpeg.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||||
|
}
|
||||||
|
|
||||||
|
QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
|
||||||
|
: QDialog(parent), profile_manager(std::make_unique<Service::Account::ProfileManager>()) {
|
||||||
|
outer_layout = new QVBoxLayout;
|
||||||
|
|
||||||
|
instruction_label = new QLabel(tr("Select a user:"));
|
||||||
|
|
||||||
|
scroll_area = new QScrollArea;
|
||||||
|
|
||||||
|
buttons = new QDialogButtonBox;
|
||||||
|
buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole);
|
||||||
|
buttons->addButton(tr("OK"), QDialogButtonBox::AcceptRole);
|
||||||
|
|
||||||
|
connect(buttons, &QDialogButtonBox::accepted, this, &QtProfileSelectionDialog::accept);
|
||||||
|
connect(buttons, &QDialogButtonBox::rejected, this, &QtProfileSelectionDialog::reject);
|
||||||
|
|
||||||
|
outer_layout->addWidget(instruction_label);
|
||||||
|
outer_layout->addWidget(scroll_area);
|
||||||
|
outer_layout->addWidget(buttons);
|
||||||
|
|
||||||
|
layout = new QVBoxLayout;
|
||||||
|
tree_view = new QTreeView;
|
||||||
|
item_model = new QStandardItemModel(tree_view);
|
||||||
|
tree_view->setModel(item_model);
|
||||||
|
|
||||||
|
tree_view->setAlternatingRowColors(true);
|
||||||
|
tree_view->setSelectionMode(QHeaderView::SingleSelection);
|
||||||
|
tree_view->setSelectionBehavior(QHeaderView::SelectRows);
|
||||||
|
tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel);
|
||||||
|
tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel);
|
||||||
|
tree_view->setSortingEnabled(true);
|
||||||
|
tree_view->setEditTriggers(QHeaderView::NoEditTriggers);
|
||||||
|
tree_view->setUniformRowHeights(true);
|
||||||
|
tree_view->setIconSize({64, 64});
|
||||||
|
tree_view->setContextMenuPolicy(Qt::NoContextMenu);
|
||||||
|
|
||||||
|
item_model->insertColumns(0, 1);
|
||||||
|
item_model->setHeaderData(0, Qt::Horizontal, "Users");
|
||||||
|
|
||||||
|
// We must register all custom types with the Qt Automoc system so that we are able to use it
|
||||||
|
// with signals/slots. In this case, QList falls under the umbrells of custom types.
|
||||||
|
qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
|
||||||
|
|
||||||
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
layout->setSpacing(0);
|
||||||
|
layout->addWidget(tree_view);
|
||||||
|
|
||||||
|
scroll_area->setLayout(layout);
|
||||||
|
|
||||||
|
connect(tree_view, &QTreeView::clicked, this, &QtProfileSelectionDialog::SelectUser);
|
||||||
|
|
||||||
|
const auto& profiles = profile_manager->GetAllUsers();
|
||||||
|
for (const auto& user : profiles) {
|
||||||
|
Service::Account::ProfileBase profile;
|
||||||
|
if (!profile_manager->GetProfileBase(user, profile))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto username = Common::StringFromFixedZeroTerminatedBuffer(
|
||||||
|
reinterpret_cast<const char*>(profile.username.data()), profile.username.size());
|
||||||
|
|
||||||
|
list_items.push_back(QList<QStandardItem*>{new QStandardItem{
|
||||||
|
GetIcon(user), FormatUserEntryText(QString::fromStdString(username), user)}});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& item : list_items)
|
||||||
|
item_model->appendRow(item);
|
||||||
|
|
||||||
|
setLayout(outer_layout);
|
||||||
|
setWindowTitle(tr("Profile Selector"));
|
||||||
|
resize(550, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
QtProfileSelectionDialog::~QtProfileSelectionDialog() = default;
|
||||||
|
|
||||||
|
void QtProfileSelectionDialog::accept() {
|
||||||
|
ok = true;
|
||||||
|
QDialog::accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtProfileSelectionDialog::reject() {
|
||||||
|
ok = false;
|
||||||
|
user_index = 0;
|
||||||
|
QDialog::reject();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QtProfileSelectionDialog::GetStatus() const {
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 QtProfileSelectionDialog::GetIndex() const {
|
||||||
|
return user_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtProfileSelectionDialog::SelectUser(const QModelIndex& index) {
|
||||||
|
user_index = index.row();
|
||||||
|
}
|
||||||
|
|
||||||
|
QtProfileSelector::QtProfileSelector(GMainWindow& parent) {
|
||||||
|
connect(this, &QtProfileSelector::MainWindowSelectProfile, &parent,
|
||||||
|
&GMainWindow::ProfileSelectorSelectProfile, Qt::QueuedConnection);
|
||||||
|
connect(&parent, &GMainWindow::ProfileSelectorFinishedSelection, this,
|
||||||
|
&QtProfileSelector::MainWindowFinishedSelection, Qt::DirectConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
QtProfileSelector::~QtProfileSelector() = default;
|
||||||
|
|
||||||
|
void QtProfileSelector::SelectProfile(
|
||||||
|
std::function<void(std::optional<Service::Account::UUID>)> callback) const {
|
||||||
|
this->callback = std::move(callback);
|
||||||
|
emit MainWindowSelectProfile();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtProfileSelector::MainWindowFinishedSelection(std::optional<Service::Account::UUID> uuid) {
|
||||||
|
// Acquire the HLE mutex
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
|
||||||
|
callback(uuid);
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
// Copyright 2018 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QList>
|
||||||
|
#include "core/frontend/applets/profile_select.h"
|
||||||
|
|
||||||
|
class GMainWindow;
|
||||||
|
class QDialogButtonBox;
|
||||||
|
class QGraphicsScene;
|
||||||
|
class QLabel;
|
||||||
|
class QScrollArea;
|
||||||
|
class QStandardItem;
|
||||||
|
class QStandardItemModel;
|
||||||
|
class QTreeView;
|
||||||
|
class QVBoxLayout;
|
||||||
|
|
||||||
|
class QtProfileSelectionDialog final : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit QtProfileSelectionDialog(QWidget* parent);
|
||||||
|
~QtProfileSelectionDialog() override;
|
||||||
|
|
||||||
|
void accept() override;
|
||||||
|
void reject() override;
|
||||||
|
|
||||||
|
bool GetStatus() const;
|
||||||
|
u32 GetIndex() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool ok = false;
|
||||||
|
u32 user_index = 0;
|
||||||
|
|
||||||
|
void SelectUser(const QModelIndex& index);
|
||||||
|
|
||||||
|
QVBoxLayout* layout;
|
||||||
|
QTreeView* tree_view;
|
||||||
|
QStandardItemModel* item_model;
|
||||||
|
QGraphicsScene* scene;
|
||||||
|
|
||||||
|
std::vector<QList<QStandardItem*>> list_items;
|
||||||
|
|
||||||
|
QVBoxLayout* outer_layout;
|
||||||
|
QLabel* instruction_label;
|
||||||
|
QScrollArea* scroll_area;
|
||||||
|
QDialogButtonBox* buttons;
|
||||||
|
|
||||||
|
std::unique_ptr<Service::Account::ProfileManager> profile_manager;
|
||||||
|
};
|
||||||
|
|
||||||
|
class QtProfileSelector final : public QObject, public Core::Frontend::ProfileSelectApplet {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit QtProfileSelector(GMainWindow& parent);
|
||||||
|
~QtProfileSelector() override;
|
||||||
|
|
||||||
|
void SelectProfile(
|
||||||
|
std::function<void(std::optional<Service::Account::UUID>)> callback) const override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void MainWindowSelectProfile() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void MainWindowFinishedSelection(std::optional<Service::Account::UUID> uuid);
|
||||||
|
|
||||||
|
mutable std::function<void(std::optional<Service::Account::UUID>)> callback;
|
||||||
|
};
|
||||||
@@ -14,6 +14,8 @@
|
|||||||
#include "input_common/keyboard.h"
|
#include "input_common/keyboard.h"
|
||||||
#include "input_common/main.h"
|
#include "input_common/main.h"
|
||||||
#include "input_common/motion_emu.h"
|
#include "input_common/motion_emu.h"
|
||||||
|
#include "video_core/renderer_base.h"
|
||||||
|
#include "video_core/video_core.h"
|
||||||
#include "yuzu/bootmanager.h"
|
#include "yuzu/bootmanager.h"
|
||||||
|
|
||||||
EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {}
|
EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {}
|
||||||
@@ -333,6 +335,22 @@ void GRenderWindow::InitRenderTarget() {
|
|||||||
BackupGeometry();
|
BackupGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GRenderWindow::CaptureScreenshot(u16 res_scale, const QString& screenshot_path) {
|
||||||
|
auto& renderer = Core::System::GetInstance().Renderer();
|
||||||
|
|
||||||
|
if (!res_scale)
|
||||||
|
res_scale = VideoCore::GetResolutionScaleFactor(renderer);
|
||||||
|
|
||||||
|
const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)};
|
||||||
|
screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
|
||||||
|
renderer.RequestScreenshot(screenshot_image.bits(),
|
||||||
|
[=] {
|
||||||
|
screenshot_image.mirrored(false, true).save(screenshot_path);
|
||||||
|
LOG_INFO(Frontend, "The screenshot is saved.");
|
||||||
|
},
|
||||||
|
layout);
|
||||||
|
}
|
||||||
|
|
||||||
void GRenderWindow::OnMinimalClientAreaChangeRequest(
|
void GRenderWindow::OnMinimalClientAreaChangeRequest(
|
||||||
const std::pair<unsigned, unsigned>& minimal_size) {
|
const std::pair<unsigned, unsigned>& minimal_size) {
|
||||||
setMinimumSize(minimal_size.first, minimal_size.second);
|
setMinimumSize(minimal_size.first, minimal_size.second);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <QGLWidget>
|
#include <QGLWidget>
|
||||||
|
#include <QImage>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
@@ -139,6 +140,8 @@ public:
|
|||||||
|
|
||||||
void InitRenderTarget();
|
void InitRenderTarget();
|
||||||
|
|
||||||
|
void CaptureScreenshot(u16 res_scale, const QString& screenshot_path);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void moveContext(); // overridden
|
void moveContext(); // overridden
|
||||||
|
|
||||||
@@ -165,6 +168,9 @@ private:
|
|||||||
|
|
||||||
EmuThread* emu_thread;
|
EmuThread* emu_thread;
|
||||||
|
|
||||||
|
/// Temporary storage of the screenshot taken
|
||||||
|
QImage screenshot_image;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void showEvent(QShowEvent* event) override;
|
void showEvent(QShowEvent* event) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include "common/file_util.h"
|
#include "common/file_util.h"
|
||||||
|
#include "configure_input_simple.h"
|
||||||
#include "core/hle/service/acc/profile_manager.h"
|
#include "core/hle/service/acc/profile_manager.h"
|
||||||
#include "core/hle/service/hid/controllers/npad.h"
|
#include "core/hle/service/hid/controllers/npad.h"
|
||||||
#include "input_common/main.h"
|
#include "input_common/main.h"
|
||||||
@@ -339,6 +340,13 @@ void Config::ReadTouchscreenValues() {
|
|||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Config::ApplyDefaultProfileIfInputInvalid() {
|
||||||
|
if (!std::any_of(Settings::values.players.begin(), Settings::values.players.end(),
|
||||||
|
[](const Settings::PlayerInput& in) { return in.connected; })) {
|
||||||
|
ApplyInputProfileConfiguration(UISettings::values.profile_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Config::ReadValues() {
|
void Config::ReadValues() {
|
||||||
qt_config->beginGroup("Controls");
|
qt_config->beginGroup("Controls");
|
||||||
|
|
||||||
@@ -460,6 +468,8 @@ void Config::ReadValues() {
|
|||||||
UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString();
|
UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString();
|
||||||
UISettings::values.enable_discord_presence =
|
UISettings::values.enable_discord_presence =
|
||||||
qt_config->value("enable_discord_presence", true).toBool();
|
qt_config->value("enable_discord_presence", true).toBool();
|
||||||
|
UISettings::values.screenshot_resolution_factor =
|
||||||
|
static_cast<u16>(qt_config->value("screenshot_resolution_factor", 0).toUInt());
|
||||||
|
|
||||||
qt_config->beginGroup("UIGameList");
|
qt_config->beginGroup("UIGameList");
|
||||||
UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool();
|
UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool();
|
||||||
@@ -518,6 +528,9 @@ void Config::ReadValues() {
|
|||||||
UISettings::values.first_start = qt_config->value("firstStart", true).toBool();
|
UISettings::values.first_start = qt_config->value("firstStart", true).toBool();
|
||||||
UISettings::values.callout_flags = qt_config->value("calloutFlags", 0).toUInt();
|
UISettings::values.callout_flags = qt_config->value("calloutFlags", 0).toUInt();
|
||||||
UISettings::values.show_console = qt_config->value("showConsole", false).toBool();
|
UISettings::values.show_console = qt_config->value("showConsole", false).toBool();
|
||||||
|
UISettings::values.profile_index = qt_config->value("profileIndex", 0).toUInt();
|
||||||
|
|
||||||
|
ApplyDefaultProfileIfInputInvalid();
|
||||||
|
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
}
|
}
|
||||||
@@ -678,6 +691,8 @@ void Config::SaveValues() {
|
|||||||
qt_config->beginGroup("UI");
|
qt_config->beginGroup("UI");
|
||||||
qt_config->setValue("theme", UISettings::values.theme);
|
qt_config->setValue("theme", UISettings::values.theme);
|
||||||
qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence);
|
qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence);
|
||||||
|
qt_config->setValue("screenshot_resolution_factor",
|
||||||
|
UISettings::values.screenshot_resolution_factor);
|
||||||
|
|
||||||
qt_config->beginGroup("UIGameList");
|
qt_config->beginGroup("UIGameList");
|
||||||
qt_config->setValue("show_unknown", UISettings::values.show_unknown);
|
qt_config->setValue("show_unknown", UISettings::values.show_unknown);
|
||||||
@@ -699,6 +714,7 @@ void Config::SaveValues() {
|
|||||||
qt_config->beginGroup("Paths");
|
qt_config->beginGroup("Paths");
|
||||||
qt_config->setValue("romsPath", UISettings::values.roms_path);
|
qt_config->setValue("romsPath", UISettings::values.roms_path);
|
||||||
qt_config->setValue("symbolsPath", UISettings::values.symbols_path);
|
qt_config->setValue("symbolsPath", UISettings::values.symbols_path);
|
||||||
|
qt_config->setValue("screenshotPath", UISettings::values.screenshot_path);
|
||||||
qt_config->setValue("gameListRootDir", UISettings::values.gamedir);
|
qt_config->setValue("gameListRootDir", UISettings::values.gamedir);
|
||||||
qt_config->setValue("gameListDeepScan", UISettings::values.gamedir_deepscan);
|
qt_config->setValue("gameListDeepScan", UISettings::values.gamedir_deepscan);
|
||||||
qt_config->setValue("recentFiles", UISettings::values.recent_files);
|
qt_config->setValue("recentFiles", UISettings::values.recent_files);
|
||||||
@@ -720,6 +736,7 @@ void Config::SaveValues() {
|
|||||||
qt_config->setValue("firstStart", UISettings::values.first_start);
|
qt_config->setValue("firstStart", UISettings::values.first_start);
|
||||||
qt_config->setValue("calloutFlags", UISettings::values.callout_flags);
|
qt_config->setValue("calloutFlags", UISettings::values.callout_flags);
|
||||||
qt_config->setValue("showConsole", UISettings::values.show_console);
|
qt_config->setValue("showConsole", UISettings::values.show_console);
|
||||||
|
qt_config->setValue("profileIndex", UISettings::values.profile_index);
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ private:
|
|||||||
void ReadKeyboardValues();
|
void ReadKeyboardValues();
|
||||||
void ReadMouseValues();
|
void ReadMouseValues();
|
||||||
void ReadTouchscreenValues();
|
void ReadTouchscreenValues();
|
||||||
|
void ApplyDefaultProfileIfInputInvalid();
|
||||||
|
|
||||||
void SaveValues();
|
void SaveValues();
|
||||||
void SavePlayerValues();
|
void SavePlayerValues();
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>461</width>
|
<width>461</width>
|
||||||
<height>500</height>
|
<height>659</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@@ -24,17 +24,17 @@
|
|||||||
<string>General</string>
|
<string>General</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="ConfigureGameList" name="gameListTab">
|
<widget class="ConfigureGameList" name="gameListTab">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Game List</string>
|
<string>Game List</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="ConfigureSystem" name="systemTab">
|
<widget class="ConfigureSystem" name="systemTab">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>System</string>
|
<string>System</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="ConfigureInput" name="inputTab">
|
<widget class="ConfigureInputSimple" name="inputTab">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Input</string>
|
<string>Input</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
@@ -54,11 +54,11 @@
|
|||||||
<string>Debug</string>
|
<string>Debug</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="ConfigureWeb" name="webTab">
|
<widget class="ConfigureWeb" name="webTab">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Web</string>
|
<string>Web</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
@@ -77,12 +77,12 @@
|
|||||||
<header>configuration/configure_general.h</header>
|
<header>configuration/configure_general.h</header>
|
||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>ConfigureGameList</class>
|
<class>ConfigureGameList</class>
|
||||||
<extends>QWidget</extends>
|
<extends>QWidget</extends>
|
||||||
<header>configuration/configure_gamelist.h</header>
|
<header>configuration/configure_gamelist.h</header>
|
||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>ConfigureSystem</class>
|
<class>ConfigureSystem</class>
|
||||||
<extends>QWidget</extends>
|
<extends>QWidget</extends>
|
||||||
@@ -102,9 +102,9 @@
|
|||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>ConfigureInput</class>
|
<class>ConfigureInputSimple</class>
|
||||||
<extends>QWidget</extends>
|
<extends>QWidget</extends>
|
||||||
<header>configuration/configure_input.h</header>
|
<header>configuration/configure_input_simple.h</header>
|
||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
@@ -113,12 +113,12 @@
|
|||||||
<header>configuration/configure_graphics.h</header>
|
<header>configuration/configure_graphics.h</header>
|
||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>ConfigureWeb</class>
|
<class>ConfigureWeb</class>
|
||||||
<extends>QWidget</extends>
|
<extends>QWidget</extends>
|
||||||
<header>configuration/configure_web.h</header>
|
<header>configuration/configure_web.h</header>
|
||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections>
|
<connections>
|
||||||
|
|||||||
@@ -20,6 +20,33 @@
|
|||||||
#include "yuzu/configuration/configure_input_player.h"
|
#include "yuzu/configuration/configure_input_player.h"
|
||||||
#include "yuzu/configuration/configure_mouse_advanced.h"
|
#include "yuzu/configuration/configure_mouse_advanced.h"
|
||||||
|
|
||||||
|
void OnDockedModeChanged(bool last_state, bool new_state) {
|
||||||
|
if (last_state == new_state) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::System& system{Core::System::GetInstance()};
|
||||||
|
if (!system.IsPoweredOn()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Service::SM::ServiceManager& sm = system.ServiceManager();
|
||||||
|
|
||||||
|
// Message queue is shared between these services, we just need to signal an operation
|
||||||
|
// change to one and it will handle both automatically
|
||||||
|
auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
|
||||||
|
auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
|
||||||
|
bool has_signalled = false;
|
||||||
|
|
||||||
|
if (applet_oe != nullptr) {
|
||||||
|
applet_oe->GetMessageQueue()->OperationModeChanged();
|
||||||
|
has_signalled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (applet_ae != nullptr && !has_signalled) {
|
||||||
|
applet_ae->GetMessageQueue()->OperationModeChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
template <typename Dialog, typename... Args>
|
template <typename Dialog, typename... Args>
|
||||||
void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
|
void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
|
||||||
@@ -34,7 +61,7 @@ void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
|
|||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
ConfigureInput::ConfigureInput(QWidget* parent)
|
ConfigureInput::ConfigureInput(QWidget* parent)
|
||||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
|
: QDialog(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
players_controller = {
|
players_controller = {
|
||||||
@@ -90,37 +117,6 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
|||||||
|
|
||||||
ConfigureInput::~ConfigureInput() = default;
|
ConfigureInput::~ConfigureInput() = default;
|
||||||
|
|
||||||
void ConfigureInput::OnDockedModeChanged(bool last_state, bool new_state) {
|
|
||||||
if (ui->use_docked_mode->isChecked() && ui->handheld_connected->isChecked()) {
|
|
||||||
ui->handheld_connected->setChecked(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (last_state == new_state) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Core::System& system{Core::System::GetInstance()};
|
|
||||||
if (!system.IsPoweredOn()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Service::SM::ServiceManager& sm = system.ServiceManager();
|
|
||||||
|
|
||||||
// Message queue is shared between these services, we just need to signal an operation
|
|
||||||
// change to one and it will handle both automatically
|
|
||||||
auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
|
|
||||||
auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
|
|
||||||
bool has_signalled = false;
|
|
||||||
|
|
||||||
if (applet_oe != nullptr) {
|
|
||||||
applet_oe->GetMessageQueue()->OperationModeChanged();
|
|
||||||
has_signalled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (applet_ae != nullptr && !has_signalled) {
|
|
||||||
applet_ae->GetMessageQueue()->OperationModeChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConfigureInput::applyConfiguration() {
|
void ConfigureInput::applyConfiguration() {
|
||||||
for (std::size_t i = 0; i < players_controller.size(); ++i) {
|
for (std::size_t i = 0; i < players_controller.size(); ++i) {
|
||||||
const auto controller_type_index = players_controller[i]->currentIndex();
|
const auto controller_type_index = players_controller[i]->currentIndex();
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
#include "ui_configure_input.h"
|
#include "ui_configure_input.h"
|
||||||
|
|
||||||
@@ -20,7 +20,9 @@ namespace Ui {
|
|||||||
class ConfigureInput;
|
class ConfigureInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConfigureInput : public QWidget {
|
void OnDockedModeChanged(bool last_state, bool new_state);
|
||||||
|
|
||||||
|
class ConfigureInput : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -33,8 +35,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
void updateUIEnabled();
|
void updateUIEnabled();
|
||||||
|
|
||||||
void OnDockedModeChanged(bool last_state, bool new_state);
|
|
||||||
|
|
||||||
/// Load configuration settings.
|
/// Load configuration settings.
|
||||||
void loadConfiguration();
|
void loadConfiguration();
|
||||||
/// Restore all buttons to their default values.
|
/// Restore all buttons to their default values.
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<ui version="4.0">
|
<ui version="4.0">
|
||||||
<class>ConfigureInput</class>
|
<class>ConfigureInput</class>
|
||||||
<widget class="QWidget" name="ConfigureInput">
|
<widget class="QDialog" name="ConfigureInput">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>473</width>
|
<width>384</width>
|
||||||
<height>685</height>
|
<height>576</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@@ -478,6 +478,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
@@ -485,5 +492,38 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>ConfigureInput</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>294</x>
|
||||||
|
<y>553</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>191</x>
|
||||||
|
<y>287</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>ConfigureInput</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>294</x>
|
||||||
|
<y>553</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>191</x>
|
||||||
|
<y>287</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
</ui>
|
</ui>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <QColorDialog>
|
#include <QColorDialog>
|
||||||
|
#include <QGridLayout>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
// Copyright 2016 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
#include "ui_configure_input_simple.h"
|
||||||
|
#include "yuzu/configuration/configure_input.h"
|
||||||
|
#include "yuzu/configuration/configure_input_player.h"
|
||||||
|
#include "yuzu/configuration/configure_input_simple.h"
|
||||||
|
#include "yuzu/ui_settings.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template <typename Dialog, typename... Args>
|
||||||
|
void CallConfigureDialog(ConfigureInputSimple* caller, Args&&... args) {
|
||||||
|
caller->applyConfiguration();
|
||||||
|
Dialog dialog(caller, std::forward<Args>(args)...);
|
||||||
|
|
||||||
|
const auto res = dialog.exec();
|
||||||
|
if (res == QDialog::Accepted) {
|
||||||
|
dialog.applyConfiguration();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnProfileSelect functions should (when applicable):
|
||||||
|
// - Set controller types
|
||||||
|
// - Set controller enabled
|
||||||
|
// - Set docked mode
|
||||||
|
// - Set advanced controller config/enabled (i.e. debug, kbd, mouse, touch)
|
||||||
|
//
|
||||||
|
// OnProfileSelect function should NOT however:
|
||||||
|
// - Reset any button mappings
|
||||||
|
// - Open any dialogs
|
||||||
|
// - Block in any way
|
||||||
|
|
||||||
|
constexpr std::size_t HANDHELD_INDEX = 8;
|
||||||
|
|
||||||
|
void HandheldOnProfileSelect() {
|
||||||
|
Settings::values.players[HANDHELD_INDEX].connected = true;
|
||||||
|
Settings::values.players[HANDHELD_INDEX].type = Settings::ControllerType::DualJoycon;
|
||||||
|
|
||||||
|
for (std::size_t player = 0; player < HANDHELD_INDEX; ++player) {
|
||||||
|
Settings::values.players[player].connected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings::values.use_docked_mode = false;
|
||||||
|
Settings::values.keyboard_enabled = false;
|
||||||
|
Settings::values.mouse_enabled = false;
|
||||||
|
Settings::values.debug_pad_enabled = false;
|
||||||
|
Settings::values.touchscreen.enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DualJoyconsDockedOnProfileSelect() {
|
||||||
|
Settings::values.players[0].connected = true;
|
||||||
|
Settings::values.players[0].type = Settings::ControllerType::DualJoycon;
|
||||||
|
|
||||||
|
for (std::size_t player = 1; player <= HANDHELD_INDEX; ++player) {
|
||||||
|
Settings::values.players[player].connected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings::values.use_docked_mode = true;
|
||||||
|
Settings::values.keyboard_enabled = false;
|
||||||
|
Settings::values.mouse_enabled = false;
|
||||||
|
Settings::values.debug_pad_enabled = false;
|
||||||
|
Settings::values.touchscreen.enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name, OnProfileSelect (called when selected in drop down), OnConfigure (called when configure
|
||||||
|
// is clicked)
|
||||||
|
using InputProfile = std::tuple<const char*, void (*)(), void (*)(ConfigureInputSimple*)>;
|
||||||
|
|
||||||
|
constexpr std::array<InputProfile, 3> INPUT_PROFILES{{
|
||||||
|
{QT_TR_NOOP("Single Player - Handheld - Undocked"), HandheldOnProfileSelect,
|
||||||
|
[](ConfigureInputSimple* caller) {
|
||||||
|
CallConfigureDialog<ConfigureInputPlayer>(caller, HANDHELD_INDEX, false);
|
||||||
|
}},
|
||||||
|
{QT_TR_NOOP("Single Player - Dual Joycons - Docked"), DualJoyconsDockedOnProfileSelect,
|
||||||
|
[](ConfigureInputSimple* caller) {
|
||||||
|
CallConfigureDialog<ConfigureInputPlayer>(caller, 1, false);
|
||||||
|
}},
|
||||||
|
{QT_TR_NOOP("Custom"), [] {}, CallConfigureDialog<ConfigureInput>},
|
||||||
|
}};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void ApplyInputProfileConfiguration(int profile_index) {
|
||||||
|
std::get<1>(
|
||||||
|
INPUT_PROFILES.at(std::min(profile_index, static_cast<int>(INPUT_PROFILES.size() - 1))))();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigureInputSimple::ConfigureInputSimple(QWidget* parent)
|
||||||
|
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInputSimple>()) {
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
for (const auto& profile : INPUT_PROFILES) {
|
||||||
|
const QString label = tr(std::get<0>(profile));
|
||||||
|
ui->profile_combobox->addItem(label, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(ui->profile_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||||
|
&ConfigureInputSimple::OnSelectProfile);
|
||||||
|
connect(ui->profile_configure, &QPushButton::pressed, this, &ConfigureInputSimple::OnConfigure);
|
||||||
|
|
||||||
|
this->loadConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigureInputSimple::~ConfigureInputSimple() = default;
|
||||||
|
|
||||||
|
void ConfigureInputSimple::applyConfiguration() {
|
||||||
|
auto index = ui->profile_combobox->currentIndex();
|
||||||
|
// Make the stored index for "Custom" very large so that if new profiles are added it
|
||||||
|
// doesn't change.
|
||||||
|
if (index >= static_cast<int>(INPUT_PROFILES.size() - 1))
|
||||||
|
index = std::numeric_limits<int>::max();
|
||||||
|
|
||||||
|
UISettings::values.profile_index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureInputSimple::loadConfiguration() {
|
||||||
|
const auto index = UISettings::values.profile_index;
|
||||||
|
if (index >= static_cast<int>(INPUT_PROFILES.size()) || index < 0)
|
||||||
|
ui->profile_combobox->setCurrentIndex(static_cast<int>(INPUT_PROFILES.size() - 1));
|
||||||
|
else
|
||||||
|
ui->profile_combobox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureInputSimple::OnSelectProfile(int index) {
|
||||||
|
const auto old_docked = Settings::values.use_docked_mode;
|
||||||
|
ApplyInputProfileConfiguration(index);
|
||||||
|
OnDockedModeChanged(old_docked, Settings::values.use_docked_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureInputSimple::OnConfigure() {
|
||||||
|
std::get<2>(INPUT_PROFILES.at(ui->profile_combobox->currentIndex()))(this);
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// Copyright 2016 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class QPushButton;
|
||||||
|
class QString;
|
||||||
|
class QTimer;
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class ConfigureInputSimple;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used by configuration loader to apply a profile if the input is invalid.
|
||||||
|
void ApplyInputProfileConfiguration(int profile_index);
|
||||||
|
|
||||||
|
class ConfigureInputSimple : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ConfigureInputSimple(QWidget* parent = nullptr);
|
||||||
|
~ConfigureInputSimple() override;
|
||||||
|
|
||||||
|
/// Save all button configurations to settings file
|
||||||
|
void applyConfiguration();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Load configuration settings.
|
||||||
|
void loadConfiguration();
|
||||||
|
|
||||||
|
void OnSelectProfile(int index);
|
||||||
|
void OnConfigure();
|
||||||
|
|
||||||
|
std::unique_ptr<Ui::ConfigureInputSimple> ui;
|
||||||
|
};
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>ConfigureInputSimple</class>
|
||||||
|
<widget class="QWidget" name="ConfigureInputSimple">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>473</width>
|
||||||
|
<height>685</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>ConfigureInputSimple</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="gridGroupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Profile</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QPushButton" name="profile_configure">
|
||||||
|
<property name="text">
|
||||||
|
<string>Configure</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="3">
|
||||||
|
<spacer name="horizontalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QComboBox" name="profile_combobox">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>250</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1" colspan="2">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Choose a controller configuration:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
@@ -47,8 +47,8 @@ ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id)
|
|||||||
tree_view->setContextMenuPolicy(Qt::NoContextMenu);
|
tree_view->setContextMenuPolicy(Qt::NoContextMenu);
|
||||||
|
|
||||||
item_model->insertColumns(0, 2);
|
item_model->insertColumns(0, 2);
|
||||||
item_model->setHeaderData(0, Qt::Horizontal, "Patch Name");
|
item_model->setHeaderData(0, Qt::Horizontal, tr("Patch Name"));
|
||||||
item_model->setHeaderData(1, Qt::Horizontal, "Version");
|
item_model->setHeaderData(1, Qt::Horizontal, tr("Version"));
|
||||||
|
|
||||||
// We must register all custom types with the Qt Automoc system so that we are able to use it
|
// We must register all custom types with the Qt Automoc system so that we are able to use it
|
||||||
// with signals/slots. In this case, QList falls under the umbrells of custom types.
|
// with signals/slots. In this case, QList falls under the umbrells of custom types.
|
||||||
@@ -108,9 +108,9 @@ void ConfigurePerGameGeneral::loadConfiguration() {
|
|||||||
if (loader->ReadTitle(title) == Loader::ResultStatus::Success)
|
if (loader->ReadTitle(title) == Loader::ResultStatus::Success)
|
||||||
ui->display_name->setText(QString::fromStdString(title));
|
ui->display_name->setText(QString::fromStdString(title));
|
||||||
|
|
||||||
std::string developer;
|
FileSys::NACP nacp;
|
||||||
if (loader->ReadDeveloper(developer) == Loader::ResultStatus::Success)
|
if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success)
|
||||||
ui->display_developer->setText(QString::fromStdString(developer));
|
ui->display_developer->setText(QString::fromStdString(nacp.GetDeveloperName()));
|
||||||
|
|
||||||
ui->display_version->setText(QStringLiteral("1.0.0"));
|
ui->display_version->setText(QStringLiteral("1.0.0"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ static Tegra::Texture::TextureFormat ConvertToTextureFormat(
|
|||||||
return Tegra::Texture::TextureFormat::A2B10G10R10;
|
return Tegra::Texture::TextureFormat::A2B10G10R10;
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented RT format");
|
UNIMPLEMENTED_MSG("Unimplemented RT format");
|
||||||
|
return Tegra::Texture::TextureFormat::A8R8G8B8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -221,6 +221,9 @@ QString WaitTreeThread::GetText() const {
|
|||||||
case Kernel::ThreadStatus::Ready:
|
case Kernel::ThreadStatus::Ready:
|
||||||
status = tr("ready");
|
status = tr("ready");
|
||||||
break;
|
break;
|
||||||
|
case Kernel::ThreadStatus::Paused:
|
||||||
|
status = tr("paused");
|
||||||
|
break;
|
||||||
case Kernel::ThreadStatus::WaitHLEEvent:
|
case Kernel::ThreadStatus::WaitHLEEvent:
|
||||||
status = tr("waiting for HLE return");
|
status = tr("waiting for HLE return");
|
||||||
break;
|
break;
|
||||||
@@ -262,6 +265,8 @@ QColor WaitTreeThread::GetColor() const {
|
|||||||
return QColor(Qt::GlobalColor::darkGreen);
|
return QColor(Qt::GlobalColor::darkGreen);
|
||||||
case Kernel::ThreadStatus::Ready:
|
case Kernel::ThreadStatus::Ready:
|
||||||
return QColor(Qt::GlobalColor::darkBlue);
|
return QColor(Qt::GlobalColor::darkBlue);
|
||||||
|
case Kernel::ThreadStatus::Paused:
|
||||||
|
return QColor(Qt::GlobalColor::lightGray);
|
||||||
case Kernel::ThreadStatus::WaitHLEEvent:
|
case Kernel::ThreadStatus::WaitHLEEvent:
|
||||||
case Kernel::ThreadStatus::WaitIPC:
|
case Kernel::ThreadStatus::WaitIPC:
|
||||||
return QColor(Qt::GlobalColor::darkRed);
|
return QColor(Qt::GlobalColor::darkRed);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
|
// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
|
||||||
|
#include "applets/profile_select.h"
|
||||||
#include "applets/software_keyboard.h"
|
#include "applets/software_keyboard.h"
|
||||||
#include "configuration/configure_per_general.h"
|
#include "configuration/configure_per_general.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs.h"
|
||||||
@@ -208,6 +209,28 @@ GMainWindow::~GMainWindow() {
|
|||||||
delete render_window;
|
delete render_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GMainWindow::ProfileSelectorSelectProfile() {
|
||||||
|
QtProfileSelectionDialog dialog(this);
|
||||||
|
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
|
||||||
|
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
|
||||||
|
dialog.setWindowModality(Qt::WindowModal);
|
||||||
|
dialog.exec();
|
||||||
|
|
||||||
|
if (!dialog.GetStatus()) {
|
||||||
|
emit ProfileSelectorFinishedSelection(std::nullopt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Service::Account::ProfileManager manager;
|
||||||
|
const auto uuid = manager.GetUser(dialog.GetIndex());
|
||||||
|
if (!uuid.has_value()) {
|
||||||
|
emit ProfileSelectorFinishedSelection(std::nullopt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit ProfileSelectorFinishedSelection(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
void GMainWindow::SoftwareKeyboardGetText(
|
void GMainWindow::SoftwareKeyboardGetText(
|
||||||
const Core::Frontend::SoftwareKeyboardParameters& parameters) {
|
const Core::Frontend::SoftwareKeyboardParameters& parameters) {
|
||||||
QtSoftwareKeyboardDialog dialog(this, parameters);
|
QtSoftwareKeyboardDialog dialog(this, parameters);
|
||||||
@@ -335,6 +358,9 @@ void GMainWindow::InitializeHotkeys() {
|
|||||||
Qt::ApplicationShortcut);
|
Qt::ApplicationShortcut);
|
||||||
hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2),
|
hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2),
|
||||||
Qt::ApplicationShortcut);
|
Qt::ApplicationShortcut);
|
||||||
|
hotkey_registry.RegisterHotkey("Main Window", "Capture Screenshot",
|
||||||
|
QKeySequence(QKeySequence::Print));
|
||||||
|
|
||||||
hotkey_registry.LoadHotkeys();
|
hotkey_registry.LoadHotkeys();
|
||||||
|
|
||||||
connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
|
connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
|
||||||
@@ -394,6 +420,12 @@ void GMainWindow::InitializeHotkeys() {
|
|||||||
OnLoadAmiibo();
|
OnLoadAmiibo();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
connect(hotkey_registry.GetHotkey("Main Window", "Capture Screenshot", this),
|
||||||
|
&QShortcut::activated, this, [&] {
|
||||||
|
if (emu_thread->IsRunning()) {
|
||||||
|
OnCaptureScreenshot();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::SetDefaultUIGeometry() {
|
void GMainWindow::SetDefaultUIGeometry() {
|
||||||
@@ -491,6 +523,10 @@ void GMainWindow::ConnectMenuEvents() {
|
|||||||
hotkey_registry.GetHotkey("Main Window", "Fullscreen", this)->key());
|
hotkey_registry.GetHotkey("Main Window", "Fullscreen", this)->key());
|
||||||
connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
|
connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
|
||||||
|
|
||||||
|
// Movie
|
||||||
|
connect(ui.action_Capture_Screenshot, &QAction::triggered, this,
|
||||||
|
&GMainWindow::OnCaptureScreenshot);
|
||||||
|
|
||||||
// Help
|
// Help
|
||||||
connect(ui.action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder);
|
connect(ui.action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder);
|
||||||
connect(ui.action_Rederive, &QAction::triggered, this,
|
connect(ui.action_Rederive, &QAction::triggered, this,
|
||||||
@@ -574,6 +610,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
|||||||
|
|
||||||
system.SetGPUDebugContext(debug_context);
|
system.SetGPUDebugContext(debug_context);
|
||||||
|
|
||||||
|
system.SetProfileSelector(std::make_unique<QtProfileSelector>(*this));
|
||||||
system.SetSoftwareKeyboard(std::make_unique<QtSoftwareKeyboard>(*this));
|
system.SetSoftwareKeyboard(std::make_unique<QtSoftwareKeyboard>(*this));
|
||||||
|
|
||||||
const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())};
|
const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())};
|
||||||
@@ -727,6 +764,7 @@ void GMainWindow::ShutdownGame() {
|
|||||||
ui.action_Restart->setEnabled(false);
|
ui.action_Restart->setEnabled(false);
|
||||||
ui.action_Report_Compatibility->setEnabled(false);
|
ui.action_Report_Compatibility->setEnabled(false);
|
||||||
ui.action_Load_Amiibo->setEnabled(false);
|
ui.action_Load_Amiibo->setEnabled(false);
|
||||||
|
ui.action_Capture_Screenshot->setEnabled(false);
|
||||||
render_window->hide();
|
render_window->hide();
|
||||||
game_list->show();
|
game_list->show();
|
||||||
game_list->setFilterFocus();
|
game_list->setFilterFocus();
|
||||||
@@ -1290,6 +1328,7 @@ void GMainWindow::OnStartGame() {
|
|||||||
|
|
||||||
discord_rpc->Update();
|
discord_rpc->Update();
|
||||||
ui.action_Load_Amiibo->setEnabled(true);
|
ui.action_Load_Amiibo->setEnabled(true);
|
||||||
|
ui.action_Capture_Screenshot->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnPauseGame() {
|
void GMainWindow::OnPauseGame() {
|
||||||
@@ -1298,6 +1337,7 @@ void GMainWindow::OnPauseGame() {
|
|||||||
ui.action_Start->setEnabled(true);
|
ui.action_Start->setEnabled(true);
|
||||||
ui.action_Pause->setEnabled(false);
|
ui.action_Pause->setEnabled(false);
|
||||||
ui.action_Stop->setEnabled(true);
|
ui.action_Stop->setEnabled(true);
|
||||||
|
ui.action_Capture_Screenshot->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnStopGame() {
|
void GMainWindow::OnStopGame() {
|
||||||
@@ -1460,6 +1500,18 @@ void GMainWindow::OnToggleFilterBar() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GMainWindow::OnCaptureScreenshot() {
|
||||||
|
OnPauseGame();
|
||||||
|
const QString path =
|
||||||
|
QFileDialog::getSaveFileName(this, tr("Capture Screenshot"),
|
||||||
|
UISettings::values.screenshot_path, tr("PNG Image (*.png)"));
|
||||||
|
if (!path.isEmpty()) {
|
||||||
|
UISettings::values.screenshot_path = QFileInfo(path).path();
|
||||||
|
render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, path);
|
||||||
|
}
|
||||||
|
OnStartGame();
|
||||||
|
}
|
||||||
|
|
||||||
void GMainWindow::UpdateStatusBar() {
|
void GMainWindow::UpdateStatusBar() {
|
||||||
if (emu_thread == nullptr) {
|
if (emu_thread == nullptr) {
|
||||||
status_bar_update_timer.stop();
|
status_bar_update_timer.stop();
|
||||||
|
|||||||
@@ -99,10 +99,12 @@ signals:
|
|||||||
// Signal that tells widgets to update icons to use the current theme
|
// Signal that tells widgets to update icons to use the current theme
|
||||||
void UpdateThemedIcons();
|
void UpdateThemedIcons();
|
||||||
|
|
||||||
|
void ProfileSelectorFinishedSelection(std::optional<Service::Account::UUID> uuid);
|
||||||
void SoftwareKeyboardFinishedText(std::optional<std::u16string> text);
|
void SoftwareKeyboardFinishedText(std::optional<std::u16string> text);
|
||||||
void SoftwareKeyboardFinishedCheckDialog();
|
void SoftwareKeyboardFinishedCheckDialog();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
void ProfileSelectorSelectProfile();
|
||||||
void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters);
|
void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters);
|
||||||
void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message);
|
void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message);
|
||||||
|
|
||||||
@@ -187,6 +189,7 @@ private slots:
|
|||||||
void ShowFullscreen();
|
void ShowFullscreen();
|
||||||
void HideFullscreen();
|
void HideFullscreen();
|
||||||
void ToggleWindowMode();
|
void ToggleWindowMode();
|
||||||
|
void OnCaptureScreenshot();
|
||||||
void OnCoreError(Core::System::ResultStatus, std::string);
|
void OnCoreError(Core::System::ResultStatus, std::string);
|
||||||
void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
|
void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
|
||||||
|
|
||||||
|
|||||||
+50
-40
@@ -101,11 +101,13 @@
|
|||||||
<addaction name="action_Show_Status_Bar"/>
|
<addaction name="action_Show_Status_Bar"/>
|
||||||
<addaction name="menu_View_Debugging"/>
|
<addaction name="menu_View_Debugging"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class ="QMenu" name="menu_Tools">
|
<widget class="QMenu" name="menu_Tools">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Tools</string>
|
<string>Tools</string>
|
||||||
</property>
|
</property>
|
||||||
<addaction name="action_Rederive" />
|
<addaction name="action_Rederive"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="action_Capture_Screenshot"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menu_Help">
|
<widget class="QMenu" name="menu_Help">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
@@ -118,7 +120,7 @@
|
|||||||
<addaction name="menu_File"/>
|
<addaction name="menu_File"/>
|
||||||
<addaction name="menu_Emulation"/>
|
<addaction name="menu_Emulation"/>
|
||||||
<addaction name="menu_View"/>
|
<addaction name="menu_View"/>
|
||||||
<addaction name="menu_Tools" />
|
<addaction name="menu_Tools"/>
|
||||||
<addaction name="menu_Help"/>
|
<addaction name="menu_Help"/>
|
||||||
</widget>
|
</widget>
|
||||||
<action name="action_Install_File_NAND">
|
<action name="action_Install_File_NAND">
|
||||||
@@ -173,11 +175,11 @@
|
|||||||
<string>&Stop</string>
|
<string>&Stop</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="action_Rederive">
|
<action name="action_Rederive">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Reinitialize keys...</string>
|
<string>Reinitialize keys...</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="action_About">
|
<action name="action_About">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>About yuzu</string>
|
<string>About yuzu</string>
|
||||||
@@ -252,39 +254,47 @@
|
|||||||
<string>Fullscreen</string>
|
<string>Fullscreen</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="action_Restart">
|
<action name="action_Restart">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Restart</string>
|
<string>Restart</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
|
||||||
<action name="action_Load_Amiibo">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Load Amiibo...</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
</action>
|
||||||
<action name="action_Report_Compatibility">
|
<action name="action_Load_Amiibo">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Report Compatibility</string>
|
<string>Load Amiibo...</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="visible">
|
</action>
|
||||||
<bool>false</bool>
|
<action name="action_Report_Compatibility">
|
||||||
</property>
|
<property name="enabled">
|
||||||
</action>
|
<bool>false</bool>
|
||||||
<action name="action_Open_yuzu_Folder">
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Open yuzu Folder</string>
|
<string>Report Compatibility</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
<property name="visible">
|
||||||
</widget>
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="action_Open_yuzu_Folder">
|
||||||
|
<property name="text">
|
||||||
|
<string>Open yuzu Folder</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="action_Capture_Screenshot">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Capture Screenshot</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
namespace UISettings {
|
namespace UISettings {
|
||||||
|
|
||||||
@@ -42,8 +43,11 @@ struct Values {
|
|||||||
// Discord RPC
|
// Discord RPC
|
||||||
bool enable_discord_presence;
|
bool enable_discord_presence;
|
||||||
|
|
||||||
|
u16 screenshot_resolution_factor;
|
||||||
|
|
||||||
QString roms_path;
|
QString roms_path;
|
||||||
QString symbols_path;
|
QString symbols_path;
|
||||||
|
QString screenshot_path;
|
||||||
QString gamedir;
|
QString gamedir;
|
||||||
bool gamedir_deepscan;
|
bool gamedir_deepscan;
|
||||||
QStringList recent_files;
|
QStringList recent_files;
|
||||||
@@ -58,6 +62,9 @@ struct Values {
|
|||||||
// logging
|
// logging
|
||||||
bool show_console;
|
bool show_console;
|
||||||
|
|
||||||
|
// Controllers
|
||||||
|
int profile_index;
|
||||||
|
|
||||||
// Game List
|
// Game List
|
||||||
bool show_unknown;
|
bool show_unknown;
|
||||||
bool show_add_ons;
|
bool show_add_ons;
|
||||||
|
|||||||
Reference in New Issue
Block a user