Compare commits
104 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
289adf87ac | ||
|
|
1291f3f820 | ||
|
|
e7e209d900 | ||
|
|
5716496239 | ||
|
|
0f3d8c2574 | ||
|
|
75d807788c | ||
|
|
d9ca6351dd | ||
|
|
2ff2732a78 | ||
|
|
7d6dca0d0a | ||
|
|
5dfb43531c | ||
|
|
848a49112a | ||
|
|
496d155d7b | ||
|
|
40c63073a9 | ||
|
|
4cccfb4190 | ||
|
|
259da93567 | ||
|
|
8e1239fbc5 | ||
|
|
ff6b2d4574 | ||
|
|
59a004f915 | ||
|
|
17315cee16 | ||
|
|
bcb5b924fd | ||
|
|
7b39107e3a | ||
|
|
aa620c14af | ||
|
|
65df593951 | ||
|
|
d9923b0dbc | ||
|
|
1226a5706e | ||
|
|
5c5b4e8e7d | ||
|
|
fcad3a734d | ||
|
|
38fa3aae73 | ||
|
|
e2416bbd1f | ||
|
|
314a948373 | ||
|
|
12fa570d49 | ||
|
|
ca5a93167e | ||
|
|
981faea4d6 | ||
|
|
5ea4cfd499 | ||
|
|
edb1c36a87 | ||
|
|
ae7f55947e | ||
|
|
a806c78a1a | ||
|
|
f034121620 | ||
|
|
a279d80a19 | ||
|
|
10a2d20e26 | ||
|
|
3b8c0f8885 | ||
|
|
98a27b1ec7 | ||
|
|
adb9eda105 | ||
|
|
c1e5525fc6 | ||
|
|
d53c73adaa | ||
|
|
dd1ee39426 | ||
|
|
08e574eec4 | ||
|
|
896c0f61a0 | ||
|
|
8a86c8d48b | ||
|
|
381baf783d | ||
|
|
61ef8af1e2 | ||
|
|
8dc7db7e33 | ||
|
|
3ec795d95e | ||
|
|
a03600ba28 | ||
|
|
b1f8bff7db | ||
|
|
60317e6306 | ||
|
|
4849569565 | ||
|
|
bf66930fb9 | ||
|
|
52b25e0fb9 | ||
|
|
298ebf444f | ||
|
|
7e0d2fc994 | ||
|
|
c91be25b93 | ||
|
|
0aef2b9c26 | ||
|
|
12401a0d87 | ||
|
|
7a7dad05c0 | ||
|
|
1833498617 | ||
|
|
a653be3510 | ||
|
|
2b9fd23058 | ||
|
|
4f52800822 | ||
|
|
f6c5a48dd1 | ||
|
|
d0cda7fe40 | ||
|
|
d16bafc99d | ||
|
|
a056b284cf | ||
|
|
7e665c2721 | ||
|
|
bcde71d4d9 | ||
|
|
a5d853a9f8 | ||
|
|
6b333d862b | ||
|
|
fdd82b754a | ||
|
|
7f152f2273 | ||
|
|
e5d428cf1e | ||
|
|
0291a86f60 | ||
|
|
f109615be0 | ||
|
|
98c7a6d622 | ||
|
|
33830aa65a | ||
|
|
452aa30cb7 | ||
|
|
7f52dc1790 | ||
|
|
7eb2328d8e | ||
|
|
929ed59f1f | ||
|
|
aeda743446 | ||
|
|
aeca224890 | ||
|
|
46202e984e | ||
|
|
119b47f366 | ||
|
|
41fb25349a | ||
|
|
871350ae35 | ||
|
|
441b5b97bd | ||
|
|
53e77ffbfe | ||
|
|
d6604fa765 | ||
|
|
4783ad54de | ||
|
|
6312eec5ef | ||
|
|
4fc8ad67bf | ||
|
|
3d65aa4caf | ||
|
|
d93cdc2750 | ||
|
|
d46e2a6e7a | ||
|
|
08d751d882 |
@@ -170,7 +170,7 @@ endif()
|
||||
# On modern Unixes, this is typically already the case. The lone exception is
|
||||
# glibc, which may default to 32 bits. glibc allows this to be configured
|
||||
# by setting _FILE_OFFSET_BITS.
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR MINGW)
|
||||
add_definitions(-D_FILE_OFFSET_BITS=64)
|
||||
endif()
|
||||
|
||||
@@ -178,10 +178,6 @@ endif()
|
||||
set_property(DIRECTORY APPEND PROPERTY
|
||||
COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
|
||||
|
||||
|
||||
math(EXPR EMU_ARCH_BITS ${CMAKE_SIZEOF_VOID_P}*8)
|
||||
add_definitions(-DEMU_ARCH_BITS=${EMU_ARCH_BITS})
|
||||
|
||||
# System imported libraries
|
||||
# ======================
|
||||
|
||||
|
||||
@@ -64,8 +64,6 @@ add_library(common STATIC
|
||||
logging/text_formatter.cpp
|
||||
logging/text_formatter.h
|
||||
math_util.h
|
||||
memory_util.cpp
|
||||
memory_util.h
|
||||
microprofile.cpp
|
||||
microprofile.h
|
||||
microprofileui.h
|
||||
|
||||
@@ -15,21 +15,24 @@
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
// windows.h needs to be included before other windows headers
|
||||
#include <commdlg.h> // for GetSaveFileName
|
||||
#include <direct.h> // getcwd
|
||||
#include <direct.h> // getcwd
|
||||
#include <io.h>
|
||||
#include <shellapi.h>
|
||||
#include <shlobj.h> // for SHGetFolderPath
|
||||
#include <tchar.h>
|
||||
#include "common/string_util.h"
|
||||
|
||||
// 64 bit offsets for windows
|
||||
#ifdef _MSC_VER
|
||||
// 64 bit offsets for MSVC
|
||||
#define fseeko _fseeki64
|
||||
#define ftello _ftelli64
|
||||
#define atoll _atoi64
|
||||
#define fileno _fileno
|
||||
#endif
|
||||
|
||||
// 64 bit offsets for MSVC and MinGW. MinGW also needs this for using _wstat64
|
||||
#define stat _stat64
|
||||
#define fstat _fstat64
|
||||
#define fileno _fileno
|
||||
|
||||
#else
|
||||
#ifdef __APPLE__
|
||||
#include <sys/param.h>
|
||||
|
||||
@@ -91,6 +91,7 @@ enum class Class : ClassType {
|
||||
Service_PM, ///< The PM service
|
||||
Service_PREPO, ///< The PREPO (Play report) service
|
||||
Service_PSC, ///< The PSC service
|
||||
Service_PSM, ///< The PSM service
|
||||
Service_SET, ///< The SET (Settings) service
|
||||
Service_SM, ///< The SM (Service manager) service
|
||||
Service_SPL, ///< The SPL service
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/memory_util.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
// Windows.h needs to be included before psapi.h
|
||||
#include <psapi.h>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/string_util.h"
|
||||
#else
|
||||
#include <cstdlib>
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
|
||||
#include <unistd.h>
|
||||
#define PAGE_MASK (getpagesize() - 1)
|
||||
#define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK))
|
||||
#endif
|
||||
|
||||
// This is purposely not a full wrapper for virtualalloc/mmap, but it
|
||||
// provides exactly the primitive operations that Dolphin needs.
|
||||
|
||||
void* AllocateExecutableMemory(std::size_t size, bool low) {
|
||||
#if defined(_WIN32)
|
||||
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||
#else
|
||||
static char* map_hint = nullptr;
|
||||
#if defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
|
||||
// This OS has no flag to enforce allocation below the 4 GB boundary,
|
||||
// but if we hint that we want a low address it is very likely we will
|
||||
// get one.
|
||||
// An older version of this code used MAP_FIXED, but that has the side
|
||||
// effect of discarding already mapped pages that happen to be in the
|
||||
// requested virtual memory range (such as the emulated RAM, sometimes).
|
||||
if (low && (!map_hint))
|
||||
map_hint = (char*)round_page(512 * 1024 * 1024); /* 0.5 GB rounded up to the next page */
|
||||
#endif
|
||||
void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_ANON | MAP_PRIVATE
|
||||
#if defined(ARCHITECTURE_x86_64) && defined(MAP_32BIT)
|
||||
| (low ? MAP_32BIT : 0)
|
||||
#endif
|
||||
,
|
||||
-1, 0);
|
||||
#endif /* defined(_WIN32) */
|
||||
|
||||
#ifdef _WIN32
|
||||
if (ptr == nullptr) {
|
||||
#else
|
||||
if (ptr == MAP_FAILED) {
|
||||
ptr = nullptr;
|
||||
#endif
|
||||
LOG_ERROR(Common_Memory, "Failed to allocate executable memory");
|
||||
}
|
||||
#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
|
||||
else {
|
||||
if (low) {
|
||||
map_hint += size;
|
||||
map_hint = (char*)round_page(map_hint); /* round up to the next page */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if EMU_ARCH_BITS == 64
|
||||
if ((u64)ptr >= 0x80000000 && low == true)
|
||||
LOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!");
|
||||
#endif
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) {
|
||||
#ifdef _WIN32
|
||||
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
#else
|
||||
void* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
|
||||
if (ptr == MAP_FAILED)
|
||||
ptr = nullptr;
|
||||
#endif
|
||||
|
||||
if (ptr == nullptr)
|
||||
LOG_ERROR(Common_Memory, "Failed to allocate raw memory");
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* AllocateAlignedMemory(std::size_t size, std::size_t alignment) {
|
||||
#ifdef _WIN32
|
||||
void* ptr = _aligned_malloc(size, alignment);
|
||||
#else
|
||||
void* ptr = nullptr;
|
||||
#ifdef ANDROID
|
||||
ptr = memalign(alignment, size);
|
||||
#else
|
||||
if (posix_memalign(&ptr, alignment, size) != 0)
|
||||
LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (ptr == nullptr)
|
||||
LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void FreeMemoryPages(void* ptr, std::size_t size) {
|
||||
if (ptr) {
|
||||
#ifdef _WIN32
|
||||
if (!VirtualFree(ptr, 0, MEM_RELEASE))
|
||||
LOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n{}", GetLastErrorMsg());
|
||||
#else
|
||||
munmap(ptr, size);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void FreeAlignedMemory(void* ptr) {
|
||||
if (ptr) {
|
||||
#ifdef _WIN32
|
||||
_aligned_free(ptr);
|
||||
#else
|
||||
free(ptr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void WriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) {
|
||||
#ifdef _WIN32
|
||||
DWORD oldValue;
|
||||
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue))
|
||||
LOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n{}", GetLastErrorMsg());
|
||||
#else
|
||||
mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ);
|
||||
#endif
|
||||
}
|
||||
|
||||
void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) {
|
||||
#ifdef _WIN32
|
||||
DWORD oldValue;
|
||||
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE,
|
||||
&oldValue))
|
||||
LOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n{}", GetLastErrorMsg());
|
||||
#else
|
||||
mprotect(ptr, size,
|
||||
allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string MemUsage() {
|
||||
#ifdef _WIN32
|
||||
#pragma comment(lib, "psapi")
|
||||
DWORD processID = GetCurrentProcessId();
|
||||
HANDLE hProcess;
|
||||
PROCESS_MEMORY_COUNTERS pmc;
|
||||
std::string Ret;
|
||||
|
||||
// Print information about the memory usage of the process.
|
||||
|
||||
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
|
||||
if (nullptr == hProcess)
|
||||
return "MemUsage Error";
|
||||
|
||||
if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
|
||||
Ret = fmt::format("{} K", Common::ThousandSeparate(pmc.WorkingSetSize / 1024, 7));
|
||||
|
||||
CloseHandle(hProcess);
|
||||
return Ret;
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
void* AllocateExecutableMemory(std::size_t size, bool low = true);
|
||||
void* AllocateMemoryPages(std::size_t size);
|
||||
void FreeMemoryPages(void* ptr, std::size_t size);
|
||||
void* AllocateAlignedMemory(std::size_t size, std::size_t alignment);
|
||||
void FreeAlignedMemory(void* ptr);
|
||||
void WriteProtectMemory(void* ptr, std::size_t size, bool executable = false);
|
||||
void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute = false);
|
||||
std::string MemUsage();
|
||||
|
||||
inline int GetPageSize() {
|
||||
return 4096;
|
||||
}
|
||||
@@ -156,6 +156,8 @@ add_library(core STATIC
|
||||
hle/service/am/omm.h
|
||||
hle/service/am/spsm.cpp
|
||||
hle/service/am/spsm.h
|
||||
hle/service/am/tcap.cpp
|
||||
hle/service/am/tcap.h
|
||||
hle/service/aoc/aoc_u.cpp
|
||||
hle/service/aoc/aoc_u.h
|
||||
hle/service/apm/apm.cpp
|
||||
@@ -280,6 +282,8 @@ add_library(core STATIC
|
||||
hle/service/nifm/nifm.h
|
||||
hle/service/nim/nim.cpp
|
||||
hle/service/nim/nim.h
|
||||
hle/service/npns/npns.cpp
|
||||
hle/service/npns/npns.h
|
||||
hle/service/ns/ns.cpp
|
||||
hle/service/ns/ns.h
|
||||
hle/service/ns/pl_u.cpp
|
||||
@@ -327,6 +331,8 @@ add_library(core STATIC
|
||||
hle/service/prepo/prepo.h
|
||||
hle/service/psc/psc.cpp
|
||||
hle/service/psc/psc.h
|
||||
hle/service/ptm/psm.cpp
|
||||
hle/service/ptm/psm.h
|
||||
hle/service/service.cpp
|
||||
hle/service/service.h
|
||||
hle/service/set/set.cpp
|
||||
|
||||
@@ -375,8 +375,7 @@ const Kernel::Process* System::CurrentProcess() const {
|
||||
}
|
||||
|
||||
ARM_Interface& System::ArmInterface(std::size_t core_index) {
|
||||
ASSERT(core_index < NUM_CPU_CORES);
|
||||
return impl->cpu_cores[core_index]->ArmInterface();
|
||||
return CpuCore(core_index).ArmInterface();
|
||||
}
|
||||
|
||||
Cpu& System::CpuCore(std::size_t core_index) {
|
||||
|
||||
@@ -516,7 +516,8 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa
|
||||
out.insert(out.end(), rodata.begin(), rodata.end());
|
||||
out.insert(out.end(), data.begin(), data.end());
|
||||
|
||||
offset += sizeof(KIPHeader) + out.size();
|
||||
offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
|
||||
kip.sections[1].size_compressed + kip.sections[2].size_compressed;
|
||||
|
||||
if (name == "FS")
|
||||
package2_fs[static_cast<size_t>(type)] = std::move(out);
|
||||
|
||||
@@ -97,11 +97,288 @@ union NCASectionHeader {
|
||||
};
|
||||
static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size.");
|
||||
|
||||
bool IsValidNCA(const NCAHeader& header) {
|
||||
static bool IsValidNCA(const NCAHeader& header) {
|
||||
// TODO(DarkLordZach): Add NCA2/NCA0 support.
|
||||
return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
|
||||
}
|
||||
|
||||
NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
|
||||
: file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) {
|
||||
if (file == nullptr) {
|
||||
status = Loader::ResultStatus::ErrorNullFile;
|
||||
return;
|
||||
}
|
||||
|
||||
if (sizeof(NCAHeader) != file->ReadObject(&header)) {
|
||||
LOG_ERROR(Loader, "File reader errored out during header read.");
|
||||
status = Loader::ResultStatus::ErrorBadNCAHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HandlePotentialHeaderDecryption()) {
|
||||
return;
|
||||
}
|
||||
|
||||
has_rights_id = std::any_of(header.rights_id.begin(), header.rights_id.end(),
|
||||
[](char c) { return c != '\0'; });
|
||||
|
||||
const std::vector<NCASectionHeader> sections = ReadSectionHeaders();
|
||||
is_update = std::any_of(sections.begin(), sections.end(), [](const NCASectionHeader& header) {
|
||||
return header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
|
||||
});
|
||||
|
||||
if (!ReadSections(sections, bktr_base_ivfc_offset)) {
|
||||
return;
|
||||
}
|
||||
|
||||
status = Loader::ResultStatus::Success;
|
||||
}
|
||||
|
||||
NCA::~NCA() = default;
|
||||
|
||||
bool NCA::CheckSupportedNCA(const NCAHeader& nca_header) {
|
||||
if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
|
||||
status = Loader::ResultStatus::ErrorNCA2;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
|
||||
status = Loader::ResultStatus::ErrorNCA0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NCA::HandlePotentialHeaderDecryption() {
|
||||
if (IsValidNCA(header)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!CheckSupportedNCA(header)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NCAHeader dec_header{};
|
||||
Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
|
||||
keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
|
||||
cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200,
|
||||
Core::Crypto::Op::Decrypt);
|
||||
if (IsValidNCA(dec_header)) {
|
||||
header = dec_header;
|
||||
encrypted = true;
|
||||
} else {
|
||||
if (!CheckSupportedNCA(dec_header)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (keys.HasKey(Core::Crypto::S256KeyType::Header)) {
|
||||
status = Loader::ResultStatus::ErrorIncorrectHeaderKey;
|
||||
} else {
|
||||
status = Loader::ResultStatus::ErrorMissingHeaderKey;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<NCASectionHeader> NCA::ReadSectionHeaders() const {
|
||||
const std::ptrdiff_t number_sections =
|
||||
std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
|
||||
[](NCASectionTableEntry entry) { return entry.media_offset > 0; });
|
||||
|
||||
std::vector<NCASectionHeader> sections(number_sections);
|
||||
const auto length_sections = SECTION_HEADER_SIZE * number_sections;
|
||||
|
||||
if (encrypted) {
|
||||
auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET);
|
||||
Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
|
||||
keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
|
||||
cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE,
|
||||
Core::Crypto::Op::Decrypt);
|
||||
} else {
|
||||
file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
|
||||
}
|
||||
|
||||
return sections;
|
||||
}
|
||||
|
||||
bool NCA::ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_base_ivfc_offset) {
|
||||
for (std::size_t i = 0; i < sections.size(); ++i) {
|
||||
const auto& section = sections[i];
|
||||
|
||||
if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
|
||||
if (!ReadRomFSSection(section, header.section_tables[i], bktr_base_ivfc_offset)) {
|
||||
return false;
|
||||
}
|
||||
} else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
|
||||
if (!ReadPFS0Section(section, header.section_tables[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry,
|
||||
u64 bktr_base_ivfc_offset) {
|
||||
const std::size_t base_offset = entry.media_offset * MEDIA_OFFSET_MULTIPLIER;
|
||||
ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
|
||||
const std::size_t romfs_offset = base_offset + ivfc_offset;
|
||||
const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
|
||||
auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
|
||||
auto dec = Decrypt(section, raw, romfs_offset);
|
||||
|
||||
if (dec == nullptr) {
|
||||
if (status != Loader::ResultStatus::Success)
|
||||
return false;
|
||||
if (has_rights_id)
|
||||
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
|
||||
else
|
||||
status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) {
|
||||
if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') ||
|
||||
section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) {
|
||||
status = Loader::ResultStatus::ErrorBadBKTRHeader;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (section.bktr.relocation.offset + section.bktr.relocation.size !=
|
||||
section.bktr.subsection.offset) {
|
||||
status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation;
|
||||
return false;
|
||||
}
|
||||
|
||||
const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset);
|
||||
if (section.bktr.subsection.offset + section.bktr.subsection.size != size) {
|
||||
status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd;
|
||||
return false;
|
||||
}
|
||||
|
||||
const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
|
||||
RelocationBlock relocation_block{};
|
||||
if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) !=
|
||||
sizeof(RelocationBlock)) {
|
||||
status = Loader::ResultStatus::ErrorBadRelocationBlock;
|
||||
return false;
|
||||
}
|
||||
SubsectionBlock subsection_block{};
|
||||
if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) !=
|
||||
sizeof(RelocationBlock)) {
|
||||
status = Loader::ResultStatus::ErrorBadSubsectionBlock;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<RelocationBucketRaw> relocation_buckets_raw(
|
||||
(section.bktr.relocation.size - sizeof(RelocationBlock)) / sizeof(RelocationBucketRaw));
|
||||
if (dec->ReadBytes(relocation_buckets_raw.data(),
|
||||
section.bktr.relocation.size - sizeof(RelocationBlock),
|
||||
section.bktr.relocation.offset + sizeof(RelocationBlock) - offset) !=
|
||||
section.bktr.relocation.size - sizeof(RelocationBlock)) {
|
||||
status = Loader::ResultStatus::ErrorBadRelocationBuckets;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<SubsectionBucketRaw> subsection_buckets_raw(
|
||||
(section.bktr.subsection.size - sizeof(SubsectionBlock)) / sizeof(SubsectionBucketRaw));
|
||||
if (dec->ReadBytes(subsection_buckets_raw.data(),
|
||||
section.bktr.subsection.size - sizeof(SubsectionBlock),
|
||||
section.bktr.subsection.offset + sizeof(SubsectionBlock) - offset) !=
|
||||
section.bktr.subsection.size - sizeof(SubsectionBlock)) {
|
||||
status = Loader::ResultStatus::ErrorBadSubsectionBuckets;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
|
||||
std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
|
||||
relocation_buckets.begin(), &ConvertRelocationBucketRaw);
|
||||
std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size());
|
||||
std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(),
|
||||
subsection_buckets.begin(), &ConvertSubsectionBucketRaw);
|
||||
|
||||
u32 ctr_low;
|
||||
std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low));
|
||||
subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low});
|
||||
subsection_buckets.back().entries.push_back({size, {0}, 0});
|
||||
|
||||
boost::optional<Core::Crypto::Key128> key = boost::none;
|
||||
if (encrypted) {
|
||||
if (has_rights_id) {
|
||||
status = Loader::ResultStatus::Success;
|
||||
key = GetTitlekey();
|
||||
if (key == boost::none) {
|
||||
status = Loader::ResultStatus::ErrorMissingTitlekey;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
|
||||
if (key == boost::none) {
|
||||
status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bktr_base_romfs == nullptr) {
|
||||
status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto bktr = std::make_shared<BKTR>(
|
||||
bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
|
||||
relocation_block, relocation_buckets, subsection_block, subsection_buckets, encrypted,
|
||||
encrypted ? key.get() : Core::Crypto::Key128{}, base_offset, bktr_base_ivfc_offset,
|
||||
section.raw.section_ctr);
|
||||
|
||||
// BKTR applies to entire IVFC, so make an offset version to level 6
|
||||
files.push_back(std::make_shared<OffsetVfsFile>(
|
||||
bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset));
|
||||
} else {
|
||||
files.push_back(std::move(dec));
|
||||
}
|
||||
|
||||
romfs = files.back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NCA::ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry) {
|
||||
const u64 offset = (static_cast<u64>(entry.media_offset) * MEDIA_OFFSET_MULTIPLIER) +
|
||||
section.pfs0.pfs0_header_offset;
|
||||
const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset);
|
||||
|
||||
auto dec = Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
|
||||
if (dec != nullptr) {
|
||||
auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
|
||||
|
||||
if (npfs->GetStatus() == Loader::ResultStatus::Success) {
|
||||
dirs.push_back(std::move(npfs));
|
||||
if (IsDirectoryExeFS(dirs.back()))
|
||||
exefs = dirs.back();
|
||||
} else {
|
||||
if (has_rights_id)
|
||||
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
|
||||
else
|
||||
status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (status != Loader::ResultStatus::Success)
|
||||
return false;
|
||||
if (has_rights_id)
|
||||
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
|
||||
else
|
||||
status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u8 NCA::GetCryptoRevision() const {
|
||||
u8 master_key_id = header.crypto_type;
|
||||
if (header.crypto_type_2 > master_key_id)
|
||||
@@ -167,7 +444,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
|
||||
return titlekey;
|
||||
}
|
||||
|
||||
VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) {
|
||||
VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 starting_offset) {
|
||||
if (!encrypted)
|
||||
return in;
|
||||
|
||||
@@ -215,256 +492,6 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
|
||||
}
|
||||
}
|
||||
|
||||
NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
|
||||
: file(std::move(file_)),
|
||||
bktr_base_romfs(bktr_base_romfs_ ? std::move(bktr_base_romfs_) : nullptr) {
|
||||
status = Loader::ResultStatus::Success;
|
||||
|
||||
if (file == nullptr) {
|
||||
status = Loader::ResultStatus::ErrorNullFile;
|
||||
return;
|
||||
}
|
||||
|
||||
if (sizeof(NCAHeader) != file->ReadObject(&header)) {
|
||||
LOG_ERROR(Loader, "File reader errored out during header read.");
|
||||
status = Loader::ResultStatus::ErrorBadNCAHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
encrypted = false;
|
||||
|
||||
if (!IsValidNCA(header)) {
|
||||
if (header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
|
||||
status = Loader::ResultStatus::ErrorNCA2;
|
||||
return;
|
||||
}
|
||||
if (header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
|
||||
status = Loader::ResultStatus::ErrorNCA0;
|
||||
return;
|
||||
}
|
||||
|
||||
NCAHeader dec_header{};
|
||||
Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
|
||||
keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
|
||||
cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200,
|
||||
Core::Crypto::Op::Decrypt);
|
||||
if (IsValidNCA(dec_header)) {
|
||||
header = dec_header;
|
||||
encrypted = true;
|
||||
} else {
|
||||
if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
|
||||
status = Loader::ResultStatus::ErrorNCA2;
|
||||
return;
|
||||
}
|
||||
if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
|
||||
status = Loader::ResultStatus::ErrorNCA0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!keys.HasKey(Core::Crypto::S256KeyType::Header))
|
||||
status = Loader::ResultStatus::ErrorMissingHeaderKey;
|
||||
else
|
||||
status = Loader::ResultStatus::ErrorIncorrectHeaderKey;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
has_rights_id = std::find_if_not(header.rights_id.begin(), header.rights_id.end(),
|
||||
[](char c) { return c == '\0'; }) != header.rights_id.end();
|
||||
|
||||
const std::ptrdiff_t number_sections =
|
||||
std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
|
||||
[](NCASectionTableEntry entry) { return entry.media_offset > 0; });
|
||||
|
||||
std::vector<NCASectionHeader> sections(number_sections);
|
||||
const auto length_sections = SECTION_HEADER_SIZE * number_sections;
|
||||
|
||||
if (encrypted) {
|
||||
auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET);
|
||||
Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
|
||||
keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
|
||||
cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE,
|
||||
Core::Crypto::Op::Decrypt);
|
||||
} else {
|
||||
file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
|
||||
}
|
||||
|
||||
is_update = std::find_if(sections.begin(), sections.end(), [](const NCASectionHeader& header) {
|
||||
return header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
|
||||
}) != sections.end();
|
||||
ivfc_offset = 0;
|
||||
|
||||
for (std::ptrdiff_t i = 0; i < number_sections; ++i) {
|
||||
auto section = sections[i];
|
||||
|
||||
if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
|
||||
const std::size_t base_offset =
|
||||
header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER;
|
||||
ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
|
||||
const std::size_t romfs_offset = base_offset + ivfc_offset;
|
||||
const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
|
||||
auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
|
||||
auto dec = Decrypt(section, raw, romfs_offset);
|
||||
|
||||
if (dec == nullptr) {
|
||||
if (status != Loader::ResultStatus::Success)
|
||||
return;
|
||||
if (has_rights_id)
|
||||
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
|
||||
else
|
||||
status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
|
||||
return;
|
||||
}
|
||||
|
||||
if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) {
|
||||
if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') ||
|
||||
section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) {
|
||||
status = Loader::ResultStatus::ErrorBadBKTRHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
if (section.bktr.relocation.offset + section.bktr.relocation.size !=
|
||||
section.bktr.subsection.offset) {
|
||||
status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation;
|
||||
return;
|
||||
}
|
||||
|
||||
const u64 size =
|
||||
MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
|
||||
header.section_tables[i].media_offset);
|
||||
if (section.bktr.subsection.offset + section.bktr.subsection.size != size) {
|
||||
status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd;
|
||||
return;
|
||||
}
|
||||
|
||||
const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
|
||||
RelocationBlock relocation_block{};
|
||||
if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) !=
|
||||
sizeof(RelocationBlock)) {
|
||||
status = Loader::ResultStatus::ErrorBadRelocationBlock;
|
||||
return;
|
||||
}
|
||||
SubsectionBlock subsection_block{};
|
||||
if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) !=
|
||||
sizeof(RelocationBlock)) {
|
||||
status = Loader::ResultStatus::ErrorBadSubsectionBlock;
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<RelocationBucketRaw> relocation_buckets_raw(
|
||||
(section.bktr.relocation.size - sizeof(RelocationBlock)) /
|
||||
sizeof(RelocationBucketRaw));
|
||||
if (dec->ReadBytes(relocation_buckets_raw.data(),
|
||||
section.bktr.relocation.size - sizeof(RelocationBlock),
|
||||
section.bktr.relocation.offset + sizeof(RelocationBlock) -
|
||||
offset) !=
|
||||
section.bktr.relocation.size - sizeof(RelocationBlock)) {
|
||||
status = Loader::ResultStatus::ErrorBadRelocationBuckets;
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<SubsectionBucketRaw> subsection_buckets_raw(
|
||||
(section.bktr.subsection.size - sizeof(SubsectionBlock)) /
|
||||
sizeof(SubsectionBucketRaw));
|
||||
if (dec->ReadBytes(subsection_buckets_raw.data(),
|
||||
section.bktr.subsection.size - sizeof(SubsectionBlock),
|
||||
section.bktr.subsection.offset + sizeof(SubsectionBlock) -
|
||||
offset) !=
|
||||
section.bktr.subsection.size - sizeof(SubsectionBlock)) {
|
||||
status = Loader::ResultStatus::ErrorBadSubsectionBuckets;
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
|
||||
std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
|
||||
relocation_buckets.begin(), &ConvertRelocationBucketRaw);
|
||||
std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size());
|
||||
std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(),
|
||||
subsection_buckets.begin(), &ConvertSubsectionBucketRaw);
|
||||
|
||||
u32 ctr_low;
|
||||
std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low));
|
||||
subsection_buckets.back().entries.push_back(
|
||||
{section.bktr.relocation.offset, {0}, ctr_low});
|
||||
subsection_buckets.back().entries.push_back({size, {0}, 0});
|
||||
|
||||
boost::optional<Core::Crypto::Key128> key = boost::none;
|
||||
if (encrypted) {
|
||||
if (has_rights_id) {
|
||||
status = Loader::ResultStatus::Success;
|
||||
key = GetTitlekey();
|
||||
if (key == boost::none) {
|
||||
status = Loader::ResultStatus::ErrorMissingTitlekey;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
|
||||
if (key == boost::none) {
|
||||
status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bktr_base_romfs == nullptr) {
|
||||
status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
|
||||
return;
|
||||
}
|
||||
|
||||
auto bktr = std::make_shared<BKTR>(
|
||||
bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
|
||||
relocation_block, relocation_buckets, subsection_block, subsection_buckets,
|
||||
encrypted, encrypted ? key.get() : Core::Crypto::Key128{}, base_offset,
|
||||
bktr_base_ivfc_offset, section.raw.section_ctr);
|
||||
|
||||
// BKTR applies to entire IVFC, so make an offset version to level 6
|
||||
|
||||
files.push_back(std::make_shared<OffsetVfsFile>(
|
||||
bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset));
|
||||
romfs = files.back();
|
||||
} else {
|
||||
files.push_back(std::move(dec));
|
||||
romfs = files.back();
|
||||
}
|
||||
} else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
|
||||
u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
|
||||
MEDIA_OFFSET_MULTIPLIER) +
|
||||
section.pfs0.pfs0_header_offset;
|
||||
u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
|
||||
header.section_tables[i].media_offset);
|
||||
auto dec =
|
||||
Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
|
||||
if (dec != nullptr) {
|
||||
auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
|
||||
|
||||
if (npfs->GetStatus() == Loader::ResultStatus::Success) {
|
||||
dirs.push_back(std::move(npfs));
|
||||
if (IsDirectoryExeFS(dirs.back()))
|
||||
exefs = dirs.back();
|
||||
} else {
|
||||
if (has_rights_id)
|
||||
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
|
||||
else
|
||||
status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (status != Loader::ResultStatus::Success)
|
||||
return;
|
||||
if (has_rights_id)
|
||||
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
|
||||
else
|
||||
status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
status = Loader::ResultStatus::Success;
|
||||
}
|
||||
|
||||
NCA::~NCA() = default;
|
||||
|
||||
Loader::ResultStatus NCA::GetStatus() const {
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -73,8 +73,6 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
|
||||
return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr;
|
||||
}
|
||||
|
||||
bool IsValidNCA(const NCAHeader& header);
|
||||
|
||||
// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner.
|
||||
// After construction, use GetStatus to determine if the file is valid and ready to be used.
|
||||
class NCA : public ReadOnlyVfsDirectory {
|
||||
@@ -106,10 +104,19 @@ protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
private:
|
||||
bool CheckSupportedNCA(const NCAHeader& header);
|
||||
bool HandlePotentialHeaderDecryption();
|
||||
|
||||
std::vector<NCASectionHeader> ReadSectionHeaders() const;
|
||||
bool ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_base_ivfc_offset);
|
||||
bool ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry,
|
||||
u64 bktr_base_ivfc_offset);
|
||||
bool ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry);
|
||||
|
||||
u8 GetCryptoRevision() const;
|
||||
boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
|
||||
boost::optional<Core::Crypto::Key128> GetTitlekey();
|
||||
VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset);
|
||||
VirtualFile Decrypt(const NCASectionHeader& header, VirtualFile in, u64 starting_offset);
|
||||
|
||||
std::vector<VirtualDir> dirs;
|
||||
std::vector<VirtualFile> files;
|
||||
@@ -118,15 +125,15 @@ private:
|
||||
VirtualDir exefs = nullptr;
|
||||
VirtualFile file;
|
||||
VirtualFile bktr_base_romfs;
|
||||
u64 ivfc_offset;
|
||||
u64 ivfc_offset = 0;
|
||||
|
||||
NCAHeader header{};
|
||||
bool has_rights_id{};
|
||||
|
||||
Loader::ResultStatus status{};
|
||||
|
||||
bool encrypted;
|
||||
bool is_update;
|
||||
bool encrypted = false;
|
||||
bool is_update = false;
|
||||
|
||||
Core::Crypto::KeyManager keys;
|
||||
};
|
||||
|
||||
@@ -584,6 +584,10 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
|
||||
return ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
if (!IsValidAddressRange(addr, size)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
const auto permissions_type = static_cast<MemoryPermission>(permissions);
|
||||
if (permissions_type != MemoryPermission::Read &&
|
||||
permissions_type != MemoryPermission::ReadWrite) {
|
||||
@@ -597,8 +601,14 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
return shared_memory->Map(Core::CurrentProcess(), addr, permissions_type,
|
||||
MemoryPermission::DontCare);
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
const auto& vm_manager = current_process->VMManager();
|
||||
|
||||
if (!vm_manager.IsWithinASLRRegion(addr, size)) {
|
||||
return ERR_INVALID_MEMORY_RANGE;
|
||||
}
|
||||
|
||||
return shared_memory->Map(current_process, addr, permissions_type, MemoryPermission::DontCare);
|
||||
}
|
||||
|
||||
static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) {
|
||||
@@ -613,10 +623,24 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
|
||||
return ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
if (!IsValidAddressRange(addr, size)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle);
|
||||
if (!shared_memory) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
return shared_memory->Unmap(Core::CurrentProcess(), addr);
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
const auto& vm_manager = current_process->VMManager();
|
||||
|
||||
if (!vm_manager.IsWithinASLRRegion(addr, size)) {
|
||||
return ERR_INVALID_MEMORY_RANGE;
|
||||
}
|
||||
|
||||
return shared_memory->Unmap(current_process, addr);
|
||||
}
|
||||
|
||||
/// Query process memory
|
||||
@@ -630,7 +654,7 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_i
|
||||
}
|
||||
auto vma = process->VMManager().FindVMA(addr);
|
||||
memory_info->attributes = 0;
|
||||
if (vma == Core::CurrentProcess()->VMManager().vma_map.end()) {
|
||||
if (vma == process->VMManager().vma_map.end()) {
|
||||
memory_info->base_address = 0;
|
||||
memory_info->permission = static_cast<u32>(VMAPermission::None);
|
||||
memory_info->size = 0;
|
||||
|
||||
@@ -507,6 +507,26 @@ u64 VMManager::GetASLRRegionSize() const {
|
||||
return aslr_region_end - aslr_region_base;
|
||||
}
|
||||
|
||||
bool VMManager::IsWithinASLRRegion(VAddr begin, u64 size) const {
|
||||
const VAddr range_end = begin + size;
|
||||
const VAddr aslr_start = GetASLRRegionBaseAddress();
|
||||
const VAddr aslr_end = GetASLRRegionEndAddress();
|
||||
|
||||
if (aslr_start > begin || begin > range_end || range_end - 1 > aslr_end - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (range_end > heap_region_base && heap_region_end > begin) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (range_end > map_region_base && map_region_end > begin) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
VAddr VMManager::GetCodeRegionBaseAddress() const {
|
||||
return code_region_base;
|
||||
}
|
||||
|
||||
@@ -211,6 +211,9 @@ public:
|
||||
/// Gets the end address of the ASLR region.
|
||||
VAddr GetASLRRegionEndAddress() const;
|
||||
|
||||
/// Determines whether or not the specified address range is within the ASLR region.
|
||||
bool IsWithinASLRRegion(VAddr address, u64 size) const;
|
||||
|
||||
/// Gets the size of the ASLR region
|
||||
u64 GetASLRRegionSize() const;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "core/hle/service/am/idle.h"
|
||||
#include "core/hle/service/am/omm.h"
|
||||
#include "core/hle/service/am/spsm.h"
|
||||
#include "core/hle/service/am/tcap.h"
|
||||
#include "core/hle/service/apm/apm.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/nvflinger/nvflinger.h"
|
||||
@@ -26,13 +27,18 @@
|
||||
namespace Service::AM {
|
||||
|
||||
IWindowController::IWindowController() : ServiceFramework("IWindowController") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "CreateWindow"},
|
||||
{1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"},
|
||||
{10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"},
|
||||
{11, nullptr, "ReleaseForegroundRights"},
|
||||
{12, nullptr, "RejectToChangeIntoBackground"},
|
||||
{20, nullptr, "SetAppletWindowVisibility"},
|
||||
{21, nullptr, "SetAppletGpuTimeSlice"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -87,6 +93,7 @@ void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestCo
|
||||
}
|
||||
|
||||
IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetLastForegroundCaptureImage"},
|
||||
{1, nullptr, "UpdateLastForegroundCaptureImage"},
|
||||
@@ -117,7 +124,11 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController"
|
||||
{25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"},
|
||||
{26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"},
|
||||
{27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"},
|
||||
// 6.0.0+
|
||||
{28, nullptr, "TakeScreenShotOfOwnLayerEx"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -128,6 +139,7 @@ IDebugFunctions::~IDebugFunctions() = default;
|
||||
|
||||
ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Exit"},
|
||||
{1, &ISelfController::LockExit, "LockExit"},
|
||||
@@ -136,10 +148,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
|
||||
{4, nullptr, "LeaveFatalSection"},
|
||||
{9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"},
|
||||
{10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"},
|
||||
{11, &ISelfController::SetOperationModeChangedNotification,
|
||||
"SetOperationModeChangedNotification"},
|
||||
{12, &ISelfController::SetPerformanceModeChangedNotification,
|
||||
"SetPerformanceModeChangedNotification"},
|
||||
{11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"},
|
||||
{12, &ISelfController::SetPerformanceModeChangedNotification, "SetPerformanceModeChangedNotification"},
|
||||
{13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"},
|
||||
{14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"},
|
||||
{15, nullptr, "SetScreenShotAppletIdentityInfo"},
|
||||
@@ -165,7 +175,12 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
|
||||
{69, nullptr, "IsAutoSleepDisabled"},
|
||||
{70, nullptr, "ReportMultimediaError"},
|
||||
{80, nullptr, "SetWirelessPriorityMode"},
|
||||
{90, nullptr, "GetAccumulatedSuspendedTickValue"},
|
||||
{91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"},
|
||||
{1000, nullptr, "GetDebugStorageChannel"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
@@ -312,6 +327,7 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
|
||||
}
|
||||
|
||||
ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
|
||||
{1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"},
|
||||
@@ -336,11 +352,12 @@ ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter"
|
||||
{52, nullptr, "SwitchLcdBacklight"},
|
||||
{55, nullptr, "IsInControllerFirmwareUpdateSection"},
|
||||
{60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
|
||||
{61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent,
|
||||
"GetDefaultDisplayResolutionChangeEvent"},
|
||||
{61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"},
|
||||
{62, nullptr, "GetHdcpAuthenticationState"},
|
||||
{63, nullptr, "GetHdcpAuthenticationStateChangeEvent"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
@@ -432,11 +449,14 @@ class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
|
||||
public:
|
||||
explicit IStorageAccessor(std::vector<u8> buffer)
|
||||
: ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IStorageAccessor::GetSize, "GetSize"},
|
||||
{10, &IStorageAccessor::Write, "Write"},
|
||||
{11, &IStorageAccessor::Read, "Read"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -489,10 +509,13 @@ class IStorage final : public ServiceFramework<IStorage> {
|
||||
public:
|
||||
explicit IStorage(std::vector<u8> buffer)
|
||||
: ServiceFramework("IStorage"), buffer(std::move(buffer)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IStorage::Open, "Open"},
|
||||
{1, nullptr, "OpenTransferStorage"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -512,6 +535,7 @@ private:
|
||||
class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
|
||||
public:
|
||||
explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
|
||||
{1, nullptr, "IsCompleted"},
|
||||
@@ -532,6 +556,8 @@ public:
|
||||
{150, nullptr, "RequestForAppletToGetForeground"},
|
||||
{160, nullptr, "GetIndirectLayerConsumerHandle"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
@@ -624,13 +650,13 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"},
|
||||
{10, nullptr, "CreateApplicationAndPushAndRequestToStart"},
|
||||
{11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"},
|
||||
{12, nullptr, "CreateApplicationAndRequestToStart"},
|
||||
{13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest,
|
||||
"CreateApplicationAndRequestToStartForQuest"},
|
||||
{13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"},
|
||||
{20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"},
|
||||
{21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"},
|
||||
{22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
|
||||
@@ -638,10 +664,10 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
|
||||
{24, nullptr, "GetLaunchStorageInfoForDebug"},
|
||||
{25, nullptr, "ExtendSaveData"},
|
||||
{26, nullptr, "GetSaveDataSize"},
|
||||
{30, nullptr, "BeginBlockingHomeButtonShortAndLongPressed"},
|
||||
{31, nullptr, "EndBlockingHomeButtonShortAndLongPressed"},
|
||||
{32, nullptr, "BeginBlockingHomeButton"},
|
||||
{33, nullptr, "EndBlockingHomeButton"},
|
||||
{30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"},
|
||||
{31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"},
|
||||
{32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
|
||||
{33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"},
|
||||
{40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
|
||||
{50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
|
||||
{60, nullptr, "SetMediaPlaybackStateForApplication"},
|
||||
@@ -664,11 +690,39 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
|
||||
{1000, nullptr, "CreateMovieMaker"},
|
||||
{1001, nullptr, "PrepareForJit"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IApplicationFunctions::~IApplicationFunctions() = default;
|
||||
|
||||
void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(
|
||||
Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed(
|
||||
Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void IApplicationFunctions::BeginBlockingHomeButton(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
|
||||
constexpr std::array<u8, 0x88> data{{
|
||||
0xca, 0x97, 0x94, 0xc7, // Magic
|
||||
@@ -776,9 +830,11 @@ void InstallInterfaces(SM::ServiceManager& service_manager,
|
||||
std::make_shared<IdleSys>()->InstallAsService(service_manager);
|
||||
std::make_shared<OMM>()->InstallAsService(service_manager);
|
||||
std::make_shared<SPSM>()->InstallAsService(service_manager);
|
||||
std::make_shared<TCAP>()->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"},
|
||||
{11, nullptr, "LockForeground"},
|
||||
@@ -787,7 +843,10 @@ IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions"
|
||||
{21, nullptr, "GetPopFromGeneralChannelEvent"},
|
||||
{30, nullptr, "GetHomeButtonWriterLockAccessor"},
|
||||
{31, nullptr, "GetWriterLockAccessorEx"},
|
||||
{100, nullptr, "PopRequestLaunchApplicationForDebug"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -800,6 +859,7 @@ void IHomeMenuFunctions::RequestToGetForeground(Kernel::HLERequestContext& ctx)
|
||||
}
|
||||
|
||||
IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStateController") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "RequestToEnterSleep"},
|
||||
{1, nullptr, "EnterSleep"},
|
||||
@@ -813,18 +873,23 @@ IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStat
|
||||
{14, nullptr, "ShouldSleepOnBoot"},
|
||||
{15, nullptr, "GetHdcpAuthenticationFailedEvent"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IGlobalStateController::~IGlobalStateController() = default;
|
||||
|
||||
IApplicationCreator::IApplicationCreator() : ServiceFramework("IApplicationCreator") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "CreateApplication"},
|
||||
{1, nullptr, "PopLaunchRequestedApplication"},
|
||||
{10, nullptr, "CreateSystemApplication"},
|
||||
{100, nullptr, "PopFloatingApplicationForDevelopment"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -832,6 +897,7 @@ IApplicationCreator::~IApplicationCreator() = default;
|
||||
|
||||
IProcessWindingController::IProcessWindingController()
|
||||
: ServiceFramework("IProcessWindingController") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetLaunchReason"},
|
||||
{11, nullptr, "OpenCallingLibraryApplet"},
|
||||
@@ -842,6 +908,8 @@ IProcessWindingController::IProcessWindingController()
|
||||
{40, nullptr, "ReserveToStartAndWaitAndUnwindThis"},
|
||||
{41, nullptr, "ReserveToStartAndWait"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
|
||||
@@ -154,6 +154,10 @@ private:
|
||||
void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx);
|
||||
void NotifyRunning(Kernel::HLERequestContext& ctx);
|
||||
void GetPseudoDeviceId(Kernel::HLERequestContext& ctx);
|
||||
void BeginBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
|
||||
void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
|
||||
void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
|
||||
void EndBlockingHomeButton(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
|
||||
|
||||
@@ -211,6 +211,7 @@ void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
|
||||
{200, &AppletAE::OpenLibraryAppletProxyOld, "OpenLibraryAppletProxyOld"},
|
||||
@@ -218,7 +219,10 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
{300, nullptr, "OpenOverlayAppletProxy"},
|
||||
{350, nullptr, "OpenSystemApplicationProxy"},
|
||||
{400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"},
|
||||
{401, nullptr, "GetSystemAppletControllerForDebug"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
|
||||
public:
|
||||
explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
|
||||
{1, &IApplicationProxy::GetSelfController, "GetSelfController"},
|
||||
@@ -25,6 +26,8 @@ public:
|
||||
{20, &IApplicationProxy::GetApplicationFunctions, "GetApplicationFunctions"},
|
||||
{1000, &IApplicationProxy::GetDebugFunctions, "GetDebugFunctions"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@ IdleSys::IdleSys() : ServiceFramework{"idle:sys"} {
|
||||
{0, nullptr, "GetAutoPowerDownEvent"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
{3, nullptr, "SetHandlingContext"},
|
||||
{4, nullptr, "LoadAndApplySettings"},
|
||||
{5, nullptr, "ReportUserIsActive"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -17,22 +17,24 @@ OMM::OMM() : ServiceFramework{"omm"} {
|
||||
{5, nullptr, "GetCradleStatus"},
|
||||
{6, nullptr, "FadeInDisplay"},
|
||||
{7, nullptr, "FadeOutDisplay"},
|
||||
{8, nullptr, "Unknown1"},
|
||||
{9, nullptr, "Unknown2"},
|
||||
{10, nullptr, "Unknown3"},
|
||||
{11, nullptr, "Unknown4"},
|
||||
{12, nullptr, "Unknown5"},
|
||||
{13, nullptr, "Unknown6"},
|
||||
{14, nullptr, "Unknown7"},
|
||||
{15, nullptr, "Unknown8"},
|
||||
{16, nullptr, "Unknown9"},
|
||||
{17, nullptr, "Unknown10"},
|
||||
{18, nullptr, "Unknown11"},
|
||||
{19, nullptr, "Unknown12"},
|
||||
{20, nullptr, "Unknown13"},
|
||||
{21, nullptr, "Unknown14"},
|
||||
{22, nullptr, "Unknown15"},
|
||||
{23, nullptr, "Unknown16"},
|
||||
{8, nullptr, "GetCradleFwVersion"},
|
||||
{9, nullptr, "NotifyCecSettingsChanged"},
|
||||
{10, nullptr, "SetOperationModePolicy"},
|
||||
{11, nullptr, "GetDefaultDisplayResolution"},
|
||||
{12, nullptr, "GetDefaultDisplayResolutionChangeEvent"},
|
||||
{13, nullptr, "UpdateDefaultDisplayResolution"},
|
||||
{14, nullptr, "ShouldSleepOnBoot"},
|
||||
{15, nullptr, "NotifyHdcpApplicationExecutionStarted"},
|
||||
{16, nullptr, "NotifyHdcpApplicationExecutionFinished"},
|
||||
{17, nullptr, "NotifyHdcpApplicationDrawingStarted"},
|
||||
{18, nullptr, "NotifyHdcpApplicationDrawingFinished"},
|
||||
{19, nullptr, "GetHdcpAuthenticationFailedEvent"},
|
||||
{20, nullptr, "GetHdcpAuthenticationFailedEmulationEnabled"},
|
||||
{21, nullptr, "SetHdcpAuthenticationFailedEmulation"},
|
||||
{22, nullptr, "GetHdcpStateChangeEvent"},
|
||||
{23, nullptr, "GetHdcpState"},
|
||||
{24, nullptr, "ShowCardUpdateProcessing"},
|
||||
{25, nullptr, "SetApplicationCecSettingsAndNotifyChanged"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
23
src/core/hle/service/am/tcap.cpp
Normal file
23
src/core/hle/service/am/tcap.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/am/tcap.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
TCAP::TCAP() : ServiceFramework{"tcap"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetContinuousHighSkinTemperatureEvent"},
|
||||
{1, nullptr, "SetOperationMode"},
|
||||
{2, nullptr, "LoadAndApplySettings"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
TCAP::~TCAP() = default;
|
||||
|
||||
} // namespace Service::AM
|
||||
17
src/core/hle/service/am/tcap.h
Normal file
17
src/core/hle/service/am/tcap.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
class TCAP final : public ServiceFramework<TCAP> {
|
||||
public:
|
||||
explicit TCAP();
|
||||
~TCAP() override;
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/aoc/aoc_u.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
@@ -55,9 +56,13 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs
|
||||
{5, &AOC_U::GetAddOnContentBaseId, "GetAddOnContentBaseId"},
|
||||
{6, nullptr, "PrepareAddOnContentByApplicationId"},
|
||||
{7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"},
|
||||
{8, nullptr, "GetAddOnContentListChangedEvent"},
|
||||
{8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
aoc_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::Sticky,
|
||||
"GetAddOnContentListChanged:Event");
|
||||
}
|
||||
|
||||
AOC_U::~AOC_U() = default;
|
||||
@@ -130,6 +135,14 @@ void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void AOC_U::GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(aoc_change_event);
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
std::make_shared<AOC_U>()->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
@@ -18,8 +18,10 @@ private:
|
||||
void ListAddOnContent(Kernel::HLERequestContext& ctx);
|
||||
void GetAddOnContentBaseId(Kernel::HLERequestContext& ctx);
|
||||
void PrepareAddOnContent(Kernel::HLERequestContext& ctx);
|
||||
void GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::vector<u64> add_on_content;
|
||||
Kernel::SharedPtr<Kernel::Event> aoc_change_event;
|
||||
};
|
||||
|
||||
/// Registers all AOC services with the specified service manager.
|
||||
|
||||
@@ -22,20 +22,22 @@ class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
|
||||
public:
|
||||
explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params)
|
||||
: ServiceFramework("IAudioRenderer") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAudioRenderer::GetAudioRendererSampleRate, "GetAudioRendererSampleRate"},
|
||||
{1, &IAudioRenderer::GetAudioRendererSampleCount, "GetAudioRendererSampleCount"},
|
||||
{2, &IAudioRenderer::GetAudioRendererMixBufferCount, "GetAudioRendererMixBufferCount"},
|
||||
{3, &IAudioRenderer::GetAudioRendererState, "GetAudioRendererState"},
|
||||
{4, &IAudioRenderer::RequestUpdateAudioRenderer, "RequestUpdateAudioRenderer"},
|
||||
{5, &IAudioRenderer::StartAudioRenderer, "StartAudioRenderer"},
|
||||
{6, &IAudioRenderer::StopAudioRenderer, "StopAudioRenderer"},
|
||||
{0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
|
||||
{1, &IAudioRenderer::GetSampleCount, "GetSampleCount"},
|
||||
{2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"},
|
||||
{3, &IAudioRenderer::GetState, "GetState"},
|
||||
{4, &IAudioRenderer::RequestUpdate, "RequestUpdate"},
|
||||
{5, &IAudioRenderer::Start, "Start"},
|
||||
{6, &IAudioRenderer::Stop, "Stop"},
|
||||
{7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
|
||||
{8, nullptr, "SetAudioRendererRenderingTimeLimit"},
|
||||
{9, nullptr, "GetAudioRendererRenderingTimeLimit"},
|
||||
{10, nullptr, "RequestUpdateAudioRendererAuto"},
|
||||
{8, nullptr, "SetRenderingTimeLimit"},
|
||||
{9, nullptr, "GetRenderingTimeLimit"},
|
||||
{10, nullptr, "RequestUpdateAuto"},
|
||||
{11, nullptr, "ExecuteAudioRendererRendering"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
@@ -49,42 +51,42 @@ private:
|
||||
system_event->Signal();
|
||||
}
|
||||
|
||||
void GetAudioRendererSampleRate(Kernel::HLERequestContext& ctx) {
|
||||
void GetSampleRate(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(renderer->GetSampleRate());
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
}
|
||||
|
||||
void GetAudioRendererSampleCount(Kernel::HLERequestContext& ctx) {
|
||||
void GetSampleCount(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(renderer->GetSampleCount());
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
}
|
||||
|
||||
void GetAudioRendererState(Kernel::HLERequestContext& ctx) {
|
||||
void GetState(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(static_cast<u32>(renderer->GetStreamState()));
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
}
|
||||
|
||||
void GetAudioRendererMixBufferCount(Kernel::HLERequestContext& ctx) {
|
||||
void GetMixBufferCount(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(renderer->GetMixBufferCount());
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
}
|
||||
|
||||
void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) {
|
||||
void RequestUpdate(Kernel::HLERequestContext& ctx) {
|
||||
ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void StartAudioRenderer(Kernel::HLERequestContext& ctx) {
|
||||
void Start(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -92,7 +94,7 @@ private:
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void StopAudioRenderer(Kernel::HLERequestContext& ctx) {
|
||||
void Stop(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -129,6 +131,7 @@ public:
|
||||
{10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"},
|
||||
{11, nullptr, "QueryAudioDeviceInputEvent"},
|
||||
{12, nullptr, "QueryAudioDeviceOutputEvent"},
|
||||
{13, nullptr, "GetAudioSystemMasterVolumeSetting"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Service::ES {
|
||||
class ETicket final : public ServiceFramework<ETicket> {
|
||||
public:
|
||||
explicit ETicket() : ServiceFramework{"es"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, nullptr, "ImportTicket"},
|
||||
{2, nullptr, "ImportTicketCertificateSet"},
|
||||
@@ -37,15 +38,18 @@ public:
|
||||
{25, nullptr, "DeletePrepurchaseRecord"},
|
||||
{26, nullptr, "DeleteAllPrepurchaseRecord"},
|
||||
{27, nullptr, "CountPrepurchaseRecord"},
|
||||
{28, nullptr, "ListPrepurchaseRecord"},
|
||||
{28, nullptr, "ListPrepurchaseRecordRightsIds"},
|
||||
{29, nullptr, "ListPrepurchaseRecordInfo"},
|
||||
{30, nullptr, "Unknown1"},
|
||||
{31, nullptr, "Unknown2"},
|
||||
{32, nullptr, "Unknown3"},
|
||||
{33, nullptr, "Unknown4"},
|
||||
{34, nullptr, "Unknown5"},
|
||||
{35, nullptr, "Unknown6"},
|
||||
{30, nullptr, "CountTicket"},
|
||||
{31, nullptr, "ListTicketRightsIds"},
|
||||
{32, nullptr, "CountPrepurchaseRecordEx"},
|
||||
{33, nullptr, "ListPrepurchaseRecordRightsIdsEx"},
|
||||
{34, nullptr, "GetEncryptedTicketSize"},
|
||||
{35, nullptr, "GetEncryptedTicketData"},
|
||||
{36, nullptr, "DeleteAllInactiveELicenseRequiredPersonalizedTicket"},
|
||||
{503, nullptr, "GetTitleKey"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
ControllerBase::ControllerBase() = default;
|
||||
ControllerBase::~ControllerBase() = default;
|
||||
|
||||
void ControllerBase::ActivateController() {
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
namespace Service::HID {
|
||||
class ControllerBase {
|
||||
public:
|
||||
ControllerBase() = default;
|
||||
virtual ~ControllerBase() = 0;
|
||||
ControllerBase();
|
||||
virtual ~ControllerBase();
|
||||
|
||||
// Called when the controller is initialized
|
||||
virtual void OnInit() = 0;
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
|
||||
#include <cstring>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/service/hid/controllers/debug_pad.h"
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
Controller_DebugPad::Controller_DebugPad() = default;
|
||||
Controller_DebugPad::~Controller_DebugPad() = default;
|
||||
|
||||
void Controller_DebugPad::OnInit() {}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Service::HID {
|
||||
class Controller_DebugPad final : public ControllerBase {
|
||||
public:
|
||||
Controller_DebugPad();
|
||||
~Controller_DebugPad() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
void OnInit() override;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <cstring>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/service/hid/controllers/gesture.h"
|
||||
|
||||
@@ -12,6 +11,7 @@ namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
|
||||
|
||||
Controller_Gesture::Controller_Gesture() = default;
|
||||
Controller_Gesture::~Controller_Gesture() = default;
|
||||
|
||||
void Controller_Gesture::OnInit() {}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Service::HID {
|
||||
class Controller_Gesture final : public ControllerBase {
|
||||
public:
|
||||
Controller_Gesture();
|
||||
~Controller_Gesture() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
void OnInit() override;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <cstring>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/service/hid/controllers/keyboard.h"
|
||||
|
||||
@@ -12,6 +11,7 @@ namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
|
||||
|
||||
Controller_Keyboard::Controller_Keyboard() = default;
|
||||
Controller_Keyboard::~Controller_Keyboard() = default;
|
||||
|
||||
void Controller_Keyboard::OnInit() {}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Service::HID {
|
||||
class Controller_Keyboard final : public ControllerBase {
|
||||
public:
|
||||
Controller_Keyboard();
|
||||
~Controller_Keyboard() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
void OnInit() override;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <cstring>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/service/hid/controllers/mouse.h"
|
||||
|
||||
@@ -12,6 +11,7 @@ namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
|
||||
|
||||
Controller_Mouse::Controller_Mouse() = default;
|
||||
Controller_Mouse::~Controller_Mouse() = default;
|
||||
|
||||
void Controller_Mouse::OnInit() {}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Service::HID {
|
||||
class Controller_Mouse final : public ControllerBase {
|
||||
public:
|
||||
Controller_Mouse();
|
||||
~Controller_Mouse() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
void OnInit() override;
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
@@ -11,7 +9,6 @@
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/frontend/input.h"
|
||||
@@ -20,6 +17,7 @@
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
|
||||
constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
|
||||
constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
|
||||
@@ -28,9 +26,22 @@ constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
|
||||
constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
|
||||
constexpr std::size_t NPAD_OFFSET = 0x9A00;
|
||||
constexpr u32 BATTERY_FULL = 2;
|
||||
enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
|
||||
constexpr u32 NPAD_HANDHELD = 32;
|
||||
constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
|
||||
constexpr u32 MAX_NPAD_ID = 7;
|
||||
constexpr Controller_NPad::NPadControllerType PREFERRED_CONTROLLER =
|
||||
Controller_NPad::NPadControllerType::JoyDual;
|
||||
constexpr std::array<u32, 10> npad_id_list{
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 32, 16,
|
||||
};
|
||||
|
||||
enum class JoystickId : std::size_t {
|
||||
Joystick_Left,
|
||||
Joystick_Right,
|
||||
};
|
||||
|
||||
Controller_NPad::Controller_NPad() = default;
|
||||
Controller_NPad::~Controller_NPad() = default;
|
||||
|
||||
void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
|
||||
const auto controller_type = connected_controllers[controller_idx].type;
|
||||
@@ -114,7 +125,7 @@ void Controller_NPad::OnInit() {
|
||||
supported_npad_id_types.resize(npad_id_list.size());
|
||||
std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
|
||||
npad_id_list.size() * sizeof(u32));
|
||||
AddNewController(NPadControllerType::JoyDual);
|
||||
AddNewController(PREFERRED_CONTROLLER);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,6 +222,51 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
|
||||
rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
|
||||
rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
|
||||
|
||||
if (controller_type == NPadControllerType::JoyLeft ||
|
||||
controller_type == NPadControllerType::JoyRight) {
|
||||
if (npad.properties.is_horizontal) {
|
||||
ControllerPadState state{};
|
||||
AnalogPosition temp_lstick_entry{};
|
||||
AnalogPosition temp_rstick_entry{};
|
||||
if (controller_type == NPadControllerType::JoyLeft) {
|
||||
state.d_down.Assign(pad_state.d_left.Value());
|
||||
state.d_left.Assign(pad_state.d_up.Value());
|
||||
state.d_right.Assign(pad_state.d_down.Value());
|
||||
state.d_up.Assign(pad_state.d_right.Value());
|
||||
state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
|
||||
state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
|
||||
|
||||
state.zl.Assign(pad_state.zl.Value());
|
||||
state.plus.Assign(pad_state.minus.Value());
|
||||
|
||||
temp_lstick_entry = lstick_entry;
|
||||
temp_rstick_entry = rstick_entry;
|
||||
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.a.Value());
|
||||
state.a.Assign(pad_state.b.Value());
|
||||
state.b.Assign(pad_state.y.Value());
|
||||
state.y.Assign(pad_state.b.Value());
|
||||
|
||||
state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
|
||||
state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
|
||||
state.zr.Assign(pad_state.zr.Value());
|
||||
state.plus.Assign(pad_state.plus.Value());
|
||||
|
||||
temp_lstick_entry = lstick_entry;
|
||||
temp_rstick_entry = rstick_entry;
|
||||
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.raw = state.raw;
|
||||
lstick_entry = temp_lstick_entry;
|
||||
rstick_entry = temp_rstick_entry;
|
||||
}
|
||||
}
|
||||
|
||||
auto& main_controller =
|
||||
npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index];
|
||||
auto& handheld_entry =
|
||||
@@ -313,9 +369,19 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
|
||||
supported_npad_id_types.clear();
|
||||
supported_npad_id_types.resize(length / sizeof(u32));
|
||||
std::memcpy(supported_npad_id_types.data(), data, length);
|
||||
for (std::size_t i = 0; i < connected_controllers.size(); i++) {
|
||||
auto& controller = connected_controllers[i];
|
||||
if (!controller.is_connected) {
|
||||
continue;
|
||||
}
|
||||
if (!IsControllerSupported(PREFERRED_CONTROLLER)) {
|
||||
controller.type = DecideBestController(PREFERRED_CONTROLLER);
|
||||
InitNewlyAddedControler(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
|
||||
void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
|
||||
ASSERT(max_length < supported_npad_id_types.size());
|
||||
std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size());
|
||||
}
|
||||
@@ -344,11 +410,11 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
|
||||
for (std::size_t i = 0; i < controller_ids.size(); i++) {
|
||||
std::size_t controller_pos = i;
|
||||
// Handheld controller conversion
|
||||
if (controller_pos == 32) {
|
||||
if (controller_pos == NPAD_HANDHELD) {
|
||||
controller_pos = 8;
|
||||
}
|
||||
// Unknown controller conversion
|
||||
if (controller_pos == 16) {
|
||||
if (controller_pos == NPAD_UNKNOWN) {
|
||||
controller_pos = 9;
|
||||
}
|
||||
if (connected_controllers[controller_pos].is_connected) {
|
||||
@@ -426,4 +492,128 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
|
||||
void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
|
||||
can_controllers_vibrate = can_vibrate;
|
||||
}
|
||||
|
||||
bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const {
|
||||
const bool support_handheld =
|
||||
std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) !=
|
||||
supported_npad_id_types.end();
|
||||
if (controller == NPadControllerType::Handheld) {
|
||||
// Handheld is not even a supported type, lets stop here
|
||||
if (!support_handheld) {
|
||||
return false;
|
||||
}
|
||||
// Handheld should not be supported in docked mode
|
||||
if (Settings::values.use_docked_mode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
if (std::any_of(supported_npad_id_types.begin(), supported_npad_id_types.end(),
|
||||
[](u32 npad_id) { return npad_id <= MAX_NPAD_ID; })) {
|
||||
switch (controller) {
|
||||
case NPadControllerType::ProController:
|
||||
return style.pro_controller;
|
||||
case NPadControllerType::JoyDual:
|
||||
return style.joycon_dual;
|
||||
case NPadControllerType::JoyLeft:
|
||||
return style.joycon_left;
|
||||
case NPadControllerType::JoyRight:
|
||||
return style.joycon_right;
|
||||
case NPadControllerType::Pokeball:
|
||||
return style.pokeball;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Controller_NPad::NPadControllerType Controller_NPad::DecideBestController(
|
||||
NPadControllerType priority) const {
|
||||
if (IsControllerSupported(priority)) {
|
||||
return priority;
|
||||
}
|
||||
const auto is_docked = Settings::values.use_docked_mode;
|
||||
if (is_docked && priority == NPadControllerType::Handheld) {
|
||||
priority = NPadControllerType::JoyDual;
|
||||
if (IsControllerSupported(priority)) {
|
||||
return priority;
|
||||
}
|
||||
}
|
||||
std::vector<NPadControllerType> priority_list;
|
||||
switch (priority) {
|
||||
case NPadControllerType::ProController:
|
||||
priority_list.push_back(NPadControllerType::JoyDual);
|
||||
if (!is_docked) {
|
||||
priority_list.push_back(NPadControllerType::Handheld);
|
||||
}
|
||||
priority_list.push_back(NPadControllerType::JoyLeft);
|
||||
priority_list.push_back(NPadControllerType::JoyRight);
|
||||
priority_list.push_back(NPadControllerType::Pokeball);
|
||||
break;
|
||||
case NPadControllerType::Handheld:
|
||||
priority_list.push_back(NPadControllerType::JoyDual);
|
||||
priority_list.push_back(NPadControllerType::ProController);
|
||||
priority_list.push_back(NPadControllerType::JoyLeft);
|
||||
priority_list.push_back(NPadControllerType::JoyRight);
|
||||
priority_list.push_back(NPadControllerType::Pokeball);
|
||||
break;
|
||||
case NPadControllerType::JoyDual:
|
||||
if (!is_docked) {
|
||||
priority_list.push_back(NPadControllerType::Handheld);
|
||||
}
|
||||
priority_list.push_back(NPadControllerType::ProController);
|
||||
priority_list.push_back(NPadControllerType::JoyLeft);
|
||||
priority_list.push_back(NPadControllerType::JoyRight);
|
||||
priority_list.push_back(NPadControllerType::Pokeball);
|
||||
break;
|
||||
case NPadControllerType::JoyLeft:
|
||||
priority_list.push_back(NPadControllerType::JoyRight);
|
||||
priority_list.push_back(NPadControllerType::JoyDual);
|
||||
if (!is_docked) {
|
||||
priority_list.push_back(NPadControllerType::Handheld);
|
||||
}
|
||||
priority_list.push_back(NPadControllerType::ProController);
|
||||
priority_list.push_back(NPadControllerType::Pokeball);
|
||||
break;
|
||||
case NPadControllerType::JoyRight:
|
||||
priority_list.push_back(NPadControllerType::JoyLeft);
|
||||
priority_list.push_back(NPadControllerType::JoyDual);
|
||||
if (!is_docked) {
|
||||
priority_list.push_back(NPadControllerType::Handheld);
|
||||
}
|
||||
priority_list.push_back(NPadControllerType::ProController);
|
||||
priority_list.push_back(NPadControllerType::Pokeball);
|
||||
break;
|
||||
case NPadControllerType::Pokeball:
|
||||
priority_list.push_back(NPadControllerType::JoyLeft);
|
||||
priority_list.push_back(NPadControllerType::JoyRight);
|
||||
priority_list.push_back(NPadControllerType::JoyDual);
|
||||
if (!is_docked) {
|
||||
priority_list.push_back(NPadControllerType::Handheld);
|
||||
}
|
||||
priority_list.push_back(NPadControllerType::ProController);
|
||||
break;
|
||||
default:
|
||||
priority_list.push_back(NPadControllerType::JoyDual);
|
||||
if (!is_docked) {
|
||||
priority_list.push_back(NPadControllerType::Handheld);
|
||||
}
|
||||
priority_list.push_back(NPadControllerType::ProController);
|
||||
priority_list.push_back(NPadControllerType::JoyLeft);
|
||||
priority_list.push_back(NPadControllerType::JoyRight);
|
||||
priority_list.push_back(NPadControllerType::JoyDual);
|
||||
}
|
||||
|
||||
const auto iter = std::find_if(priority_list.begin(), priority_list.end(),
|
||||
[this](auto type) { return IsControllerSupported(type); });
|
||||
if (iter == priority_list.end()) {
|
||||
UNIMPLEMENTED_MSG("Could not find supported controller!");
|
||||
return priority;
|
||||
}
|
||||
|
||||
return *iter;
|
||||
}
|
||||
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace Service::HID {
|
||||
class Controller_NPad final : public ControllerBase {
|
||||
public:
|
||||
Controller_NPad();
|
||||
~Controller_NPad() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
void OnInit() override;
|
||||
@@ -77,7 +78,7 @@ public:
|
||||
position1.Assign(light2);
|
||||
position1.Assign(light3);
|
||||
position1.Assign(light4);
|
||||
};
|
||||
}
|
||||
union {
|
||||
u64 raw{};
|
||||
BitField<0, 1, u64> position1;
|
||||
@@ -91,7 +92,7 @@ public:
|
||||
NPadType GetSupportedStyleSet() const;
|
||||
|
||||
void SetSupportedNPadIdTypes(u8* data, std::size_t length);
|
||||
const void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
|
||||
void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
|
||||
std::size_t GetSupportedNPadIdTypesSize() const;
|
||||
|
||||
void SetHoldType(NpadHoldType joy_hold_type);
|
||||
@@ -277,12 +278,12 @@ private:
|
||||
std::vector<u32> supported_npad_id_types{};
|
||||
NpadHoldType hold_type{NpadHoldType::Vertical};
|
||||
Kernel::SharedPtr<Kernel::Event> styleset_changed_event;
|
||||
std::size_t dump_idx{};
|
||||
Vibration last_processed_vibration{};
|
||||
static constexpr std::array<u32, 10> npad_id_list{0, 1, 2, 3, 4, 5, 6, 7, 32, 16};
|
||||
std::array<ControllerHolder, 10> connected_controllers{};
|
||||
bool can_controllers_vibrate{true};
|
||||
|
||||
void InitNewlyAddedControler(std::size_t controller_idx);
|
||||
bool IsControllerSupported(NPadControllerType controller) const;
|
||||
NPadControllerType DecideBestController(NPadControllerType priority) const;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
|
||||
#include <cstring>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/service/hid/controllers/stubbed.h"
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
Controller_Stubbed::Controller_Stubbed() = default;
|
||||
Controller_Stubbed::~Controller_Stubbed() = default;
|
||||
|
||||
void Controller_Stubbed::OnInit() {}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Service::HID {
|
||||
class Controller_Stubbed final : public ControllerBase {
|
||||
public:
|
||||
Controller_Stubbed();
|
||||
~Controller_Stubbed() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
void OnInit() override;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <cstring>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/frontend/input.h"
|
||||
@@ -15,6 +14,7 @@ namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
|
||||
|
||||
Controller_Touchscreen::Controller_Touchscreen() = default;
|
||||
Controller_Touchscreen::~Controller_Touchscreen() = default;
|
||||
|
||||
void Controller_Touchscreen::OnInit() {}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Service::HID {
|
||||
class Controller_Touchscreen final : public ControllerBase {
|
||||
public:
|
||||
Controller_Touchscreen();
|
||||
~Controller_Touchscreen() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
void OnInit() override;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <cstring>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/service/hid/controllers/xpad.h"
|
||||
|
||||
@@ -12,6 +11,7 @@ namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
|
||||
|
||||
Controller_XPad::Controller_XPad() = default;
|
||||
Controller_XPad::~Controller_XPad() = default;
|
||||
|
||||
void Controller_XPad::OnInit() {}
|
||||
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
|
||||
namespace Service::HID {
|
||||
class Controller_XPad final : public ControllerBase {
|
||||
public:
|
||||
Controller_XPad();
|
||||
~Controller_XPad() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
void OnInit() override;
|
||||
|
||||
@@ -177,6 +177,7 @@ public:
|
||||
{11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"},
|
||||
{21, &Hid::ActivateMouse, "ActivateMouse"},
|
||||
{31, &Hid::ActivateKeyboard, "ActivateKeyboard"},
|
||||
{32, nullptr, "SendKeyboardLockKeyEvent"},
|
||||
{40, nullptr, "AcquireXpadIdEventHandle"},
|
||||
{41, nullptr, "ReleaseXpadIdEventHandle"},
|
||||
{51, &Hid::ActivateXpad, "ActivateXpad"},
|
||||
@@ -207,6 +208,7 @@ public:
|
||||
{80, nullptr, "GetGyroscopeZeroDriftMode"},
|
||||
{81, nullptr, "ResetGyroscopeZeroDriftMode"},
|
||||
{82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
|
||||
{83, nullptr, "IsFirmwareUpdateAvailableForSixAxisSensor"},
|
||||
{91, &Hid::ActivateGesture, "ActivateGesture"},
|
||||
{100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
|
||||
{101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
|
||||
@@ -252,6 +254,7 @@ public:
|
||||
{307, nullptr, "FinalizeSevenSixAxisSensor"},
|
||||
{308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
|
||||
{309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
|
||||
{310, nullptr, "ResetSevenSixAxisSensorTimestamp"},
|
||||
{400, nullptr, "IsUsbFullKeyControllerEnabled"},
|
||||
{401, nullptr, "EnableUsbFullKeyController"},
|
||||
{402, nullptr, "IsUsbFullKeyControllerConnected"},
|
||||
@@ -267,12 +270,24 @@ public:
|
||||
{505, nullptr, "SetPalmaFrModeType"},
|
||||
{506, nullptr, "ReadPalmaStep"},
|
||||
{507, nullptr, "EnablePalmaStep"},
|
||||
{508, nullptr, "SuspendPalmaStep"},
|
||||
{509, nullptr, "ResetPalmaStep"},
|
||||
{510, nullptr, "ReadPalmaApplicationSection"},
|
||||
{511, nullptr, "WritePalmaApplicationSection"},
|
||||
{512, nullptr, "ReadPalmaUniqueCode"},
|
||||
{513, nullptr, "SetPalmaUniqueCodeInvalid"},
|
||||
{508, nullptr, "ResetPalmaStep"},
|
||||
{509, nullptr, "ReadPalmaApplicationSection"},
|
||||
{510, nullptr, "WritePalmaApplicationSection"},
|
||||
{511, nullptr, "ReadPalmaUniqueCode"},
|
||||
{512, nullptr, "SetPalmaUniqueCodeInvalid"},
|
||||
{513, nullptr, "WritePalmaActivityEntry"},
|
||||
{514, nullptr, "WritePalmaRgbLedPatternEntry"},
|
||||
{515, nullptr, "WritePalmaWaveEntry"},
|
||||
{516, nullptr, "SetPalmaDataBaseIdentificationVersion"},
|
||||
{517, nullptr, "GetPalmaDataBaseIdentificationVersion"},
|
||||
{518, nullptr, "SuspendPalmaFeature"},
|
||||
{519, nullptr, "GetPalmaOperationResult"},
|
||||
{520, nullptr, "ReadPalmaPlayLog"},
|
||||
{521, nullptr, "ResetPalmaPlayLog"},
|
||||
{522, nullptr, "SetIsPalmaAllConnectable"},
|
||||
{523, nullptr, "SetIsPalmaPairedConnectable"},
|
||||
{524, nullptr, "PairPalma"},
|
||||
{525, nullptr, "SetPalmaBoostMode"},
|
||||
{1000, nullptr, "SetNpadCommunicationMode"},
|
||||
{1001, nullptr, "GetNpadCommunicationMode"},
|
||||
};
|
||||
@@ -620,6 +635,7 @@ public:
|
||||
{140, nullptr, "DeactivateConsoleSixAxisSensor"},
|
||||
{141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"},
|
||||
{142, nullptr, "DeactivateSevenSixAxisSensor"},
|
||||
{143, nullptr, "GetConsoleSixAxisSensorCountStates"},
|
||||
{201, nullptr, "ActivateFirmwareUpdate"},
|
||||
{202, nullptr, "DeactivateFirmwareUpdate"},
|
||||
{203, nullptr, "StartFirmwareUpdate"},
|
||||
@@ -630,12 +646,23 @@ public:
|
||||
{208, nullptr, "StartFirmwareUpdateForRevert"},
|
||||
{209, nullptr, "GetAvailableFirmwareVersionForRevert"},
|
||||
{210, nullptr, "IsFirmwareUpdatingDevice"},
|
||||
{211, nullptr, "StartFirmwareUpdateIndividual"},
|
||||
{215, nullptr, "SetUsbFirmwareForceUpdateEnabled"},
|
||||
{216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"},
|
||||
{221, nullptr, "UpdateControllerColor"},
|
||||
{222, nullptr, "ConnectUsbPadsAsync"},
|
||||
{223, nullptr, "DisconnectUsbPadsAsync"},
|
||||
{224, nullptr, "UpdateDesignInfo"},
|
||||
{225, nullptr, "GetUniquePadDriverState"},
|
||||
{226, nullptr, "GetSixAxisSensorDriverStates"},
|
||||
{227, nullptr, "GetRxPacketHistory"},
|
||||
{228, nullptr, "AcquireOperationEventHandle"},
|
||||
{229, nullptr, "ReadSerialFlash"},
|
||||
{230, nullptr, "WriteSerialFlash"},
|
||||
{231, nullptr, "GetOperationResult"},
|
||||
{232, nullptr, "EnableShipmentMode"},
|
||||
{233, nullptr, "ClearPairingInfo"},
|
||||
{234, nullptr, "GetUniquePadDeviceTypeSetInternal"},
|
||||
{301, nullptr, "GetAbstractedPadHandles"},
|
||||
{302, nullptr, "GetAbstractedPadState"},
|
||||
{303, nullptr, "GetAbstractedPadsState"},
|
||||
@@ -643,6 +670,8 @@ public:
|
||||
{322, nullptr, "UnsetAutoPilotVirtualPadState"},
|
||||
{323, nullptr, "UnsetAllAutoPilotVirtualPadState"},
|
||||
{350, nullptr, "AddRegisteredDevice"},
|
||||
{400, nullptr, "DisableExternalMcuOnNxDevice"},
|
||||
{401, nullptr, "DisableRailDeviceFiltering"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -678,7 +707,9 @@ public:
|
||||
{307, nullptr, "GetNpadSystemExtStyle"},
|
||||
{308, nullptr, "ApplyNpadSystemCommonPolicyFull"},
|
||||
{309, nullptr, "GetNpadFullKeyGripColor"},
|
||||
{310, nullptr, "GetMaskedSupportedNpadStyleSet"},
|
||||
{311, nullptr, "SetNpadPlayerLedBlinkingDevice"},
|
||||
{312, nullptr, "SetSupportedNpadStyleSetAll"},
|
||||
{321, nullptr, "GetUniquePadsFromNpad"},
|
||||
{322, nullptr, "GetIrSensorState"},
|
||||
{323, nullptr, "GetXcdHandleForNpadWithIrSensor"},
|
||||
@@ -703,6 +734,7 @@ public:
|
||||
{546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"},
|
||||
{547, nullptr, "GetAllowedBluetoothLinksCount"},
|
||||
{548, nullptr, "GetRegisteredDevices"},
|
||||
{549, nullptr, "GetConnectableRegisteredDevices"},
|
||||
{700, nullptr, "ActivateUniquePad"},
|
||||
{702, nullptr, "AcquireUniquePadConnectionEventHandle"},
|
||||
{703, nullptr, "GetUniquePadIds"},
|
||||
@@ -731,6 +763,7 @@ public:
|
||||
{850, nullptr, "IsUsbFullKeyControllerEnabled"},
|
||||
{851, nullptr, "EnableUsbFullKeyController"},
|
||||
{852, nullptr, "IsUsbConnected"},
|
||||
{870, nullptr, "IsHandheldButtonPressedOnConsoleMode"},
|
||||
{900, nullptr, "ActivateInputDetector"},
|
||||
{901, nullptr, "NotifyInputDetector"},
|
||||
{1000, nullptr, "InitializeFirmwareUpdate"},
|
||||
@@ -750,6 +783,12 @@ public:
|
||||
{1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"},
|
||||
{1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"},
|
||||
{1100, nullptr, "GetHidbusSystemServiceObject"},
|
||||
{1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"},
|
||||
{1130, nullptr, "InitializeUsbFirmwareUpdate"},
|
||||
{1131, nullptr, "FinalizeUsbFirmwareUpdate"},
|
||||
{1132, nullptr, "CheckUsbFirmwareUpdateRequired"},
|
||||
{1133, nullptr, "StartUsbFirmwareUpdate"},
|
||||
{1134, nullptr, "GetUsbFirmwareUpdateState"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -788,6 +827,7 @@ public:
|
||||
{11, nullptr, "EnableJoyPollingReceiveMode"},
|
||||
{12, nullptr, "DisableJoyPollingReceiveMode"},
|
||||
{13, nullptr, "GetPollingData"},
|
||||
{14, nullptr, "SetStatusManagerType"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -18,35 +18,35 @@ public:
|
||||
explicit LBL() : ServiceFramework{"lbl"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown1"},
|
||||
{1, nullptr, "Unknown2"},
|
||||
{2, nullptr, "Unknown3"},
|
||||
{3, nullptr, "GetCurrentBacklightLevel"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "GetAlsComputedBacklightLevel"},
|
||||
{6, nullptr, "TurnOffBacklight"},
|
||||
{7, nullptr, "TurnOnBacklight"},
|
||||
{8, nullptr, "GetBacklightStatus"},
|
||||
{9, nullptr, "Unknown5"},
|
||||
{10, nullptr, "Unknown6"},
|
||||
{11, nullptr, "Unknown7"},
|
||||
{12, nullptr, "Unknown8"},
|
||||
{13, nullptr, "Unknown9"},
|
||||
{14, nullptr, "Unknown10"},
|
||||
{15, nullptr, "GetAutoBrightnessSetting"},
|
||||
{16, nullptr, "ReadRawLightSensor"},
|
||||
{17, nullptr, "Unknown11"},
|
||||
{18, nullptr, "Unknown12"},
|
||||
{19, nullptr, "Unknown13"},
|
||||
{20, nullptr, "Unknown14"},
|
||||
{21, nullptr, "Unknown15"},
|
||||
{22, nullptr, "Unknown16"},
|
||||
{23, nullptr, "Unknown17"},
|
||||
{24, nullptr, "Unknown18"},
|
||||
{25, nullptr, "Unknown19"},
|
||||
{0, nullptr, "SaveCurrentSetting"},
|
||||
{1, nullptr, "LoadCurrentSetting"},
|
||||
{2, nullptr, "SetCurrentBrightnessSetting"},
|
||||
{3, nullptr, "GetCurrentBrightnessSetting"},
|
||||
{4, nullptr, "ApplyCurrentBrightnessSettingToBacklight"},
|
||||
{5, nullptr, "GetBrightnessSettingAppliedToBacklight"},
|
||||
{6, nullptr, "SwitchBacklightOn"},
|
||||
{7, nullptr, "SwitchBacklightOff"},
|
||||
{8, nullptr, "GetBacklightSwitchStatus"},
|
||||
{9, nullptr, "EnableDimming"},
|
||||
{10, nullptr, "DisableDimming"},
|
||||
{11, nullptr, "IsDimmingEnabled"},
|
||||
{12, nullptr, "EnableAutoBrightnessControl"},
|
||||
{13, nullptr, "DisableAutoBrightnessControl"},
|
||||
{14, nullptr, "IsAutoBrightnessControlEnabled"},
|
||||
{15, nullptr, "SetAmbientLightSensorValue"},
|
||||
{16, nullptr, "GetAmbientLightSensorValue"},
|
||||
{17, nullptr, "SetBrightnessReflectionDelayLevel"},
|
||||
{18, nullptr, "GetBrightnessReflectionDelayLevel"},
|
||||
{19, nullptr, "SetCurrentBrightnessMapping"},
|
||||
{20, nullptr, "GetCurrentBrightnessMapping"},
|
||||
{21, nullptr, "SetCurrentAmbientLightSensorMapping"},
|
||||
{22, nullptr, "GetCurrentAmbientLightSensorMapping"},
|
||||
{23, nullptr, "IsAmbientLightSensorAvailable"},
|
||||
{24, nullptr, "SetCurrentBrightnessSettingForVrMode"},
|
||||
{25, nullptr, "GetCurrentBrightnessSettingForVrMode"},
|
||||
{26, &LBL::EnableVrMode, "EnableVrMode"},
|
||||
{27, &LBL::DisableVrMode, "DisableVrMode"},
|
||||
{28, &LBL::GetVrMode, "GetVrMode"},
|
||||
{28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -72,7 +72,7 @@ private:
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
}
|
||||
|
||||
void GetVrMode(Kernel::HLERequestContext& ctx) {
|
||||
void IsVrModeEnabled(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(vr_mode_enabled);
|
||||
|
||||
@@ -14,14 +14,14 @@ public:
|
||||
explicit MM_U() : ServiceFramework{"mm:u"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &MM_U::Initialize, "InitializeOld"},
|
||||
{1, &MM_U::Finalize, "FinalizeOld"},
|
||||
{2, &MM_U::SetAndWait, "SetAndWaitOld"},
|
||||
{3, &MM_U::Get, "GetOld"},
|
||||
{4, &MM_U::Initialize, "Initialize"},
|
||||
{5, &MM_U::Finalize, "Finalize"},
|
||||
{6, &MM_U::SetAndWait, "SetAndWait"},
|
||||
{7, &MM_U::Get, "Get"},
|
||||
{0, &MM_U::Initialize, "Initialize"},
|
||||
{1, &MM_U::Finalize, "Finalize"},
|
||||
{2, &MM_U::SetAndWait, "SetAndWait"},
|
||||
{3, &MM_U::Get, "Get"},
|
||||
{4, &MM_U::InitializeWithId, "InitializeWithId"},
|
||||
{5, &MM_U::FinalizeWithId, "FinalizeWithId"},
|
||||
{6, &MM_U::SetAndWaitWithId, "SetAndWaitWithId"},
|
||||
{7, &MM_U::GetWithId, "GetWithId"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -59,9 +59,43 @@ private:
|
||||
rb.Push(current);
|
||||
}
|
||||
|
||||
void InitializeWithId(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_MM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(id); // Any non zero value
|
||||
}
|
||||
|
||||
void FinalizeWithId(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_MM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void SetAndWaitWithId(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 input_id = rp.Pop<u32>();
|
||||
min = rp.Pop<u32>();
|
||||
max = rp.Pop<u32>();
|
||||
current = min;
|
||||
|
||||
LOG_WARNING(Service_MM, "(STUBBED) called, input_id=0x{:X}, min=0x{:X}, max=0x{:X}",
|
||||
input_id, min, max);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetWithId(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_MM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(current);
|
||||
}
|
||||
|
||||
u32 min{0};
|
||||
u32 max{0};
|
||||
u32 current{0};
|
||||
u32 id{1};
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
|
||||
@@ -219,6 +219,7 @@ IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
|
||||
{35, nullptr, "GetScanData"},
|
||||
{36, nullptr, "GetCurrentAccessPoint"},
|
||||
{37, nullptr, "Shutdown"},
|
||||
{38, nullptr, "GetAllowedChannels"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -71,6 +71,22 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class NIM_ECA final : public ServiceFramework<NIM_ECA> {
|
||||
public:
|
||||
explicit NIM_ECA() : ServiceFramework{"nim:eca"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "CreateServerInterface"},
|
||||
{1, nullptr, "RefreshDebugAvailability"},
|
||||
{2, nullptr, "ClearDebugResponse"},
|
||||
{3, nullptr, "RegisterDebugResponse"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
class NIM_SHP final : public ServiceFramework<NIM_SHP> {
|
||||
public:
|
||||
explicit NIM_SHP() : ServiceFramework{"nim:shp"} {
|
||||
@@ -214,6 +230,7 @@ private:
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm) {
|
||||
std::make_shared<NIM>()->InstallAsService(sm);
|
||||
std::make_shared<NIM_ECA>()->InstallAsService(sm);
|
||||
std::make_shared<NIM_SHP>()->InstallAsService(sm);
|
||||
std::make_shared<NTC>()->InstallAsService(sm);
|
||||
}
|
||||
|
||||
88
src/core/hle/service/npns/npns.cpp
Normal file
88
src/core/hle/service/npns/npns.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "core/hle/service/npns/npns.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::NPNS {
|
||||
|
||||
class NPNS_S final : public ServiceFramework<NPNS_S> {
|
||||
public:
|
||||
explicit NPNS_S() : ServiceFramework{"npns:s"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, nullptr, "ListenAll"},
|
||||
{2, nullptr, "ListenTo"},
|
||||
{3, nullptr, "Receive"},
|
||||
{4, nullptr, "ReceiveRaw"},
|
||||
{5, nullptr, "GetReceiveEvent"},
|
||||
{6, nullptr, "ListenUndelivered"},
|
||||
{7, nullptr, "GetStateChangeEVent"},
|
||||
{11, nullptr, "SubscribeTopic"},
|
||||
{12, nullptr, "UnsubscribeTopic"},
|
||||
{13, nullptr, "QueryIsTopicExist"},
|
||||
{21, nullptr, "CreateToken"},
|
||||
{22, nullptr, "CreateTokenWithApplicationId"},
|
||||
{23, nullptr, "DestroyToken"},
|
||||
{24, nullptr, "DestroyTokenWithApplicationId"},
|
||||
{25, nullptr, "QueryIsTokenValid"},
|
||||
{31, nullptr, "UploadTokenToBaaS"},
|
||||
{32, nullptr, "DestroyTokenForBaaS"},
|
||||
{33, nullptr, "CreateTokenForBaaS"},
|
||||
{34, nullptr, "SetBaaSDeviceAccountIdList"},
|
||||
{101, nullptr, "Suspend"},
|
||||
{102, nullptr, "Resume"},
|
||||
{103, nullptr, "GetState"},
|
||||
{104, nullptr, "GetStatistics"},
|
||||
{105, nullptr, "GetPlayReportRequestEvent"},
|
||||
{111, nullptr, "GetJid"},
|
||||
{112, nullptr, "CreateJid"},
|
||||
{113, nullptr, "DestroyJid"},
|
||||
{114, nullptr, "AttachJid"},
|
||||
{115, nullptr, "DetachJid"},
|
||||
{201, nullptr, "RequestChangeStateForceTimed"},
|
||||
{102, nullptr, "RequestChangeStateForceAsync"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
class NPNS_U final : public ServiceFramework<NPNS_U> {
|
||||
public:
|
||||
explicit NPNS_U() : ServiceFramework{"npns:u"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, nullptr, "ListenAll"},
|
||||
{2, nullptr, "ListenTo"},
|
||||
{3, nullptr, "Receive"},
|
||||
{4, nullptr, "ReceiveRaw"},
|
||||
{5, nullptr, "GetReceiveEvent"},
|
||||
{7, nullptr, "GetStateChangeEVent"},
|
||||
{21, nullptr, "CreateToken"},
|
||||
{23, nullptr, "DestroyToken"},
|
||||
{25, nullptr, "QueryIsTokenValid"},
|
||||
{26, nullptr, "ListenToMyApplicationId"},
|
||||
{101, nullptr, "Suspend"},
|
||||
{102, nullptr, "Resume"},
|
||||
{103, nullptr, "GetState"},
|
||||
{104, nullptr, "GetStatistics"},
|
||||
{111, nullptr, "GetJid"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm) {
|
||||
std::make_shared<NPNS_S>()->InstallAsService(sm);
|
||||
std::make_shared<NPNS_U>()->InstallAsService(sm);
|
||||
}
|
||||
|
||||
} // namespace Service::NPNS
|
||||
15
src/core/hle/service/npns/npns.h
Normal file
15
src/core/hle/service/npns/npns.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Service::SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
|
||||
namespace Service::NPNS {
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm);
|
||||
|
||||
} // namespace Service::NPNS
|
||||
@@ -93,13 +93,23 @@ public:
|
||||
{86, nullptr, "EnableApplicationCrashReport"},
|
||||
{87, nullptr, "IsApplicationCrashReportEnabled"},
|
||||
{90, nullptr, "BoostSystemMemoryResourceLimit"},
|
||||
{91, nullptr, "Unknown1"},
|
||||
{92, nullptr, "Unknown2"},
|
||||
{93, nullptr, "GetMainApplicationProgramIndex"},
|
||||
{94, nullptr, "LaunchApplication2"},
|
||||
{95, nullptr, "GetApplicationLaunchInfo"},
|
||||
{96, nullptr, "AcquireApplicationLaunchInfo"},
|
||||
{97, nullptr, "GetMainApplicationProgramIndex2"},
|
||||
{98, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
|
||||
{100, nullptr, "ResetToFactorySettings"},
|
||||
{101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
|
||||
{102, nullptr, "ResetToFactorySettingsForRefurbishment"},
|
||||
{200, nullptr, "CalculateUserSaveDataStatistics"},
|
||||
{201, nullptr, "DeleteUserSaveDataAll"},
|
||||
{210, nullptr, "DeleteUserSystemSaveData"},
|
||||
{211, nullptr, "DeleteSaveData"},
|
||||
{220, nullptr, "UnregisterNetworkServiceAccount"},
|
||||
{221, nullptr, "UnregisterNetworkServiceAccountWithUserSaveDataDeletion"},
|
||||
{300, nullptr, "GetApplicationShellEvent"},
|
||||
{301, nullptr, "PopApplicationShellEventInfo"},
|
||||
{302, nullptr, "LaunchLibraryApplet"},
|
||||
@@ -114,6 +124,7 @@ public:
|
||||
{403, nullptr, "GetMaxApplicationControlCacheCount"},
|
||||
{404, nullptr, "InvalidateApplicationControlCache"},
|
||||
{405, nullptr, "ListApplicationControlCacheEntryInfo"},
|
||||
{406, nullptr, "GetApplicationControlProperty"},
|
||||
{502, nullptr, "RequestCheckGameCardRegistration"},
|
||||
{503, nullptr, "RequestGameCardRegistrationGoldPoint"},
|
||||
{504, nullptr, "RequestRegisterGameCard"},
|
||||
@@ -129,6 +140,7 @@ public:
|
||||
{604, nullptr, "RegisterContentsExternalKey"},
|
||||
{605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
|
||||
{606, nullptr, "GetContentMetaStorage"},
|
||||
{607, nullptr, "ListAvailableAddOnContent"},
|
||||
{700, nullptr, "PushDownloadTaskList"},
|
||||
{701, nullptr, "ClearTaskStatusList"},
|
||||
{702, nullptr, "RequestDownloadTaskList"},
|
||||
@@ -148,6 +160,9 @@ public:
|
||||
{907, nullptr, "WithdrawApplicationUpdateRequest"},
|
||||
{908, nullptr, "ListApplicationRecordInstalledContentMeta"},
|
||||
{909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"},
|
||||
{910, nullptr, "Unknown3"},
|
||||
{911, nullptr, "SetPreInstalledApplication"},
|
||||
{912, nullptr, "ClearPreInstalledApplicationFlag"},
|
||||
{1000, nullptr, "RequestVerifyApplicationDeprecated"},
|
||||
{1001, nullptr, "CorruptApplicationForDebug"},
|
||||
{1002, nullptr, "RequestVerifyAddOnContentsRights"},
|
||||
@@ -162,6 +177,8 @@ public:
|
||||
{1305, nullptr, "TryDeleteRunningApplicationEntity"},
|
||||
{1306, nullptr, "TryDeleteRunningApplicationCompletely"},
|
||||
{1307, nullptr, "TryDeleteRunningApplicationContentEntities"},
|
||||
{1308, nullptr, "DeleteApplicationCompletelyForDebug"},
|
||||
{1309, nullptr, "CleanupUnavailableAddOnContents"},
|
||||
{1400, nullptr, "PrepareShutdown"},
|
||||
{1500, nullptr, "FormatSdCard"},
|
||||
{1501, nullptr, "NeedsSystemUpdateToFormatSdCard"},
|
||||
@@ -199,6 +216,28 @@ public:
|
||||
{2015, nullptr, "CompareSystemDeliveryInfo"},
|
||||
{2016, nullptr, "ListNotCommittedContentMeta"},
|
||||
{2017, nullptr, "CreateDownloadTask"},
|
||||
{2018, nullptr, "Unknown4"},
|
||||
{2050, nullptr, "Unknown5"},
|
||||
{2100, nullptr, "Unknown6"},
|
||||
{2101, nullptr, "Unknown7"},
|
||||
{2150, nullptr, "CreateRightsEnvironment"},
|
||||
{2151, nullptr, "DestroyRightsEnvironment"},
|
||||
{2152, nullptr, "ActivateRightsEnvironment"},
|
||||
{2153, nullptr, "DeactivateRightsEnvironment"},
|
||||
{2154, nullptr, "ForceActivateRightsContextForExit"},
|
||||
{2160, nullptr, "AddTargetApplicationToRightsEnvironment"},
|
||||
{2161, nullptr, "SetUsersToRightsEnvironment"},
|
||||
{2170, nullptr, "GetRightsEnvironmentStatus"},
|
||||
{2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"},
|
||||
{2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
|
||||
{2181, nullptr, "GetLastResultOfExtendRightsInRightsEnvironment"},
|
||||
{2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
|
||||
{2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
|
||||
{2199, nullptr, "GetRightsEnvironmentCountForDebug"},
|
||||
{2200, nullptr, "Unknown8"},
|
||||
{2201, nullptr, "Unknown9"},
|
||||
{2250, nullptr, "Unknown10"},
|
||||
{2300, nullptr, "Unknown11"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -348,12 +387,15 @@ public:
|
||||
{0, nullptr, "LaunchProgram"},
|
||||
{1, nullptr, "TerminateProcess"},
|
||||
{2, nullptr, "TerminateProgram"},
|
||||
{3, nullptr, "GetShellEventHandle"},
|
||||
{4, nullptr, "GetShellEventInfo"},
|
||||
{5, nullptr, "TerminateApplication"},
|
||||
{6, nullptr, "PrepareLaunchProgramFromHost"},
|
||||
{7, nullptr, "LaunchApplication"},
|
||||
{8, nullptr, "LaunchApplicationWithStorageId"},
|
||||
{4, nullptr, "GetShellEventHandle"},
|
||||
{5, nullptr, "GetShellEventInfo"},
|
||||
{6, nullptr, "TerminateApplication"},
|
||||
{7, nullptr, "PrepareLaunchProgramFromHost"},
|
||||
{8, nullptr, "LaunchApplication"},
|
||||
{9, nullptr, "LaunchApplicationWithStorageId"},
|
||||
{10, nullptr, "TerminateApplication2"},
|
||||
{11, nullptr, "GetRunningApplicationProcessId"},
|
||||
{12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActive"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -388,6 +430,7 @@ public:
|
||||
{19, nullptr, "GetReceivedEulaDataSize"},
|
||||
{20, nullptr, "GetReceivedEulaData"},
|
||||
{21, nullptr, "SetupToReceiveSystemUpdate"},
|
||||
{22, nullptr, "RequestCheckLatestUpdateIncludesRebootlessUpdate"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -14,20 +14,24 @@ public:
|
||||
explicit PlayReport(const char* name) : ServiceFramework{name} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{10100, nullptr, "SaveReport"},
|
||||
{10101, &PlayReport::SaveReportWithUser, "SaveReportWithUser"},
|
||||
{10100, nullptr, "SaveReportOld"},
|
||||
{10101, &PlayReport::SaveReportWithUserOld, "SaveReportWithUserOld"},
|
||||
{10102, nullptr, "SaveReport"},
|
||||
{10103, nullptr, "SaveReportWithUser"},
|
||||
{10200, nullptr, "RequestImmediateTransmission"},
|
||||
{10300, nullptr, "GetTransmissionStatus"},
|
||||
{20100, nullptr, "SaveSystemReport"},
|
||||
{20200, nullptr, "SetOperationMode"},
|
||||
{20101, nullptr, "SaveSystemReportWithUser"},
|
||||
{20200, nullptr, "SetOperationMode"},
|
||||
{30100, nullptr, "ClearStorage"},
|
||||
{30200, nullptr, "ClearStatistics"},
|
||||
{30300, nullptr, "GetStorageUsage"},
|
||||
{30400, nullptr, "GetStatistics"},
|
||||
{30401, nullptr, "GetThroughputHistory"},
|
||||
{30500, nullptr, "GetLastUploadError"},
|
||||
{40100, nullptr, "IsUserAgreementCheckEnabled"},
|
||||
{40101, nullptr, "SetUserAgreementCheckEnabled"},
|
||||
{90100, nullptr, "GetStorageUsage"},
|
||||
{90200, nullptr, "GetStatistics"},
|
||||
{90201, nullptr, "GetThroughputHistory"},
|
||||
{90300, nullptr, "GetLastUploadError"},
|
||||
{90100, nullptr, "ReadAllReportFiles"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -35,7 +39,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void SaveReportWithUser(Kernel::HLERequestContext& ctx) {
|
||||
void SaveReportWithUserOld(Kernel::HLERequestContext& ctx) {
|
||||
// TODO(ogniK): Do we want to add play report?
|
||||
LOG_WARNING(Service_PREPO, "(STUBBED) called");
|
||||
|
||||
@@ -46,6 +50,7 @@ private:
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
std::make_shared<PlayReport>("prepo:a")->InstallAsService(service_manager);
|
||||
std::make_shared<PlayReport>("prepo:a2")->InstallAsService(service_manager);
|
||||
std::make_shared<PlayReport>("prepo:m")->InstallAsService(service_manager);
|
||||
std::make_shared<PlayReport>("prepo:s")->InstallAsService(service_manager);
|
||||
std::make_shared<PlayReport>("prepo:u")->InstallAsService(service_manager);
|
||||
|
||||
71
src/core/hle/service/ptm/psm.cpp
Normal file
71
src/core/hle/service/ptm/psm.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/ptm/psm.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::PSM {
|
||||
|
||||
constexpr u32 BATTERY_FULLY_CHARGED = 100; // 100% Full
|
||||
constexpr u32 BATTERY_CURRENTLY_CHARGING = 1; // Plugged into an official dock
|
||||
|
||||
class PSM final : public ServiceFramework<PSM> {
|
||||
public:
|
||||
explicit PSM() : ServiceFramework{"psm"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"},
|
||||
{1, &PSM::GetChargerType, "GetChargerType"},
|
||||
{2, nullptr, "EnableBatteryCharging"},
|
||||
{3, nullptr, "DisableBatteryCharging"},
|
||||
{4, nullptr, "IsBatteryChargingEnabled"},
|
||||
{5, nullptr, "AcquireControllerPowerSupply"},
|
||||
{6, nullptr, "ReleaseControllerPowerSupply"},
|
||||
{7, nullptr, "OpenSession"},
|
||||
{8, nullptr, "EnableEnoughPowerChargeEmulation"},
|
||||
{9, nullptr, "DisableEnoughPowerChargeEmulation"},
|
||||
{10, nullptr, "EnableFastBatteryCharging"},
|
||||
{11, nullptr, "DisableFastBatteryCharging"},
|
||||
{12, nullptr, "GetBatteryVoltageState"},
|
||||
{13, nullptr, "GetRawBatteryChargePercentage"},
|
||||
{14, nullptr, "IsEnoughPowerSupplied"},
|
||||
{15, nullptr, "GetBatteryAgePercentage"},
|
||||
{16, nullptr, "GetBatteryChargeInfoEvent"},
|
||||
{17, nullptr, "GetBatteryChargeInfoFields"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
~PSM() override = default;
|
||||
|
||||
private:
|
||||
void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_PSM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(BATTERY_FULLY_CHARGED);
|
||||
}
|
||||
|
||||
void GetChargerType(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_PSM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(BATTERY_CURRENTLY_CHARGING);
|
||||
}
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm) {
|
||||
std::make_shared<PSM>()->InstallAsService(sm);
|
||||
}
|
||||
|
||||
} // namespace Service::PSM
|
||||
15
src/core/hle/service/ptm/psm.h
Normal file
15
src/core/hle/service/ptm/psm.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Service::SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
|
||||
namespace Service::PSM {
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm);
|
||||
|
||||
} // namespace Service::PSM
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "core/hle/service/apm/apm.h"
|
||||
#include "core/hle/service/arp/arp.h"
|
||||
#include "core/hle/service/audio/audio.h"
|
||||
#include "core/hle/service/bcat/bcat.h"
|
||||
#include "core/hle/service/bcat/module.h"
|
||||
#include "core/hle/service/bpc/bpc.h"
|
||||
#include "core/hle/service/btdrv/btdrv.h"
|
||||
#include "core/hle/service/btm/btm.h"
|
||||
@@ -48,15 +48,17 @@
|
||||
#include "core/hle/service/nfp/nfp.h"
|
||||
#include "core/hle/service/nifm/nifm.h"
|
||||
#include "core/hle/service/nim/nim.h"
|
||||
#include "core/hle/service/npns/npns.h"
|
||||
#include "core/hle/service/ns/ns.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
#include "core/hle/service/nvflinger/nvflinger.h"
|
||||
#include "core/hle/service/pcie/pcie.h"
|
||||
#include "core/hle/service/pctl/pctl.h"
|
||||
#include "core/hle/service/pctl/module.h"
|
||||
#include "core/hle/service/pcv/pcv.h"
|
||||
#include "core/hle/service/pm/pm.h"
|
||||
#include "core/hle/service/prepo/prepo.h"
|
||||
#include "core/hle/service/psc/psc.h"
|
||||
#include "core/hle/service/ptm/psm.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/set/settings.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
@@ -236,6 +238,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs)
|
||||
NFP::InstallInterfaces(*sm);
|
||||
NIFM::InstallInterfaces(*sm);
|
||||
NIM::InstallInterfaces(*sm);
|
||||
NPNS::InstallInterfaces(*sm);
|
||||
NS::InstallInterfaces(*sm);
|
||||
Nvidia::InstallInterfaces(*sm, *nv_flinger);
|
||||
PCIe::InstallInterfaces(*sm);
|
||||
@@ -244,6 +247,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs)
|
||||
PlayReport::InstallInterfaces(*sm);
|
||||
PM::InstallInterfaces(*sm);
|
||||
PSC::InstallInterfaces(*sm);
|
||||
PSM::InstallInterfaces(*sm);
|
||||
Set::InstallInterfaces(*sm);
|
||||
Sockets::InstallInterfaces(*sm);
|
||||
SPL::InstallInterfaces(*sm);
|
||||
|
||||
@@ -39,7 +39,8 @@ SET_CAL::SET_CAL() : ServiceFramework("set:cal") {
|
||||
{29, nullptr, "GetAmiiboEcqvBlsKey"},
|
||||
{30, nullptr, "GetAmiiboEcqvBlsCertificate"},
|
||||
{31, nullptr, "GetAmiiboEcqvBlsRootCertificate"},
|
||||
{32, nullptr, "GetUnknownId"},
|
||||
{32, nullptr, "GetUsbTypeCPowerSourceCircuitVersion"},
|
||||
{33, nullptr, "GetBatteryVersion"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -47,9 +47,12 @@ void Fermi2D::HandleSurfaceCopy() {
|
||||
u32 dst_bytes_per_pixel = RenderTargetBytesPerPixel(regs.dst.format);
|
||||
|
||||
if (!rasterizer.AccelerateSurfaceCopy(regs.src, regs.dst)) {
|
||||
// TODO(bunnei): The below implementation currently will not get hit, as
|
||||
// AccelerateSurfaceCopy tries to always copy and will always return success. This should be
|
||||
// changed once we properly support flushing.
|
||||
rasterizer.FlushRegion(source_cpu, src_bytes_per_pixel * regs.src.width * regs.src.height);
|
||||
// We have to invalidate the destination region to evict any outdated surfaces from the
|
||||
// cache. We do this before actually writing the new data because the destination address
|
||||
// might contain a dirty surface that will have to be written back to memory.
|
||||
rasterizer.InvalidateRegion(dest_cpu,
|
||||
dst_bytes_per_pixel * regs.dst.width * regs.dst.height);
|
||||
|
||||
if (regs.src.linear == regs.dst.linear) {
|
||||
// If the input layout and the output layout are the same, just perform a raw copy.
|
||||
|
||||
@@ -5,10 +5,14 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/engines/kepler_memory.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
KeplerMemory::KeplerMemory(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
|
||||
KeplerMemory::KeplerMemory(VideoCore::RasterizerInterface& rasterizer,
|
||||
MemoryManager& memory_manager)
|
||||
: memory_manager(memory_manager), rasterizer{rasterizer} {}
|
||||
|
||||
KeplerMemory::~KeplerMemory() = default;
|
||||
|
||||
void KeplerMemory::WriteReg(u32 method, u32 value) {
|
||||
@@ -37,6 +41,11 @@ void KeplerMemory::ProcessData(u32 data) {
|
||||
VAddr dest_address =
|
||||
*memory_manager.GpuToCpuAddress(address + state.write_offset * sizeof(u32));
|
||||
|
||||
// We have to invalidate the destination region to evict any outdated surfaces from the cache.
|
||||
// We do this before actually writing the new data because the destination address might contain
|
||||
// a dirty surface that will have to be written back to memory.
|
||||
rasterizer.InvalidateRegion(dest_address, sizeof(u32));
|
||||
|
||||
Memory::Write32(dest_address, data);
|
||||
|
||||
state.write_offset++;
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
|
||||
namespace VideoCore {
|
||||
class RasterizerInterface;
|
||||
}
|
||||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
#define KEPLERMEMORY_REG_INDEX(field_name) \
|
||||
@@ -18,7 +22,7 @@ namespace Tegra::Engines {
|
||||
|
||||
class KeplerMemory final {
|
||||
public:
|
||||
KeplerMemory(MemoryManager& memory_manager);
|
||||
KeplerMemory(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager);
|
||||
~KeplerMemory();
|
||||
|
||||
/// Write the value to the register identified by method.
|
||||
@@ -72,6 +76,7 @@ public:
|
||||
|
||||
private:
|
||||
MemoryManager& memory_manager;
|
||||
VideoCore::RasterizerInterface& rasterizer;
|
||||
|
||||
void ProcessData(u32 data);
|
||||
};
|
||||
|
||||
@@ -13,8 +13,7 @@
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
|
||||
namespace Tegra {
|
||||
namespace Engines {
|
||||
namespace Tegra::Engines {
|
||||
|
||||
/// First register id that is actually a Macro call.
|
||||
constexpr u32 MacroRegistersStart = 0xE00;
|
||||
@@ -408,5 +407,4 @@ void Maxwell3D::ProcessClearBuffers() {
|
||||
rasterizer.Clear();
|
||||
}
|
||||
|
||||
} // namespace Engines
|
||||
} // namespace Tegra
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -643,8 +643,10 @@ public:
|
||||
u32 d3d_cull_mode;
|
||||
|
||||
ComparisonOp depth_test_func;
|
||||
float alpha_test_ref;
|
||||
ComparisonOp alpha_test_func;
|
||||
|
||||
INSERT_PADDING_WORDS(0xB);
|
||||
INSERT_PADDING_WORDS(0x9);
|
||||
|
||||
struct {
|
||||
u32 separate_alpha;
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
#include "core/core.h"
|
||||
#include "video_core/engines/maxwell_compute.h"
|
||||
|
||||
namespace Tegra {
|
||||
namespace Engines {
|
||||
namespace Tegra::Engines {
|
||||
|
||||
void MaxwellCompute::WriteReg(u32 method, u32 value) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
||||
@@ -26,5 +25,4 @@ void MaxwellCompute::WriteReg(u32 method, u32 value) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Engines
|
||||
} // namespace Tegra
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
|
||||
#include "core/memory.h"
|
||||
#include "video_core/engines/maxwell_dma.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/textures/decoders.h"
|
||||
|
||||
namespace Tegra {
|
||||
namespace Engines {
|
||||
namespace Tegra::Engines {
|
||||
|
||||
MaxwellDMA::MaxwellDMA(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
|
||||
MaxwellDMA::MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
|
||||
: memory_manager(memory_manager), rasterizer{rasterizer} {}
|
||||
|
||||
void MaxwellDMA::WriteReg(u32 method, u32 value) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
||||
@@ -44,40 +45,77 @@ void MaxwellDMA::HandleCopy() {
|
||||
ASSERT(regs.exec.query_mode == Regs::QueryMode::None);
|
||||
ASSERT(regs.exec.query_intr == Regs::QueryIntr::None);
|
||||
ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2);
|
||||
ASSERT(regs.src_params.pos_x == 0);
|
||||
ASSERT(regs.src_params.pos_y == 0);
|
||||
ASSERT(regs.dst_params.pos_x == 0);
|
||||
ASSERT(regs.dst_params.pos_y == 0);
|
||||
|
||||
if (regs.exec.is_dst_linear == regs.exec.is_src_linear) {
|
||||
std::size_t copy_size = regs.x_count;
|
||||
if (!regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
|
||||
// If both the source and the destination are in block layout, assert.
|
||||
UNREACHABLE_MSG("Tiled->Tiled DMA transfers are not yet implemented");
|
||||
return;
|
||||
}
|
||||
|
||||
if (regs.exec.is_dst_linear && regs.exec.is_src_linear) {
|
||||
// When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D
|
||||
// buffer of length `x_count`, otherwise we copy a 2D buffer of size (x_count, y_count).
|
||||
if (regs.exec.enable_2d) {
|
||||
copy_size = copy_size * regs.y_count;
|
||||
// buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count,
|
||||
// y_count).
|
||||
if (!regs.exec.enable_2d) {
|
||||
Memory::CopyBlock(dest_cpu, source_cpu, regs.x_count);
|
||||
return;
|
||||
}
|
||||
|
||||
Memory::CopyBlock(dest_cpu, source_cpu, copy_size);
|
||||
// If both the source and the destination are in linear layout, perform a line-by-line
|
||||
// copy. We're going to take a subrect of size (x_count, y_count) from the source
|
||||
// rectangle. There is no need to manually flush/invalidate the regions because
|
||||
// CopyBlock does that for us.
|
||||
for (u32 line = 0; line < regs.y_count; ++line) {
|
||||
const VAddr source_line = source_cpu + line * regs.src_pitch;
|
||||
const VAddr dest_line = dest_cpu + line * regs.dst_pitch;
|
||||
Memory::CopyBlock(dest_line, source_line, regs.x_count);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(regs.exec.enable_2d == 1);
|
||||
u8* src_buffer = Memory::GetPointer(source_cpu);
|
||||
u8* dst_buffer = Memory::GetPointer(dest_cpu);
|
||||
|
||||
const std::size_t copy_size = regs.x_count * regs.y_count;
|
||||
|
||||
const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) {
|
||||
// TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated
|
||||
// copying.
|
||||
rasterizer.FlushRegion(source_cpu, src_size);
|
||||
|
||||
// We have to invalidate the destination region to evict any outdated surfaces from the
|
||||
// cache. We do this before actually writing the new data because the destination address
|
||||
// might contain a dirty surface that will have to be written back to memory.
|
||||
rasterizer.InvalidateRegion(dest_cpu, dst_size);
|
||||
};
|
||||
|
||||
if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
|
||||
ASSERT(regs.src_params.size_z == 1);
|
||||
// If the input is tiled and the output is linear, deswizzle the input and copy it over.
|
||||
Texture::CopySwizzledData(regs.src_params.size_x, regs.src_params.size_y,
|
||||
regs.src_params.size_z, 1, 1, src_buffer, dst_buffer, true,
|
||||
regs.src_params.BlockHeight(), regs.src_params.BlockDepth());
|
||||
|
||||
const u32 src_bytes_per_pixel = regs.src_pitch / regs.src_params.size_x;
|
||||
|
||||
FlushAndInvalidate(regs.src_pitch * regs.src_params.size_y,
|
||||
copy_size * src_bytes_per_pixel);
|
||||
|
||||
Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch,
|
||||
regs.src_params.size_x, src_bytes_per_pixel, source_cpu, dest_cpu,
|
||||
regs.src_params.BlockHeight(), regs.src_params.pos_x,
|
||||
regs.src_params.pos_y);
|
||||
} else {
|
||||
ASSERT(regs.dst_params.size_z == 1);
|
||||
ASSERT(regs.src_pitch == regs.x_count);
|
||||
|
||||
const u32 src_bpp = regs.src_pitch / regs.x_count;
|
||||
|
||||
FlushAndInvalidate(regs.src_pitch * regs.y_count,
|
||||
regs.dst_params.size_x * regs.dst_params.size_y * src_bpp);
|
||||
|
||||
// If the input is linear and the output is tiled, swizzle the input and copy it over.
|
||||
Texture::CopySwizzledData(regs.dst_params.size_x, regs.dst_params.size_y,
|
||||
regs.dst_params.size_z, 1, 1, dst_buffer, src_buffer, false,
|
||||
regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth());
|
||||
Texture::SwizzleSubrect(regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x,
|
||||
src_bpp, dest_cpu, source_cpu, regs.dst_params.BlockHeight());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Engines
|
||||
} // namespace Tegra
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -12,11 +12,15 @@
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
|
||||
namespace VideoCore {
|
||||
class RasterizerInterface;
|
||||
}
|
||||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
class MaxwellDMA final {
|
||||
public:
|
||||
explicit MaxwellDMA(MemoryManager& memory_manager);
|
||||
explicit MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager);
|
||||
~MaxwellDMA() = default;
|
||||
|
||||
/// Write the value to the register identified by method.
|
||||
@@ -133,6 +137,8 @@ public:
|
||||
MemoryManager& memory_manager;
|
||||
|
||||
private:
|
||||
VideoCore::RasterizerInterface& rasterizer;
|
||||
|
||||
/// Performs the copy from the source buffer to the destination buffer as configured in the
|
||||
/// registers.
|
||||
void HandleCopy();
|
||||
|
||||
@@ -214,7 +214,7 @@ enum class IMinMaxExchange : u64 {
|
||||
XHi = 3,
|
||||
};
|
||||
|
||||
enum class VmadType : u64 {
|
||||
enum class VideoType : u64 {
|
||||
Size16_Low = 0,
|
||||
Size16_High = 1,
|
||||
Size32 = 2,
|
||||
@@ -335,6 +335,26 @@ enum class IsberdMode : u64 {
|
||||
|
||||
enum class IsberdShift : u64 { None = 0, U16 = 1, B32 = 2 };
|
||||
|
||||
enum class HalfType : u64 {
|
||||
H0_H1 = 0,
|
||||
F32 = 1,
|
||||
H0_H0 = 2,
|
||||
H1_H1 = 3,
|
||||
};
|
||||
|
||||
enum class HalfMerge : u64 {
|
||||
H0_H1 = 0,
|
||||
F32 = 1,
|
||||
Mrg_H0 = 2,
|
||||
Mrg_H1 = 3,
|
||||
};
|
||||
|
||||
enum class HalfPrecision : u64 {
|
||||
None = 0,
|
||||
FTZ = 1,
|
||||
FMZ = 2,
|
||||
};
|
||||
|
||||
enum class IpaInterpMode : u64 {
|
||||
Linear = 0,
|
||||
Perspective = 1,
|
||||
@@ -543,6 +563,10 @@ union Instruction {
|
||||
BitField<48, 1, u64> negate_b;
|
||||
} fmul;
|
||||
|
||||
union {
|
||||
BitField<55, 1, u64> saturate;
|
||||
} fmul32;
|
||||
|
||||
union {
|
||||
BitField<48, 1, u64> is_signed;
|
||||
} shift;
|
||||
@@ -553,6 +577,70 @@ union Instruction {
|
||||
BitField<49, 1, u64> negate_a;
|
||||
} alu_integer;
|
||||
|
||||
union {
|
||||
BitField<39, 1, u64> ftz;
|
||||
BitField<32, 1, u64> saturate;
|
||||
BitField<49, 2, HalfMerge> merge;
|
||||
|
||||
BitField<43, 1, u64> negate_a;
|
||||
BitField<44, 1, u64> abs_a;
|
||||
BitField<47, 2, HalfType> type_a;
|
||||
|
||||
BitField<31, 1, u64> negate_b;
|
||||
BitField<30, 1, u64> abs_b;
|
||||
BitField<47, 2, HalfType> type_b;
|
||||
|
||||
BitField<35, 2, HalfType> type_c;
|
||||
} alu_half;
|
||||
|
||||
union {
|
||||
BitField<39, 2, HalfPrecision> precision;
|
||||
BitField<39, 1, u64> ftz;
|
||||
BitField<52, 1, u64> saturate;
|
||||
BitField<49, 2, HalfMerge> merge;
|
||||
|
||||
BitField<43, 1, u64> negate_a;
|
||||
BitField<44, 1, u64> abs_a;
|
||||
BitField<47, 2, HalfType> type_a;
|
||||
} alu_half_imm;
|
||||
|
||||
union {
|
||||
BitField<29, 1, u64> first_negate;
|
||||
BitField<20, 9, u64> first;
|
||||
|
||||
BitField<56, 1, u64> second_negate;
|
||||
BitField<30, 9, u64> second;
|
||||
|
||||
u32 PackImmediates() const {
|
||||
// Immediates are half floats shifted.
|
||||
constexpr u32 imm_shift = 6;
|
||||
return static_cast<u32>((first << imm_shift) | (second << (16 + imm_shift)));
|
||||
}
|
||||
} half_imm;
|
||||
|
||||
union {
|
||||
union {
|
||||
BitField<37, 2, HalfPrecision> precision;
|
||||
BitField<32, 1, u64> saturate;
|
||||
|
||||
BitField<30, 1, u64> negate_c;
|
||||
BitField<35, 2, HalfType> type_c;
|
||||
} rr;
|
||||
|
||||
BitField<57, 2, HalfPrecision> precision;
|
||||
BitField<52, 1, u64> saturate;
|
||||
|
||||
BitField<49, 2, HalfMerge> merge;
|
||||
|
||||
BitField<47, 2, HalfType> type_a;
|
||||
|
||||
BitField<56, 1, u64> negate_b;
|
||||
BitField<28, 2, HalfType> type_b;
|
||||
|
||||
BitField<51, 1, u64> negate_c;
|
||||
BitField<53, 2, HalfType> type_reg39;
|
||||
} hfma2;
|
||||
|
||||
union {
|
||||
BitField<40, 1, u64> invert;
|
||||
} popc;
|
||||
@@ -669,7 +757,6 @@ union Instruction {
|
||||
BitField<45, 2, PredOperation> op;
|
||||
BitField<47, 1, u64> ftz;
|
||||
BitField<48, 4, PredCondition> cond;
|
||||
BitField<56, 1, u64> neg_b;
|
||||
} fsetp;
|
||||
|
||||
union {
|
||||
@@ -695,6 +782,14 @@ union Instruction {
|
||||
BitField<45, 2, PredOperation> op;
|
||||
} psetp;
|
||||
|
||||
union {
|
||||
BitField<43, 4, PredCondition> cond;
|
||||
BitField<45, 2, PredOperation> op;
|
||||
BitField<3, 3, u64> pred3;
|
||||
BitField<0, 3, u64> pred0;
|
||||
BitField<39, 3, u64> pred39;
|
||||
} vsetp;
|
||||
|
||||
union {
|
||||
BitField<12, 3, u64> pred12;
|
||||
BitField<15, 1, u64> neg_pred12;
|
||||
@@ -716,6 +811,23 @@ union Instruction {
|
||||
BitField<45, 4, PredOperation> op; // op with pred39
|
||||
} csetp;
|
||||
|
||||
union {
|
||||
BitField<35, 4, PredCondition> cond;
|
||||
BitField<49, 1, u64> h_and;
|
||||
BitField<6, 1, u64> ftz;
|
||||
BitField<45, 2, PredOperation> op;
|
||||
BitField<3, 3, u64> pred3;
|
||||
BitField<0, 3, u64> pred0;
|
||||
BitField<43, 1, u64> negate_a;
|
||||
BitField<44, 1, u64> abs_a;
|
||||
BitField<47, 2, HalfType> type_a;
|
||||
BitField<31, 1, u64> negate_b;
|
||||
BitField<30, 1, u64> abs_b;
|
||||
BitField<28, 2, HalfType> type_b;
|
||||
BitField<42, 1, u64> neg_pred;
|
||||
BitField<39, 3, u64> pred39;
|
||||
} hsetp2;
|
||||
|
||||
union {
|
||||
BitField<39, 3, u64> pred39;
|
||||
BitField<42, 1, u64> neg_pred;
|
||||
@@ -727,9 +839,23 @@ union Instruction {
|
||||
BitField<53, 1, u64> neg_b;
|
||||
BitField<54, 1, u64> abs_a;
|
||||
BitField<55, 1, u64> ftz;
|
||||
BitField<56, 1, u64> neg_imm;
|
||||
} fset;
|
||||
|
||||
union {
|
||||
BitField<49, 1, u64> bf;
|
||||
BitField<35, 3, PredCondition> cond;
|
||||
BitField<50, 1, u64> ftz;
|
||||
BitField<45, 2, PredOperation> op;
|
||||
BitField<43, 1, u64> negate_a;
|
||||
BitField<44, 1, u64> abs_a;
|
||||
BitField<47, 2, HalfType> type_a;
|
||||
BitField<31, 1, u64> negate_b;
|
||||
BitField<30, 1, u64> abs_b;
|
||||
BitField<28, 2, HalfType> type_b;
|
||||
BitField<42, 1, u64> neg_pred;
|
||||
BitField<39, 3, u64> pred39;
|
||||
} hset2;
|
||||
|
||||
union {
|
||||
BitField<39, 3, u64> pred39;
|
||||
BitField<42, 1, u64> neg_pred;
|
||||
@@ -1036,15 +1162,17 @@ union Instruction {
|
||||
union {
|
||||
BitField<48, 1, u64> signed_a;
|
||||
BitField<38, 1, u64> is_byte_chunk_a;
|
||||
BitField<36, 2, VmadType> type_a;
|
||||
BitField<36, 2, VideoType> type_a;
|
||||
BitField<36, 2, u64> byte_height_a;
|
||||
|
||||
BitField<49, 1, u64> signed_b;
|
||||
BitField<50, 1, u64> use_register_b;
|
||||
BitField<30, 1, u64> is_byte_chunk_b;
|
||||
BitField<28, 2, VmadType> type_b;
|
||||
BitField<28, 2, VideoType> type_b;
|
||||
BitField<28, 2, u64> byte_height_b;
|
||||
} video;
|
||||
|
||||
union {
|
||||
BitField<51, 2, VmadShr> shr;
|
||||
BitField<55, 1, u64> saturate; // Saturates the result (a * b + c)
|
||||
BitField<47, 1, u64> cc;
|
||||
@@ -1095,11 +1223,13 @@ public:
|
||||
KIL,
|
||||
SSY,
|
||||
SYNC,
|
||||
BRK,
|
||||
DEPBAR,
|
||||
BFE_C,
|
||||
BFE_R,
|
||||
BFE_IMM,
|
||||
BRA,
|
||||
PBK,
|
||||
LD_A,
|
||||
LD_C,
|
||||
ST_A,
|
||||
@@ -1118,6 +1248,7 @@ public:
|
||||
OUT_R, // Emit vertex/primitive
|
||||
ISBERD,
|
||||
VMAD,
|
||||
VSETP,
|
||||
FFMA_IMM, // Fused Multiply and Add
|
||||
FFMA_CR,
|
||||
FFMA_RC,
|
||||
@@ -1145,6 +1276,18 @@ public:
|
||||
LEA_RZ,
|
||||
LEA_IMM,
|
||||
LEA_HI,
|
||||
HADD2_C,
|
||||
HADD2_R,
|
||||
HADD2_IMM,
|
||||
HMUL2_C,
|
||||
HMUL2_R,
|
||||
HMUL2_IMM,
|
||||
HFMA2_CR,
|
||||
HFMA2_RC,
|
||||
HFMA2_RR,
|
||||
HFMA2_IMM_R,
|
||||
HSETP2_R,
|
||||
HSET2_R,
|
||||
POPC_C,
|
||||
POPC_R,
|
||||
POPC_IMM,
|
||||
@@ -1218,9 +1361,12 @@ public:
|
||||
ArithmeticImmediate,
|
||||
ArithmeticInteger,
|
||||
ArithmeticIntegerImmediate,
|
||||
ArithmeticHalf,
|
||||
ArithmeticHalfImmediate,
|
||||
Bfe,
|
||||
Shift,
|
||||
Ffma,
|
||||
Hfma2,
|
||||
Flow,
|
||||
Synch,
|
||||
Memory,
|
||||
@@ -1228,6 +1374,8 @@ public:
|
||||
FloatSetPredicate,
|
||||
IntegerSet,
|
||||
IntegerSetPredicate,
|
||||
HalfSet,
|
||||
HalfSetPredicate,
|
||||
PredicateSetPredicate,
|
||||
PredicateSetRegister,
|
||||
Conversion,
|
||||
@@ -1239,7 +1387,7 @@ public:
|
||||
/// conditionally executed).
|
||||
static bool IsPredicatedInstruction(Id opcode) {
|
||||
// TODO(Subv): Add the rest of unpredicated instructions.
|
||||
return opcode != Id::SSY;
|
||||
return opcode != Id::SSY && opcode != Id::PBK;
|
||||
}
|
||||
|
||||
class Matcher {
|
||||
@@ -1335,9 +1483,11 @@ private:
|
||||
#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name)
|
||||
INST("111000110011----", Id::KIL, Type::Flow, "KIL"),
|
||||
INST("111000101001----", Id::SSY, Type::Flow, "SSY"),
|
||||
INST("111000101010----", Id::PBK, Type::Flow, "PBK"),
|
||||
INST("111000100100----", Id::BRA, Type::Flow, "BRA"),
|
||||
INST("1111000011111---", Id::SYNC, Type::Flow, "SYNC"),
|
||||
INST("111000110100---", Id::BRK, Type::Flow, "BRK"),
|
||||
INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"),
|
||||
INST("1111000011111---", Id::SYNC, Type::Synch, "SYNC"),
|
||||
INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"),
|
||||
INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"),
|
||||
INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"),
|
||||
@@ -1356,6 +1506,7 @@ private:
|
||||
INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"),
|
||||
INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"),
|
||||
INST("01011111--------", Id::VMAD, Type::Trivial, "VMAD"),
|
||||
INST("0101000011110---", Id::VSETP, Type::Trivial, "VSETP"),
|
||||
INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
|
||||
INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"),
|
||||
INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"),
|
||||
@@ -1389,6 +1540,18 @@ private:
|
||||
INST("001101101101----", Id::LEA_IMM, Type::ArithmeticInteger, "LEA_IMM"),
|
||||
INST("010010111101----", Id::LEA_RZ, Type::ArithmeticInteger, "LEA_RZ"),
|
||||
INST("00011000--------", Id::LEA_HI, Type::ArithmeticInteger, "LEA_HI"),
|
||||
INST("0111101-1-------", Id::HADD2_C, Type::ArithmeticHalf, "HADD2_C"),
|
||||
INST("0101110100010---", Id::HADD2_R, Type::ArithmeticHalf, "HADD2_R"),
|
||||
INST("0111101-0-------", Id::HADD2_IMM, Type::ArithmeticHalfImmediate, "HADD2_IMM"),
|
||||
INST("0111100-1-------", Id::HMUL2_C, Type::ArithmeticHalf, "HMUL2_C"),
|
||||
INST("0101110100001---", Id::HMUL2_R, Type::ArithmeticHalf, "HMUL2_R"),
|
||||
INST("0111100-0-------", Id::HMUL2_IMM, Type::ArithmeticHalfImmediate, "HMUL2_IMM"),
|
||||
INST("01110---1-------", Id::HFMA2_CR, Type::Hfma2, "HFMA2_CR"),
|
||||
INST("01100---1-------", Id::HFMA2_RC, Type::Hfma2, "HFMA2_RC"),
|
||||
INST("0101110100000---", Id::HFMA2_RR, Type::Hfma2, "HFMA2_RR"),
|
||||
INST("01110---0-------", Id::HFMA2_IMM_R, Type::Hfma2, "HFMA2_R_IMM"),
|
||||
INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP_R"),
|
||||
INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"),
|
||||
INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"),
|
||||
INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"),
|
||||
INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"),
|
||||
@@ -1463,4 +1626,4 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Tegra::Shader
|
||||
} // namespace Tegra::Shader
|
||||
@@ -27,8 +27,8 @@ GPU::GPU(VideoCore::RasterizerInterface& rasterizer) {
|
||||
maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager);
|
||||
fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager);
|
||||
maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
|
||||
maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager);
|
||||
kepler_memory = std::make_unique<Engines::KeplerMemory>(*memory_manager);
|
||||
maxwell_dma = std::make_unique<Engines::MaxwellDMA>(rasterizer, *memory_manager);
|
||||
kepler_memory = std::make_unique<Engines::KeplerMemory>(rasterizer, *memory_manager);
|
||||
}
|
||||
|
||||
GPU::~GPU() = default;
|
||||
|
||||
@@ -570,10 +570,11 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
SyncBlendState();
|
||||
SyncLogicOpState();
|
||||
SyncCullMode();
|
||||
SyncAlphaTest();
|
||||
SyncScissorTest();
|
||||
// Alpha Testing is synced on shaders.
|
||||
SyncTransformFeedback();
|
||||
SyncPointState();
|
||||
CheckAlphaTests();
|
||||
|
||||
// TODO(bunnei): Sync framebuffer_scale uniform here
|
||||
// TODO(bunnei): Sync scissorbox uniform(s) here
|
||||
@@ -659,6 +660,12 @@ void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) {
|
||||
bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& dst) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Blits);
|
||||
|
||||
if (Settings::values.use_accurate_gpu_emulation) {
|
||||
// Skip the accelerated copy and perform a slow but more accurate copy
|
||||
return false;
|
||||
}
|
||||
|
||||
res_cache.FermiCopySurface(src, dst);
|
||||
return true;
|
||||
}
|
||||
@@ -1001,17 +1008,6 @@ void RasterizerOpenGL::SyncLogicOpState() {
|
||||
state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncAlphaTest() {
|
||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||
|
||||
// TODO(Rodrigo): Alpha testing is a legacy OpenGL feature, but it can be
|
||||
// implemented with a test+discard in fragment shaders.
|
||||
if (regs.alpha_test_enabled != 0) {
|
||||
LOG_CRITICAL(Render_OpenGL, "Alpha testing is not implemented");
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncScissorTest() {
|
||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||
|
||||
@@ -1046,4 +1042,15 @@ void RasterizerOpenGL::SyncPointState() {
|
||||
state.point.size = regs.point_size == 0 ? 1 : regs.point_size;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::CheckAlphaTests() {
|
||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||
|
||||
if (regs.alpha_test_enabled != 0 && regs.rt_control.count > 1) {
|
||||
LOG_CRITICAL(
|
||||
Render_OpenGL,
|
||||
"Alpha Testing is enabled with Multiple Render Targets, this behavior is undefined.");
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -162,9 +162,6 @@ private:
|
||||
/// Syncs the LogicOp state to match the guest state
|
||||
void SyncLogicOpState();
|
||||
|
||||
/// Syncs the alpha test state to match the guest state
|
||||
void SyncAlphaTest();
|
||||
|
||||
/// Syncs the scissor test state to match the guest state
|
||||
void SyncScissorTest();
|
||||
|
||||
@@ -174,6 +171,9 @@ private:
|
||||
/// Syncs the point state to match the guest state
|
||||
void SyncPointState();
|
||||
|
||||
/// Check asserts for alpha testing.
|
||||
void CheckAlphaTests();
|
||||
|
||||
bool has_ARB_direct_state_access = false;
|
||||
bool has_ARB_multi_bind = false;
|
||||
bool has_ARB_separate_shader_objects = false;
|
||||
|
||||
@@ -30,8 +30,6 @@ using Tegra::Shader::SubOp;
|
||||
constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH;
|
||||
constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header);
|
||||
|
||||
enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 };
|
||||
|
||||
constexpr u32 MAX_GEOMETRY_BUFFERS = 6;
|
||||
constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested
|
||||
|
||||
@@ -165,10 +163,11 @@ private:
|
||||
const ExitMethod jmp = Scan(target, end, labels);
|
||||
return exit_method = ParallelExit(no_jmp, jmp);
|
||||
}
|
||||
case OpCode::Id::SSY: {
|
||||
// The SSY instruction uses a similar encoding as the BRA instruction.
|
||||
case OpCode::Id::SSY:
|
||||
case OpCode::Id::PBK: {
|
||||
// The SSY and PBK use a similar encoding as the BRA instruction.
|
||||
ASSERT_MSG(instr.bra.constant_buffer == 0,
|
||||
"Constant buffer SSY is not supported");
|
||||
"Constant buffer branching is not supported");
|
||||
const u32 target = offset + instr.bra.GetBranchTarget();
|
||||
labels.insert(target);
|
||||
// Continue scanning for an exit method.
|
||||
@@ -375,12 +374,56 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes code that does a register assignment to a half float value operation.
|
||||
* @param reg The destination register to use.
|
||||
* @param elem The element to use for the operation.
|
||||
* @param value The code representing the value to assign. Type has to be half float.
|
||||
* @param merge Half float kind of assignment.
|
||||
* @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 dest_elem Optional, the destination element to use for the operation.
|
||||
*/
|
||||
void SetRegisterToHalfFloat(const Register& reg, u64 elem, const std::string& value,
|
||||
Tegra::Shader::HalfMerge merge, u64 dest_num_components,
|
||||
u64 value_num_components, bool is_saturated = false,
|
||||
u64 dest_elem = 0) {
|
||||
ASSERT_MSG(!is_saturated, "Unimplemented");
|
||||
|
||||
const std::string result = [&]() {
|
||||
switch (merge) {
|
||||
case Tegra::Shader::HalfMerge::H0_H1:
|
||||
return "uintBitsToFloat(packHalf2x16(" + value + "))";
|
||||
case Tegra::Shader::HalfMerge::F32:
|
||||
// Half float instructions take the first component when doing a float cast.
|
||||
return "float(" + value + ".x)";
|
||||
case Tegra::Shader::HalfMerge::Mrg_H0:
|
||||
// TODO(Rodrigo): I guess Mrg_H0 and Mrg_H1 take their respective component from the
|
||||
// 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) +
|
||||
" & 0xffff0000) | (packHalf2x16(" + value + ") & 0x0000ffff))";
|
||||
case Tegra::Shader::HalfMerge::Mrg_H1:
|
||||
return "((" + GetRegisterAsInteger(reg, 0, false) +
|
||||
" & 0x0000ffff) | (packHalf2x16(" + value + ") & 0xffff0000))";
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return std::string("0");
|
||||
}
|
||||
}();
|
||||
|
||||
SetRegister(reg, elem, result, dest_num_components, value_num_components, dest_elem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes code that does a register assignment to input attribute operation. Input attributes
|
||||
* are stored as floats, so this may require conversion.
|
||||
* @param reg The destination register to use.
|
||||
* @param elem The element to use for the operation.
|
||||
* @param attribute The input attribute to use as the source value.
|
||||
* @param input_mode The input mode.
|
||||
* @param vertex The register that decides which vertex to read from (used in GS).
|
||||
*/
|
||||
void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute,
|
||||
@@ -548,13 +591,6 @@ private:
|
||||
|
||||
/// Generates declarations for input attributes.
|
||||
void GenerateInputAttrs() {
|
||||
if (stage != Maxwell3D::Regs::ShaderStage::Vertex) {
|
||||
const std::string attr =
|
||||
stage == Maxwell3D::Regs::ShaderStage::Geometry ? "gs_position[]" : "position";
|
||||
declarations.AddLine("layout (location = " + std::to_string(POSITION_VARYING_LOCATION) +
|
||||
") in vec4 " + attr + ';');
|
||||
}
|
||||
|
||||
for (const auto element : declr_input_attribute) {
|
||||
// TODO(bunnei): Use proper number of elements for these
|
||||
u32 idx =
|
||||
@@ -577,10 +613,6 @@ private:
|
||||
|
||||
/// Generates declarations for output attributes.
|
||||
void GenerateOutputAttrs() {
|
||||
if (stage != Maxwell3D::Regs::ShaderStage::Fragment) {
|
||||
declarations.AddLine("layout (location = " + std::to_string(POSITION_VARYING_LOCATION) +
|
||||
") out vec4 position;");
|
||||
}
|
||||
for (const auto& index : declr_output_attribute) {
|
||||
// TODO(bunnei): Use proper number of elements for these
|
||||
const u32 idx = static_cast<u32>(index) -
|
||||
@@ -877,6 +909,19 @@ private:
|
||||
return fmt::format("uintBitsToFloat({})", instr.alu.GetImm20_32());
|
||||
}
|
||||
|
||||
/// Generates code representing a vec2 pair unpacked from a half float immediate
|
||||
static std::string UnpackHalfImmediate(const Instruction& instr, bool negate) {
|
||||
const std::string immediate = GetHalfFloat(std::to_string(instr.half_imm.PackImmediates()));
|
||||
if (!negate) {
|
||||
return immediate;
|
||||
}
|
||||
const std::string negate_first = instr.half_imm.first_negate != 0 ? "-" : "";
|
||||
const std::string negate_second = instr.half_imm.second_negate != 0 ? "-" : "";
|
||||
const std::string negate_vec = "vec2(" + negate_first + "1, " + negate_second + "1)";
|
||||
|
||||
return '(' + immediate + " * " + negate_vec + ')';
|
||||
}
|
||||
|
||||
/// Generates code representing a texture sampler.
|
||||
std::string GetSampler(const Sampler& sampler, Tegra::Shader::TextureType type, bool is_array,
|
||||
bool is_shadow) {
|
||||
@@ -908,7 +953,7 @@ private:
|
||||
// Can't assign to the constant predicate.
|
||||
ASSERT(pred != static_cast<u64>(Pred::UnusedIndex));
|
||||
|
||||
const std::string variable = 'p' + std::to_string(pred) + '_' + suffix;
|
||||
std::string variable = 'p' + std::to_string(pred) + '_' + suffix;
|
||||
shader.AddLine(variable + " = " + value + ';');
|
||||
declr_predicates.insert(std::move(variable));
|
||||
}
|
||||
@@ -1012,6 +1057,41 @@ private:
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transforms the input string GLSL operand into an unpacked half float pair.
|
||||
* @note This function returns a float type pair instead of a half float pair. This is because
|
||||
* real half floats are not standardized in GLSL but unpackHalf2x16 (which returns a vec2) is.
|
||||
* @param operand Input operand. It has to be an unsigned integer.
|
||||
* @param type How to unpack the unsigned integer to a half float pair.
|
||||
* @param abs Get the absolute value of unpacked half floats.
|
||||
* @param neg Get the negative value of unpacked half floats.
|
||||
* @returns String corresponding to a half float pair.
|
||||
*/
|
||||
static std::string GetHalfFloat(const std::string& operand,
|
||||
Tegra::Shader::HalfType type = Tegra::Shader::HalfType::H0_H1,
|
||||
bool abs = false, bool neg = false) {
|
||||
// "vec2" calls emitted in this function are intended to alias components.
|
||||
const std::string value = [&]() {
|
||||
switch (type) {
|
||||
case Tegra::Shader::HalfType::H0_H1:
|
||||
return "unpackHalf2x16(" + operand + ')';
|
||||
case Tegra::Shader::HalfType::F32:
|
||||
return "vec2(uintBitsToFloat(" + operand + "))";
|
||||
case Tegra::Shader::HalfType::H0_H0:
|
||||
case Tegra::Shader::HalfType::H1_H1: {
|
||||
const bool high = type == Tegra::Shader::HalfType::H1_H1;
|
||||
const char unpack_index = "xy"[high ? 1 : 0];
|
||||
return "vec2(unpackHalf2x16(" + operand + ")." + unpack_index + ')';
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return std::string("vec2(0)");
|
||||
}
|
||||
}();
|
||||
|
||||
return GetOperandAbsNeg(value, abs, neg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether the instruction at the specified offset is a 'sched' instruction.
|
||||
* Sched instructions always appear before a sequence of 3 instructions.
|
||||
@@ -1154,27 +1234,27 @@ private:
|
||||
}
|
||||
|
||||
/*
|
||||
* Emits code to push the input target address to the SSY address stack, incrementing the stack
|
||||
* Emits code to push the input target address to the flow address stack, incrementing the stack
|
||||
* top.
|
||||
*/
|
||||
void EmitPushToSSYStack(u32 target) {
|
||||
void EmitPushToFlowStack(u32 target) {
|
||||
shader.AddLine('{');
|
||||
++shader.scope;
|
||||
shader.AddLine("ssy_stack[ssy_stack_top] = " + std::to_string(target) + "u;");
|
||||
shader.AddLine("ssy_stack_top++;");
|
||||
shader.AddLine("flow_stack[flow_stack_top] = " + std::to_string(target) + "u;");
|
||||
shader.AddLine("flow_stack_top++;");
|
||||
--shader.scope;
|
||||
shader.AddLine('}');
|
||||
}
|
||||
|
||||
/*
|
||||
* Emits code to pop an address from the SSY address stack, setting the jump address to the
|
||||
* Emits code to pop an address from the flow address stack, setting the jump address to the
|
||||
* popped address and decrementing the stack top.
|
||||
*/
|
||||
void EmitPopFromSSYStack() {
|
||||
void EmitPopFromFlowStack() {
|
||||
shader.AddLine('{');
|
||||
++shader.scope;
|
||||
shader.AddLine("ssy_stack_top--;");
|
||||
shader.AddLine("jmp_to = ssy_stack[ssy_stack_top];");
|
||||
shader.AddLine("flow_stack_top--;");
|
||||
shader.AddLine("jmp_to = flow_stack[flow_stack_top];");
|
||||
shader.AddLine("break;");
|
||||
--shader.scope;
|
||||
shader.AddLine('}');
|
||||
@@ -1186,9 +1266,29 @@ private:
|
||||
|
||||
ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented");
|
||||
|
||||
shader.AddLine("if (alpha_test[0] != 0) {");
|
||||
++shader.scope;
|
||||
// We start on the register containing the alpha value in the first RT.
|
||||
u32 current_reg = 3;
|
||||
for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets;
|
||||
++render_target) {
|
||||
// TODO(Blinkhawk): verify the behavior of alpha testing on hardware when
|
||||
// multiple render targets are used.
|
||||
if (header.ps.IsColorComponentOutputEnabled(render_target, 0) ||
|
||||
header.ps.IsColorComponentOutputEnabled(render_target, 1) ||
|
||||
header.ps.IsColorComponentOutputEnabled(render_target, 2) ||
|
||||
header.ps.IsColorComponentOutputEnabled(render_target, 3)) {
|
||||
shader.AddLine(fmt::format("if (!AlphaFunc({})) discard;",
|
||||
regs.GetRegisterAsFloat(current_reg)));
|
||||
current_reg += 4;
|
||||
}
|
||||
}
|
||||
--shader.scope;
|
||||
shader.AddLine('}');
|
||||
|
||||
// Write the color outputs using the data in the shader registers, disabled
|
||||
// rendertargets/components are skipped in the register assignment.
|
||||
u32 current_reg = 0;
|
||||
current_reg = 0;
|
||||
for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets;
|
||||
++render_target) {
|
||||
// TODO(Subv): Figure out how dual-source blending is configured in the Switch.
|
||||
@@ -1212,6 +1312,63 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
/// Unpacks a video instruction operand (e.g. VMAD).
|
||||
std::string GetVideoOperand(const std::string& op, bool is_chunk, bool is_signed,
|
||||
Tegra::Shader::VideoType type, u64 byte_height) {
|
||||
const std::string value = [&]() {
|
||||
if (!is_chunk) {
|
||||
const auto offset = static_cast<u32>(byte_height * 8);
|
||||
return "((" + op + " >> " + std::to_string(offset) + ") & 0xff)";
|
||||
}
|
||||
const std::string zero = "0";
|
||||
|
||||
switch (type) {
|
||||
case Tegra::Shader::VideoType::Size16_Low:
|
||||
return '(' + op + " & 0xffff)";
|
||||
case Tegra::Shader::VideoType::Size16_High:
|
||||
return '(' + op + " >> 16)";
|
||||
case Tegra::Shader::VideoType::Size32:
|
||||
// TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when
|
||||
// this type is used (1 * 1 + 0 == 0x5b800000). Until a better
|
||||
// explanation is found: assert.
|
||||
UNIMPLEMENTED();
|
||||
return zero;
|
||||
case Tegra::Shader::VideoType::Invalid:
|
||||
UNREACHABLE_MSG("Invalid instruction encoding");
|
||||
return zero;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return zero;
|
||||
}
|
||||
}();
|
||||
|
||||
if (is_signed) {
|
||||
return "int(" + value + ')';
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
/// Gets the A operand for a video instruction.
|
||||
std::string GetVideoOperandA(Instruction instr) {
|
||||
return GetVideoOperand(regs.GetRegisterAsInteger(instr.gpr8, 0, false),
|
||||
instr.video.is_byte_chunk_a != 0, instr.video.signed_a,
|
||||
instr.video.type_a, instr.video.byte_height_a);
|
||||
}
|
||||
|
||||
/// Gets the B operand for a video instruction.
|
||||
std::string GetVideoOperandB(Instruction instr) {
|
||||
if (instr.video.use_register_b) {
|
||||
return GetVideoOperand(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
|
||||
instr.video.is_byte_chunk_b != 0, instr.video.signed_b,
|
||||
instr.video.type_b, instr.video.byte_height_b);
|
||||
} else {
|
||||
return '(' +
|
||||
std::to_string(instr.video.signed_b ? static_cast<s16>(instr.alu.GetImm20_16())
|
||||
: instr.alu.GetImm20_16()) +
|
||||
')';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a single instruction from Tegra to GLSL.
|
||||
* @param offset the offset of the Tegra shader instruction.
|
||||
@@ -1381,9 +1538,10 @@ private:
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::FMUL32_IMM: {
|
||||
regs.SetRegisterToFloat(
|
||||
instr.gpr0, 0,
|
||||
regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1);
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0,
|
||||
regs.GetRegisterAsFloat(instr.gpr8) + " * " +
|
||||
GetImmediate32(instr),
|
||||
1, 1, instr.fmul32.saturate);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::FADD32I: {
|
||||
@@ -1748,6 +1906,86 @@ private:
|
||||
|
||||
break;
|
||||
}
|
||||
case OpCode::Type::ArithmeticHalf: {
|
||||
if (opcode->GetId() == OpCode::Id::HADD2_C || opcode->GetId() == OpCode::Id::HADD2_R) {
|
||||
ASSERT_MSG(instr.alu_half.ftz == 0, "Unimplemented");
|
||||
}
|
||||
const bool negate_a =
|
||||
opcode->GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0;
|
||||
const bool negate_b =
|
||||
opcode->GetId() != OpCode::Id::HMUL2_C && instr.alu_half.negate_b != 0;
|
||||
|
||||
const std::string op_a =
|
||||
GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.alu_half.type_a,
|
||||
instr.alu_half.abs_a != 0, negate_a);
|
||||
|
||||
std::string op_b;
|
||||
switch (opcode->GetId()) {
|
||||
case OpCode::Id::HADD2_C:
|
||||
case OpCode::Id::HMUL2_C:
|
||||
op_b = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
|
||||
GLSLRegister::Type::UnsignedInteger);
|
||||
break;
|
||||
case OpCode::Id::HADD2_R:
|
||||
case OpCode::Id::HMUL2_R:
|
||||
op_b = regs.GetRegisterAsInteger(instr.gpr20, 0, false);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
op_b = "0";
|
||||
break;
|
||||
}
|
||||
op_b = GetHalfFloat(op_b, instr.alu_half.type_b, instr.alu_half.abs_b != 0, negate_b);
|
||||
|
||||
const std::string result = [&]() {
|
||||
switch (opcode->GetId()) {
|
||||
case OpCode::Id::HADD2_C:
|
||||
case OpCode::Id::HADD2_R:
|
||||
return '(' + op_a + " + " + op_b + ')';
|
||||
case OpCode::Id::HMUL2_C:
|
||||
case OpCode::Id::HMUL2_R:
|
||||
return '(' + op_a + " * " + op_b + ')';
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unhandled half float instruction: {}", opcode->GetName());
|
||||
UNREACHABLE();
|
||||
return std::string("0");
|
||||
}
|
||||
}();
|
||||
|
||||
regs.SetRegisterToHalfFloat(instr.gpr0, 0, result, instr.alu_half.merge, 1, 1,
|
||||
instr.alu_half.saturate != 0);
|
||||
break;
|
||||
}
|
||||
case OpCode::Type::ArithmeticHalfImmediate: {
|
||||
if (opcode->GetId() == OpCode::Id::HADD2_IMM) {
|
||||
ASSERT_MSG(instr.alu_half_imm.ftz == 0, "Unimplemented");
|
||||
} else {
|
||||
ASSERT_MSG(instr.alu_half_imm.precision == Tegra::Shader::HalfPrecision::None,
|
||||
"Unimplemented");
|
||||
}
|
||||
|
||||
const std::string op_a = GetHalfFloat(
|
||||
regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.alu_half_imm.type_a,
|
||||
instr.alu_half_imm.abs_a != 0, instr.alu_half_imm.negate_a != 0);
|
||||
|
||||
const std::string op_b = UnpackHalfImmediate(instr, true);
|
||||
|
||||
const std::string result = [&]() {
|
||||
switch (opcode->GetId()) {
|
||||
case OpCode::Id::HADD2_IMM:
|
||||
return op_a + " + " + op_b;
|
||||
case OpCode::Id::HMUL2_IMM:
|
||||
return op_a + " * " + op_b;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return std::string("0");
|
||||
}
|
||||
}();
|
||||
|
||||
regs.SetRegisterToHalfFloat(instr.gpr0, 0, result, instr.alu_half_imm.merge, 1, 1,
|
||||
instr.alu_half_imm.saturate != 0);
|
||||
break;
|
||||
}
|
||||
case OpCode::Type::Ffma: {
|
||||
const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
std::string op_b = instr.ffma.negate_b ? "-" : "";
|
||||
@@ -1792,6 +2030,59 @@ private:
|
||||
instr.alu.saturate_d);
|
||||
break;
|
||||
}
|
||||
case OpCode::Type::Hfma2: {
|
||||
if (opcode->GetId() == OpCode::Id::HFMA2_RR) {
|
||||
ASSERT_MSG(instr.hfma2.rr.precision == Tegra::Shader::HalfPrecision::None,
|
||||
"Unimplemented");
|
||||
} else {
|
||||
ASSERT_MSG(instr.hfma2.precision == Tegra::Shader::HalfPrecision::None,
|
||||
"Unimplemented");
|
||||
}
|
||||
const bool saturate = opcode->GetId() == OpCode::Id::HFMA2_RR
|
||||
? instr.hfma2.rr.saturate != 0
|
||||
: instr.hfma2.saturate != 0;
|
||||
|
||||
const std::string op_a =
|
||||
GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hfma2.type_a);
|
||||
std::string op_b, op_c;
|
||||
|
||||
switch (opcode->GetId()) {
|
||||
case OpCode::Id::HFMA2_CR:
|
||||
op_b = GetHalfFloat(regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
|
||||
GLSLRegister::Type::UnsignedInteger),
|
||||
instr.hfma2.type_b, false, instr.hfma2.negate_b);
|
||||
op_c = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr39, 0, false),
|
||||
instr.hfma2.type_reg39, false, instr.hfma2.negate_c);
|
||||
break;
|
||||
case OpCode::Id::HFMA2_RC:
|
||||
op_b = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr39, 0, false),
|
||||
instr.hfma2.type_reg39, false, instr.hfma2.negate_b);
|
||||
op_c = GetHalfFloat(regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
|
||||
GLSLRegister::Type::UnsignedInteger),
|
||||
instr.hfma2.type_b, false, instr.hfma2.negate_c);
|
||||
break;
|
||||
case OpCode::Id::HFMA2_RR:
|
||||
op_b = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
|
||||
instr.hfma2.type_b, false, instr.hfma2.negate_b);
|
||||
op_c = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr39, 0, false),
|
||||
instr.hfma2.rr.type_c, false, instr.hfma2.rr.negate_c);
|
||||
break;
|
||||
case OpCode::Id::HFMA2_IMM_R:
|
||||
op_b = UnpackHalfImmediate(instr, true);
|
||||
op_c = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr39, 0, false),
|
||||
instr.hfma2.type_reg39, false, instr.hfma2.negate_c);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
op_c = op_b = "vec2(0)";
|
||||
break;
|
||||
}
|
||||
|
||||
const std::string result = '(' + op_a + " * " + op_b + " + " + op_c + ')';
|
||||
|
||||
regs.SetRegisterToHalfFloat(instr.gpr0, 0, result, instr.hfma2.merge, 1, 1, saturate);
|
||||
break;
|
||||
}
|
||||
case OpCode::Type::Conversion: {
|
||||
switch (opcode->GetId()) {
|
||||
case OpCode::Id::I2I_R: {
|
||||
@@ -2525,20 +2816,13 @@ private:
|
||||
break;
|
||||
}
|
||||
case OpCode::Type::FloatSetPredicate: {
|
||||
std::string op_a = instr.fsetp.neg_a ? "-" : "";
|
||||
op_a += regs.GetRegisterAsFloat(instr.gpr8);
|
||||
const std::string op_a =
|
||||
GetOperandAbsNeg(regs.GetRegisterAsFloat(instr.gpr8), instr.fsetp.abs_a != 0,
|
||||
instr.fsetp.neg_a != 0);
|
||||
|
||||
if (instr.fsetp.abs_a) {
|
||||
op_a = "abs(" + op_a + ')';
|
||||
}
|
||||
|
||||
std::string op_b{};
|
||||
std::string op_b;
|
||||
|
||||
if (instr.is_b_imm) {
|
||||
if (instr.fsetp.neg_b) {
|
||||
// Only the immediate version of fsetp has a neg_b bit.
|
||||
op_b += '-';
|
||||
}
|
||||
op_b += '(' + GetImmediate19(instr) + ')';
|
||||
} else {
|
||||
if (instr.is_b_gpr) {
|
||||
@@ -2611,6 +2895,51 @@ private:
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Type::HalfSetPredicate: {
|
||||
ASSERT_MSG(instr.hsetp2.ftz == 0, "Unimplemented");
|
||||
|
||||
const std::string op_a =
|
||||
GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hsetp2.type_a,
|
||||
instr.hsetp2.abs_a, instr.hsetp2.negate_a);
|
||||
|
||||
const std::string op_b = [&]() {
|
||||
switch (opcode->GetId()) {
|
||||
case OpCode::Id::HSETP2_R:
|
||||
return GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
|
||||
instr.hsetp2.type_b, instr.hsetp2.abs_a,
|
||||
instr.hsetp2.negate_b);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return std::string("vec2(0)");
|
||||
}
|
||||
}();
|
||||
|
||||
// We can't use the constant predicate as destination.
|
||||
ASSERT(instr.hsetp2.pred3 != static_cast<u64>(Pred::UnusedIndex));
|
||||
|
||||
const std::string second_pred =
|
||||
GetPredicateCondition(instr.hsetp2.pred39, instr.hsetp2.neg_pred != 0);
|
||||
|
||||
const std::string combiner = GetPredicateCombiner(instr.hsetp2.op);
|
||||
|
||||
const std::string component_combiner = instr.hsetp2.h_and ? "&&" : "||";
|
||||
const std::string predicate =
|
||||
'(' + GetPredicateComparison(instr.hsetp2.cond, op_a + ".x", op_b + ".x") + ' ' +
|
||||
component_combiner + ' ' +
|
||||
GetPredicateComparison(instr.hsetp2.cond, op_a + ".y", op_b + ".y") + ')';
|
||||
|
||||
// Set the primary predicate to the result of Predicate OP SecondPredicate
|
||||
SetPredicate(instr.hsetp2.pred3,
|
||||
'(' + predicate + ") " + combiner + " (" + second_pred + ')');
|
||||
|
||||
if (instr.hsetp2.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
|
||||
// Set the secondary predicate to the result of !Predicate OP SecondPredicate,
|
||||
// if enabled
|
||||
SetPredicate(instr.hsetp2.pred0,
|
||||
"!(" + predicate + ") " + combiner + " (" + second_pred + ')');
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Type::PredicateSetRegister: {
|
||||
const std::string op_a =
|
||||
GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0);
|
||||
@@ -2689,33 +3018,24 @@ private:
|
||||
break;
|
||||
}
|
||||
case OpCode::Type::FloatSet: {
|
||||
std::string op_a = instr.fset.neg_a ? "-" : "";
|
||||
op_a += regs.GetRegisterAsFloat(instr.gpr8);
|
||||
const std::string op_a = GetOperandAbsNeg(regs.GetRegisterAsFloat(instr.gpr8),
|
||||
instr.fset.abs_a != 0, instr.fset.neg_a != 0);
|
||||
|
||||
if (instr.fset.abs_a) {
|
||||
op_a = "abs(" + op_a + ')';
|
||||
}
|
||||
|
||||
std::string op_b = instr.fset.neg_b ? "-" : "";
|
||||
std::string op_b;
|
||||
|
||||
if (instr.is_b_imm) {
|
||||
const std::string imm = GetImmediate19(instr);
|
||||
if (instr.fset.neg_imm)
|
||||
op_b += "(-" + imm + ')';
|
||||
else
|
||||
op_b += imm;
|
||||
op_b = imm;
|
||||
} else {
|
||||
if (instr.is_b_gpr) {
|
||||
op_b += regs.GetRegisterAsFloat(instr.gpr20);
|
||||
op_b = regs.GetRegisterAsFloat(instr.gpr20);
|
||||
} else {
|
||||
op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
|
||||
GLSLRegister::Type::Float);
|
||||
op_b = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
|
||||
GLSLRegister::Type::Float);
|
||||
}
|
||||
}
|
||||
|
||||
if (instr.fset.abs_b) {
|
||||
op_b = "abs(" + op_b + ')';
|
||||
}
|
||||
op_b = GetOperandAbsNeg(op_b, instr.fset.abs_b != 0, instr.fset.neg_b != 0);
|
||||
|
||||
// The fset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the
|
||||
// condition is true, and to 0 otherwise.
|
||||
@@ -2771,6 +3091,50 @@ private:
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Type::HalfSet: {
|
||||
ASSERT_MSG(instr.hset2.ftz == 0, "Unimplemented");
|
||||
|
||||
const std::string op_a =
|
||||
GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hset2.type_a,
|
||||
instr.hset2.abs_a != 0, instr.hset2.negate_a != 0);
|
||||
|
||||
const std::string op_b = [&]() {
|
||||
switch (opcode->GetId()) {
|
||||
case OpCode::Id::HSET2_R:
|
||||
return GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
|
||||
instr.hset2.type_b, instr.hset2.abs_b != 0,
|
||||
instr.hset2.negate_b != 0);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return std::string("vec2(0)");
|
||||
}
|
||||
}();
|
||||
|
||||
const std::string second_pred =
|
||||
GetPredicateCondition(instr.hset2.pred39, instr.hset2.neg_pred != 0);
|
||||
|
||||
const std::string combiner = GetPredicateCombiner(instr.hset2.op);
|
||||
|
||||
// HSET2 operates on each half float in the pack.
|
||||
std::string result;
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
const std::string float_value = i == 0 ? "0x00003c00" : "0x3c000000";
|
||||
const std::string integer_value = i == 0 ? "0x0000ffff" : "0xffff0000";
|
||||
const std::string value = instr.hset2.bf == 1 ? float_value : integer_value;
|
||||
|
||||
const std::string comp = std::string(".") + "xy"[i];
|
||||
const std::string predicate =
|
||||
"((" + GetPredicateComparison(instr.hset2.cond, op_a + comp, op_b + comp) +
|
||||
") " + combiner + " (" + second_pred + "))";
|
||||
|
||||
result += '(' + predicate + " ? " + value + " : 0)";
|
||||
if (i == 0) {
|
||||
result += " | ";
|
||||
}
|
||||
}
|
||||
regs.SetRegisterToInteger(instr.gpr0, false, 0, '(' + result + ')', 1, 1);
|
||||
break;
|
||||
}
|
||||
case OpCode::Type::Xmad: {
|
||||
ASSERT_MSG(!instr.xmad.sign_a, "Unimplemented");
|
||||
ASSERT_MSG(!instr.xmad.sign_b, "Unimplemented");
|
||||
@@ -2979,16 +3343,32 @@ private:
|
||||
// The SSY opcode tells the GPU where to re-converge divergent execution paths, it
|
||||
// sets the target of the jump that the SYNC instruction will make. The SSY opcode
|
||||
// has a similar structure to the BRA opcode.
|
||||
ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer SSY is not supported");
|
||||
ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer flow is not supported");
|
||||
|
||||
const u32 target = offset + instr.bra.GetBranchTarget();
|
||||
EmitPushToSSYStack(target);
|
||||
EmitPushToFlowStack(target);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::PBK: {
|
||||
// PBK pushes to a stack the address where BRK will jump to. This shares stack with
|
||||
// SSY but using SYNC on a PBK address will kill the shader execution. We don't
|
||||
// emulate this because it's very unlikely a driver will emit such invalid shader.
|
||||
ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer PBK is not supported");
|
||||
|
||||
const u32 target = offset + instr.bra.GetBranchTarget();
|
||||
EmitPushToFlowStack(target);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::SYNC: {
|
||||
// The SYNC opcode jumps to the address previously set by the SSY opcode
|
||||
ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always);
|
||||
EmitPopFromSSYStack();
|
||||
EmitPopFromFlowStack();
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::BRK: {
|
||||
// The BRK opcode jumps to the address previously set by the PBK opcode
|
||||
ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always);
|
||||
EmitPopFromFlowStack();
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::DEPBAR: {
|
||||
@@ -2998,87 +3378,51 @@ private:
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::VMAD: {
|
||||
const bool signed_a = instr.vmad.signed_a == 1;
|
||||
const bool signed_b = instr.vmad.signed_b == 1;
|
||||
const bool result_signed = signed_a || signed_b;
|
||||
boost::optional<std::string> forced_result;
|
||||
|
||||
auto Unpack = [&](const std::string& op, bool is_chunk, bool is_signed,
|
||||
Tegra::Shader::VmadType type, u64 byte_height) {
|
||||
const std::string value = [&]() {
|
||||
if (!is_chunk) {
|
||||
const auto offset = static_cast<u32>(byte_height * 8);
|
||||
return "((" + op + " >> " + std::to_string(offset) + ") & 0xff)";
|
||||
}
|
||||
const std::string zero = "0";
|
||||
|
||||
switch (type) {
|
||||
case Tegra::Shader::VmadType::Size16_Low:
|
||||
return '(' + op + " & 0xffff)";
|
||||
case Tegra::Shader::VmadType::Size16_High:
|
||||
return '(' + op + " >> 16)";
|
||||
case Tegra::Shader::VmadType::Size32:
|
||||
// TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when
|
||||
// this type is used (1 * 1 + 0 == 0x5b800000). Until a better
|
||||
// explanation is found: assert.
|
||||
UNREACHABLE_MSG("Unimplemented");
|
||||
return zero;
|
||||
case Tegra::Shader::VmadType::Invalid:
|
||||
// Note(Rodrigo): This flag is invalid according to nvdisasm. From my
|
||||
// testing (even though it's invalid) this makes the whole instruction
|
||||
// assign zero to target register.
|
||||
forced_result = boost::make_optional(zero);
|
||||
return zero;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return zero;
|
||||
}
|
||||
}();
|
||||
|
||||
if (is_signed) {
|
||||
return "int(" + value + ')';
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const std::string op_a = Unpack(regs.GetRegisterAsInteger(instr.gpr8, 0, false),
|
||||
instr.vmad.is_byte_chunk_a != 0, signed_a,
|
||||
instr.vmad.type_a, instr.vmad.byte_height_a);
|
||||
|
||||
std::string op_b;
|
||||
if (instr.vmad.use_register_b) {
|
||||
op_b = Unpack(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
|
||||
instr.vmad.is_byte_chunk_b != 0, signed_b, instr.vmad.type_b,
|
||||
instr.vmad.byte_height_b);
|
||||
} else {
|
||||
op_b = '(' +
|
||||
std::to_string(signed_b ? static_cast<s16>(instr.alu.GetImm20_16())
|
||||
: instr.alu.GetImm20_16()) +
|
||||
')';
|
||||
}
|
||||
|
||||
const bool result_signed = instr.video.signed_a == 1 || instr.video.signed_b == 1;
|
||||
const std::string op_a = GetVideoOperandA(instr);
|
||||
const std::string op_b = GetVideoOperandB(instr);
|
||||
const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39, 0, result_signed);
|
||||
|
||||
std::string result;
|
||||
if (forced_result) {
|
||||
result = *forced_result;
|
||||
} else {
|
||||
result = '(' + op_a + " * " + op_b + " + " + op_c + ')';
|
||||
std::string result = '(' + op_a + " * " + op_b + " + " + op_c + ')';
|
||||
|
||||
switch (instr.vmad.shr) {
|
||||
case Tegra::Shader::VmadShr::Shr7:
|
||||
result = '(' + result + " >> 7)";
|
||||
break;
|
||||
case Tegra::Shader::VmadShr::Shr15:
|
||||
result = '(' + result + " >> 15)";
|
||||
break;
|
||||
}
|
||||
switch (instr.vmad.shr) {
|
||||
case Tegra::Shader::VmadShr::Shr7:
|
||||
result = '(' + result + " >> 7)";
|
||||
break;
|
||||
case Tegra::Shader::VmadShr::Shr15:
|
||||
result = '(' + result + " >> 15)";
|
||||
break;
|
||||
}
|
||||
|
||||
regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1,
|
||||
instr.vmad.saturate == 1, 0, Register::Size::Word,
|
||||
instr.vmad.cc);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::VSETP: {
|
||||
const std::string op_a = GetVideoOperandA(instr);
|
||||
const std::string op_b = GetVideoOperandB(instr);
|
||||
|
||||
// We can't use the constant predicate as destination.
|
||||
ASSERT(instr.vsetp.pred3 != static_cast<u64>(Pred::UnusedIndex));
|
||||
|
||||
const std::string second_pred = GetPredicateCondition(instr.vsetp.pred39, false);
|
||||
|
||||
const std::string combiner = GetPredicateCombiner(instr.vsetp.op);
|
||||
|
||||
const std::string predicate = GetPredicateComparison(instr.vsetp.cond, op_a, op_b);
|
||||
// Set the primary predicate to the result of Predicate OP SecondPredicate
|
||||
SetPredicate(instr.vsetp.pred3,
|
||||
'(' + predicate + ") " + combiner + " (" + second_pred + ')');
|
||||
|
||||
if (instr.vsetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
|
||||
// Set the secondary predicate to the result of !Predicate OP SecondPredicate,
|
||||
// if enabled
|
||||
SetPredicate(instr.vsetp.pred0,
|
||||
"!(" + predicate + ") " + combiner + " (" + second_pred + ')');
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName());
|
||||
UNREACHABLE();
|
||||
@@ -3142,11 +3486,11 @@ private:
|
||||
labels.insert(subroutine.begin);
|
||||
shader.AddLine("uint jmp_to = " + std::to_string(subroutine.begin) + "u;");
|
||||
|
||||
// TODO(Subv): Figure out the actual depth of the SSY stack, for now it seems
|
||||
// unlikely that shaders will use 20 nested SSYs.
|
||||
constexpr u32 SSY_STACK_SIZE = 20;
|
||||
shader.AddLine("uint ssy_stack[" + std::to_string(SSY_STACK_SIZE) + "];");
|
||||
shader.AddLine("uint ssy_stack_top = 0u;");
|
||||
// TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
|
||||
// unlikely that shaders will use 20 nested SSYs and PBKs.
|
||||
constexpr u32 FLOW_STACK_SIZE = 20;
|
||||
shader.AddLine("uint flow_stack[" + std::to_string(FLOW_STACK_SIZE) + "];");
|
||||
shader.AddLine("uint flow_stack_top = 0u;");
|
||||
|
||||
shader.AddLine("while (true) {");
|
||||
++shader.scope;
|
||||
@@ -3213,7 +3557,7 @@ private:
|
||||
|
||||
// Declarations
|
||||
std::set<std::string> declr_predicates;
|
||||
}; // namespace Decompiler
|
||||
}; // namespace OpenGL::GLShader::Decompiler
|
||||
|
||||
std::string GetCommonDeclarations() {
|
||||
return fmt::format("#define MAX_CONSTBUFFER_ELEMENTS {}\n",
|
||||
|
||||
@@ -23,10 +23,13 @@ out gl_PerVertex {
|
||||
vec4 gl_Position;
|
||||
};
|
||||
|
||||
layout (location = 0) out vec4 position;
|
||||
|
||||
layout(std140) uniform vs_config {
|
||||
vec4 viewport_flip;
|
||||
uvec4 instance_id;
|
||||
uvec4 flip_stage;
|
||||
uvec4 alpha_test;
|
||||
};
|
||||
)";
|
||||
|
||||
@@ -96,10 +99,14 @@ out gl_PerVertex {
|
||||
vec4 gl_Position;
|
||||
};
|
||||
|
||||
layout (location = 0) in vec4 gs_position[];
|
||||
layout (location = 0) out vec4 position;
|
||||
|
||||
layout (std140) uniform gs_config {
|
||||
vec4 viewport_flip;
|
||||
uvec4 instance_id;
|
||||
uvec4 flip_stage;
|
||||
uvec4 alpha_test;
|
||||
};
|
||||
|
||||
void main() {
|
||||
@@ -131,12 +138,39 @@ layout(location = 5) out vec4 FragColor5;
|
||||
layout(location = 6) out vec4 FragColor6;
|
||||
layout(location = 7) out vec4 FragColor7;
|
||||
|
||||
layout (location = 0) in vec4 position;
|
||||
|
||||
layout (std140) uniform fs_config {
|
||||
vec4 viewport_flip;
|
||||
uvec4 instance_id;
|
||||
uvec4 flip_stage;
|
||||
uvec4 alpha_test;
|
||||
};
|
||||
|
||||
bool AlphaFunc(in float value) {
|
||||
float ref = uintBitsToFloat(alpha_test[2]);
|
||||
switch (alpha_test[1]) {
|
||||
case 1:
|
||||
return false;
|
||||
case 2:
|
||||
return value < ref;
|
||||
case 3:
|
||||
return value == ref;
|
||||
case 4:
|
||||
return value <= ref;
|
||||
case 5:
|
||||
return value > ref;
|
||||
case 6:
|
||||
return value != ref;
|
||||
case 7:
|
||||
return value >= ref;
|
||||
case 8:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
exec_fragment();
|
||||
}
|
||||
@@ -145,4 +179,4 @@ void main() {
|
||||
out += program.first;
|
||||
return {out, program.second};
|
||||
}
|
||||
} // namespace OpenGL::GLShader
|
||||
} // namespace OpenGL::GLShader
|
||||
|
||||
@@ -16,6 +16,8 @@ namespace OpenGL::GLShader {
|
||||
constexpr std::size_t MAX_PROGRAM_CODE_LENGTH{0x1000};
|
||||
using ProgramCode = std::vector<u64>;
|
||||
|
||||
enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 };
|
||||
|
||||
class ConstBufferEntry {
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
|
||||
|
||||
@@ -16,6 +16,17 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh
|
||||
viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f;
|
||||
viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f;
|
||||
|
||||
u32 func = static_cast<u32>(regs.alpha_test_func);
|
||||
// Normalize the gl variants of opCompare to be the same as the normal variants
|
||||
u32 op_gl_variant_base = static_cast<u32>(Tegra::Engines::Maxwell3D::Regs::ComparisonOp::Never);
|
||||
if (func >= op_gl_variant_base) {
|
||||
func = func - op_gl_variant_base + 1U;
|
||||
}
|
||||
|
||||
alpha_test.enabled = regs.alpha_test_enabled;
|
||||
alpha_test.func = func;
|
||||
alpha_test.ref = regs.alpha_test_ref;
|
||||
|
||||
// We only assign the instance to the first component of the vector, the rest is just padding.
|
||||
instance_id[0] = state.current_instance;
|
||||
|
||||
|
||||
@@ -22,8 +22,14 @@ struct MaxwellUniformData {
|
||||
alignas(16) GLvec4 viewport_flip;
|
||||
alignas(16) GLuvec4 instance_id;
|
||||
alignas(16) GLuvec4 flip_stage;
|
||||
struct alignas(16) {
|
||||
GLuint enabled;
|
||||
GLuint func;
|
||||
GLfloat ref;
|
||||
GLuint padding;
|
||||
} alpha_test;
|
||||
};
|
||||
static_assert(sizeof(MaxwellUniformData) == 48, "MaxwellUniformData structure size is incorrect");
|
||||
static_assert(sizeof(MaxwellUniformData) == 64, "MaxwellUniformData structure size is incorrect");
|
||||
static_assert(sizeof(MaxwellUniformData) < 16384,
|
||||
"MaxwellUniformData structure must be less than 16kb as per the OpenGL spec");
|
||||
|
||||
|
||||
@@ -82,8 +82,20 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
|
||||
return {};
|
||||
}
|
||||
|
||||
case Maxwell::VertexAttribute::Type::Float:
|
||||
return GL_FLOAT;
|
||||
case Maxwell::VertexAttribute::Type::Float: {
|
||||
switch (attrib.size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
return GL_HALF_FLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
|
||||
return GL_FLOAT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString());
|
||||
|
||||
@@ -237,6 +237,46 @@ std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pix
|
||||
return unswizzled_data;
|
||||
}
|
||||
|
||||
void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
|
||||
u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
|
||||
u32 block_height) {
|
||||
const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + 63) / 64};
|
||||
for (u32 line = 0; line < subrect_height; ++line) {
|
||||
const u32 gob_address_y =
|
||||
(line / (8 * block_height)) * 512 * block_height * image_width_in_gobs +
|
||||
(line % (8 * block_height) / 8) * 512;
|
||||
const auto& table = legacy_swizzle_table[line % 8];
|
||||
for (u32 x = 0; x < subrect_width; ++x) {
|
||||
const u32 gob_address = gob_address_y + (x * bytes_per_pixel / 64) * 512 * block_height;
|
||||
const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % 64];
|
||||
const VAddr source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel;
|
||||
const VAddr dest_addr = swizzled_data + swizzled_offset;
|
||||
|
||||
Memory::CopyBlock(dest_addr, source_line, bytes_per_pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width,
|
||||
u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
|
||||
u32 block_height, u32 offset_x, u32 offset_y) {
|
||||
for (u32 line = 0; line < subrect_height; ++line) {
|
||||
const u32 y2 = line + offset_y;
|
||||
const u32 gob_address_y =
|
||||
(y2 / (8 * block_height)) * 512 * block_height + (y2 % (8 * block_height) / 8) * 512;
|
||||
const auto& table = legacy_swizzle_table[y2 % 8];
|
||||
for (u32 x = 0; x < subrect_width; ++x) {
|
||||
const u32 x2 = (x + offset_x) * bytes_per_pixel;
|
||||
const u32 gob_address = gob_address_y + (x2 / 64) * 512 * block_height;
|
||||
const u32 swizzled_offset = gob_address + table[x2 % 64];
|
||||
const VAddr dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel;
|
||||
const VAddr source_addr = swizzled_data + swizzled_offset;
|
||||
|
||||
Memory::CopyBlock(dest_line, source_addr, bytes_per_pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width,
|
||||
u32 height) {
|
||||
std::vector<u8> rgba_data;
|
||||
|
||||
@@ -35,4 +35,13 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
|
||||
std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
|
||||
u32 block_height, u32 block_depth);
|
||||
|
||||
/// Copies an untiled subrectangle into a tiled surface.
|
||||
void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
|
||||
u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
|
||||
u32 block_height);
|
||||
/// Copies a tiled subrectangle into a linear surface.
|
||||
void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width,
|
||||
u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
|
||||
u32 block_height, u32 offset_x, u32 offset_y);
|
||||
|
||||
} // namespace Tegra::Texture
|
||||
|
||||
@@ -12,5 +12,5 @@ create_target_directory_groups(web_service)
|
||||
get_directory_property(OPENSSL_LIBS
|
||||
DIRECTORY ${CMAKE_SOURCE_DIR}/externals/libressl
|
||||
DEFINITION OPENSSL_LIBS)
|
||||
target_compile_definitions(web_service PUBLIC -DCPPHTTPLIB_OPENSSL_SUPPORT)
|
||||
target_compile_definitions(web_service PRIVATE -DCPPHTTPLIB_OPENSSL_SUPPORT)
|
||||
target_link_libraries(web_service PRIVATE common json-headers ${OPENSSL_LIBS} httplib lurlparser)
|
||||
|
||||
@@ -121,7 +121,7 @@ target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets)
|
||||
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
||||
|
||||
if (YUZU_ENABLE_COMPATIBILITY_REPORTING)
|
||||
add_definitions(-DYUZU_ENABLE_COMPATIBILITY_REPORTING)
|
||||
target_compile_definitions(yuzu PRIVATE -DYUZU_ENABLE_COMPATIBILITY_REPORTING)
|
||||
endif()
|
||||
|
||||
if (USE_DISCORD_PRESENCE)
|
||||
|
||||
@@ -97,18 +97,24 @@
|
||||
<addaction name="action_Show_Status_Bar"/>
|
||||
<addaction name="menu_View_Debugging"/>
|
||||
</widget>
|
||||
<widget class ="QMenu" name="menu_Tools">
|
||||
<property name="title">
|
||||
<string>Tools</string>
|
||||
</property>
|
||||
<addaction name="action_Rederive" />
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Help">
|
||||
<property name="title">
|
||||
<string>&Help</string>
|
||||
</property>
|
||||
<addaction name="action_Report_Compatibility"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Rederive"/>
|
||||
<addaction name="action_About"/>
|
||||
</widget>
|
||||
<addaction name="menu_File"/>
|
||||
<addaction name="menu_Emulation"/>
|
||||
<addaction name="menu_View"/>
|
||||
<addaction name="menu_Tools" />
|
||||
<addaction name="menu_Help"/>
|
||||
</widget>
|
||||
<action name="action_Install_File_NAND">
|
||||
|
||||
Reference in New Issue
Block a user