Compare commits
44 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2020ba06e1 | ||
|
|
af9336df57 | ||
|
|
a73c7c73eb | ||
|
|
19bfbb28ce | ||
|
|
357bc956ae | ||
|
|
9aa68212d9 | ||
|
|
fbeaa330a3 | ||
|
|
e84e4fd3f8 | ||
|
|
fbe900ba6d | ||
|
|
795335af0f | ||
|
|
fa9acc26d9 | ||
|
|
8e23e40a72 | ||
|
|
2991c747c9 | ||
|
|
7477c12fe2 | ||
|
|
c4515d305b | ||
|
|
c643f364b4 | ||
|
|
2e6b67a079 | ||
|
|
4082c4eda6 | ||
|
|
0756f29a2c | ||
|
|
417e1ef09c | ||
|
|
5c4259ec1a | ||
|
|
4a6ba58073 | ||
|
|
73c9ffc422 | ||
|
|
17fa0ffff9 | ||
|
|
1392597ede | ||
|
|
c9269a4a4b | ||
|
|
faa9110541 | ||
|
|
67fa21e143 | ||
|
|
33056dd833 | ||
|
|
7e622c5575 | ||
|
|
bbf8d6bf01 | ||
|
|
946777601b | ||
|
|
7e72b5e453 | ||
|
|
1120e0b4d2 | ||
|
|
002ae08bbd | ||
|
|
d09fb82113 | ||
|
|
10824c5d63 | ||
|
|
e0e84aede0 | ||
|
|
010bc677f3 | ||
|
|
0f216d20e3 | ||
|
|
3dc59b74ec | ||
|
|
27caf71204 | ||
|
|
6ff5135521 | ||
|
|
fc8da2d5e3 |
@@ -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 {};
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ add_library(common STATIC
|
||||
detached_tasks.cpp
|
||||
detached_tasks.h
|
||||
bit_field.h
|
||||
bit_util.h
|
||||
cityhash.cpp
|
||||
cityhash.h
|
||||
color.h
|
||||
|
||||
61
src/common/bit_util.h
Normal file
61
src/common/bit_util.h
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
/// Gets the size of a specified type T in bits.
|
||||
template <typename T>
|
||||
constexpr std::size_t BitSize() {
|
||||
return sizeof(T) * CHAR_BIT;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
inline u32 CountLeadingZeroes32(u32 value) {
|
||||
unsigned long leading_zero = 0;
|
||||
|
||||
if (_BitScanReverse(&leading_zero, value) != 0) {
|
||||
return 31 - leading_zero;
|
||||
}
|
||||
|
||||
return 32;
|
||||
}
|
||||
|
||||
inline u64 CountLeadingZeroes64(u64 value) {
|
||||
unsigned long leading_zero = 0;
|
||||
|
||||
if (_BitScanReverse64(&leading_zero, value) != 0) {
|
||||
return 63 - leading_zero;
|
||||
}
|
||||
|
||||
return 64;
|
||||
}
|
||||
#else
|
||||
inline u32 CountLeadingZeroes32(u32 value) {
|
||||
if (value == 0) {
|
||||
return 32;
|
||||
}
|
||||
|
||||
return __builtin_clz(value);
|
||||
}
|
||||
|
||||
inline u64 CountLeadingZeroes64(u64 value) {
|
||||
if (value == 0) {
|
||||
return 64;
|
||||
}
|
||||
|
||||
return __builtin_clzll(value);
|
||||
}
|
||||
#endif
|
||||
} // namespace Common
|
||||
@@ -115,6 +115,8 @@ add_library(core STATIC
|
||||
hle/kernel/object.h
|
||||
hle/kernel/process.cpp
|
||||
hle/kernel/process.h
|
||||
hle/kernel/process_capability.cpp
|
||||
hle/kernel/process_capability.h
|
||||
hle/kernel/readable_event.cpp
|
||||
hle/kernel/readable_event.h
|
||||
hle/kernel/resource_limit.cpp
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -40,6 +40,13 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
|
||||
if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset))
|
||||
return Loader::ResultStatus::ErrorBadFileAccessHeader;
|
||||
|
||||
aci_kernel_capabilities.resize(aci_header.kac_size / sizeof(u32));
|
||||
const u64 read_size = aci_header.kac_size;
|
||||
const u64 read_offset = npdm_header.aci_offset + aci_header.kac_offset;
|
||||
if (file->ReadBytes(aci_kernel_capabilities.data(), read_size, read_offset) != read_size) {
|
||||
return Loader::ResultStatus::ErrorBadKernelCapabilityDescriptors;
|
||||
}
|
||||
|
||||
return Loader::ResultStatus::Success;
|
||||
}
|
||||
|
||||
@@ -71,6 +78,10 @@ u64 ProgramMetadata::GetFilesystemPermissions() const {
|
||||
return aci_file_access.permissions;
|
||||
}
|
||||
|
||||
const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const {
|
||||
return aci_kernel_capabilities;
|
||||
}
|
||||
|
||||
void ProgramMetadata::Print() const {
|
||||
LOG_DEBUG(Service_FS, "Magic: {:.4}", npdm_header.magic.data());
|
||||
LOG_DEBUG(Service_FS, "Main thread priority: 0x{:02X}", npdm_header.main_thread_priority);
|
||||
@@ -81,16 +92,20 @@ void ProgramMetadata::Print() const {
|
||||
LOG_DEBUG(Service_FS, " > 64-bit instructions: {}",
|
||||
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);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
@@ -38,6 +39,8 @@ enum class ProgramFilePermission : u64 {
|
||||
*/
|
||||
class ProgramMetadata {
|
||||
public:
|
||||
using KernelCapabilityDescriptors = std::vector<u32>;
|
||||
|
||||
ProgramMetadata();
|
||||
~ProgramMetadata();
|
||||
|
||||
@@ -50,6 +53,7 @@ public:
|
||||
u32 GetMainThreadStackSize() const;
|
||||
u64 GetTitleID() const;
|
||||
u64 GetFilesystemPermissions() const;
|
||||
const KernelCapabilityDescriptors& GetKernelCapabilities() const;
|
||||
|
||||
void Print() const;
|
||||
|
||||
@@ -154,6 +158,8 @@ private:
|
||||
|
||||
FileAccessControl acid_file_access;
|
||||
FileAccessHeader aci_file_access;
|
||||
|
||||
KernelCapabilityDescriptors aci_kernel_capabilities;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -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);
|
||||
@@ -132,4 +134,32 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Kernel {
|
||||
// Confirmed Switch kernel error codes
|
||||
|
||||
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
|
||||
constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14};
|
||||
constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
|
||||
constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
|
||||
constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
|
||||
@@ -30,6 +31,7 @@ constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121};
|
||||
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};
|
||||
constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -43,6 +43,9 @@ enum KernelHandle : Handle {
|
||||
*/
|
||||
class HandleTable final : NonCopyable {
|
||||
public:
|
||||
/// This is the maximum limit of handles allowed per process in Horizon
|
||||
static constexpr std::size_t MAX_COUNT = 1024;
|
||||
|
||||
HandleTable();
|
||||
~HandleTable();
|
||||
|
||||
@@ -91,9 +94,6 @@ public:
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
/// This is the maximum limit of handles allowed per process in Horizon
|
||||
static constexpr std::size_t MAX_COUNT = 1024;
|
||||
|
||||
/// Stores the Object referenced by the handle or null if the slot is empty.
|
||||
std::array<SharedPtr<Object>, MAX_COUNT> objects;
|
||||
|
||||
|
||||
@@ -28,13 +28,11 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
|
||||
SharedPtr<Process> process(new Process(kernel));
|
||||
|
||||
process->name = std::move(name);
|
||||
process->flags.raw = 0;
|
||||
process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
|
||||
process->resource_limit = kernel.GetSystemResourceLimit();
|
||||
process->status = ProcessStatus::Created;
|
||||
process->program_id = 0;
|
||||
process->process_id = kernel.CreateNewProcessID();
|
||||
process->svc_access_mask.set();
|
||||
process->capabilities.InitializeForMetadatalessProcess();
|
||||
|
||||
std::mt19937 rng(Settings::values.rng_seed.value_or(0));
|
||||
std::uniform_int_distribution<u64> distribution;
|
||||
@@ -64,83 +62,15 @@ ResultCode Process::ClearSignalState() {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
|
||||
ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
|
||||
program_id = metadata.GetTitleID();
|
||||
ideal_processor = metadata.GetMainThreadCore();
|
||||
is_64bit_process = metadata.Is64BitProgram();
|
||||
|
||||
vm_manager.Reset(metadata.GetAddressSpaceType());
|
||||
}
|
||||
|
||||
void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) {
|
||||
for (std::size_t i = 0; i < len; ++i) {
|
||||
u32 descriptor = kernel_caps[i];
|
||||
u32 type = descriptor >> 20;
|
||||
|
||||
if (descriptor == 0xFFFFFFFF) {
|
||||
// Unused descriptor entry
|
||||
continue;
|
||||
} else if ((type & 0xF00) == 0xE00) { // 0x0FFF
|
||||
// Allowed interrupts list
|
||||
LOG_WARNING(Loader, "ExHeader allowed interrupts list ignored");
|
||||
} else if ((type & 0xF80) == 0xF00) { // 0x07FF
|
||||
// Allowed syscalls mask
|
||||
unsigned int index = ((descriptor >> 24) & 7) * 24;
|
||||
u32 bits = descriptor & 0xFFFFFF;
|
||||
|
||||
while (bits && index < svc_access_mask.size()) {
|
||||
svc_access_mask.set(index, bits & 1);
|
||||
++index;
|
||||
bits >>= 1;
|
||||
}
|
||||
} else if ((type & 0xFF0) == 0xFE0) { // 0x00FF
|
||||
// Handle table size
|
||||
handle_table_size = descriptor & 0x3FF;
|
||||
} else if ((type & 0xFF8) == 0xFF0) { // 0x007F
|
||||
// Misc. flags
|
||||
flags.raw = descriptor & 0xFFFF;
|
||||
} else if ((type & 0xFFE) == 0xFF8) { // 0x001F
|
||||
// Mapped memory range
|
||||
if (i + 1 >= len || ((kernel_caps[i + 1] >> 20) & 0xFFE) != 0xFF8) {
|
||||
LOG_WARNING(Loader, "Incomplete exheader memory range descriptor ignored.");
|
||||
continue;
|
||||
}
|
||||
u32 end_desc = kernel_caps[i + 1];
|
||||
++i; // Skip over the second descriptor on the next iteration
|
||||
|
||||
AddressMapping mapping;
|
||||
mapping.address = descriptor << 12;
|
||||
VAddr end_address = end_desc << 12;
|
||||
|
||||
if (mapping.address < end_address) {
|
||||
mapping.size = end_address - mapping.address;
|
||||
} else {
|
||||
mapping.size = 0;
|
||||
}
|
||||
|
||||
mapping.read_only = (descriptor & (1 << 20)) != 0;
|
||||
mapping.unk_flag = (end_desc & (1 << 20)) != 0;
|
||||
|
||||
address_mappings.push_back(mapping);
|
||||
} else if ((type & 0xFFF) == 0xFFE) { // 0x000F
|
||||
// Mapped memory page
|
||||
AddressMapping mapping;
|
||||
mapping.address = descriptor << 12;
|
||||
mapping.size = Memory::PAGE_SIZE;
|
||||
mapping.read_only = false;
|
||||
mapping.unk_flag = false;
|
||||
|
||||
address_mappings.push_back(mapping);
|
||||
} else if ((type & 0xFE0) == 0xFC0) { // 0x01FF
|
||||
// Kernel version
|
||||
kernel_version = descriptor & 0xFFFF;
|
||||
|
||||
int minor = kernel_version & 0xFF;
|
||||
int major = (kernel_version >> 8) & 0xFF;
|
||||
LOG_INFO(Loader, "ExHeader kernel version: {}.{}", major, minor);
|
||||
} else {
|
||||
LOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x{:08X}", descriptor);
|
||||
}
|
||||
}
|
||||
const auto& caps = metadata.GetKernelCapabilities();
|
||||
return capabilities.InitializeForUserProcess(caps.data(), caps.size(), vm_manager);
|
||||
}
|
||||
|
||||
void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
|
||||
@@ -268,22 +198,6 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
|
||||
Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
|
||||
}
|
||||
|
||||
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() {}
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/process_capability.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
@@ -42,24 +42,6 @@ enum class MemoryRegion : u16 {
|
||||
BASE = 3,
|
||||
};
|
||||
|
||||
union ProcessFlags {
|
||||
u16 raw;
|
||||
|
||||
BitField<0, 1, u16>
|
||||
allow_debug; ///< Allows other processes to attach to and debug this process.
|
||||
BitField<1, 1, u16> force_debug; ///< Allows this process to attach to processes even if they
|
||||
/// don't have allow_debug set.
|
||||
BitField<2, 1, u16> allow_nonalphanum;
|
||||
BitField<3, 1, u16> shared_page_writable; ///< Shared page is mapped with write permissions.
|
||||
BitField<4, 1, u16> privileged_priority; ///< Can use priority levels higher than 24.
|
||||
BitField<5, 1, u16> allow_main_args;
|
||||
BitField<6, 1, u16> shared_device_mem;
|
||||
BitField<7, 1, u16> runnable_on_sleep;
|
||||
BitField<8, 4, MemoryRegion>
|
||||
memory_region; ///< Default region for memory allocations for this process
|
||||
BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000).
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates the status of a Process instance.
|
||||
*
|
||||
@@ -192,13 +174,13 @@ public:
|
||||
}
|
||||
|
||||
/// Gets the bitmask of allowed CPUs that this process' threads can run on.
|
||||
u32 GetAllowedProcessorMask() const {
|
||||
return allowed_processor_mask;
|
||||
u64 GetAllowedProcessorMask() const {
|
||||
return capabilities.GetCoreMask();
|
||||
}
|
||||
|
||||
/// Gets the bitmask of allowed thread priorities.
|
||||
u32 GetAllowedThreadPriorityMask() const {
|
||||
return allowed_thread_priority_mask;
|
||||
u64 GetAllowedThreadPriorityMask() const {
|
||||
return capabilities.GetPriorityMask();
|
||||
}
|
||||
|
||||
u32 IsVirtualMemoryEnabled() const {
|
||||
@@ -239,15 +221,12 @@ public:
|
||||
* Loads process-specifics configuration info with metadata provided
|
||||
* by an executable.
|
||||
*
|
||||
* @param metadata The provided metadata to load process specific info.
|
||||
* @param metadata The provided metadata to load process specific info from.
|
||||
*
|
||||
* @returns RESULT_SUCCESS if all relevant metadata was able to be
|
||||
* loaded and parsed. Otherwise, an error code is returned.
|
||||
*/
|
||||
void LoadFromMetadata(const FileSys::ProgramMetadata& metadata);
|
||||
|
||||
/**
|
||||
* Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them
|
||||
* to this process.
|
||||
*/
|
||||
void ParseKernelCaps(const u32* kernel_caps, std::size_t len);
|
||||
ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata);
|
||||
|
||||
/**
|
||||
* Applies address space changes and launches the process main thread.
|
||||
@@ -263,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);
|
||||
@@ -271,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;
|
||||
@@ -308,22 +280,8 @@ private:
|
||||
/// Resource limit descriptor for this process
|
||||
SharedPtr<ResourceLimit> resource_limit;
|
||||
|
||||
/// The process may only call SVCs which have the corresponding bit set.
|
||||
std::bitset<0x80> svc_access_mask;
|
||||
/// Maximum size of the handle table for the process.
|
||||
u32 handle_table_size = 0x200;
|
||||
/// Special memory ranges mapped into this processes address space. This is used to give
|
||||
/// processes access to specific I/O regions and device memory.
|
||||
boost::container::static_vector<AddressMapping, 8> address_mappings;
|
||||
ProcessFlags flags;
|
||||
/// Kernel compatibility version for this process
|
||||
u16 kernel_version = 0;
|
||||
/// The default CPU for this process, threads are scheduled on this cpu by default.
|
||||
u8 ideal_processor = 0;
|
||||
/// Bitmask of allowed CPUs that this process' threads can run on. TODO(Subv): Actually parse
|
||||
/// this value from the process header.
|
||||
u32 allowed_processor_mask = THREADPROCESSORID_DEFAULT_MASK;
|
||||
u32 allowed_thread_priority_mask = 0xFFFFFFFF;
|
||||
u32 is_virtual_address_memory_enabled = 0;
|
||||
|
||||
/// The Thread Local Storage area is allocated as processes create threads,
|
||||
@@ -333,6 +291,9 @@ private:
|
||||
/// This vector will grow as more pages are allocated for new threads.
|
||||
std::vector<std::bitset<8>> tls_slots;
|
||||
|
||||
/// Contains the parsed process capability descriptors.
|
||||
ProcessCapabilities capabilities;
|
||||
|
||||
/// Whether or not this process is AArch64, or AArch32.
|
||||
/// By default, we currently assume this is true, unless otherwise
|
||||
/// specified by metadata provided to the process during loading.
|
||||
|
||||
355
src/core/hle/kernel/process_capability.cpp
Normal file
355
src/core/hle/kernel/process_capability.cpp
Normal file
@@ -0,0 +1,355 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/bit_util.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/process_capability.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
|
||||
namespace Kernel {
|
||||
namespace {
|
||||
|
||||
// clang-format off
|
||||
|
||||
// Shift offsets for kernel capability types.
|
||||
enum : u32 {
|
||||
CapabilityOffset_PriorityAndCoreNum = 3,
|
||||
CapabilityOffset_Syscall = 4,
|
||||
CapabilityOffset_MapPhysical = 6,
|
||||
CapabilityOffset_MapIO = 7,
|
||||
CapabilityOffset_Interrupt = 11,
|
||||
CapabilityOffset_ProgramType = 13,
|
||||
CapabilityOffset_KernelVersion = 14,
|
||||
CapabilityOffset_HandleTableSize = 15,
|
||||
CapabilityOffset_Debug = 16,
|
||||
};
|
||||
|
||||
// Combined mask of all parameters that may be initialized only once.
|
||||
constexpr u32 InitializeOnceMask = (1U << CapabilityOffset_PriorityAndCoreNum) |
|
||||
(1U << CapabilityOffset_ProgramType) |
|
||||
(1U << CapabilityOffset_KernelVersion) |
|
||||
(1U << CapabilityOffset_HandleTableSize) |
|
||||
(1U << CapabilityOffset_Debug);
|
||||
|
||||
// Packed kernel version indicating 10.4.0
|
||||
constexpr u32 PackedKernelVersion = 0x520000;
|
||||
|
||||
// Indicates possible types of capabilities that can be specified.
|
||||
enum class CapabilityType : u32 {
|
||||
Unset = 0U,
|
||||
PriorityAndCoreNum = (1U << CapabilityOffset_PriorityAndCoreNum) - 1,
|
||||
Syscall = (1U << CapabilityOffset_Syscall) - 1,
|
||||
MapPhysical = (1U << CapabilityOffset_MapPhysical) - 1,
|
||||
MapIO = (1U << CapabilityOffset_MapIO) - 1,
|
||||
Interrupt = (1U << CapabilityOffset_Interrupt) - 1,
|
||||
ProgramType = (1U << CapabilityOffset_ProgramType) - 1,
|
||||
KernelVersion = (1U << CapabilityOffset_KernelVersion) - 1,
|
||||
HandleTableSize = (1U << CapabilityOffset_HandleTableSize) - 1,
|
||||
Debug = (1U << CapabilityOffset_Debug) - 1,
|
||||
Ignorable = 0xFFFFFFFFU,
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
|
||||
constexpr CapabilityType GetCapabilityType(u32 value) {
|
||||
return static_cast<CapabilityType>((~value & (value + 1)) - 1);
|
||||
}
|
||||
|
||||
u32 GetFlagBitOffset(CapabilityType type) {
|
||||
const auto value = static_cast<u32>(type);
|
||||
return static_cast<u32>(Common::BitSize<u32>() - Common::CountLeadingZeroes32(value));
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities,
|
||||
std::size_t num_capabilities,
|
||||
VMManager& vm_manager) {
|
||||
Clear();
|
||||
|
||||
// Allow all cores and priorities.
|
||||
core_mask = 0xF;
|
||||
priority_mask = 0xFFFFFFFFFFFFFFFF;
|
||||
kernel_version = PackedKernelVersion;
|
||||
|
||||
return ParseCapabilities(capabilities, num_capabilities, vm_manager);
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::InitializeForUserProcess(const u32* capabilities,
|
||||
std::size_t num_capabilities,
|
||||
VMManager& vm_manager) {
|
||||
Clear();
|
||||
|
||||
return ParseCapabilities(capabilities, num_capabilities, vm_manager);
|
||||
}
|
||||
|
||||
void ProcessCapabilities::InitializeForMetadatalessProcess() {
|
||||
// Allow all cores and priorities
|
||||
core_mask = 0xF;
|
||||
priority_mask = 0xFFFFFFFFFFFFFFFF;
|
||||
kernel_version = PackedKernelVersion;
|
||||
|
||||
// Allow all system calls and interrupts.
|
||||
svc_capabilities.set();
|
||||
interrupt_capabilities.set();
|
||||
|
||||
// Allow using the maximum possible amount of handles
|
||||
handle_table_size = static_cast<u32>(HandleTable::MAX_COUNT);
|
||||
|
||||
// Allow all debugging capabilities.
|
||||
is_debuggable = true;
|
||||
can_force_debug = true;
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities,
|
||||
std::size_t num_capabilities,
|
||||
VMManager& vm_manager) {
|
||||
u32 set_flags = 0;
|
||||
u32 set_svc_bits = 0;
|
||||
|
||||
for (std::size_t i = 0; i < num_capabilities; ++i) {
|
||||
const u32 descriptor = capabilities[i];
|
||||
const auto type = GetCapabilityType(descriptor);
|
||||
|
||||
if (type == CapabilityType::MapPhysical) {
|
||||
i++;
|
||||
|
||||
// The MapPhysical type uses two descriptor flags for its parameters.
|
||||
// If there's only one, then there's a problem.
|
||||
if (i >= num_capabilities) {
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
const auto size_flags = capabilities[i];
|
||||
if (GetCapabilityType(size_flags) != CapabilityType::MapPhysical) {
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
const auto result = HandleMapPhysicalFlags(descriptor, size_flags, vm_manager);
|
||||
if (result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
const auto result =
|
||||
ParseSingleFlagCapability(set_flags, set_svc_bits, descriptor, vm_manager);
|
||||
if (result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits,
|
||||
u32 flag, VMManager& vm_manager) {
|
||||
const auto type = GetCapabilityType(flag);
|
||||
|
||||
if (type == CapabilityType::Unset) {
|
||||
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
|
||||
}
|
||||
|
||||
// Bail early on ignorable entries, as one would expect,
|
||||
// ignorable descriptors can be ignored.
|
||||
if (type == CapabilityType::Ignorable) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
// Ensure that the give flag hasn't already been initialized before.
|
||||
// If it has been, then bail.
|
||||
const u32 flag_length = GetFlagBitOffset(type);
|
||||
const u32 set_flag = 1U << flag_length;
|
||||
if ((set_flag & set_flags & InitializeOnceMask) != 0) {
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
set_flags |= set_flag;
|
||||
|
||||
switch (type) {
|
||||
case CapabilityType::PriorityAndCoreNum:
|
||||
return HandlePriorityCoreNumFlags(flag);
|
||||
case CapabilityType::Syscall:
|
||||
return HandleSyscallFlags(set_svc_bits, flag);
|
||||
case CapabilityType::MapIO:
|
||||
return HandleMapIOFlags(flag, vm_manager);
|
||||
case CapabilityType::Interrupt:
|
||||
return HandleInterruptFlags(flag);
|
||||
case CapabilityType::ProgramType:
|
||||
return HandleProgramTypeFlags(flag);
|
||||
case CapabilityType::KernelVersion:
|
||||
return HandleKernelVersionFlags(flag);
|
||||
case CapabilityType::HandleTableSize:
|
||||
return HandleHandleTableFlags(flag);
|
||||
case CapabilityType::Debug:
|
||||
return HandleDebugFlags(flag);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
|
||||
}
|
||||
|
||||
void ProcessCapabilities::Clear() {
|
||||
svc_capabilities.reset();
|
||||
interrupt_capabilities.reset();
|
||||
|
||||
core_mask = 0;
|
||||
priority_mask = 0;
|
||||
|
||||
handle_table_size = 0;
|
||||
kernel_version = 0;
|
||||
|
||||
program_type = ProgramType::SysModule;
|
||||
|
||||
is_debuggable = false;
|
||||
can_force_debug = false;
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
|
||||
if (priority_mask != 0 || core_mask != 0) {
|
||||
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
|
||||
}
|
||||
|
||||
const u32 core_num_min = (flags >> 16) & 0xFF;
|
||||
const u32 core_num_max = (flags >> 24) & 0xFF;
|
||||
if (core_num_min > core_num_max) {
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
const u32 priority_min = (flags >> 10) & 0x3F;
|
||||
const u32 priority_max = (flags >> 4) & 0x3F;
|
||||
if (priority_min > priority_max) {
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
// The switch only has 4 usable cores.
|
||||
if (core_num_max >= 4) {
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
|
||||
const auto make_mask = [](u64 min, u64 max) {
|
||||
const u64 range = max - min + 1;
|
||||
const u64 mask = (1ULL << range) - 1;
|
||||
|
||||
return mask << min;
|
||||
};
|
||||
|
||||
core_mask = make_mask(core_num_min, core_num_max);
|
||||
priority_mask = make_mask(priority_min, priority_max);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) {
|
||||
const u32 index = flags >> 29;
|
||||
const u32 svc_bit = 1U << index;
|
||||
|
||||
// If we've already set this svc before, bail.
|
||||
if ((set_svc_bits & svc_bit) != 0) {
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
set_svc_bits |= svc_bit;
|
||||
|
||||
const u32 svc_mask = (flags >> 5) & 0xFFFFFF;
|
||||
for (u32 i = 0; i < 24; ++i) {
|
||||
const u32 svc_number = index * 24 + i;
|
||||
|
||||
if ((svc_mask & (1U << i)) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (svc_number >= svc_capabilities.size()) {
|
||||
return ERR_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
svc_capabilities[svc_number] = true;
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags,
|
||||
VMManager& vm_manager) {
|
||||
// TODO(Lioncache): Implement once the memory manager can handle this.
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, VMManager& vm_manager) {
|
||||
// TODO(Lioncache): Implement once the memory manager can handle this.
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) {
|
||||
constexpr u32 interrupt_ignore_value = 0x3FF;
|
||||
const u32 interrupt0 = (flags >> 12) & 0x3FF;
|
||||
const u32 interrupt1 = (flags >> 22) & 0x3FF;
|
||||
|
||||
for (u32 interrupt : {interrupt0, interrupt1}) {
|
||||
if (interrupt == interrupt_ignore_value) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE:
|
||||
// This should be checking a generic interrupt controller value
|
||||
// as part of the calculation, however, given we don't currently
|
||||
// emulate that, it's sufficient to mark every interrupt as defined.
|
||||
|
||||
if (interrupt >= interrupt_capabilities.size()) {
|
||||
return ERR_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
interrupt_capabilities[interrupt] = true;
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) {
|
||||
const u32 reserved = flags >> 17;
|
||||
if (reserved != 0) {
|
||||
return ERR_RESERVED_VALUE;
|
||||
}
|
||||
|
||||
program_type = static_cast<ProgramType>((flags >> 14) & 0b111);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) {
|
||||
// Yes, the internal member variable is checked in the actual kernel here.
|
||||
// This might look odd for options that are only allowed to be initialized
|
||||
// just once, however the kernel has a separate initialization function for
|
||||
// kernel processes and userland processes. The kernel variant sets this
|
||||
// member variable ahead of time.
|
||||
|
||||
const u32 major_version = kernel_version >> 19;
|
||||
|
||||
if (major_version != 0 || flags < 0x80000) {
|
||||
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
|
||||
}
|
||||
|
||||
kernel_version = flags;
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
|
||||
const u32 reserved = flags >> 26;
|
||||
if (reserved != 0) {
|
||||
return ERR_RESERVED_VALUE;
|
||||
}
|
||||
|
||||
handle_table_size = (flags >> 16) & 0x3FF;
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::HandleDebugFlags(u32 flags) {
|
||||
const u32 reserved = flags >> 19;
|
||||
if (reserved != 0) {
|
||||
return ERR_RESERVED_VALUE;
|
||||
}
|
||||
|
||||
is_debuggable = (flags & 0x20000) != 0;
|
||||
can_force_debug = (flags & 0x40000) != 0;
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
264
src/core/hle/kernel/process_capability.h
Normal file
264
src/core/hle/kernel/process_capability.h
Normal file
@@ -0,0 +1,264 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
union ResultCode;
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class VMManager;
|
||||
|
||||
/// The possible types of programs that may be indicated
|
||||
/// by the program type capability descriptor.
|
||||
enum class ProgramType {
|
||||
SysModule,
|
||||
Application,
|
||||
Applet,
|
||||
};
|
||||
|
||||
/// Handles kernel capability descriptors that are provided by
|
||||
/// application metadata. These descriptors provide information
|
||||
/// that alters certain parameters for kernel process instance
|
||||
/// that will run said application (or applet).
|
||||
///
|
||||
/// Capabilities are a sequence of flag descriptors, that indicate various
|
||||
/// configurations and constraints for a particular process.
|
||||
///
|
||||
/// Flag types are indicated by a sequence of set low bits. E.g. the
|
||||
/// types are indicated with the low bits as follows (where x indicates "don't care"):
|
||||
///
|
||||
/// - Priority and core mask : 0bxxxxxxxxxxxx0111
|
||||
/// - Allowed service call mask: 0bxxxxxxxxxxx01111
|
||||
/// - Map physical memory : 0bxxxxxxxxx0111111
|
||||
/// - Map IO memory : 0bxxxxxxxx01111111
|
||||
/// - Interrupts : 0bxxxx011111111111
|
||||
/// - Application type : 0bxx01111111111111
|
||||
/// - Kernel version : 0bx011111111111111
|
||||
/// - Handle table size : 0b0111111111111111
|
||||
/// - Debugger flags : 0b1111111111111111
|
||||
///
|
||||
/// These are essentially a bit offset subtracted by 1 to create a mask.
|
||||
/// e.g. The first entry in the above list is simply bit 3 (value 8 -> 0b1000)
|
||||
/// subtracted by one (7 -> 0b0111)
|
||||
///
|
||||
/// An example of a bit layout (using the map physical layout):
|
||||
/// <example>
|
||||
/// The MapPhysical type indicates a sequence entry pair of:
|
||||
///
|
||||
/// [initial, memory_flags], where:
|
||||
///
|
||||
/// initial:
|
||||
/// bits:
|
||||
/// 7-24: Starting page to map memory at.
|
||||
/// 25 : Indicates if the memory should be mapped as read only.
|
||||
///
|
||||
/// memory_flags:
|
||||
/// bits:
|
||||
/// 7-20 : Number of pages to map
|
||||
/// 21-25: Seems to be reserved (still checked against though)
|
||||
/// 26 : Whether or not the memory being mapped is IO memory, or physical memory
|
||||
/// </example>
|
||||
///
|
||||
class ProcessCapabilities {
|
||||
public:
|
||||
using InterruptCapabilities = std::bitset<1024>;
|
||||
using SyscallCapabilities = std::bitset<128>;
|
||||
|
||||
ProcessCapabilities() = default;
|
||||
ProcessCapabilities(const ProcessCapabilities&) = delete;
|
||||
ProcessCapabilities(ProcessCapabilities&&) = default;
|
||||
|
||||
ProcessCapabilities& operator=(const ProcessCapabilities&) = delete;
|
||||
ProcessCapabilities& operator=(ProcessCapabilities&&) = default;
|
||||
|
||||
/// Initializes this process capabilities instance for a kernel process.
|
||||
///
|
||||
/// @param capabilities The capabilities to parse
|
||||
/// @param num_capabilities The number of capabilities to parse.
|
||||
/// @param vm_manager The memory manager to use for handling any mapping-related
|
||||
/// operations (such as mapping IO memory, etc).
|
||||
///
|
||||
/// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized,
|
||||
/// otherwise, an error code upon failure.
|
||||
///
|
||||
ResultCode InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities,
|
||||
VMManager& vm_manager);
|
||||
|
||||
/// Initializes this process capabilities instance for a userland process.
|
||||
///
|
||||
/// @param capabilities The capabilities to parse.
|
||||
/// @param num_capabilities The total number of capabilities to parse.
|
||||
/// @param vm_manager The memory manager to use for handling any mapping-related
|
||||
/// operations (such as mapping IO memory, etc).
|
||||
///
|
||||
/// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized,
|
||||
/// otherwise, an error code upon failure.
|
||||
///
|
||||
ResultCode InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities,
|
||||
VMManager& vm_manager);
|
||||
|
||||
/// Initializes this process capabilities instance for a process that does not
|
||||
/// have any metadata to parse.
|
||||
///
|
||||
/// This is necessary, as we allow running raw executables, and the internal
|
||||
/// kernel process capabilities also determine what CPU cores the process is
|
||||
/// allowed to run on, and what priorities are allowed for threads. It also
|
||||
/// determines the max handle table size, what the program type is, whether or
|
||||
/// not the process can be debugged, or whether it's possible for a process to
|
||||
/// forcibly debug another process.
|
||||
///
|
||||
/// Given the above, this essentially enables all capabilities across the board
|
||||
/// for the process. It allows the process to:
|
||||
///
|
||||
/// - Run on any core
|
||||
/// - Use any thread priority
|
||||
/// - Use the maximum amount of handles a process is allowed to.
|
||||
/// - Be debuggable
|
||||
/// - Forcibly debug other processes.
|
||||
///
|
||||
/// Note that this is not a behavior that the kernel allows a process to do via
|
||||
/// a single function like this. This is yuzu-specific behavior to handle
|
||||
/// executables with no capability descriptors whatsoever to derive behavior from.
|
||||
/// It being yuzu-specific is why this is also not the default behavior and not
|
||||
/// done by default in the constructor.
|
||||
///
|
||||
void InitializeForMetadatalessProcess();
|
||||
|
||||
/// Gets the allowable core mask
|
||||
u64 GetCoreMask() const {
|
||||
return core_mask;
|
||||
}
|
||||
|
||||
/// Gets the allowable priority mask
|
||||
u64 GetPriorityMask() const {
|
||||
return priority_mask;
|
||||
}
|
||||
|
||||
/// Gets the SVC access permission bits
|
||||
const SyscallCapabilities& GetServiceCapabilities() const {
|
||||
return svc_capabilities;
|
||||
}
|
||||
|
||||
/// Gets the valid interrupt bits.
|
||||
const InterruptCapabilities& GetInterruptCapabilities() const {
|
||||
return interrupt_capabilities;
|
||||
}
|
||||
|
||||
/// Gets the program type for this process.
|
||||
ProgramType GetProgramType() const {
|
||||
return program_type;
|
||||
}
|
||||
|
||||
/// Gets the number of total allowable handles for the process' handle table.
|
||||
u32 GetHandleTableSize() const {
|
||||
return handle_table_size;
|
||||
}
|
||||
|
||||
/// Gets the kernel version value.
|
||||
u32 GetKernelVersion() const {
|
||||
return kernel_version;
|
||||
}
|
||||
|
||||
/// Whether or not this process can be debugged.
|
||||
bool IsDebuggable() const {
|
||||
return is_debuggable;
|
||||
}
|
||||
|
||||
/// Whether or not this process can forcibly debug another
|
||||
/// process, even if that process is not considered debuggable.
|
||||
bool CanForceDebug() const {
|
||||
return can_force_debug;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Attempts to parse a given sequence of capability descriptors.
|
||||
///
|
||||
/// @param capabilities The sequence of capability descriptors to parse.
|
||||
/// @param num_capabilities The number of descriptors within the given sequence.
|
||||
/// @param vm_manager The memory manager that will perform any memory
|
||||
/// mapping if necessary.
|
||||
///
|
||||
/// @return RESULT_SUCCESS if no errors occur, otherwise an error code.
|
||||
///
|
||||
ResultCode ParseCapabilities(const u32* capabilities, std::size_t num_capabilities,
|
||||
VMManager& vm_manager);
|
||||
|
||||
/// Attempts to parse a capability descriptor that is only represented by a
|
||||
/// single flag set.
|
||||
///
|
||||
/// @param set_flags Running set of flags that are used to catch
|
||||
/// flags being initialized more than once when they shouldn't be.
|
||||
/// @param set_svc_bits Running set of bits representing the allowed supervisor calls mask.
|
||||
/// @param flag The flag to attempt to parse.
|
||||
/// @param vm_manager The memory manager that will perform any memory
|
||||
/// mapping if necessary.
|
||||
///
|
||||
/// @return RESULT_SUCCESS if no errors occurred, otherwise an error code.
|
||||
///
|
||||
ResultCode ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag,
|
||||
VMManager& vm_manager);
|
||||
|
||||
/// Clears the internal state of this process capability instance. Necessary,
|
||||
/// to have a sane starting point due to us allowing running executables without
|
||||
/// configuration metadata. We assume a process is not going to have metadata,
|
||||
/// and if it turns out that the process does, in fact, have metadata, then
|
||||
/// we attempt to parse it. Thus, we need this to reset data members back to
|
||||
/// a good state.
|
||||
///
|
||||
/// DO NOT ever make this a public member function. This isn't an invariant
|
||||
/// anything external should depend upon (and if anything comes to rely on it,
|
||||
/// you should immediately be questioning the design of that thing, not this
|
||||
/// class. If the kernel itself can run without depending on behavior like that,
|
||||
/// then so can yuzu).
|
||||
///
|
||||
void Clear();
|
||||
|
||||
/// Handles flags related to the priority and core number capability flags.
|
||||
ResultCode HandlePriorityCoreNumFlags(u32 flags);
|
||||
|
||||
/// Handles flags related to determining the allowable SVC mask.
|
||||
ResultCode HandleSyscallFlags(u32& set_svc_bits, u32 flags);
|
||||
|
||||
/// Handles flags related to mapping physical memory pages.
|
||||
ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, VMManager& vm_manager);
|
||||
|
||||
/// Handles flags related to mapping IO pages.
|
||||
ResultCode HandleMapIOFlags(u32 flags, VMManager& vm_manager);
|
||||
|
||||
/// Handles flags related to the interrupt capability flags.
|
||||
ResultCode HandleInterruptFlags(u32 flags);
|
||||
|
||||
/// Handles flags related to the program type.
|
||||
ResultCode HandleProgramTypeFlags(u32 flags);
|
||||
|
||||
/// Handles flags related to the handle table size.
|
||||
ResultCode HandleHandleTableFlags(u32 flags);
|
||||
|
||||
/// Handles flags related to the kernel version capability flags.
|
||||
ResultCode HandleKernelVersionFlags(u32 flags);
|
||||
|
||||
/// Handles flags related to debug-specific capabilities.
|
||||
ResultCode HandleDebugFlags(u32 flags);
|
||||
|
||||
SyscallCapabilities svc_capabilities;
|
||||
InterruptCapabilities interrupt_capabilities;
|
||||
|
||||
u64 core_mask = 0;
|
||||
u64 priority_mask = 0;
|
||||
|
||||
u32 handle_table_size = 0;
|
||||
u32 kernel_version = 0;
|
||||
|
||||
ProgramType program_type = ProgramType::SysModule;
|
||||
|
||||
bool is_debuggable = false;
|
||||
bool can_force_debug = false;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
@@ -865,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"},
|
||||
@@ -1043,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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -129,7 +129,10 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
|
||||
return ResultStatus::Error32BitISA;
|
||||
}
|
||||
|
||||
process.LoadFromMetadata(metadata);
|
||||
if (process.LoadFromMetadata(metadata).IsError()) {
|
||||
return ResultStatus::ErrorUnableToParseKernelMetadata;
|
||||
}
|
||||
|
||||
const FileSys::PatchManager pm(metadata.GetTitleID());
|
||||
|
||||
// Load NSO modules
|
||||
|
||||
@@ -93,7 +93,7 @@ std::string GetFileTypeString(FileType type) {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
constexpr std::array<const char*, 60> RESULT_MESSAGES{
|
||||
constexpr std::array<const char*, 62> RESULT_MESSAGES{
|
||||
"The operation completed successfully.",
|
||||
"The loader requested to load is already loaded.",
|
||||
"The operation is not implemented.",
|
||||
@@ -103,6 +103,7 @@ constexpr std::array<const char*, 60> RESULT_MESSAGES{
|
||||
"The NPDM has a bad ACI header,",
|
||||
"The NPDM file has a bad file access control.",
|
||||
"The NPDM has a bad file access header.",
|
||||
"The NPDM has bad kernel capability descriptors.",
|
||||
"The PFS/HFS partition has a bad header.",
|
||||
"The PFS/HFS partition has incorrect size as determined by the header.",
|
||||
"The NCA file has a bad header.",
|
||||
@@ -125,6 +126,7 @@ constexpr std::array<const char*, 60> RESULT_MESSAGES{
|
||||
"The file could not be found or does not exist.",
|
||||
"The game is missing a program metadata file (main.npdm).",
|
||||
"The game uses the currently-unimplemented 32-bit architecture.",
|
||||
"Unable to completely parse the kernel metadata when loading the emulated process",
|
||||
"The RomFS could not be found.",
|
||||
"The ELF file has incorrect size as determined by the header.",
|
||||
"There was a general error loading the NRO into emulated memory.",
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
class NACP;
|
||||
} // namespace FileSys
|
||||
|
||||
namespace Kernel {
|
||||
struct AddressMapping;
|
||||
class Process;
|
||||
@@ -67,6 +71,7 @@ enum class ResultStatus : u16 {
|
||||
ErrorBadACIHeader,
|
||||
ErrorBadFileAccessControl,
|
||||
ErrorBadFileAccessHeader,
|
||||
ErrorBadKernelCapabilityDescriptors,
|
||||
ErrorBadPFSHeader,
|
||||
ErrorIncorrectPFSFileSize,
|
||||
ErrorBadNCAHeader,
|
||||
@@ -89,6 +94,7 @@ enum class ResultStatus : u16 {
|
||||
ErrorNullFile,
|
||||
ErrorMissingNPDM,
|
||||
Error32BitISA,
|
||||
ErrorUnableToParseKernelMetadata,
|
||||
ErrorNoRomFS,
|
||||
ErrorIncorrectELFFileSize,
|
||||
ErrorLoadingNRO,
|
||||
@@ -245,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;
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -288,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
|
||||
@@ -620,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
|
||||
@@ -664,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -681,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:
|
||||
|
||||
@@ -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.
|
||||
@@ -401,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -419,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,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();
|
||||
@@ -1275,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: {
|
||||
@@ -1299,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;
|
||||
@@ -1319,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;
|
||||
}
|
||||
@@ -1342,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) {
|
||||
@@ -1357,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);
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
@@ -2071,9 +2086,10 @@ private:
|
||||
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: {
|
||||
@@ -2095,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 + ')';
|
||||
@@ -2105,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:
|
||||
@@ -2113,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());
|
||||
@@ -2127,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 + ')';
|
||||
@@ -2148,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: {
|
||||
@@ -2177,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 + ')';
|
||||
@@ -2186,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);
|
||||
|
||||
@@ -2249,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 + ')';
|
||||
@@ -2267,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:
|
||||
@@ -2291,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 + ')';
|
||||
@@ -2301,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;
|
||||
|
||||
@@ -2319,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);
|
||||
@@ -2335,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:
|
||||
@@ -2396,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;
|
||||
}
|
||||
@@ -2501,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: {
|
||||
@@ -2532,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: {
|
||||
@@ -2603,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) {
|
||||
@@ -2635,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) {
|
||||
@@ -2674,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) {
|
||||
@@ -2724,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: {
|
||||
@@ -2887,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{};
|
||||
@@ -2896,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;
|
||||
}
|
||||
@@ -2982,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;
|
||||
}
|
||||
@@ -3231,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);
|
||||
@@ -3248,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;
|
||||
}
|
||||
@@ -3368,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;
|
||||
}
|
||||
@@ -3462,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;
|
||||
@@ -3548,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: {
|
||||
@@ -3752,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: {
|
||||
|
||||
@@ -17,7 +17,7 @@ class EmuWindow;
|
||||
}
|
||||
|
||||
namespace Layout {
|
||||
class FramebufferLayout;
|
||||
struct FramebufferLayout;
|
||||
}
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
@@ -196,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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3,12 +3,8 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "ui_configure_input_simple.h"
|
||||
#include "yuzu/configuration/configure_input.h"
|
||||
#include "yuzu/configuration/configure_input_player.h"
|
||||
@@ -73,20 +69,18 @@ void DualJoyconsDockedOnProfileSelect() {
|
||||
|
||||
// Name, OnProfileSelect (called when selected in drop down), OnConfigure (called when configure
|
||||
// is clicked)
|
||||
using InputProfile =
|
||||
std::tuple<QString, std::function<void()>, std::function<void(ConfigureInputSimple*)>>;
|
||||
using InputProfile = std::tuple<const char*, void (*)(), void (*)(ConfigureInputSimple*)>;
|
||||
|
||||
const std::array<InputProfile, 3> INPUT_PROFILES{{
|
||||
{ConfigureInputSimple::tr("Single Player - Handheld - Undocked"), HandheldOnProfileSelect,
|
||||
constexpr std::array<InputProfile, 3> INPUT_PROFILES{{
|
||||
{QT_TR_NOOP("Single Player - Handheld - Undocked"), HandheldOnProfileSelect,
|
||||
[](ConfigureInputSimple* caller) {
|
||||
CallConfigureDialog<ConfigureInputPlayer>(caller, HANDHELD_INDEX, false);
|
||||
}},
|
||||
{ConfigureInputSimple::tr("Single Player - Dual Joycons - Docked"),
|
||||
DualJoyconsDockedOnProfileSelect,
|
||||
{QT_TR_NOOP("Single Player - Dual Joycons - Docked"), DualJoyconsDockedOnProfileSelect,
|
||||
[](ConfigureInputSimple* caller) {
|
||||
CallConfigureDialog<ConfigureInputPlayer>(caller, 1, false);
|
||||
}},
|
||||
{ConfigureInputSimple::tr("Custom"), [] {}, CallConfigureDialog<ConfigureInput>},
|
||||
{QT_TR_NOOP("Custom"), [] {}, CallConfigureDialog<ConfigureInput>},
|
||||
}};
|
||||
|
||||
} // namespace
|
||||
@@ -101,7 +95,8 @@ ConfigureInputSimple::ConfigureInputSimple(QWidget* parent)
|
||||
ui->setupUi(this);
|
||||
|
||||
for (const auto& profile : INPUT_PROFILES) {
|
||||
ui->profile_combobox->addItem(std::get<0>(profile), std::get<0>(profile));
|
||||
const QString label = tr(std::get<0>(profile));
|
||||
ui->profile_combobox->addItem(label, label);
|
||||
}
|
||||
|
||||
connect(ui->profile_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user