Compare commits
107 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
351f010cfc | ||
|
|
fe3bf4f075 | ||
|
|
f96bb2520b | ||
|
|
09ffa0cb21 | ||
|
|
244b46af5a | ||
|
|
b4242633ad | ||
|
|
3a8d38be7e | ||
|
|
8769604144 | ||
|
|
205e6d3b97 | ||
|
|
039e58a984 | ||
|
|
a17dd30057 | ||
|
|
776ce5d74c | ||
|
|
387bffda5e | ||
|
|
e5dfbe22ee | ||
|
|
331c252509 | ||
|
|
f80229b676 | ||
|
|
fa97f50bff | ||
|
|
614de1dba7 | ||
|
|
af51f123fa | ||
|
|
22d4e10664 | ||
|
|
8495f1df2a | ||
|
|
2020ba06e1 | ||
|
|
af9336df57 | ||
|
|
a73c7c73eb | ||
|
|
a81ff6f54c | ||
|
|
f80bc712ea | ||
|
|
771431f625 | ||
|
|
19bfbb28ce | ||
|
|
357bc956ae | ||
|
|
9aa68212d9 | ||
|
|
fbeaa330a3 | ||
|
|
e84e4fd3f8 | ||
|
|
fbe900ba6d | ||
|
|
795335af0f | ||
|
|
fa9acc26d9 | ||
|
|
8e23e40a72 | ||
|
|
2991c747c9 | ||
|
|
7477c12fe2 | ||
|
|
c4515d305b | ||
|
|
c643f364b4 | ||
|
|
2e6b67a079 | ||
|
|
4082c4eda6 | ||
|
|
0756f29a2c | ||
|
|
417e1ef09c | ||
|
|
5c4259ec1a | ||
|
|
4a6ba58073 | ||
|
|
73c9ffc422 | ||
|
|
17fa0ffff9 | ||
|
|
1392597ede | ||
|
|
c9269a4a4b | ||
|
|
0c18d47348 | ||
|
|
faa9110541 | ||
|
|
67fa21e143 | ||
|
|
33056dd833 | ||
|
|
8047873a66 | ||
|
|
ae582b6669 | ||
|
|
46b8b03015 | ||
|
|
7e622c5575 | ||
|
|
aaa0e6c346 | ||
|
|
9a22a94a51 | ||
|
|
52726342bd | ||
|
|
f95f6c7d86 | ||
|
|
d08bdc861f | ||
|
|
bbf8d6bf01 | ||
|
|
946777601b | ||
|
|
7e72b5e453 | ||
|
|
acddf16e57 | ||
|
|
e75e8b9580 | ||
|
|
42427b9c7a | ||
|
|
59ac3346eb | ||
|
|
41cbd088c2 | ||
|
|
1120e0b4d2 | ||
|
|
4923df10cc | ||
|
|
3050f3a7ba | ||
|
|
80d36634e1 | ||
|
|
b74eb88c68 | ||
|
|
62d4377053 | ||
|
|
0906302ca9 | ||
|
|
8435451093 | ||
|
|
43e1189688 | ||
|
|
9b3a38e3d3 | ||
|
|
807e7640aa | ||
|
|
08d5663cb8 | ||
|
|
20859802f0 | ||
|
|
fdd649e2ef | ||
|
|
2a533f0067 | ||
|
|
a2be49305d | ||
|
|
37280cf555 | ||
|
|
dd272298aa | ||
|
|
a6daed74f5 | ||
|
|
c07059e7fd | ||
|
|
233a804196 | ||
|
|
59ca8d458d | ||
|
|
20dffc22a2 | ||
|
|
281b64daf4 | ||
|
|
a3d78b77f8 | ||
|
|
3e75175d02 | ||
|
|
e11e65b3d6 | ||
|
|
bf90f2402d | ||
|
|
60b59d554d | ||
|
|
4fb59fdfe1 | ||
|
|
6deccc7e6b | ||
|
|
58fd0a1c50 | ||
|
|
d17f38494b | ||
|
|
877b31b33e | ||
|
|
5102c91256 | ||
|
|
7149332712 |
2
externals/fmt
vendored
2
externals/fmt
vendored
Submodule externals/fmt updated: 3e75ad9822...9e554999ce
@@ -22,8 +22,7 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
|
||||
return Stream::Format::Multi51Channel16;
|
||||
}
|
||||
|
||||
LOG_CRITICAL(Audio, "Unimplemented num_channels={}", num_channels);
|
||||
UNREACHABLE();
|
||||
UNIMPLEMENTED_MSG("Unimplemented num_channels={}", num_channels);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -260,8 +260,7 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_CRITICAL(Audio, "Unimplemented sample_format={}", info.sample_format);
|
||||
UNREACHABLE();
|
||||
UNIMPLEMENTED_MSG("Unimplemented sample_format={}", info.sample_format);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -280,8 +279,7 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_CRITICAL(Audio, "Unimplemented channel_count={}", info.channel_count);
|
||||
UNREACHABLE();
|
||||
UNIMPLEMENTED_MSG("Unimplemented channel_count={}", info.channel_count);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,7 @@ u32 Stream::GetNumChannels() const {
|
||||
case Format::Multi51Channel16:
|
||||
return 6;
|
||||
}
|
||||
LOG_CRITICAL(Audio, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
UNIMPLEMENTED_MSG("Unimplemented format={}", static_cast<u32>(format));
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -49,7 +48,7 @@ void Stream::Play() {
|
||||
|
||||
void Stream::Stop() {
|
||||
state = State::Stopped;
|
||||
ASSERT_MSG(false, "Unimplemented");
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
Stream::State Stream::GetState() const {
|
||||
@@ -120,7 +119,7 @@ bool Stream::QueueBuffer(BufferPtr&& buffer) {
|
||||
}
|
||||
|
||||
bool Stream::ContainsBuffer(Buffer::Tag tag) const {
|
||||
ASSERT_MSG(false, "Unimplemented");
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ template <typename T>
|
||||
class Quaternion {
|
||||
public:
|
||||
Math::Vec3<T> xyz;
|
||||
T w;
|
||||
T w{};
|
||||
|
||||
Quaternion<decltype(-T{})> Inverse() const {
|
||||
return {-xyz, w};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
add_library(core STATIC
|
||||
arm/arm_interface.h
|
||||
arm/arm_interface.cpp
|
||||
arm/exclusive_monitor.cpp
|
||||
arm/exclusive_monitor.h
|
||||
arm/unicorn/arm_unicorn.cpp
|
||||
@@ -83,6 +84,8 @@ add_library(core STATIC
|
||||
file_sys/vfs_vector.h
|
||||
file_sys/xts_archive.cpp
|
||||
file_sys/xts_archive.h
|
||||
frontend/applets/profile_select.cpp
|
||||
frontend/applets/profile_select.h
|
||||
frontend/applets/software_keyboard.cpp
|
||||
frontend/applets/software_keyboard.h
|
||||
frontend/emu_window.cpp
|
||||
@@ -164,6 +167,8 @@ add_library(core STATIC
|
||||
hle/service/am/applet_oe.h
|
||||
hle/service/am/applets/applets.cpp
|
||||
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.h
|
||||
hle/service/am/applets/stub_applet.cpp
|
||||
|
||||
27
src/core/arm/arm_interface.cpp
Normal file
27
src/core/arm/arm_interface.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Core {
|
||||
void ARM_Interface::LogBacktrace() const {
|
||||
VAddr fp = GetReg(29);
|
||||
VAddr lr = GetReg(30);
|
||||
const VAddr sp = GetReg(13);
|
||||
const VAddr pc = GetPC();
|
||||
|
||||
LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
|
||||
while (true) {
|
||||
LOG_ERROR(Core_ARM, "{:016X}", lr);
|
||||
if (!fp) {
|
||||
break;
|
||||
}
|
||||
lr = Memory::Read64(fp + 8) - 4;
|
||||
fp = Memory::Read64(fp);
|
||||
}
|
||||
}
|
||||
} // namespace Core
|
||||
@@ -141,6 +141,14 @@ public:
|
||||
|
||||
/// Prepare core for thread reschedule (if needed to correctly handle state)
|
||||
virtual void PrepareReschedule() = 0;
|
||||
|
||||
/// fp (= r29) points to the last frame record.
|
||||
/// Note that this is the frame record for the *previous* frame, not the current one.
|
||||
/// Note we need to subtract 4 from our last read to get the proper address
|
||||
/// Frame records are two words long:
|
||||
/// fp+0 : pointer to previous frame record
|
||||
/// fp+8 : value of lr for frame
|
||||
void LogBacktrace() const;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
|
||||
@@ -99,6 +99,8 @@ struct System::Impl {
|
||||
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
|
||||
|
||||
/// 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)
|
||||
software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
|
||||
|
||||
@@ -229,6 +231,7 @@ struct System::Impl {
|
||||
bool is_powered_on = false;
|
||||
|
||||
/// Frontend applets
|
||||
std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector;
|
||||
std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
|
||||
|
||||
/// Service manager
|
||||
@@ -424,6 +427,14 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
|
||||
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) {
|
||||
impl->software_keyboard = std::move(applet);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "frontend/applets/profile_select.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
@@ -241,6 +242,10 @@ public:
|
||||
|
||||
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);
|
||||
|
||||
const Core::Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const;
|
||||
|
||||
@@ -36,18 +36,20 @@ std::string LanguageEntry::GetDeveloperName() const {
|
||||
developer_name.size());
|
||||
}
|
||||
|
||||
NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
|
||||
file->ReadObject(raw.get());
|
||||
NACP::NACP() = default;
|
||||
|
||||
NACP::NACP(VirtualFile file) {
|
||||
file->ReadObject(&raw);
|
||||
}
|
||||
|
||||
NACP::~NACP() = default;
|
||||
|
||||
const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
|
||||
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())
|
||||
return language_entry;
|
||||
}
|
||||
@@ -65,21 +67,29 @@ std::string NACP::GetDeveloperName(Language language) const {
|
||||
}
|
||||
|
||||
u64 NACP::GetTitleId() const {
|
||||
return raw->title_id;
|
||||
return raw.title_id;
|
||||
}
|
||||
|
||||
u64 NACP::GetDLCBaseTitleId() const {
|
||||
return raw->dlc_base_title_id;
|
||||
return raw.dlc_base_title_id;
|
||||
}
|
||||
|
||||
std::string NACP::GetVersionString() const {
|
||||
return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(),
|
||||
raw->version_string.size());
|
||||
return Common::StringFromFixedZeroTerminatedBuffer(raw.version_string.data(),
|
||||
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> out(sizeof(RawNACP));
|
||||
std::memcpy(out.data(), raw.get(), sizeof(RawNACP));
|
||||
std::memcpy(out.data(), &raw, sizeof(RawNACP));
|
||||
return out;
|
||||
}
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -28,17 +28,30 @@ static_assert(sizeof(LanguageEntry) == 0x300, "LanguageEntry has incorrect size.
|
||||
// The raw file format of a NACP file.
|
||||
struct RawNACP {
|
||||
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;
|
||||
INSERT_PADDING_BYTES(0x20);
|
||||
std::array<u8, 0x20> rating_age;
|
||||
std::array<char, 0x10> version_string;
|
||||
u64_le dlc_base_title_id;
|
||||
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 title_id_3;
|
||||
std::array<u64_le, 0x7> title_id_array;
|
||||
INSERT_PADDING_BYTES(0x8);
|
||||
std::array<u64_le, 0x8> local_communication;
|
||||
u8 logo_type;
|
||||
u8 logo_handling;
|
||||
bool runtime_add_on_content_install;
|
||||
INSERT_PADDING_BYTES(5);
|
||||
u64_le title_id_update;
|
||||
std::array<u8, 0x40> bcat_passphrase;
|
||||
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.
|
||||
class NACP {
|
||||
public:
|
||||
explicit NACP();
|
||||
explicit NACP(VirtualFile file);
|
||||
~NACP();
|
||||
|
||||
@@ -81,10 +95,12 @@ public:
|
||||
u64 GetTitleId() const;
|
||||
u64 GetDLCBaseTitleId() const;
|
||||
std::string GetVersionString() const;
|
||||
u64 GetDefaultNormalSaveSize() const;
|
||||
u64 GetDefaultJournalSaveSize() const;
|
||||
std::vector<u8> GetRawBytes() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<RawNACP> raw;
|
||||
RawNACP raw{};
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -92,16 +92,20 @@ void ProgramMetadata::Print() const {
|
||||
LOG_DEBUG(Service_FS, " > 64-bit instructions: {}",
|
||||
npdm_header.has_64_bit_instructions ? "YES" : "NO");
|
||||
|
||||
auto address_space = "Unknown";
|
||||
const char* address_space = "Unknown";
|
||||
switch (npdm_header.address_space_type) {
|
||||
case ProgramAddressSpaceType::Is36Bit:
|
||||
address_space = "64-bit (36-bit address space)";
|
||||
break;
|
||||
case ProgramAddressSpaceType::Is39Bit:
|
||||
address_space = "64-bit";
|
||||
address_space = "64-bit (39-bit address space)";
|
||||
break;
|
||||
case ProgramAddressSpaceType::Is32Bit:
|
||||
case ProgramAddressSpaceType::Is32BitNoMap:
|
||||
address_space = "32-bit";
|
||||
break;
|
||||
case ProgramAddressSpaceType::Is32BitNoMap:
|
||||
address_space = "32-bit (no map region)";
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_FS, " > Address space: {}\n", address_space);
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
|
||||
|
||||
std::string SaveDataDescriptor::DebugInfo() const {
|
||||
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);
|
||||
@@ -128,7 +130,36 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
|
||||
return fmt::format("{}save/cache/{:016X}", out, title_id);
|
||||
default:
|
||||
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
|
||||
|
||||
@@ -46,6 +46,11 @@ struct SaveDataDescriptor {
|
||||
};
|
||||
static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorrect size.");
|
||||
|
||||
struct SaveDataSize {
|
||||
u64 normal;
|
||||
u64 journal;
|
||||
};
|
||||
|
||||
/// File system interface to the SaveData archive
|
||||
class SaveDataFactory {
|
||||
public:
|
||||
@@ -60,6 +65,9 @@ public:
|
||||
static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_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:
|
||||
VirtualDir dir;
|
||||
};
|
||||
|
||||
@@ -150,7 +150,7 @@ public:
|
||||
template <typename T>
|
||||
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.");
|
||||
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.
|
||||
@@ -166,7 +166,7 @@ public:
|
||||
template <typename T>
|
||||
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.");
|
||||
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.
|
||||
|
||||
19
src/core/frontend/applets/profile_select.cpp
Normal file
19
src/core/frontend/applets/profile_select.cpp
Normal file
@@ -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
|
||||
27
src/core/frontend/applets/profile_select.h
Normal file
27
src/core/frontend/applets/profile_select.h
Normal file
@@ -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 "core/frontend/framebuffer_layout.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Layout {
|
||||
|
||||
@@ -42,4 +43,18 @@ FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height) {
|
||||
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
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
namespace Layout {
|
||||
|
||||
enum ScreenUndocked : unsigned { Width = 1280, Height = 720 };
|
||||
enum ScreenDocked : unsigned { WidthDocked = 1920, HeightDocked = 1080 };
|
||||
|
||||
/// Describes the layout of the window framebuffer
|
||||
struct FramebufferLayout {
|
||||
@@ -34,4 +35,10 @@ struct FramebufferLayout {
|
||||
*/
|
||||
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
|
||||
|
||||
@@ -201,11 +201,11 @@ void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) {
|
||||
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++) {
|
||||
const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
|
||||
for (auto& thread : threads) {
|
||||
if (thread->GetThreadID() == static_cast<u32>(id)) {
|
||||
if (thread->GetThreadID() == static_cast<u64>(id)) {
|
||||
current_core = core;
|
||||
return thread.get();
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118};
|
||||
constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119};
|
||||
constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120};
|
||||
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_INVALID_STATE{ErrorModule::Kernel, 125};
|
||||
constexpr ResultCode ERR_RESERVED_VALUE{ErrorModule::Kernel, 126};
|
||||
|
||||
@@ -112,7 +112,7 @@ struct KernelCore::Impl {
|
||||
|
||||
void Shutdown() {
|
||||
next_object_id = 0;
|
||||
next_process_id = 10;
|
||||
next_process_id = Process::ProcessIDMin;
|
||||
next_thread_id = 1;
|
||||
|
||||
process_list.clear();
|
||||
@@ -153,10 +153,8 @@ struct KernelCore::Impl {
|
||||
}
|
||||
|
||||
std::atomic<u32> next_object_id{0};
|
||||
// TODO(Subv): Start the process ids from 10 for now, as lower PIDs are
|
||||
// reserved for low-level services
|
||||
std::atomic<u32> next_process_id{10};
|
||||
std::atomic<u32> next_thread_id{1};
|
||||
std::atomic<u64> next_process_id{Process::ProcessIDMin};
|
||||
std::atomic<u64> next_thread_id{1};
|
||||
|
||||
// Lists all processes that exist in the current session.
|
||||
std::vector<SharedPtr<Process>> process_list;
|
||||
@@ -242,11 +240,11 @@ u32 KernelCore::CreateNewObjectID() {
|
||||
return impl->next_object_id++;
|
||||
}
|
||||
|
||||
u32 KernelCore::CreateNewThreadID() {
|
||||
u64 KernelCore::CreateNewThreadID() {
|
||||
return impl->next_thread_id++;
|
||||
}
|
||||
|
||||
u32 KernelCore::CreateNewProcessID() {
|
||||
u64 KernelCore::CreateNewProcessID() {
|
||||
return impl->next_process_id++;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,10 +88,10 @@ private:
|
||||
u32 CreateNewObjectID();
|
||||
|
||||
/// 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.
|
||||
u32 CreateNewThreadID();
|
||||
u64 CreateNewThreadID();
|
||||
|
||||
/// Creates a timer callback handle for the given timer.
|
||||
ResultVal<Handle> CreateTimerCallbackHandle(const SharedPtr<Timer>& timer);
|
||||
|
||||
@@ -32,6 +32,7 @@ bool Object::IsWaitable() const {
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -20,6 +20,35 @@
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Kernel {
|
||||
namespace {
|
||||
/**
|
||||
* Sets up the primary application thread
|
||||
*
|
||||
* @param owner_process The parent process for the main thread
|
||||
* @param kernel The kernel instance to create the main thread under.
|
||||
* @param entry_point The address at which the thread should start execution
|
||||
* @param priority The priority to give the main thread
|
||||
*/
|
||||
void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) {
|
||||
// Setup page table so we can write to memory
|
||||
SetCurrentPageTable(&owner_process.VMManager().page_table);
|
||||
|
||||
// Initialize new "main" thread
|
||||
const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
|
||||
auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0,
|
||||
owner_process.GetIdealCore(), stack_top, owner_process);
|
||||
|
||||
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
|
||||
|
||||
// Register 1 must be a handle to the main thread
|
||||
const Handle guest_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
|
||||
thread->SetGuestHandle(guest_handle);
|
||||
thread->GetContext().cpu_registers[1] = guest_handle;
|
||||
|
||||
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
|
||||
thread->ResumeFromWait();
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
CodeSet::CodeSet() = default;
|
||||
CodeSet::~CodeSet() = default;
|
||||
@@ -64,7 +93,7 @@ ResultCode Process::ClearSignalState() {
|
||||
|
||||
ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
|
||||
program_id = metadata.GetTitleID();
|
||||
ideal_processor = metadata.GetMainThreadCore();
|
||||
ideal_core = metadata.GetMainThreadCore();
|
||||
is_64bit_process = metadata.Is64BitProgram();
|
||||
|
||||
vm_manager.Reset(metadata.GetAddressSpaceType());
|
||||
@@ -86,7 +115,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
|
||||
vm_manager.LogLayout();
|
||||
ChangeStatus(ProcessStatus::Running);
|
||||
|
||||
Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, *this);
|
||||
SetupMainThread(*this, kernel, entry_point, main_thread_priority);
|
||||
}
|
||||
|
||||
void Process::PrepareForTermination() {
|
||||
@@ -198,22 +227,6 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
|
||||
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() {}
|
||||
|
||||
|
||||
@@ -102,6 +102,18 @@ struct CodeSet final {
|
||||
|
||||
class Process final : public WaitObject {
|
||||
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 SharedPtr<Process> Create(KernelCore& kernel, std::string&& name);
|
||||
@@ -144,7 +156,7 @@ public:
|
||||
}
|
||||
|
||||
/// Gets the unique ID that identifies this particular process.
|
||||
u32 GetProcessID() const {
|
||||
u64 GetProcessID() const {
|
||||
return process_id;
|
||||
}
|
||||
|
||||
@@ -156,18 +168,18 @@ public:
|
||||
/// Gets the resource limit descriptor for this process
|
||||
SharedPtr<ResourceLimit> GetResourceLimit() const;
|
||||
|
||||
/// Gets the default CPU ID for this process
|
||||
u8 GetDefaultProcessorID() const {
|
||||
return ideal_processor;
|
||||
/// Gets the ideal CPU core ID for this process
|
||||
u8 GetIdealCore() const {
|
||||
return ideal_core;
|
||||
}
|
||||
|
||||
/// Gets the bitmask of allowed CPUs that this process' threads can run on.
|
||||
u64 GetAllowedProcessorMask() const {
|
||||
/// Gets the bitmask of allowed cores that this process' threads can run on.
|
||||
u64 GetCoreMask() const {
|
||||
return capabilities.GetCoreMask();
|
||||
}
|
||||
|
||||
/// Gets the bitmask of allowed thread priorities.
|
||||
u64 GetAllowedThreadPriorityMask() const {
|
||||
u64 GetPriorityMask() const {
|
||||
return capabilities.GetPriorityMask();
|
||||
}
|
||||
|
||||
@@ -230,7 +242,7 @@ public:
|
||||
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.
|
||||
VAddr MarkNextAvailableTLSSlotAsUsed(Thread& thread);
|
||||
@@ -238,13 +250,6 @@ public:
|
||||
// Frees a used TLS slot identified by the given 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:
|
||||
explicit Process(KernelCore& kernel);
|
||||
~Process() override;
|
||||
@@ -267,16 +272,16 @@ private:
|
||||
ProcessStatus status;
|
||||
|
||||
/// The ID of this process
|
||||
u32 process_id = 0;
|
||||
u64 process_id = 0;
|
||||
|
||||
/// Title ID corresponding to the process
|
||||
u64 program_id;
|
||||
u64 program_id = 0;
|
||||
|
||||
/// Resource limit descriptor for this process
|
||||
SharedPtr<ResourceLimit> resource_limit;
|
||||
|
||||
/// The default CPU for this process, threads are scheduled on this cpu by default.
|
||||
u8 ideal_processor = 0;
|
||||
/// The ideal CPU core for this process, threads are scheduled on this core by default.
|
||||
u8 ideal_core = 0;
|
||||
u32 is_virtual_address_memory_enabled = 0;
|
||||
|
||||
/// The Thread Local Storage area is allocated as processes create threads,
|
||||
|
||||
@@ -190,10 +190,16 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
|
||||
return ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
auto& process = *Core::CurrentProcess();
|
||||
const VAddr heap_base = process.VMManager().GetHeapRegionBaseAddress();
|
||||
CASCADE_RESULT(*heap_addr,
|
||||
process.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite));
|
||||
auto& vm_manager = Core::CurrentProcess()->VMManager();
|
||||
const VAddr heap_base = vm_manager.GetHeapRegionBaseAddress();
|
||||
const auto alloc_result =
|
||||
vm_manager.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite);
|
||||
|
||||
if (alloc_result.Failed()) {
|
||||
return alloc_result.Code();
|
||||
}
|
||||
|
||||
*heap_addr = *alloc_result;
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -307,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,
|
||||
src_addr, size);
|
||||
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
const auto& vm_manager = current_process->VMManager();
|
||||
|
||||
auto& vm_manager = Core::CurrentProcess()->VMManager();
|
||||
const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
|
||||
if (result != RESULT_SUCCESS) {
|
||||
|
||||
if (result.IsError()) {
|
||||
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
|
||||
@@ -323,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,
|
||||
src_addr, size);
|
||||
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
const auto& vm_manager = current_process->VMManager();
|
||||
|
||||
auto& vm_manager = Core::CurrentProcess()->VMManager();
|
||||
const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
|
||||
if (result != RESULT_SUCCESS) {
|
||||
|
||||
if (result.IsError()) {
|
||||
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
|
||||
@@ -391,7 +395,7 @@ static ResultCode SendSyncRequest(Handle handle) {
|
||||
}
|
||||
|
||||
/// 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);
|
||||
|
||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
@@ -405,20 +409,33 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// Get the ID of the specified process
|
||||
static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
|
||||
LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle);
|
||||
/// Gets the ID of the specified process or a specified thread's owning process.
|
||||
static ResultCode GetProcessId(u64* process_id, Handle handle) {
|
||||
LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
|
||||
|
||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
const SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
|
||||
if (!process) {
|
||||
LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
|
||||
process_handle);
|
||||
return ERR_INVALID_HANDLE;
|
||||
const SharedPtr<Process> process = handle_table.Get<Process>(handle);
|
||||
if (process) {
|
||||
*process_id = process->GetProcessID();
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
*process_id = process->GetProcessID();
|
||||
return RESULT_SUCCESS;
|
||||
const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle);
|
||||
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
|
||||
@@ -667,6 +684,9 @@ static void Break(u32 reason, u64 info1, u64 info2) {
|
||||
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
|
||||
reason, info1, info2);
|
||||
handle_debug_buffer(info1, info2);
|
||||
Core::System::GetInstance()
|
||||
.ArmInterface(static_cast<std::size_t>(GetCurrentThread()->GetProcessorID()))
|
||||
.LogBacktrace();
|
||||
ASSERT(false);
|
||||
|
||||
Core::CurrentProcess()->PrepareForTermination();
|
||||
@@ -695,8 +715,8 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
|
||||
enum class GetInfoType : u64 {
|
||||
// 1.0.0+
|
||||
AllowedCpuIdBitmask = 0,
|
||||
AllowedThreadPrioBitmask = 1,
|
||||
AllowedCPUCoreMask = 0,
|
||||
AllowedThreadPriorityMask = 1,
|
||||
MapRegionBaseAddr = 2,
|
||||
MapRegionSize = 3,
|
||||
HeapRegionBaseAddr = 4,
|
||||
@@ -727,8 +747,8 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
const auto info_id_type = static_cast<GetInfoType>(info_id);
|
||||
|
||||
switch (info_id_type) {
|
||||
case GetInfoType::AllowedCpuIdBitmask:
|
||||
case GetInfoType::AllowedThreadPrioBitmask:
|
||||
case GetInfoType::AllowedCPUCoreMask:
|
||||
case GetInfoType::AllowedThreadPriorityMask:
|
||||
case GetInfoType::MapRegionBaseAddr:
|
||||
case GetInfoType::MapRegionSize:
|
||||
case GetInfoType::HeapRegionBaseAddr:
|
||||
@@ -754,12 +774,12 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
}
|
||||
|
||||
switch (info_id_type) {
|
||||
case GetInfoType::AllowedCpuIdBitmask:
|
||||
*result = process->GetAllowedProcessorMask();
|
||||
case GetInfoType::AllowedCPUCoreMask:
|
||||
*result = process->GetCoreMask();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::AllowedThreadPrioBitmask:
|
||||
*result = process->GetAllowedThreadPriorityMask();
|
||||
case GetInfoType::AllowedThreadPriorityMask:
|
||||
*result = process->GetPriorityMask();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::MapRegionBaseAddr:
|
||||
@@ -919,8 +939,35 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
}
|
||||
|
||||
/// Sets the thread activity
|
||||
static ResultCode SetThreadActivity(Handle handle, u32 unknown) {
|
||||
LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, unknown=0x{:08X}", handle, unknown);
|
||||
static ResultCode SetThreadActivity(Handle handle, u32 activity) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -947,7 +994,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
|
||||
|
||||
if (thread == GetCurrentThread()) {
|
||||
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();
|
||||
@@ -1172,29 +1219,35 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
|
||||
"threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}",
|
||||
entry_point, arg, stack_top, priority, processor_id, *out_handle);
|
||||
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
|
||||
if (processor_id == THREADPROCESSORID_IDEAL) {
|
||||
// Set the target CPU to the one specified by the process.
|
||||
processor_id = current_process->GetIdealCore();
|
||||
ASSERT(processor_id != THREADPROCESSORID_IDEAL);
|
||||
}
|
||||
|
||||
if (processor_id < THREADPROCESSORID_0 || processor_id > THREADPROCESSORID_3) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid thread processor ID: {}", processor_id);
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
|
||||
const u64 core_mask = current_process->GetCoreMask();
|
||||
if ((core_mask | (1ULL << processor_id)) != core_mask) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid thread core specified ({})", processor_id);
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
|
||||
if (priority > THREADPRIO_LOWEST) {
|
||||
LOG_ERROR(Kernel_SVC, "An invalid priority was specified, expected {} but got {}",
|
||||
THREADPRIO_LOWEST, priority);
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Invalid thread priority specified ({}). Must be within the range 0-64",
|
||||
priority);
|
||||
return ERR_INVALID_THREAD_PRIORITY;
|
||||
}
|
||||
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
|
||||
if (processor_id == THREADPROCESSORID_DEFAULT) {
|
||||
// Set the target CPU to the one specified in the process' exheader.
|
||||
processor_id = current_process->GetDefaultProcessorID();
|
||||
ASSERT(processor_id != THREADPROCESSORID_DEFAULT);
|
||||
}
|
||||
|
||||
switch (processor_id) {
|
||||
case THREADPROCESSORID_0:
|
||||
case THREADPROCESSORID_1:
|
||||
case THREADPROCESSORID_2:
|
||||
case THREADPROCESSORID_3:
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Kernel_SVC, "Invalid thread processor ID: {}", processor_id);
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
if (((1ULL << priority) & current_process->GetPriorityMask()) == 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid thread priority specified ({})", priority);
|
||||
return ERR_INVALID_THREAD_PRIORITY;
|
||||
}
|
||||
|
||||
const std::string name = fmt::format("thread-{:X}", entry_point);
|
||||
@@ -1232,7 +1285,10 @@ static ResultCode StartThread(Handle thread_handle) {
|
||||
ASSERT(thread->GetStatus() == ThreadStatus::Dormant);
|
||||
|
||||
thread->ResumeFromWait();
|
||||
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
|
||||
|
||||
if (thread->GetStatus() == ThreadStatus::Ready) {
|
||||
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
@@ -1589,13 +1645,13 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (core == static_cast<u32>(THREADPROCESSORID_DEFAULT)) {
|
||||
const u8 default_processor_id = thread->GetOwnerProcess()->GetDefaultProcessorID();
|
||||
if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) {
|
||||
const u8 ideal_cpu_core = thread->GetOwnerProcess()->GetIdealCore();
|
||||
|
||||
ASSERT(default_processor_id != static_cast<u8>(THREADPROCESSORID_DEFAULT));
|
||||
ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL));
|
||||
|
||||
// Set the target CPU to the one specified in the process' exheader.
|
||||
core = default_processor_id;
|
||||
// Set the target CPU to the ideal core specified by the process.
|
||||
core = ideal_cpu_core;
|
||||
mask = 1ULL << core;
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,15 @@ void SvcWrap() {
|
||||
template <ResultCode func(u32*, u64)>
|
||||
void SvcWrap() {
|
||||
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);
|
||||
FuncReturn(retval);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/thread_queue_list.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/core.h"
|
||||
@@ -50,7 +49,7 @@ void Thread::Stop() {
|
||||
|
||||
// Clean up thread from ready queue
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -140,6 +139,11 @@ void Thread::ResumeFromWait() {
|
||||
|
||||
wakeup_callback = nullptr;
|
||||
|
||||
if (activity == ThreadActivity::Paused) {
|
||||
status = ThreadStatus::Paused;
|
||||
return;
|
||||
}
|
||||
|
||||
status = ThreadStatus::Ready;
|
||||
|
||||
ChangeScheduler();
|
||||
@@ -227,29 +231,6 @@ void Thread::BoostPriority(u32 priority) {
|
||||
current_priority = priority;
|
||||
}
|
||||
|
||||
SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
|
||||
Process& owner_process) {
|
||||
// Setup page table so we can write to memory
|
||||
SetCurrentPageTable(&owner_process.VMManager().page_table);
|
||||
|
||||
// Initialize new "main" thread
|
||||
const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
|
||||
auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0,
|
||||
stack_top, owner_process);
|
||||
|
||||
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
|
||||
|
||||
// Register 1 must be a handle to the main thread
|
||||
const Handle guest_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
|
||||
thread->SetGuestHandle(guest_handle);
|
||||
thread->GetContext().cpu_registers[1] = guest_handle;
|
||||
|
||||
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
|
||||
thread->ResumeFromWait();
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
void Thread::SetWaitSynchronizationResult(ResultCode result) {
|
||||
context.cpu_registers[0] = result.raw;
|
||||
}
|
||||
@@ -391,6 +372,23 @@ bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> t
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,12 +30,12 @@ enum ThreadPriority : u32 {
|
||||
};
|
||||
|
||||
enum ThreadProcessorId : s32 {
|
||||
THREADPROCESSORID_DEFAULT = -2, ///< Run thread on default core specified by exheader
|
||||
THREADPROCESSORID_0 = 0, ///< Run thread on core 0
|
||||
THREADPROCESSORID_1 = 1, ///< Run thread on core 1
|
||||
THREADPROCESSORID_2 = 2, ///< Run thread on core 2
|
||||
THREADPROCESSORID_3 = 3, ///< Run thread on core 3
|
||||
THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
|
||||
THREADPROCESSORID_IDEAL = -2, ///< Run thread on the ideal core specified by the process.
|
||||
THREADPROCESSORID_0 = 0, ///< Run thread on core 0
|
||||
THREADPROCESSORID_1 = 1, ///< Run thread on core 1
|
||||
THREADPROCESSORID_2 = 2, ///< Run thread on core 2
|
||||
THREADPROCESSORID_3 = 3, ///< Run thread on core 3
|
||||
THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
|
||||
|
||||
/// Allowed CPU mask
|
||||
THREADPROCESSORID_DEFAULT_MASK = (1 << THREADPROCESSORID_0) | (1 << THREADPROCESSORID_1) |
|
||||
@@ -45,6 +45,7 @@ enum ThreadProcessorId : s32 {
|
||||
enum class ThreadStatus {
|
||||
Running, ///< Currently running
|
||||
Ready, ///< Ready to run
|
||||
Paused, ///< Paused by SetThreadActivity or debug
|
||||
WaitHLEEvent, ///< Waiting for hle event to finish
|
||||
WaitSleep, ///< Waiting due to a SleepThread SVC
|
||||
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.
|
||||
};
|
||||
|
||||
enum class ThreadActivity : u32 {
|
||||
Normal = 0,
|
||||
Paused = 1,
|
||||
};
|
||||
|
||||
class Thread final : public WaitObject {
|
||||
public:
|
||||
using TLSMemory = std::vector<u8>;
|
||||
@@ -151,7 +157,7 @@ public:
|
||||
* Gets the thread's thread ID
|
||||
* @return The thread's ID
|
||||
*/
|
||||
u32 GetThreadID() const {
|
||||
u64 GetThreadID() const {
|
||||
return thread_id;
|
||||
}
|
||||
|
||||
@@ -371,6 +377,12 @@ public:
|
||||
return affinity_mask;
|
||||
}
|
||||
|
||||
ThreadActivity GetActivity() const {
|
||||
return activity;
|
||||
}
|
||||
|
||||
void SetActivity(ThreadActivity value);
|
||||
|
||||
private:
|
||||
explicit Thread(KernelCore& kernel);
|
||||
~Thread() override;
|
||||
@@ -379,7 +391,7 @@ private:
|
||||
|
||||
Core::ARM_Interface::ThreadContext context{};
|
||||
|
||||
u32 thread_id = 0;
|
||||
u64 thread_id = 0;
|
||||
|
||||
ThreadStatus status = ThreadStatus::Dormant;
|
||||
|
||||
@@ -439,18 +451,9 @@ private:
|
||||
TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>();
|
||||
|
||||
std::string name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets up the primary application thread
|
||||
* @param kernel The kernel instance to create the main thread under.
|
||||
* @param entry_point The address at which the thread should start execution
|
||||
* @param priority The priority to give the main thread
|
||||
* @param owner_process The parent process for the main thread
|
||||
* @return A shared pointer to the main thread
|
||||
*/
|
||||
SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
|
||||
Process& owner_process);
|
||||
ThreadActivity activity = ThreadActivity::Normal;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the current thread
|
||||
|
||||
@@ -190,6 +190,7 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) {
|
||||
vma.type = VMAType::Free;
|
||||
vma.permissions = VMAPermission::None;
|
||||
vma.state = MemoryState::Unmapped;
|
||||
vma.attribute = MemoryAttribute::None;
|
||||
|
||||
vma.backing_block = nullptr;
|
||||
vma.offset = 0;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <stack>
|
||||
#include "audio_core/audio_renderer.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/kernel.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_oe.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/stub_applet.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};
|
||||
|
||||
enum class AppletId : u32 {
|
||||
ProfileSelect = 0x10,
|
||||
SoftwareKeyboard = 0x11,
|
||||
};
|
||||
|
||||
@@ -71,10 +74,13 @@ IWindowController::IWindowController() : ServiceFramework("IWindowController") {
|
||||
IWindowController::~IWindowController() = default;
|
||||
|
||||
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};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(0);
|
||||
rb.Push<u64>(process_id);
|
||||
}
|
||||
|
||||
void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx) {
|
||||
@@ -772,6 +778,8 @@ ILibraryAppletCreator::~ILibraryAppletCreator() = default;
|
||||
|
||||
static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) {
|
||||
switch (id) {
|
||||
case AppletId::ProfileSelect:
|
||||
return std::make_shared<Applets::ProfileSelect>();
|
||||
case AppletId::SoftwareKeyboard:
|
||||
return std::make_shared<Applets::SoftwareKeyboard>();
|
||||
default:
|
||||
@@ -858,8 +866,8 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
|
||||
{22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
|
||||
{23, &IApplicationFunctions::GetDisplayVersion, "GetDisplayVersion"},
|
||||
{24, nullptr, "GetLaunchStorageInfoForDebug"},
|
||||
{25, nullptr, "ExtendSaveData"},
|
||||
{26, nullptr, "GetSaveDataSize"},
|
||||
{25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"},
|
||||
{26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"},
|
||||
{30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"},
|
||||
{31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"},
|
||||
{32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
|
||||
@@ -1036,6 +1044,48 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
|
||||
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,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
|
||||
auto message_queue = std::make_shared<AppletMessageQueue>();
|
||||
|
||||
@@ -206,6 +206,8 @@ private:
|
||||
void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx);
|
||||
void NotifyRunning(Kernel::HLERequestContext& ctx);
|
||||
void GetPseudoDeviceId(Kernel::HLERequestContext& ctx);
|
||||
void ExtendSaveData(Kernel::HLERequestContext& ctx);
|
||||
void GetSaveDataSize(Kernel::HLERequestContext& ctx);
|
||||
void BeginBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
|
||||
void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
|
||||
void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
|
||||
|
||||
77
src/core/hle/service/am/applets/profile_select.cpp
Normal file
77
src/core/hle/service/am/applets/profile_select.cpp
Normal file
@@ -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
|
||||
50
src/core/hle/service/am/applets/profile_select.h
Normal file
50
src/core/hle/service/am/applets/profile_select.h
Normal file
@@ -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
|
||||
@@ -111,7 +111,8 @@ static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
|
||||
}
|
||||
|
||||
static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) {
|
||||
LOG_ERROR(Service_Fatal, "Threw fatal error type {}", static_cast<u32>(fatal_type));
|
||||
LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code 0x{:X}",
|
||||
static_cast<u32>(fatal_type), error_code.raw);
|
||||
switch (fatal_type) {
|
||||
case FatalType::ErrorReportAndScreen:
|
||||
GenerateErrorReport(error_code, info);
|
||||
|
||||
@@ -8,18 +8,23 @@
|
||||
#include "common/file_util.h"
|
||||
#include "core/core.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/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/romfs_factory.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/file_sys/sdmc_factory.h"
|
||||
#include "core/file_sys/vfs.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/fsp_ldr.h"
|
||||
#include "core/hle/service/filesystem/fsp_pr.h"
|
||||
#include "core/hle/service/filesystem/fsp_srv.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Service::FileSystem {
|
||||
|
||||
@@ -28,6 +33,10 @@ namespace Service::FileSystem {
|
||||
// TODO(DarkLordZach): Eventually make this configurable in settings.
|
||||
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,
|
||||
std::string_view dir_name_) {
|
||||
std::string dir_name(FileUtil::SanitizePath(dir_name_));
|
||||
@@ -341,6 +350,44 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
|
||||
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() {
|
||||
return FileSys::RegisteredCacheUnion{
|
||||
{GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}};
|
||||
|
||||
@@ -21,9 +21,11 @@ class SDMCFactory;
|
||||
enum class ContentRecordType : u8;
|
||||
enum class Mode : u32;
|
||||
enum class SaveDataSpaceId : u8;
|
||||
enum class SaveDataType : u8;
|
||||
enum class StorageId : u8;
|
||||
|
||||
struct SaveDataDescriptor;
|
||||
struct SaveDataSize;
|
||||
} // namespace FileSys
|
||||
|
||||
namespace Service {
|
||||
@@ -48,6 +50,10 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
|
||||
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::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];
|
||||
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;
|
||||
|
||||
switch (controller_type) {
|
||||
|
||||
@@ -306,7 +306,10 @@ private:
|
||||
std::shared_ptr<IAppletResource> applet_resource;
|
||||
|
||||
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) {
|
||||
applet_resource = std::make_shared<IAppletResource>();
|
||||
@@ -318,7 +321,12 @@ private:
|
||||
}
|
||||
|
||||
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);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -326,7 +334,10 @@ private:
|
||||
}
|
||||
|
||||
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);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -334,7 +345,10 @@ private:
|
||||
}
|
||||
|
||||
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);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -342,7 +356,10 @@ private:
|
||||
}
|
||||
|
||||
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);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -350,7 +367,10 @@ private:
|
||||
}
|
||||
|
||||
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);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -358,7 +378,12 @@ private:
|
||||
}
|
||||
|
||||
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);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -367,7 +392,12 @@ private:
|
||||
|
||||
void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
|
||||
// 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);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -376,22 +406,37 @@ private:
|
||||
|
||||
void StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto handle = rp.PopRaw<u32>();
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called with handle={}", handle);
|
||||
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};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
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};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
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};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -401,8 +446,9 @@ private:
|
||||
|
||||
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto supported_styleset = rp.PopRaw<u32>();
|
||||
LOG_DEBUG(Service_HID, "called with supported_styleset={}", supported_styleset);
|
||||
const auto supported_styleset{rp.Pop<u32>()};
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, supported_styleset={}", supported_styleset);
|
||||
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.SetSupportedStyleSet({supported_styleset});
|
||||
@@ -412,7 +458,10 @@ private:
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -422,7 +471,10 @@ private:
|
||||
}
|
||||
|
||||
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)
|
||||
.SetSupportedNPadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
|
||||
@@ -431,7 +483,10 @@ private:
|
||||
}
|
||||
|
||||
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};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -440,8 +495,12 @@ private:
|
||||
|
||||
void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto npad_id = rp.PopRaw<u32>();
|
||||
LOG_DEBUG(Service_HID, "called with npad_id={}", npad_id);
|
||||
const auto npad_id{rp.Pop<u32>()};
|
||||
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};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -451,8 +510,11 @@ private:
|
||||
|
||||
void DisconnectNpad(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto npad_id = rp.PopRaw<u32>();
|
||||
LOG_DEBUG(Service_HID, "called with npad_id={}", npad_id);
|
||||
const auto npad_id{rp.Pop<u32>()};
|
||||
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)
|
||||
.DisconnectNPad(npad_id);
|
||||
@@ -462,8 +524,9 @@ private:
|
||||
|
||||
void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto npad_id = rp.PopRaw<u32>();
|
||||
LOG_DEBUG(Service_HID, "called with npad_id={}", npad_id);
|
||||
const auto npad_id{rp.Pop<u32>()};
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -474,8 +537,11 @@ private:
|
||||
|
||||
void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto hold_type = rp.PopRaw<u64>();
|
||||
LOG_DEBUG(Service_HID, "called with hold_type={}", hold_type);
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
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);
|
||||
controller.SetHoldType(Controller_NPad::NpadHoldType{hold_type});
|
||||
@@ -485,7 +551,10 @@ private:
|
||||
}
|
||||
|
||||
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 =
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||
@@ -496,15 +565,21 @@ private:
|
||||
|
||||
void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto npad_id = rp.PopRaw<u32>();
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called with npad_id={}", npad_id);
|
||||
const auto npad_id{rp.Pop<u32>()};
|
||||
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};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
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)
|
||||
.SetVibrationEnabled(true);
|
||||
@@ -523,9 +598,12 @@ private:
|
||||
|
||||
void SendVibrationValue(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto controller_id = rp.PopRaw<u32>();
|
||||
const auto vibration_values = rp.PopRaw<Controller_NPad::Vibration>();
|
||||
LOG_DEBUG(Service_HID, "called with controller_id={}", controller_id);
|
||||
const auto controller_id{rp.Pop<u32>()};
|
||||
const auto vibration_values{rp.PopRaw<Controller_NPad::Vibration>()};
|
||||
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};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -535,7 +613,10 @@ private:
|
||||
}
|
||||
|
||||
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 vibrations = ctx.ReadBuffer(1);
|
||||
@@ -557,7 +638,12 @@ private:
|
||||
}
|
||||
|
||||
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};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -568,8 +654,11 @@ private:
|
||||
|
||||
void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto npad_id = rp.PopRaw<u32>();
|
||||
LOG_DEBUG(Service_HID, "called with npad_id={}", npad_id);
|
||||
const auto npad_id{rp.Pop<u32>()};
|
||||
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);
|
||||
controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual);
|
||||
@@ -579,7 +668,14 @@ private:
|
||||
}
|
||||
|
||||
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};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -587,8 +683,11 @@ private:
|
||||
|
||||
void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto mode = rp.PopRaw<u32>();
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called with mode={}", mode);
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
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};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -612,35 +711,55 @@ private:
|
||||
}
|
||||
|
||||
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};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
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};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
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};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
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};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
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};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
@@ -318,14 +318,18 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(process->MirrorMemory(*map_address, nro_addr, nro_size,
|
||||
Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
|
||||
ASSERT(process->UnmapMemory(nro_addr, 0, nro_size) == RESULT_SUCCESS);
|
||||
ASSERT(vm_manager
|
||||
.MirrorMemory(*map_address, nro_addr, nro_size,
|
||||
Kernel::MemoryState::ModuleCodeStatic)
|
||||
.IsSuccess());
|
||||
ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess());
|
||||
|
||||
if (bss_size > 0) {
|
||||
ASSERT(process->MirrorMemory(*map_address + nro_size, bss_addr, bss_size,
|
||||
Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
|
||||
ASSERT(process->UnmapMemory(bss_addr, 0, bss_size) == RESULT_SUCCESS);
|
||||
ASSERT(vm_manager
|
||||
.MirrorMemory(*map_address + nro_size, bss_addr, bss_size,
|
||||
Kernel::MemoryState::ModuleCodeStatic)
|
||||
.IsSuccess());
|
||||
ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess());
|
||||
}
|
||||
|
||||
vm_manager.ReprotectRange(*map_address, header.text_size,
|
||||
@@ -380,13 +384,14 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
auto* process = Core::CurrentProcess();
|
||||
auto& vm_manager = process->VMManager();
|
||||
auto& vm_manager = Core::CurrentProcess()->VMManager();
|
||||
const auto& nro_size = iter->second.size;
|
||||
|
||||
ASSERT(process->MirrorMemory(heap_addr, mapped_addr, nro_size,
|
||||
Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
|
||||
ASSERT(process->UnmapMemory(mapped_addr, 0, nro_size) == RESULT_SUCCESS);
|
||||
ASSERT(vm_manager
|
||||
.MirrorMemory(heap_addr, mapped_addr, nro_size,
|
||||
Kernel::MemoryState::ModuleCodeStatic)
|
||||
.IsSuccess());
|
||||
ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess());
|
||||
|
||||
Core::System::GetInstance().InvalidateCpuInstructionCaches();
|
||||
|
||||
|
||||
@@ -317,8 +317,8 @@ private:
|
||||
}
|
||||
|
||||
bool has_attached_handle{};
|
||||
const u64 device_handle{Common::MakeMagic('Y', 'U', 'Z', 'U')};
|
||||
const u32 npad_id{0}; // Player 1 controller
|
||||
const u64 device_handle{0}; // Npad device 1
|
||||
const u32 npad_id{0}; // Player 1 controller
|
||||
State state{State::NonInitialized};
|
||||
DeviceState device_state{DeviceState::Initialized};
|
||||
Kernel::EventPair deactivate_event;
|
||||
|
||||
@@ -145,12 +145,13 @@ void SM::RegisterService(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
const std::string name(name_buf.begin(), end);
|
||||
|
||||
const auto unk_bool = static_cast<bool>(rp.PopRaw<u32>());
|
||||
const auto session_count = rp.PopRaw<u32>();
|
||||
const auto is_light = static_cast<bool>(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()) {
|
||||
LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}",
|
||||
handle.Code().raw);
|
||||
|
||||
@@ -264,14 +264,12 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto unknown_u8 = rp.PopRaw<u8>();
|
||||
|
||||
ClockSnapshot clock_snapshot{};
|
||||
const auto initial_type = rp.PopRaw<u8>();
|
||||
|
||||
const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count()};
|
||||
CalendarTime calendar_time{};
|
||||
|
||||
const std::time_t time(time_since_epoch);
|
||||
const std::tm* tm = std::localtime(&time);
|
||||
if (tm == nullptr) {
|
||||
@@ -280,16 +278,19 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(ResultCode(-1)); // TODO(ogniK): Find appropriate error code
|
||||
return;
|
||||
}
|
||||
SteadyClockTimePoint steady_clock_time_point{CoreTiming::cyclesToMs(CoreTiming::GetTicks()) /
|
||||
1000};
|
||||
|
||||
LocationName location_name{"UTC"};
|
||||
const SteadyClockTimePoint steady_clock_time_point{
|
||||
CoreTiming::cyclesToMs(CoreTiming::GetTicks()) / 1000, {}};
|
||||
|
||||
CalendarTime calendar_time{};
|
||||
calendar_time.year = tm->tm_year + 1900;
|
||||
calendar_time.month = tm->tm_mon + 1;
|
||||
calendar_time.day = tm->tm_mday;
|
||||
calendar_time.hour = tm->tm_hour;
|
||||
calendar_time.minute = tm->tm_min;
|
||||
calendar_time.second = tm->tm_sec;
|
||||
|
||||
ClockSnapshot clock_snapshot{};
|
||||
clock_snapshot.system_posix_time = time_since_epoch;
|
||||
clock_snapshot.network_posix_time = time_since_epoch;
|
||||
clock_snapshot.system_calendar_time = calendar_time;
|
||||
@@ -302,9 +303,10 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
|
||||
clock_snapshot.network_calendar_info = additional_info;
|
||||
|
||||
clock_snapshot.steady_clock_timepoint = steady_clock_time_point;
|
||||
clock_snapshot.location_name = location_name;
|
||||
clock_snapshot.location_name = LocationName{"UTC"};
|
||||
clock_snapshot.clock_auto_adjustment_enabled = 1;
|
||||
clock_snapshot.ipc_u8 = unknown_u8;
|
||||
clock_snapshot.type = initial_type;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot));
|
||||
|
||||
@@ -22,7 +22,6 @@ struct CalendarTime {
|
||||
u8 hour;
|
||||
u8 minute;
|
||||
u8 second;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
};
|
||||
static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime structure has incorrect size");
|
||||
|
||||
@@ -30,7 +29,7 @@ struct CalendarAdditionalInfo {
|
||||
u32_le day_of_week;
|
||||
u32_le day_of_year;
|
||||
std::array<u8, 8> name;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u8 is_dst;
|
||||
s32_le utc_offset;
|
||||
};
|
||||
static_assert(sizeof(CalendarAdditionalInfo) == 0x18,
|
||||
@@ -42,8 +41,10 @@ struct TimeZoneRule {
|
||||
};
|
||||
|
||||
struct SteadyClockTimePoint {
|
||||
using SourceID = std::array<u8, 16>;
|
||||
|
||||
u64_le value;
|
||||
INSERT_PADDING_WORDS(4);
|
||||
SourceID source_id;
|
||||
};
|
||||
static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size");
|
||||
|
||||
@@ -66,8 +67,9 @@ struct ClockSnapshot {
|
||||
SteadyClockTimePoint steady_clock_timepoint;
|
||||
LocationName location_name;
|
||||
u8 clock_auto_adjustment_enabled;
|
||||
u8 ipc_u8;
|
||||
INSERT_PADDING_BYTES(2);
|
||||
u8 type;
|
||||
u8 version;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
};
|
||||
static_assert(sizeof(ClockSnapshot) == 0xd0, "ClockSnapshot is an invalid size");
|
||||
|
||||
|
||||
@@ -32,11 +32,22 @@
|
||||
namespace Service::VI {
|
||||
|
||||
struct DisplayInfo {
|
||||
/// The name of this particular display.
|
||||
char display_name[0x40]{"Default"};
|
||||
u64 unknown_1{1};
|
||||
u64 unknown_2{1};
|
||||
u64 width{1280};
|
||||
u64 height{720};
|
||||
|
||||
/// Whether or not the display has a limited number of layers.
|
||||
u8 has_limited_layers{1};
|
||||
INSERT_PADDING_BYTES(7){};
|
||||
|
||||
/// Indicates the total amount of layers supported by the display.
|
||||
/// @note This is only valid if has_limited_layers is set.
|
||||
u64 max_layers{1};
|
||||
|
||||
/// Maximum width in pixels.
|
||||
u64 width{1920};
|
||||
|
||||
/// Maximum height in pixels.
|
||||
u64 height{1080};
|
||||
};
|
||||
static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
|
||||
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
class NACP;
|
||||
} // namespace FileSys
|
||||
|
||||
namespace Kernel {
|
||||
struct AddressMapping;
|
||||
class Process;
|
||||
@@ -247,11 +251,11 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the developer of the application
|
||||
* @param developer Reference to store the application developer into
|
||||
* Get the control data (CNMT) of the application
|
||||
* @param control Reference to store the application control data into
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadDeveloper(std::string& developer) {
|
||||
virtual ResultStatus ReadControlData(FileSys::NACP& control) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
|
||||
@@ -152,10 +152,10 @@ ResultStatus AppLoader_NSP::ReadTitle(std::string& title) {
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadDeveloper(std::string& developer) {
|
||||
ResultStatus AppLoader_NSP::ReadControlData(FileSys::NACP& nacp) {
|
||||
if (nacp_file == nullptr)
|
||||
return ResultStatus::ErrorNoControl;
|
||||
developer = nacp_file->GetDeveloperName();
|
||||
nacp = *nacp_file;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
} // namespace Loader
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
ResultStatus ReadDeveloper(std::string& developer) override;
|
||||
ResultStatus ReadControlData(FileSys::NACP& nacp) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::NSP> nsp;
|
||||
|
||||
@@ -121,10 +121,11 @@ ResultStatus AppLoader_XCI::ReadTitle(std::string& title) {
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadDeveloper(std::string& developer) {
|
||||
ResultStatus AppLoader_XCI::ReadControlData(FileSys::NACP& control) {
|
||||
if (nacp_file == nullptr)
|
||||
return ResultStatus::ErrorNoControl;
|
||||
developer = nacp_file->GetDeveloperName();
|
||||
control = *nacp_file;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
ResultStatus ReadDeveloper(std::string& developer) override;
|
||||
ResultStatus ReadControlData(FileSys::NACP& control) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::XCI> xci;
|
||||
|
||||
@@ -187,6 +187,7 @@ T Read(const VAddr vaddr) {
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
||||
@@ -164,6 +164,7 @@ public:
|
||||
return 3;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -871,6 +872,7 @@ public:
|
||||
return 4;
|
||||
}
|
||||
UNREACHABLE();
|
||||
return 1;
|
||||
}
|
||||
|
||||
GPUVAddr StartAddress() const {
|
||||
|
||||
@@ -1049,7 +1049,7 @@ union Instruction {
|
||||
BitField<49, 1, u64> nodep_flag;
|
||||
BitField<50, 3, u64> component_mask_selector;
|
||||
BitField<53, 4, u64> texture_info;
|
||||
BitField<60, 1, u64> fp32_flag;
|
||||
BitField<59, 1, u64> fp32_flag;
|
||||
|
||||
TextureType GetTextureType() const {
|
||||
// The TEXS instruction has a weird encoding for the texture type.
|
||||
@@ -1065,6 +1065,7 @@ union Instruction {
|
||||
LOG_CRITICAL(HW_GPU, "Unhandled texture_info: {}",
|
||||
static_cast<u32>(texture_info.Value()));
|
||||
UNREACHABLE();
|
||||
return TextureType::Texture1D;
|
||||
}
|
||||
|
||||
TextureProcessMode GetTextureProcessMode() const {
|
||||
@@ -1145,6 +1146,7 @@ union Instruction {
|
||||
LOG_CRITICAL(HW_GPU, "Unhandled texture_info: {}",
|
||||
static_cast<u32>(texture_info.Value()));
|
||||
UNREACHABLE();
|
||||
return TextureType::Texture1D;
|
||||
}
|
||||
|
||||
TextureProcessMode GetTextureProcessMode() const {
|
||||
|
||||
@@ -102,6 +102,7 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
|
||||
return 1;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast<u32>(format));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +120,7 @@ u32 DepthFormatBytesPerPixel(DepthFormat format) {
|
||||
return 2;
|
||||
default:
|
||||
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:
|
||||
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;
|
||||
}
|
||||
UNREACHABLE();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -66,8 +66,6 @@ static constexpr ConversionArray morton_to_linear_fns = {
|
||||
MortonCopy<true, PixelFormat::BC6H_UF16>,
|
||||
MortonCopy<true, PixelFormat::BC6H_SF16>,
|
||||
MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
|
||||
MortonCopy<true, PixelFormat::G8R8U>,
|
||||
MortonCopy<true, PixelFormat::G8R8S>,
|
||||
MortonCopy<true, PixelFormat::BGRA8>,
|
||||
MortonCopy<true, PixelFormat::RGBA32F>,
|
||||
MortonCopy<true, PixelFormat::RG32F>,
|
||||
@@ -138,8 +136,6 @@ static constexpr ConversionArray linear_to_morton_fns = {
|
||||
MortonCopy<false, PixelFormat::BC6H_SF16>,
|
||||
// TODO(Subv): Swizzling ASTC formats are not supported
|
||||
nullptr,
|
||||
MortonCopy<false, PixelFormat::G8R8U>,
|
||||
MortonCopy<false, PixelFormat::G8R8S>,
|
||||
MortonCopy<false, PixelFormat::BGRA8>,
|
||||
MortonCopy<false, PixelFormat::RGBA32F>,
|
||||
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)];
|
||||
}
|
||||
UNREACHABLE();
|
||||
return morton_to_linear_fns[static_cast<std::size_t>(format)];
|
||||
}
|
||||
|
||||
/// 8x8 Z-Order coordinate from 2D coordinates
|
||||
|
||||
@@ -27,4 +27,16 @@ void RendererBase::UpdateCurrentFramebufferLayout() {
|
||||
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
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <optional>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
|
||||
@@ -21,6 +22,12 @@ namespace VideoCore {
|
||||
struct RendererSettings {
|
||||
std::atomic_bool use_framelimiter{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 {
|
||||
@@ -57,9 +64,29 @@ public:
|
||||
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
|
||||
void RefreshBaseSettings();
|
||||
|
||||
/// Request a screenshot of the next frame
|
||||
void RequestScreenshot(void* data, std::function<void()> callback,
|
||||
const Layout::FramebufferLayout& layout);
|
||||
|
||||
protected:
|
||||
Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle.
|
||||
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.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(),
|
||||
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.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.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
|
||||
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_RGBA16F, GL_RGBA, GL_HALF_FLOAT, ComponentType::Float, false}, // RGBA16F
|
||||
{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,
|
||||
false}, // R11FG11FB10F
|
||||
{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,
|
||||
true}, // BC6H_SF16
|
||||
{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_RGBA32F, GL_RGBA, GL_FLOAT, ComponentType::Float, false}, // RGBA32F
|
||||
{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
|
||||
* 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.
|
||||
ConvertS8Z24ToZ24S8(data, width, height, false);
|
||||
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,
|
||||
u32 width, u32 height) {
|
||||
switch (pixel_format) {
|
||||
case PixelFormat::G8R8U:
|
||||
case PixelFormat::G8R8S:
|
||||
case PixelFormat::ASTC_2D_4X4:
|
||||
case PixelFormat::ASTC_2D_8X8:
|
||||
case PixelFormat::ASTC_2D_4X4_SRGB:
|
||||
|
||||
@@ -67,6 +67,7 @@ public:
|
||||
6, "ShaderTrianglesAdjacency");
|
||||
default:
|
||||
UNREACHABLE_MSG("Unknown primitive mode.");
|
||||
return LazyGeometryProgram(geometry_programs.points, "points", 1, "ShaderPoints");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -347,6 +347,15 @@ public:
|
||||
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.
|
||||
* @param value Value to perform integer size conversion on.
|
||||
@@ -364,6 +373,7 @@ public:
|
||||
return value;
|
||||
default:
|
||||
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 value_num_components Number of components in the 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.
|
||||
*/
|
||||
void SetRegisterToFloat(const Register& reg, u64 elem, const std::string& value,
|
||||
u64 dest_num_components, u64 value_num_components,
|
||||
bool is_saturated = false, u64 dest_elem = 0, bool precise = false) {
|
||||
|
||||
SetRegister(reg, elem, is_saturated ? "clamp(" + value + ", 0.0, 1.0)" : value,
|
||||
dest_num_components, value_num_components, dest_elem, precise);
|
||||
bool is_saturated = false, bool sets_cc = false, u64 dest_elem = 0,
|
||||
bool precise = false) {
|
||||
const std::string clamped_value = is_saturated ? "clamp(" + value + ", 0.0, 1.0)" : value;
|
||||
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 value_num_components Number of components in the 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 size Register size to use for conversion instructions.
|
||||
*/
|
||||
void SetRegisterToInteger(const Register& reg, bool is_signed, u64 elem,
|
||||
const std::string& value, u64 dest_num_components,
|
||||
u64 value_num_components, bool is_saturated = false,
|
||||
u64 dest_elem = 0, Register::Size size = Register::Size::Word,
|
||||
bool sets_cc = false) {
|
||||
bool sets_cc = false, u64 dest_elem = 0,
|
||||
Register::Size size = Register::Size::Word) {
|
||||
UNIMPLEMENTED_IF(is_saturated);
|
||||
|
||||
const std::string final_value = ConvertIntegerSize(value, size);
|
||||
const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"};
|
||||
|
||||
SetRegister(reg, elem, func + '(' + ConvertIntegerSize(value, size) + ')',
|
||||
dest_num_components, value_num_components, dest_elem, false);
|
||||
SetRegister(reg, elem, func + '(' + final_value + ')', dest_num_components,
|
||||
value_num_components, dest_elem, false);
|
||||
|
||||
if (sets_cc) {
|
||||
const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )";
|
||||
SetInternalFlag(InternalFlag::ZeroFlag, zero_condition);
|
||||
LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete.");
|
||||
if (reg == Register::ZeroIndex) {
|
||||
SetConditionalCodesFromExpression(final_value);
|
||||
} 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
|
||||
// of the time when a Mrg_* flag is used both components will be mirrored. That
|
||||
// being said, it deserves a test.
|
||||
return "((" + GetRegisterAsInteger(reg, 0, false) +
|
||||
return "uintBitsToFloat((" + GetRegisterAsInteger(reg, 0, false) +
|
||||
" & 0xffff0000) | (packHalf2x16(" + value + ") & 0x0000ffff))";
|
||||
case Tegra::Shader::HalfMerge::Mrg_H1:
|
||||
return "((" + GetRegisterAsInteger(reg, 0, false) +
|
||||
return "uintBitsToFloat((" + GetRegisterAsInteger(reg, 0, false) +
|
||||
" & 0x0000ffff) | (packHalf2x16(" + value + ") & 0xffff0000))";
|
||||
default:
|
||||
UNREACHABLE();
|
||||
@@ -626,6 +650,7 @@ public:
|
||||
return "floatBitsToInt(" + value + ')';
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1273,7 +1298,7 @@ private:
|
||||
void WriteLogicOperation(Register dest, LogicOperation logic_op, const std::string& op_a,
|
||||
const std::string& op_b,
|
||||
Tegra::Shader::PredicateResultMode predicate_mode,
|
||||
Tegra::Shader::Pred predicate) {
|
||||
Tegra::Shader::Pred predicate, const bool set_cc) {
|
||||
std::string result{};
|
||||
switch (logic_op) {
|
||||
case LogicOperation::And: {
|
||||
@@ -1297,7 +1322,7 @@ private:
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -1317,7 +1342,8 @@ private:
|
||||
}
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
@@ -1340,7 +1366,7 @@ private:
|
||||
|
||||
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) {
|
||||
@@ -1355,12 +1381,12 @@ private:
|
||||
|
||||
if (written_components < 2) {
|
||||
// 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);
|
||||
} else {
|
||||
ASSERT(instr.texs.HasTwoDestinations());
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -1591,23 +1617,21 @@ private:
|
||||
process_mode == Tegra::Shader::TextureProcessMode::LL ||
|
||||
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 = !(
|
||||
(texture_type == Tegra::Shader::TextureType::Texture2D && is_array && depth_compare) ||
|
||||
(texture_type == Tegra::Shader::TextureType::TextureCube && !is_array &&
|
||||
depth_compare));
|
||||
(texture_type == Tegra::Shader::TextureType::TextureCube && is_array && depth_compare));
|
||||
|
||||
const std::string read_method = lod_needed && gl_lod_supported ? "textureLod(" : "texture(";
|
||||
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 (gl_lod_supported) {
|
||||
texture += ", 0";
|
||||
} else {
|
||||
// Lod 0 is emulated by a big negative bias
|
||||
// in scenarios that are not supported by glsl
|
||||
texture += ", -1000";
|
||||
}
|
||||
texture += ", 0.0";
|
||||
} else {
|
||||
// If present, lod or bias are always stored in the register indexed by the
|
||||
// gpr20
|
||||
@@ -1645,15 +1669,15 @@ private:
|
||||
if (depth_compare && !is_array && texture_type == Tegra::Shader::TextureType::Texture1D) {
|
||||
coord += ",0.0";
|
||||
}
|
||||
if (is_array) {
|
||||
coord += ',' + regs.GetRegisterAsInteger(array_register);
|
||||
}
|
||||
if (depth_compare) {
|
||||
// Depth is always stored in the register signaled by gpr20
|
||||
// or in the next register if lod or bias are used
|
||||
const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0);
|
||||
coord += ',' + regs.GetRegisterAsFloat(depth_register);
|
||||
}
|
||||
if (is_array) {
|
||||
coord += ',' + regs.GetRegisterAsInteger(array_register);
|
||||
}
|
||||
coord += ");";
|
||||
return std::make_pair(
|
||||
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) {
|
||||
// Depth is always stored in the register signaled by gpr20
|
||||
// or in the next register if lod or bias are used
|
||||
const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0);
|
||||
coord += ',' + regs.GetRegisterAsFloat(depth_register);
|
||||
}
|
||||
if (is_array) {
|
||||
coord += ',' + regs.GetRegisterAsInteger(array_register);
|
||||
}
|
||||
coord += ");";
|
||||
|
||||
return std::make_pair(coord,
|
||||
@@ -1755,7 +1779,7 @@ private:
|
||||
instr.tlds.GetTextureProcessMode() == Tegra::Shader::TextureProcessMode::LL;
|
||||
|
||||
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];
|
||||
|
||||
@@ -1871,8 +1895,6 @@ private:
|
||||
instr.fmul.tab5c68_0 != 1, "FMUL tab5cb8_0({}) is not implemented",
|
||||
instr.fmul.tab5c68_0
|
||||
.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);
|
||||
|
||||
@@ -1896,20 +1918,17 @@ private:
|
||||
}
|
||||
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b + postfactor_op, 1, 1,
|
||||
instr.alu.saturate_d, 0, true);
|
||||
instr.alu.saturate_d, instr.generates_cc, 0, true);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::FADD_C:
|
||||
case OpCode::Id::FADD_R:
|
||||
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_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b);
|
||||
|
||||
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;
|
||||
}
|
||||
case OpCode::Id::MUFU: {
|
||||
@@ -1917,31 +1936,31 @@ private:
|
||||
switch (instr.sub_op) {
|
||||
case SubOp::Cos:
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, "cos(" + op_a + ')', 1, 1,
|
||||
instr.alu.saturate_d, 0, true);
|
||||
instr.alu.saturate_d, false, 0, true);
|
||||
break;
|
||||
case SubOp::Sin:
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, "sin(" + op_a + ')', 1, 1,
|
||||
instr.alu.saturate_d, 0, true);
|
||||
instr.alu.saturate_d, false, 0, true);
|
||||
break;
|
||||
case SubOp::Ex2:
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, "exp2(" + op_a + ')', 1, 1,
|
||||
instr.alu.saturate_d, 0, true);
|
||||
instr.alu.saturate_d, false, 0, true);
|
||||
break;
|
||||
case SubOp::Lg2:
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, "log2(" + op_a + ')', 1, 1,
|
||||
instr.alu.saturate_d, 0, true);
|
||||
instr.alu.saturate_d, false, 0, true);
|
||||
break;
|
||||
case SubOp::Rcp:
|
||||
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;
|
||||
case SubOp::Rsq:
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, "inversesqrt(" + op_a + ')', 1, 1,
|
||||
instr.alu.saturate_d, 0, true);
|
||||
instr.alu.saturate_d, false, 0, true);
|
||||
break;
|
||||
case SubOp::Sqrt:
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, "sqrt(" + op_a + ')', 1, 1,
|
||||
instr.alu.saturate_d, 0, true);
|
||||
instr.alu.saturate_d, false, 0, true);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unhandled MUFU sub op={0:x}",
|
||||
@@ -1952,8 +1971,9 @@ private:
|
||||
case OpCode::Id::FMNMX_C:
|
||||
case OpCode::Id::FMNMX_R:
|
||||
case OpCode::Id::FMNMX_IMM: {
|
||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
||||
"Condition codes generation in FMNMX is not implemented");
|
||||
UNIMPLEMENTED_IF_MSG(
|
||||
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_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b);
|
||||
@@ -1964,7 +1984,7 @@ private:
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0,
|
||||
'(' + condition + ") ? min(" + parameters + ") : max(" +
|
||||
parameters + ')',
|
||||
1, 1, false, 0, true);
|
||||
1, 1, false, instr.generates_cc, 0, true);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::RRO_C:
|
||||
@@ -1989,18 +2009,16 @@ private:
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::FMUL32_IMM: {
|
||||
UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
|
||||
"Condition codes generation in FMUL32 is not implemented");
|
||||
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0,
|
||||
regs.GetRegisterAsFloat(instr.gpr8) + " * " +
|
||||
GetImmediate32(instr),
|
||||
1, 1, instr.fmul32.saturate, 0, true);
|
||||
regs.SetRegisterToFloat(
|
||||
instr.gpr0, 0,
|
||||
regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1,
|
||||
instr.fmul32.saturate, instr.op_32.generates_cc, 0, true);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::FADD32I: {
|
||||
UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
|
||||
"Condition codes generation in FADD32I is not implemented");
|
||||
UNIMPLEMENTED_IF_MSG(
|
||||
instr.op_32.generates_cc,
|
||||
"Condition codes generation in FADD32I is partially implemented");
|
||||
|
||||
std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
std::string op_b = GetImmediate32(instr);
|
||||
@@ -2021,7 +2039,8 @@ private:
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -2035,16 +2054,14 @@ private:
|
||||
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::BFE_IMM: {
|
||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
||||
"Condition codes generation in BFE is not implemented");
|
||||
|
||||
std::string inner_shift =
|
||||
'(' + op_a + " << " + std::to_string(instr.bfe.GetLeftShiftValue()) + ')';
|
||||
std::string outer_shift =
|
||||
'(' + inner_shift + " >> " +
|
||||
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;
|
||||
}
|
||||
default: {
|
||||
@@ -2055,8 +2072,6 @@ private:
|
||||
break;
|
||||
}
|
||||
case OpCode::Type::Bfi: {
|
||||
UNIMPLEMENTED_IF(instr.generates_cc);
|
||||
|
||||
const auto [base, packed_shift] = [&]() -> std::tuple<std::string, std::string> {
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::BFI_IMM_R:
|
||||
@@ -2064,14 +2079,17 @@ private:
|
||||
std::to_string(instr.alu.GetSignedImm20_20())};
|
||||
default:
|
||||
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 bits = "((" + packed_shift + " >> 8) & 0xff)";
|
||||
const std::string insert = regs.GetRegisterAsInteger(instr.gpr8, 0, false);
|
||||
regs.SetRegisterToInteger(
|
||||
instr.gpr0, false, 0,
|
||||
"bitfieldInsert(" + base + ", " + insert + ", " + offset + ", " + bits + ')', 1, 1);
|
||||
regs.SetRegisterToInteger(instr.gpr0, false, 0,
|
||||
"bitfieldInsert(" + base + ", " + insert + ", " + offset +
|
||||
", " + bits + ')',
|
||||
1, 1, false, instr.generates_cc);
|
||||
break;
|
||||
}
|
||||
case OpCode::Type::Shift: {
|
||||
@@ -2093,9 +2111,6 @@ private:
|
||||
case OpCode::Id::SHR_C:
|
||||
case OpCode::Id::SHR_R:
|
||||
case OpCode::Id::SHR_IMM: {
|
||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
||||
"Condition codes generation in SHR is not implemented");
|
||||
|
||||
if (!instr.shift.is_signed) {
|
||||
// Logical shift right
|
||||
op_a = "uint(" + op_a + ')';
|
||||
@@ -2103,7 +2118,7 @@ private:
|
||||
|
||||
// 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 + ')',
|
||||
1, 1);
|
||||
1, 1, false, instr.generates_cc);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::SHL_C:
|
||||
@@ -2111,7 +2126,8 @@ private:
|
||||
case OpCode::Id::SHL_IMM:
|
||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
||||
"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;
|
||||
default: {
|
||||
UNIMPLEMENTED_MSG("Unhandled shift instruction: {}", opcode->get().GetName());
|
||||
@@ -2125,18 +2141,17 @@ private:
|
||||
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::IADD32I:
|
||||
UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
|
||||
"Condition codes generation in IADD32I is not implemented");
|
||||
UNIMPLEMENTED_IF_MSG(
|
||||
instr.op_32.generates_cc,
|
||||
"Condition codes generation in IADD32I is partially implemented");
|
||||
|
||||
if (instr.iadd32i.negate_a)
|
||||
op_a = "-(" + op_a + ')';
|
||||
|
||||
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;
|
||||
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)
|
||||
op_a = "~(" + op_a + ')';
|
||||
@@ -2146,7 +2161,7 @@ private:
|
||||
|
||||
WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b,
|
||||
Tegra::Shader::PredicateResultMode::None,
|
||||
Tegra::Shader::Pred::UnusedIndex);
|
||||
Tegra::Shader::Pred::UnusedIndex, instr.op_32.generates_cc);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -2175,7 +2190,7 @@ private:
|
||||
case OpCode::Id::IADD_R:
|
||||
case OpCode::Id::IADD_IMM: {
|
||||
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)
|
||||
op_a = "-(" + op_a + ')';
|
||||
@@ -2184,14 +2199,15 @@ private:
|
||||
op_b = "-(" + op_b + ')';
|
||||
|
||||
regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
|
||||
instr.alu.saturate_d);
|
||||
instr.alu.saturate_d, instr.generates_cc);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::IADD3_C:
|
||||
case OpCode::Id::IADD3_R:
|
||||
case OpCode::Id::IADD3_IMM: {
|
||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
||||
"Condition codes generation in IADD3 is not implemented");
|
||||
UNIMPLEMENTED_IF_MSG(
|
||||
instr.generates_cc,
|
||||
"Condition codes generation in IADD3 is partially implemented");
|
||||
|
||||
std::string op_c = regs.GetRegisterAsInteger(instr.gpr39);
|
||||
|
||||
@@ -2247,14 +2263,16 @@ private:
|
||||
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;
|
||||
}
|
||||
case OpCode::Id::ISCADD_C:
|
||||
case OpCode::Id::ISCADD_R:
|
||||
case OpCode::Id::ISCADD_IMM: {
|
||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
||||
"Condition codes generation in ISCADD is not implemented");
|
||||
UNIMPLEMENTED_IF_MSG(
|
||||
instr.generates_cc,
|
||||
"Condition codes generation in ISCADD is partially implemented");
|
||||
|
||||
if (instr.alu_integer.negate_a)
|
||||
op_a = "-(" + op_a + ')';
|
||||
@@ -2265,7 +2283,8 @@ private:
|
||||
const std::string shift = std::to_string(instr.alu_integer.shift_amount.Value());
|
||||
|
||||
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;
|
||||
}
|
||||
case OpCode::Id::POPC_C:
|
||||
@@ -2289,8 +2308,6 @@ private:
|
||||
case OpCode::Id::LOP_C:
|
||||
case OpCode::Id::LOP_R:
|
||||
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)
|
||||
op_a = "~(" + op_a + ')';
|
||||
@@ -2299,15 +2316,13 @@ private:
|
||||
op_b = "~(" + 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;
|
||||
}
|
||||
case OpCode::Id::LOP3_C:
|
||||
case OpCode::Id::LOP3_R:
|
||||
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);
|
||||
std::string lut;
|
||||
|
||||
@@ -2317,15 +2332,16 @@ private:
|
||||
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;
|
||||
}
|
||||
case OpCode::Id::IMNMX_C:
|
||||
case OpCode::Id::IMNMX_R:
|
||||
case OpCode::Id::IMNMX_IMM: {
|
||||
UNIMPLEMENTED_IF(instr.imnmx.exchange != Tegra::Shader::IMinMaxExchange::None);
|
||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
||||
"Condition codes generation in IMNMX is not implemented");
|
||||
UNIMPLEMENTED_IF_MSG(
|
||||
instr.generates_cc,
|
||||
"Condition codes generation in IMNMX is partially implemented");
|
||||
|
||||
const std::string condition =
|
||||
GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0);
|
||||
@@ -2333,7 +2349,7 @@ private:
|
||||
regs.SetRegisterToInteger(instr.gpr0, instr.imnmx.is_signed, 0,
|
||||
'(' + condition + ") ? min(" + parameters + ") : max(" +
|
||||
parameters + ')',
|
||||
1, 1);
|
||||
1, 1, false, instr.generates_cc);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::LEA_R2:
|
||||
@@ -2394,7 +2410,8 @@ private:
|
||||
UNIMPLEMENTED_IF_MSG(instr.lea.pred48 != static_cast<u64>(Pred::UnusedIndex),
|
||||
"Unhandled LEA Predicate");
|
||||
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;
|
||||
}
|
||||
@@ -2499,7 +2516,7 @@ private:
|
||||
UNIMPLEMENTED_IF_MSG(instr.ffma.tab5980_1 != 0, "FFMA tab5980_1({}) not implemented",
|
||||
instr.ffma.tab5980_1.Value());
|
||||
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()) {
|
||||
case OpCode::Id::FFMA_CR: {
|
||||
@@ -2530,7 +2547,7 @@ private:
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
case OpCode::Type::Hfma2: {
|
||||
@@ -2601,16 +2618,14 @@ private:
|
||||
}
|
||||
|
||||
regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
|
||||
1, instr.alu.saturate_d, 0, instr.conversion.dest_size,
|
||||
instr.generates_cc.Value() != 0);
|
||||
1, instr.alu.saturate_d, instr.generates_cc, 0,
|
||||
instr.conversion.dest_size);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::I2F_R:
|
||||
case OpCode::Id::I2F_C: {
|
||||
UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word);
|
||||
UNIMPLEMENTED_IF(instr.conversion.selector);
|
||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
||||
"Condition codes generation in I2F is not implemented");
|
||||
std::string op_a;
|
||||
|
||||
if (instr.is_b_gpr) {
|
||||
@@ -2633,14 +2648,12 @@ private:
|
||||
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;
|
||||
}
|
||||
case OpCode::Id::F2F_R: {
|
||||
UNIMPLEMENTED_IF(instr.conversion.dest_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);
|
||||
|
||||
if (instr.conversion.abs_a) {
|
||||
@@ -2672,14 +2685,13 @@ private:
|
||||
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;
|
||||
}
|
||||
case OpCode::Id::F2I_R:
|
||||
case OpCode::Id::F2I_C: {
|
||||
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{};
|
||||
|
||||
if (instr.is_b_gpr) {
|
||||
@@ -2722,7 +2734,8 @@ private:
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
default: {
|
||||
@@ -2885,7 +2898,7 @@ private:
|
||||
shader.AddLine(coord);
|
||||
|
||||
if (depth_compare) {
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false);
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1);
|
||||
} else {
|
||||
shader.AddLine("vec4 texture_tmp = " + texture + ';');
|
||||
std::size_t dest_elem{};
|
||||
@@ -2894,7 +2907,7 @@ private:
|
||||
// Skip disabled components
|
||||
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;
|
||||
}
|
||||
@@ -2980,7 +2993,7 @@ private:
|
||||
// Skip disabled components
|
||||
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;
|
||||
}
|
||||
@@ -3229,7 +3242,7 @@ private:
|
||||
}
|
||||
case OpCode::Type::PredicateSetRegister: {
|
||||
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 =
|
||||
GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0);
|
||||
@@ -3246,10 +3259,11 @@ private:
|
||||
const std::string result = '(' + predicate + ") " + combiner + " (" + second_pred + ')';
|
||||
if (instr.pset.bf == 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 {
|
||||
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;
|
||||
}
|
||||
@@ -3314,6 +3328,7 @@ private:
|
||||
return std::to_string(instr.r2p.immediate_mask);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return std::to_string(instr.r2p.immediate_mask);
|
||||
}
|
||||
}();
|
||||
const std::string mask = '(' + regs.GetRegisterAsInteger(instr.gpr8, 0, false) +
|
||||
@@ -3365,14 +3380,11 @@ private:
|
||||
") " + combiner + " (" + second_pred + "))";
|
||||
|
||||
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 {
|
||||
regs.SetRegisterToInteger(instr.gpr0, false, 0, predicate + " ? 0xFFFFFFFF : 0", 1,
|
||||
1);
|
||||
}
|
||||
if (instr.generates_cc.Value() != 0) {
|
||||
regs.SetInternalFlag(InternalFlag::ZeroFlag, predicate);
|
||||
LOG_WARNING(HW_GPU, "FSET Condition Code is incomplete");
|
||||
1, false, instr.generates_cc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -3459,7 +3471,7 @@ private:
|
||||
UNIMPLEMENTED_IF(instr.xmad.sign_a);
|
||||
UNIMPLEMENTED_IF(instr.xmad.sign_b);
|
||||
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_b;
|
||||
@@ -3545,7 +3557,8 @@ private:
|
||||
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;
|
||||
}
|
||||
default: {
|
||||
@@ -3749,8 +3762,7 @@ private:
|
||||
}
|
||||
|
||||
regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1,
|
||||
instr.vmad.saturate == 1, 0, Register::Size::Word,
|
||||
instr.vmad.cc);
|
||||
instr.vmad.saturate, instr.vmad.cc);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::VSETP: {
|
||||
@@ -3777,7 +3789,10 @@ private:
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: { UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName()); }
|
||||
default: {
|
||||
UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -3932,4 +3947,4 @@ std::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace OpenGL::GLShader::Decompiler
|
||||
} // namespace OpenGL::GLShader::Decompiler
|
||||
|
||||
@@ -138,7 +138,12 @@ void RendererOpenGL::SwapBuffers(
|
||||
|
||||
// Load the framebuffer from memory, draw it to the screen, and swap buffers
|
||||
LoadFBToScreenInfo(*framebuffer);
|
||||
DrawScreen();
|
||||
|
||||
if (renderer_settings.screenshot_requested)
|
||||
CaptureScreenshot();
|
||||
|
||||
DrawScreen(render_window.GetFramebufferLayout());
|
||||
|
||||
render_window.SwapBuffers();
|
||||
}
|
||||
|
||||
@@ -383,14 +388,13 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
|
||||
/**
|
||||
* Draws the emulated screens to the emulator window.
|
||||
*/
|
||||
void RendererOpenGL::DrawScreen() {
|
||||
void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
|
||||
if (renderer_settings.set_background_color) {
|
||||
// Update background color before drawing
|
||||
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
||||
0.0f);
|
||||
}
|
||||
|
||||
const auto& layout = render_window.GetFramebufferLayout();
|
||||
const auto& screen = layout.screen;
|
||||
|
||||
glViewport(0, 0, layout.width, layout.height);
|
||||
@@ -414,6 +418,37 @@ void RendererOpenGL::DrawScreen() {
|
||||
/// Updates the framerate
|
||||
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) {
|
||||
#define RET(s) \
|
||||
case GL_DEBUG_SOURCE_##s: \
|
||||
@@ -427,6 +462,7 @@ static const char* GetSource(GLenum source) {
|
||||
RET(OTHER);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return "Unknown source";
|
||||
}
|
||||
#undef RET
|
||||
}
|
||||
@@ -445,6 +481,7 @@ static const char* GetType(GLenum type) {
|
||||
RET(MARKER);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return "Unknown type";
|
||||
}
|
||||
#undef RET
|
||||
}
|
||||
|
||||
@@ -16,6 +16,10 @@ namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
}
|
||||
|
||||
namespace Layout {
|
||||
struct FramebufferLayout;
|
||||
}
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
/// Structure used for storing information about the textures for the Switch screen
|
||||
@@ -66,10 +70,12 @@ private:
|
||||
|
||||
void ConfigureFramebufferTexture(TextureInfo& texture,
|
||||
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 UpdateFramerate();
|
||||
|
||||
void CaptureScreenshot();
|
||||
|
||||
// Loads framebuffer from emulated memory into the display information structure
|
||||
void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer);
|
||||
// Fills active OpenGL texture with the given RGBA color.
|
||||
@@ -82,6 +88,7 @@ private:
|
||||
OGLVertexArray vertex_array;
|
||||
OGLBuffer vertex_buffer;
|
||||
OGLProgram shader;
|
||||
OGLFramebuffer screenshot_framebuffer;
|
||||
|
||||
/// Display information for Switch screen
|
||||
ScreenInfo screen_info;
|
||||
|
||||
@@ -65,6 +65,7 @@ PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) {
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
return PixelFormat::S8Z24;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +142,7 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format)
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
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));
|
||||
UNREACHABLE();
|
||||
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) {
|
||||
case Tegra::Texture::ComponentType::UNORM:
|
||||
return PixelFormat::G8R8U;
|
||||
return PixelFormat::RG8U;
|
||||
case Tegra::Texture::ComponentType::SNORM:
|
||||
return PixelFormat::G8R8S;
|
||||
return PixelFormat::RG8S;
|
||||
}
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
|
||||
UNREACHABLE();
|
||||
@@ -327,6 +332,7 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}", static_cast<u32>(format),
|
||||
static_cast<u32>(component_type));
|
||||
UNREACHABLE();
|
||||
return PixelFormat::ABGR8U;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,6 +352,7 @@ ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) {
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast<u32>(type));
|
||||
UNREACHABLE();
|
||||
return ComponentType::UNorm;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,6 +400,7 @@ ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format) {
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
return ComponentType::UNorm;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,6 +411,7 @@ PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
return PixelFormat::ABGR8U;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,6 +427,7 @@ ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format) {
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
return ComponentType::UNorm;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,57 +38,55 @@ enum class PixelFormat {
|
||||
BC6H_UF16 = 20,
|
||||
BC6H_SF16 = 21,
|
||||
ASTC_2D_4X4 = 22,
|
||||
G8R8U = 23,
|
||||
G8R8S = 24,
|
||||
BGRA8 = 25,
|
||||
RGBA32F = 26,
|
||||
RG32F = 27,
|
||||
R32F = 28,
|
||||
R16F = 29,
|
||||
R16U = 30,
|
||||
R16S = 31,
|
||||
R16UI = 32,
|
||||
R16I = 33,
|
||||
RG16 = 34,
|
||||
RG16F = 35,
|
||||
RG16UI = 36,
|
||||
RG16I = 37,
|
||||
RG16S = 38,
|
||||
RGB32F = 39,
|
||||
RGBA8_SRGB = 40,
|
||||
RG8U = 41,
|
||||
RG8S = 42,
|
||||
RG32UI = 43,
|
||||
R32UI = 44,
|
||||
ASTC_2D_8X8 = 45,
|
||||
ASTC_2D_8X5 = 46,
|
||||
ASTC_2D_5X4 = 47,
|
||||
BGRA8_SRGB = 48,
|
||||
DXT1_SRGB = 49,
|
||||
DXT23_SRGB = 50,
|
||||
DXT45_SRGB = 51,
|
||||
BC7U_SRGB = 52,
|
||||
ASTC_2D_4X4_SRGB = 53,
|
||||
ASTC_2D_8X8_SRGB = 54,
|
||||
ASTC_2D_8X5_SRGB = 55,
|
||||
ASTC_2D_5X4_SRGB = 56,
|
||||
ASTC_2D_5X5 = 57,
|
||||
ASTC_2D_5X5_SRGB = 58,
|
||||
ASTC_2D_10X8 = 59,
|
||||
ASTC_2D_10X8_SRGB = 60,
|
||||
BGRA8 = 23,
|
||||
RGBA32F = 24,
|
||||
RG32F = 25,
|
||||
R32F = 26,
|
||||
R16F = 27,
|
||||
R16U = 28,
|
||||
R16S = 29,
|
||||
R16UI = 30,
|
||||
R16I = 31,
|
||||
RG16 = 32,
|
||||
RG16F = 33,
|
||||
RG16UI = 34,
|
||||
RG16I = 35,
|
||||
RG16S = 36,
|
||||
RGB32F = 37,
|
||||
RGBA8_SRGB = 38,
|
||||
RG8U = 39,
|
||||
RG8S = 40,
|
||||
RG32UI = 41,
|
||||
R32UI = 42,
|
||||
ASTC_2D_8X8 = 43,
|
||||
ASTC_2D_8X5 = 44,
|
||||
ASTC_2D_5X4 = 45,
|
||||
BGRA8_SRGB = 46,
|
||||
DXT1_SRGB = 47,
|
||||
DXT23_SRGB = 48,
|
||||
DXT45_SRGB = 49,
|
||||
BC7U_SRGB = 50,
|
||||
ASTC_2D_4X4_SRGB = 51,
|
||||
ASTC_2D_8X8_SRGB = 52,
|
||||
ASTC_2D_8X5_SRGB = 53,
|
||||
ASTC_2D_5X4_SRGB = 54,
|
||||
ASTC_2D_5X5 = 55,
|
||||
ASTC_2D_5X5_SRGB = 56,
|
||||
ASTC_2D_10X8 = 57,
|
||||
ASTC_2D_10X8_SRGB = 58,
|
||||
|
||||
MaxColorFormat,
|
||||
|
||||
// Depth formats
|
||||
Z32F = 61,
|
||||
Z16 = 62,
|
||||
Z32F = 59,
|
||||
Z16 = 60,
|
||||
|
||||
MaxDepthFormat,
|
||||
|
||||
// DepthStencil formats
|
||||
Z24S8 = 63,
|
||||
S8Z24 = 64,
|
||||
Z32FS8 = 65,
|
||||
Z24S8 = 61,
|
||||
S8Z24 = 62,
|
||||
Z32FS8 = 63,
|
||||
|
||||
MaxDepthStencilFormat,
|
||||
|
||||
@@ -149,8 +147,6 @@ constexpr std::array<u32, MaxPixelFormat> compression_factor_table = {{
|
||||
4, // BC6H_UF16
|
||||
4, // BC6H_SF16
|
||||
4, // ASTC_2D_4X4
|
||||
1, // G8R8U
|
||||
1, // G8R8S
|
||||
1, // BGRA8
|
||||
1, // RGBA32F
|
||||
1, // RG32F
|
||||
@@ -232,8 +228,6 @@ constexpr std::array<u32, MaxPixelFormat> block_width_table = {{
|
||||
4, // BC6H_UF16
|
||||
4, // BC6H_SF16
|
||||
4, // ASTC_2D_4X4
|
||||
1, // G8R8U
|
||||
1, // G8R8S
|
||||
1, // BGRA8
|
||||
1, // RGBA32F
|
||||
1, // RG32F
|
||||
@@ -309,8 +303,6 @@ constexpr std::array<u32, MaxPixelFormat> block_height_table = {{
|
||||
4, // BC6H_UF16
|
||||
4, // BC6H_SF16
|
||||
4, // ASTC_2D_4X4
|
||||
1, // G8R8U
|
||||
1, // G8R8S
|
||||
1, // BGRA8
|
||||
1, // RGBA32F
|
||||
1, // RG32F
|
||||
@@ -386,8 +378,6 @@ constexpr std::array<u32, MaxPixelFormat> bpp_table = {{
|
||||
128, // BC6H_UF16
|
||||
128, // BC6H_SF16
|
||||
128, // ASTC_2D_4X4
|
||||
16, // G8R8U
|
||||
16, // G8R8S
|
||||
32, // BGRA8
|
||||
128, // RGBA32F
|
||||
64, // RG32F
|
||||
|
||||
@@ -226,7 +226,7 @@ u32 BytesPerPixel(TextureFormat format) {
|
||||
return 8;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Format not implemented");
|
||||
break;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/renderer_opengl/renderer_opengl.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);
|
||||
}
|
||||
|
||||
u16 GetResolutionScaleFactor(const RendererBase& renderer) {
|
||||
return !Settings::values.resolution_factor
|
||||
? renderer.GetRenderWindow().GetFramebufferLayout().GetScalingRatio()
|
||||
: Settings::values.resolution_factor;
|
||||
}
|
||||
|
||||
} // namespace VideoCore
|
||||
|
||||
@@ -22,4 +22,6 @@ class RendererBase;
|
||||
*/
|
||||
std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window);
|
||||
|
||||
u16 GetResolutionScaleFactor(const RendererBase& renderer);
|
||||
|
||||
} // namespace VideoCore
|
||||
|
||||
@@ -7,6 +7,8 @@ add_executable(yuzu
|
||||
Info.plist
|
||||
about_dialog.cpp
|
||||
about_dialog.h
|
||||
applets/profile_select.cpp
|
||||
applets/profile_select.h
|
||||
applets/software_keyboard.cpp
|
||||
applets/software_keyboard.h
|
||||
bootmanager.cpp
|
||||
@@ -31,6 +33,8 @@ add_executable(yuzu
|
||||
configuration/configure_input.h
|
||||
configuration/configure_input_player.cpp
|
||||
configuration/configure_input_player.h
|
||||
configuration/configure_input_simple.cpp
|
||||
configuration/configure_input_simple.h
|
||||
configuration/configure_mouse_advanced.cpp
|
||||
configuration/configure_mouse_advanced.h
|
||||
configuration/configure_system.cpp
|
||||
@@ -87,6 +91,7 @@ set(UIS
|
||||
configuration/configure_graphics.ui
|
||||
configuration/configure_input.ui
|
||||
configuration/configure_input_player.ui
|
||||
configuration/configure_input_simple.ui
|
||||
configuration/configure_mouse_advanced.ui
|
||||
configuration/configure_per_general.ui
|
||||
configuration/configure_system.ui
|
||||
|
||||
168
src/yuzu/applets/profile_select.cpp
Normal file
168
src/yuzu/applets/profile_select.cpp
Normal file
@@ -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);
|
||||
}
|
||||
73
src/yuzu/applets/profile_select.h
Normal file
73
src/yuzu/applets/profile_select.h
Normal file
@@ -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/main.h"
|
||||
#include "input_common/motion_emu.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
#include "yuzu/bootmanager.h"
|
||||
|
||||
EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {}
|
||||
@@ -333,6 +335,22 @@ void GRenderWindow::InitRenderTarget() {
|
||||
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(
|
||||
const std::pair<unsigned, unsigned>& minimal_size) {
|
||||
setMinimumSize(minimal_size.first, minimal_size.second);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <QGLWidget>
|
||||
#include <QImage>
|
||||
#include <QThread>
|
||||
#include "common/thread.h"
|
||||
#include "core/core.h"
|
||||
@@ -139,6 +140,8 @@ public:
|
||||
|
||||
void InitRenderTarget();
|
||||
|
||||
void CaptureScreenshot(u16 res_scale, const QString& screenshot_path);
|
||||
|
||||
public slots:
|
||||
void moveContext(); // overridden
|
||||
|
||||
@@ -165,6 +168,9 @@ private:
|
||||
|
||||
EmuThread* emu_thread;
|
||||
|
||||
/// Temporary storage of the screenshot taken
|
||||
QImage screenshot_image;
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* event) override;
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <QSettings>
|
||||
#include "common/file_util.h"
|
||||
#include "configure_input_simple.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/hid/controllers/npad.h"
|
||||
#include "input_common/main.h"
|
||||
@@ -339,6 +340,13 @@ void Config::ReadTouchscreenValues() {
|
||||
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() {
|
||||
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.enable_discord_presence =
|
||||
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");
|
||||
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.callout_flags = qt_config->value("calloutFlags", 0).toUInt();
|
||||
UISettings::values.show_console = qt_config->value("showConsole", false).toBool();
|
||||
UISettings::values.profile_index = qt_config->value("profileIndex", 0).toUInt();
|
||||
|
||||
ApplyDefaultProfileIfInputInvalid();
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -678,6 +691,8 @@ void Config::SaveValues() {
|
||||
qt_config->beginGroup("UI");
|
||||
qt_config->setValue("theme", UISettings::values.theme);
|
||||
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->setValue("show_unknown", UISettings::values.show_unknown);
|
||||
@@ -699,6 +714,7 @@ void Config::SaveValues() {
|
||||
qt_config->beginGroup("Paths");
|
||||
qt_config->setValue("romsPath", UISettings::values.roms_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("gameListDeepScan", UISettings::values.gamedir_deepscan);
|
||||
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("calloutFlags", UISettings::values.callout_flags);
|
||||
qt_config->setValue("showConsole", UISettings::values.show_console);
|
||||
qt_config->setValue("profileIndex", UISettings::values.profile_index);
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ private:
|
||||
void ReadKeyboardValues();
|
||||
void ReadMouseValues();
|
||||
void ReadTouchscreenValues();
|
||||
void ApplyDefaultProfileIfInputInvalid();
|
||||
|
||||
void SaveValues();
|
||||
void SavePlayerValues();
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>461</width>
|
||||
<height>500</height>
|
||||
<height>659</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -24,17 +24,17 @@
|
||||
<string>General</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureGameList" name="gameListTab">
|
||||
<attribute name="title">
|
||||
<string>Game List</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureGameList" name="gameListTab">
|
||||
<attribute name="title">
|
||||
<string>Game List</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureSystem" name="systemTab">
|
||||
<attribute name="title">
|
||||
<string>System</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureInput" name="inputTab">
|
||||
<widget class="ConfigureInputSimple" name="inputTab">
|
||||
<attribute name="title">
|
||||
<string>Input</string>
|
||||
</attribute>
|
||||
@@ -54,11 +54,11 @@
|
||||
<string>Debug</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureWeb" name="webTab">
|
||||
<attribute name="title">
|
||||
<string>Web</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureWeb" name="webTab">
|
||||
<attribute name="title">
|
||||
<string>Web</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@@ -77,12 +77,12 @@
|
||||
<header>configuration/configure_general.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureGameList</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>configuration/configure_gamelist.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureGameList</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>configuration/configure_gamelist.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureSystem</class>
|
||||
<extends>QWidget</extends>
|
||||
@@ -102,9 +102,9 @@
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureInput</class>
|
||||
<class>ConfigureInputSimple</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>configuration/configure_input.h</header>
|
||||
<header>configuration/configure_input_simple.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
@@ -113,12 +113,12 @@
|
||||
<header>configuration/configure_graphics.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureWeb</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>configuration/configure_web.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureWeb</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>configuration/configure_web.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
|
||||
@@ -20,6 +20,33 @@
|
||||
#include "yuzu/configuration/configure_input_player.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 {
|
||||
template <typename Dialog, typename... Args>
|
||||
void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
|
||||
@@ -34,7 +61,7 @@ void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
|
||||
} // Anonymous namespace
|
||||
|
||||
ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
|
||||
: QDialog(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
|
||||
ui->setupUi(this);
|
||||
|
||||
players_controller = {
|
||||
@@ -90,37 +117,6 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
|
||||
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() {
|
||||
for (std::size_t i = 0; i < players_controller.size(); ++i) {
|
||||
const auto controller_type_index = players_controller[i]->currentIndex();
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include <QDialog>
|
||||
#include <QKeyEvent>
|
||||
#include <QWidget>
|
||||
|
||||
#include "ui_configure_input.h"
|
||||
|
||||
@@ -20,7 +20,9 @@ namespace Ui {
|
||||
class ConfigureInput;
|
||||
}
|
||||
|
||||
class ConfigureInput : public QWidget {
|
||||
void OnDockedModeChanged(bool last_state, bool new_state);
|
||||
|
||||
class ConfigureInput : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@@ -33,8 +35,6 @@ public:
|
||||
private:
|
||||
void updateUIEnabled();
|
||||
|
||||
void OnDockedModeChanged(bool last_state, bool new_state);
|
||||
|
||||
/// Load configuration settings.
|
||||
void loadConfiguration();
|
||||
/// Restore all buttons to their default values.
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ConfigureInput</class>
|
||||
<widget class="QWidget" name="ConfigureInput">
|
||||
<widget class="QDialog" name="ConfigureInput">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>473</width>
|
||||
<height>685</height>
|
||||
<width>384</width>
|
||||
<height>576</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -478,6 +478,13 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -485,5 +492,38 @@
|
||||
</layout>
|
||||
</widget>
|
||||
<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>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <QColorDialog>
|
||||
#include <QGridLayout>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
|
||||
137
src/yuzu/configuration/configure_input_simple.cpp
Normal file
137
src/yuzu/configuration/configure_input_simple.cpp
Normal file
@@ -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);
|
||||
}
|
||||
40
src/yuzu/configuration/configure_input_simple.h
Normal file
40
src/yuzu/configuration/configure_input_simple.h
Normal file
@@ -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;
|
||||
};
|
||||
97
src/yuzu/configuration/configure_input_simple.ui
Normal file
97
src/yuzu/configuration/configure_input_simple.ui
Normal file
@@ -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);
|
||||
|
||||
item_model->insertColumns(0, 2);
|
||||
item_model->setHeaderData(0, Qt::Horizontal, "Patch Name");
|
||||
item_model->setHeaderData(1, Qt::Horizontal, "Version");
|
||||
item_model->setHeaderData(0, Qt::Horizontal, tr("Patch Name"));
|
||||
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
|
||||
// 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)
|
||||
ui->display_name->setText(QString::fromStdString(title));
|
||||
|
||||
std::string developer;
|
||||
if (loader->ReadDeveloper(developer) == Loader::ResultStatus::Success)
|
||||
ui->display_developer->setText(QString::fromStdString(developer));
|
||||
FileSys::NACP nacp;
|
||||
if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success)
|
||||
ui->display_developer->setText(QString::fromStdString(nacp.GetDeveloperName()));
|
||||
|
||||
ui->display_version->setText(QStringLiteral("1.0.0"));
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ static Tegra::Texture::TextureFormat ConvertToTextureFormat(
|
||||
return Tegra::Texture::TextureFormat::A2B10G10R10;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented RT format");
|
||||
return Tegra::Texture::TextureFormat::A8R8G8B8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -221,6 +221,9 @@ QString WaitTreeThread::GetText() const {
|
||||
case Kernel::ThreadStatus::Ready:
|
||||
status = tr("ready");
|
||||
break;
|
||||
case Kernel::ThreadStatus::Paused:
|
||||
status = tr("paused");
|
||||
break;
|
||||
case Kernel::ThreadStatus::WaitHLEEvent:
|
||||
status = tr("waiting for HLE return");
|
||||
break;
|
||||
@@ -262,6 +265,8 @@ QColor WaitTreeThread::GetColor() const {
|
||||
return QColor(Qt::GlobalColor::darkGreen);
|
||||
case Kernel::ThreadStatus::Ready:
|
||||
return QColor(Qt::GlobalColor::darkBlue);
|
||||
case Kernel::ThreadStatus::Paused:
|
||||
return QColor(Qt::GlobalColor::lightGray);
|
||||
case Kernel::ThreadStatus::WaitHLEEvent:
|
||||
case Kernel::ThreadStatus::WaitIPC:
|
||||
return QColor(Qt::GlobalColor::darkRed);
|
||||
@@ -288,8 +293,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
|
||||
|
||||
QString processor;
|
||||
switch (thread.GetProcessorID()) {
|
||||
case Kernel::ThreadProcessorId::THREADPROCESSORID_DEFAULT:
|
||||
processor = tr("default");
|
||||
case Kernel::ThreadProcessorId::THREADPROCESSORID_IDEAL:
|
||||
processor = tr("ideal");
|
||||
break;
|
||||
case Kernel::ThreadProcessorId::THREADPROCESSORID_0:
|
||||
case Kernel::ThreadProcessorId::THREADPROCESSORID_1:
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <thread>
|
||||
|
||||
// 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 "configuration/configure_per_general.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
@@ -208,6 +209,28 @@ GMainWindow::~GMainWindow() {
|
||||
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(
|
||||
const Core::Frontend::SoftwareKeyboardParameters& parameters) {
|
||||
QtSoftwareKeyboardDialog dialog(this, parameters);
|
||||
@@ -335,6 +358,9 @@ void GMainWindow::InitializeHotkeys() {
|
||||
Qt::ApplicationShortcut);
|
||||
hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2),
|
||||
Qt::ApplicationShortcut);
|
||||
hotkey_registry.RegisterHotkey("Main Window", "Capture Screenshot",
|
||||
QKeySequence(QKeySequence::Print));
|
||||
|
||||
hotkey_registry.LoadHotkeys();
|
||||
|
||||
connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
|
||||
@@ -394,6 +420,12 @@ void GMainWindow::InitializeHotkeys() {
|
||||
OnLoadAmiibo();
|
||||
}
|
||||
});
|
||||
connect(hotkey_registry.GetHotkey("Main Window", "Capture Screenshot", this),
|
||||
&QShortcut::activated, this, [&] {
|
||||
if (emu_thread->IsRunning()) {
|
||||
OnCaptureScreenshot();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void GMainWindow::SetDefaultUIGeometry() {
|
||||
@@ -491,6 +523,10 @@ void GMainWindow::ConnectMenuEvents() {
|
||||
hotkey_registry.GetHotkey("Main Window", "Fullscreen", this)->key());
|
||||
connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
|
||||
|
||||
// Movie
|
||||
connect(ui.action_Capture_Screenshot, &QAction::triggered, this,
|
||||
&GMainWindow::OnCaptureScreenshot);
|
||||
|
||||
// Help
|
||||
connect(ui.action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder);
|
||||
connect(ui.action_Rederive, &QAction::triggered, this,
|
||||
@@ -574,6 +610,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
||||
|
||||
system.SetGPUDebugContext(debug_context);
|
||||
|
||||
system.SetProfileSelector(std::make_unique<QtProfileSelector>(*this));
|
||||
system.SetSoftwareKeyboard(std::make_unique<QtSoftwareKeyboard>(*this));
|
||||
|
||||
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_Report_Compatibility->setEnabled(false);
|
||||
ui.action_Load_Amiibo->setEnabled(false);
|
||||
ui.action_Capture_Screenshot->setEnabled(false);
|
||||
render_window->hide();
|
||||
game_list->show();
|
||||
game_list->setFilterFocus();
|
||||
@@ -1290,6 +1328,7 @@ void GMainWindow::OnStartGame() {
|
||||
|
||||
discord_rpc->Update();
|
||||
ui.action_Load_Amiibo->setEnabled(true);
|
||||
ui.action_Capture_Screenshot->setEnabled(true);
|
||||
}
|
||||
|
||||
void GMainWindow::OnPauseGame() {
|
||||
@@ -1298,6 +1337,7 @@ void GMainWindow::OnPauseGame() {
|
||||
ui.action_Start->setEnabled(true);
|
||||
ui.action_Pause->setEnabled(false);
|
||||
ui.action_Stop->setEnabled(true);
|
||||
ui.action_Capture_Screenshot->setEnabled(false);
|
||||
}
|
||||
|
||||
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() {
|
||||
if (emu_thread == nullptr) {
|
||||
status_bar_update_timer.stop();
|
||||
|
||||
@@ -99,10 +99,12 @@ signals:
|
||||
// Signal that tells widgets to update icons to use the current theme
|
||||
void UpdateThemedIcons();
|
||||
|
||||
void ProfileSelectorFinishedSelection(std::optional<Service::Account::UUID> uuid);
|
||||
void SoftwareKeyboardFinishedText(std::optional<std::u16string> text);
|
||||
void SoftwareKeyboardFinishedCheckDialog();
|
||||
|
||||
public slots:
|
||||
void ProfileSelectorSelectProfile();
|
||||
void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters);
|
||||
void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message);
|
||||
|
||||
@@ -187,6 +189,7 @@ private slots:
|
||||
void ShowFullscreen();
|
||||
void HideFullscreen();
|
||||
void ToggleWindowMode();
|
||||
void OnCaptureScreenshot();
|
||||
void OnCoreError(Core::System::ResultStatus, std::string);
|
||||
void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
|
||||
|
||||
|
||||
@@ -101,11 +101,13 @@
|
||||
<addaction name="action_Show_Status_Bar"/>
|
||||
<addaction name="menu_View_Debugging"/>
|
||||
</widget>
|
||||
<widget class ="QMenu" name="menu_Tools">
|
||||
<widget class="QMenu" name="menu_Tools">
|
||||
<property name="title">
|
||||
<string>Tools</string>
|
||||
</property>
|
||||
<addaction name="action_Rederive" />
|
||||
<addaction name="action_Rederive"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Capture_Screenshot"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Help">
|
||||
<property name="title">
|
||||
@@ -118,7 +120,7 @@
|
||||
<addaction name="menu_File"/>
|
||||
<addaction name="menu_Emulation"/>
|
||||
<addaction name="menu_View"/>
|
||||
<addaction name="menu_Tools" />
|
||||
<addaction name="menu_Tools"/>
|
||||
<addaction name="menu_Help"/>
|
||||
</widget>
|
||||
<action name="action_Install_File_NAND">
|
||||
@@ -173,11 +175,11 @@
|
||||
<string>&Stop</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Rederive">
|
||||
<property name="text">
|
||||
<string>Reinitialize keys...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Rederive">
|
||||
<property name="text">
|
||||
<string>Reinitialize keys...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_About">
|
||||
<property name="text">
|
||||
<string>About yuzu</string>
|
||||
@@ -252,39 +254,47 @@
|
||||
<string>Fullscreen</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Restart">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Restart</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Load_Amiibo">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Load Amiibo...</string>
|
||||
</property>
|
||||
<action name="action_Restart">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Restart</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Report_Compatibility">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Report Compatibility</string>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Open_yuzu_Folder">
|
||||
<property name="text">
|
||||
<string>Open yuzu Folder</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<action name="action_Load_Amiibo">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Load Amiibo...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Report_Compatibility">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Report Compatibility</string>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<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/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace UISettings {
|
||||
|
||||
@@ -42,8 +43,11 @@ struct Values {
|
||||
// Discord RPC
|
||||
bool enable_discord_presence;
|
||||
|
||||
u16 screenshot_resolution_factor;
|
||||
|
||||
QString roms_path;
|
||||
QString symbols_path;
|
||||
QString screenshot_path;
|
||||
QString gamedir;
|
||||
bool gamedir_deepscan;
|
||||
QStringList recent_files;
|
||||
@@ -58,6 +62,9 @@ struct Values {
|
||||
// logging
|
||||
bool show_console;
|
||||
|
||||
// Controllers
|
||||
int profile_index;
|
||||
|
||||
// Game List
|
||||
bool show_unknown;
|
||||
bool show_add_ons;
|
||||
|
||||
Reference in New Issue
Block a user