Compare commits
248 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0af3b4d9f4 | ||
|
|
7e2bcf04b4 | ||
|
|
f981efdf8d | ||
|
|
a602bcaaf8 | ||
|
|
364932df3a | ||
|
|
4486103e1d | ||
|
|
fea6568955 | ||
|
|
3f87664d8f | ||
|
|
c1a8f684df | ||
|
|
81b1102090 | ||
|
|
f79823fda7 | ||
|
|
834e07d639 | ||
|
|
9a84f428e1 | ||
|
|
9738dc3058 | ||
|
|
ac54f1a967 | ||
|
|
528c15051c | ||
|
|
17d5fb6d06 | ||
|
|
357ea15a39 | ||
|
|
11f2f0f45c | ||
|
|
de33ad25f5 | ||
|
|
cd2d9628c9 | ||
|
|
dec1cbaf7f | ||
|
|
769a50661a | ||
|
|
e1b3be7ced | ||
|
|
04ac7a637a | ||
|
|
adb8a9152b | ||
|
|
7322c8bd7c | ||
|
|
03d9bbaa90 | ||
|
|
c09ff382a4 | ||
|
|
bf4dfb3ad4 | ||
|
|
a20ba09bfd | ||
|
|
55c5029171 | ||
|
|
e4fea833d4 | ||
|
|
8d7a012297 | ||
|
|
0bcc305797 | ||
|
|
8304aaf282 | ||
|
|
d7d5bffa18 | ||
|
|
799302bc9d | ||
|
|
81e09bb121 | ||
|
|
6aff1005ef | ||
|
|
5b93290183 | ||
|
|
b5e1e87922 | ||
|
|
e1d755bdda | ||
|
|
2beaaa35c5 | ||
|
|
1eb979221f | ||
|
|
dd4fe0dab1 | ||
|
|
433ca686a8 | ||
|
|
6ce5f3e1bf | ||
|
|
dfc40a3f9e | ||
|
|
4f7a1f6c8c | ||
|
|
19dcb50692 | ||
|
|
2ba4aa8a3b | ||
|
|
2548661c08 | ||
|
|
42f5fd0ab3 | ||
|
|
79189c7e3e | ||
|
|
55f8111543 | ||
|
|
b35953e4fd | ||
|
|
a32c52b1d8 | ||
|
|
5ccf2a7b82 | ||
|
|
cfac942e63 | ||
|
|
b9b23c98ff | ||
|
|
50048d9f5a | ||
|
|
ca7ca2919c | ||
|
|
b73ea457cc | ||
|
|
2c679cda51 | ||
|
|
b46e615551 | ||
|
|
7ea07c6063 | ||
|
|
cf0d01a5d7 | ||
|
|
00f0827a26 | ||
|
|
77ce85f51d | ||
|
|
e32bf646cf | ||
|
|
536c9cf006 | ||
|
|
0a650ec99e | ||
|
|
2575403acf | ||
|
|
cfb59aad3f | ||
|
|
49e3a6e924 | ||
|
|
0935c2d97b | ||
|
|
e70f16fff7 | ||
|
|
1edf018319 | ||
|
|
d0d97de1e4 | ||
|
|
de2533d389 | ||
|
|
ed74a3cb8b | ||
|
|
75561d190a | ||
|
|
e72b9044a0 | ||
|
|
ad321564ed | ||
|
|
838b6d2ff8 | ||
|
|
6ac4490751 | ||
|
|
a89cc0bafc | ||
|
|
b76df62c00 | ||
|
|
df509486c4 | ||
|
|
7259f7a733 | ||
|
|
9b2d38582f | ||
|
|
e9772a2539 | ||
|
|
3f11d1c821 | ||
|
|
d9c1b94f03 | ||
|
|
e3608578e4 | ||
|
|
665b7e8e18 | ||
|
|
cfc9d92b38 | ||
|
|
802dd3cc95 | ||
|
|
139301c5a1 | ||
|
|
8bbe930fac | ||
|
|
c6f05b586f | ||
|
|
84a8fb9264 | ||
|
|
b1a4ab2ccc | ||
|
|
215fd82738 | ||
|
|
05af9d915c | ||
|
|
2fb3b9b951 | ||
|
|
cfd885163f | ||
|
|
2eb4d27c48 | ||
|
|
21c0b4dec8 | ||
|
|
84928e6d67 | ||
|
|
52b80d231c | ||
|
|
e0920ef4ba | ||
|
|
33ac193bf6 | ||
|
|
46e2ca5475 | ||
|
|
944c07ac7d | ||
|
|
f95bdb5088 | ||
|
|
180f22f17e | ||
|
|
f424b46036 | ||
|
|
a4c5e3e339 | ||
|
|
dec3c981d0 | ||
|
|
37eaf39b44 | ||
|
|
90c9d703ba | ||
|
|
bb248a2710 | ||
|
|
f97e206348 | ||
|
|
91300bdfb2 | ||
|
|
0fa039d8d0 | ||
|
|
e5159cfb84 | ||
|
|
4b80dd23a4 | ||
|
|
88cd5e888e | ||
|
|
6640f631e2 | ||
|
|
d61199721d | ||
|
|
ef3c0f54d0 | ||
|
|
c03fb00ac1 | ||
|
|
5d645c6dd9 | ||
|
|
9218e347cd | ||
|
|
5a4564bd8e | ||
|
|
e86d2e2e5b | ||
|
|
68c9c9222d | ||
|
|
d623e38d18 | ||
|
|
3c0280cf66 | ||
|
|
bb06b98d81 | ||
|
|
16bf791939 | ||
|
|
b3d7180164 | ||
|
|
cf9cc41478 | ||
|
|
f5d416e071 | ||
|
|
6f2a8fbb13 | ||
|
|
1a2d90ab09 | ||
|
|
59f110ef31 | ||
|
|
d4f8fe24d9 | ||
|
|
c70404eab5 | ||
|
|
d8827b07b5 | ||
|
|
46177901b8 | ||
|
|
e7ab0e9127 | ||
|
|
11e9bee91d | ||
|
|
943f6da1ac | ||
|
|
2b1fcc8a14 | ||
|
|
e3b2539986 | ||
|
|
8cd3d9be26 | ||
|
|
016f2eab73 | ||
|
|
2ed896075e | ||
|
|
d81b58f320 | ||
|
|
7fba9c7224 | ||
|
|
7dbf4c1ae5 | ||
|
|
b6dcb1ae4d | ||
|
|
228e58d0a5 | ||
|
|
87b4c1ac5e | ||
|
|
3e7d37301a | ||
|
|
d0e200a894 | ||
|
|
819d229e76 | ||
|
|
195b54602f | ||
|
|
f4c15db9e8 | ||
|
|
de23847184 | ||
|
|
69215b5a55 | ||
|
|
c03b8c4c19 | ||
|
|
75e7b45d69 | ||
|
|
f78ef617b6 | ||
|
|
f49a04ba39 | ||
|
|
938d6dca30 | ||
|
|
9ffc60b5b3 | ||
|
|
dbcff5d574 | ||
|
|
9a17b20896 | ||
|
|
9c3461604c | ||
|
|
ada79fa8ad | ||
|
|
bc32474901 | ||
|
|
ed2fedac13 | ||
|
|
7a82d6f394 | ||
|
|
486c3e6085 | ||
|
|
922d8c6cb4 | ||
|
|
fd34732e26 | ||
|
|
317f1263fb | ||
|
|
58a0c13e34 | ||
|
|
6fb29764d6 | ||
|
|
784d2b6c3d | ||
|
|
0adb54abc1 | ||
|
|
73ee85e9ae | ||
|
|
911fafb967 | ||
|
|
91ec251c4a | ||
|
|
d49efbfb4a | ||
|
|
13dda1d8ed | ||
|
|
b94b08fa6f | ||
|
|
f8b1e53369 | ||
|
|
2aebbe9bf9 | ||
|
|
fadf66993c | ||
|
|
9e98100c94 | ||
|
|
6fd247c84a | ||
|
|
18cdbdafa2 | ||
|
|
e310d943b8 | ||
|
|
212b148923 | ||
|
|
81e7e63080 | ||
|
|
e09ee0ff23 | ||
|
|
ce04ab38bb | ||
|
|
0a7f09a99b | ||
|
|
634b78a4c6 | ||
|
|
7fdc644c44 | ||
|
|
683c4e523f | ||
|
|
5e4c227608 | ||
|
|
f417be9d3b | ||
|
|
5993133d5e | ||
|
|
8d6342384b | ||
|
|
c5129a3a58 | ||
|
|
4ef3329f81 | ||
|
|
3a08c3207b | ||
|
|
d4df803b2b | ||
|
|
5321cdd276 | ||
|
|
28bffb1ffa | ||
|
|
fe700e1856 | ||
|
|
c6f9e651b2 | ||
|
|
71aa9d0877 | ||
|
|
b7d412c99b | ||
|
|
bd81a03d9d | ||
|
|
06b363c9b5 | ||
|
|
002ecbea19 | ||
|
|
7632a7d6d2 | ||
|
|
2bcb8a20b4 | ||
|
|
03746be097 | ||
|
|
de93507a5a | ||
|
|
19632d2421 | ||
|
|
d672c6e759 | ||
|
|
69a2003a8e | ||
|
|
4e462d1fd7 | ||
|
|
851c01c45e | ||
|
|
1aa2b99a98 | ||
|
|
c40cff454d | ||
|
|
f0db2e3ef3 | ||
|
|
e25a7891e9 | ||
|
|
daf5b8c61b | ||
|
|
ca5638a142 |
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
patreon: yuzuteam
|
||||
@@ -82,6 +82,9 @@ set(HASH_FILES
|
||||
"${VIDEO_CORE}/shader/decode/video.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/xmad.cpp"
|
||||
"${VIDEO_CORE}/shader/decode.cpp"
|
||||
"${VIDEO_CORE}/shader/node.h"
|
||||
"${VIDEO_CORE}/shader/node_helper.cpp"
|
||||
"${VIDEO_CORE}/shader/node_helper.h"
|
||||
"${VIDEO_CORE}/shader/shader_ir.cpp"
|
||||
"${VIDEO_CORE}/shader/shader_ir.h"
|
||||
"${VIDEO_CORE}/shader/track.cpp"
|
||||
|
||||
@@ -7,7 +7,7 @@ yuzu is an experimental open-source emulator for the Nintendo Switch from the cr
|
||||
|
||||
It is written in C++ with portability in mind, with builds actively maintained for Windows, Linux and macOS. The emulator is currently only useful for homebrew development and research purposes.
|
||||
|
||||
yuzu only emulates a subset of Switch hardware and therefore is generally only useful for running/debugging homebrew applications. At this time, yuzu cannot play any commercial games without major problems. yuzu can boot some games, to varying degrees of success.
|
||||
yuzu only emulates a subset of Switch hardware and therefore is generally only useful for running/debugging homebrew applications. yuzu can boot some games, to varying degrees of success.
|
||||
|
||||
yuzu is licensed under the GPLv2 (or any later version). Refer to the license.txt file included.
|
||||
|
||||
|
||||
2
externals/Vulkan-Headers
vendored
2
externals/Vulkan-Headers
vendored
Submodule externals/Vulkan-Headers updated: 15e5c4db75...d05c8df88d
12
externals/glad/include/KHR/khrplatform.h
vendored
12
externals/glad/include/KHR/khrplatform.h
vendored
@@ -90,12 +90,20 @@
|
||||
* int arg2) KHRONOS_APIATTRIBUTES;
|
||||
*/
|
||||
|
||||
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
|
||||
# define KHRONOS_STATIC 1
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APICALL
|
||||
*-------------------------------------------------------------------------
|
||||
* This precedes the return type of the function in the function prototype.
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
||||
#if defined(KHRONOS_STATIC)
|
||||
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
|
||||
* header compatible with static linking. */
|
||||
# define KHRONOS_APICALL
|
||||
#elif defined(_WIN32)
|
||||
# define KHRONOS_APICALL __declspec(dllimport)
|
||||
#elif defined (__SYMBIAN32__)
|
||||
# define KHRONOS_APICALL IMPORT_C
|
||||
@@ -111,7 +119,7 @@
|
||||
* This follows the return type of the function and precedes the function
|
||||
* name in the function prototype.
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
|
||||
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(KHRONOS_STATIC)
|
||||
/* Win32 but not WinCE */
|
||||
# define KHRONOS_APIENTRY __stdcall
|
||||
#else
|
||||
|
||||
2388
externals/glad/include/glad/glad.h
vendored
2388
externals/glad/include/glad/glad.h
vendored
File diff suppressed because one or more lines are too long
1125
externals/glad/src/glad.c
vendored
1125
externals/glad/src/glad.c
vendored
File diff suppressed because one or more lines are too long
@@ -57,7 +57,9 @@ Stream::State Stream::GetState() const {
|
||||
|
||||
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
|
||||
const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
|
||||
return Core::Timing::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
|
||||
const auto us =
|
||||
std::chrono::microseconds((static_cast<u64>(num_samples) * 1000000) / sample_rate);
|
||||
return Core::Timing::usToCycles(us);
|
||||
}
|
||||
|
||||
static void VolumeAdjustSamples(std::vector<s16>& samples) {
|
||||
|
||||
@@ -56,6 +56,9 @@ add_custom_command(OUTPUT scm_rev.cpp
|
||||
"${VIDEO_CORE}/shader/decode/video.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/xmad.cpp"
|
||||
"${VIDEO_CORE}/shader/decode.cpp"
|
||||
"${VIDEO_CORE}/shader/node.h"
|
||||
"${VIDEO_CORE}/shader/node_helper.cpp"
|
||||
"${VIDEO_CORE}/shader/node_helper.h"
|
||||
"${VIDEO_CORE}/shader/shader_ir.cpp"
|
||||
"${VIDEO_CORE}/shader/shader_ir.h"
|
||||
"${VIDEO_CORE}/shader/track.cpp"
|
||||
@@ -123,6 +126,8 @@ add_library(common STATIC
|
||||
timer.h
|
||||
uint128.cpp
|
||||
uint128.h
|
||||
uuid.cpp
|
||||
uuid.h
|
||||
vector_math.h
|
||||
web_result.h
|
||||
zstd_compression.cpp
|
||||
|
||||
@@ -78,16 +78,17 @@ namespace FileUtil {
|
||||
// Remove any ending forward slashes from directory paths
|
||||
// Modifies argument.
|
||||
static void StripTailDirSlashes(std::string& fname) {
|
||||
if (fname.length() > 1) {
|
||||
std::size_t i = fname.length();
|
||||
while (i > 0 && fname[i - 1] == DIR_SEP_CHR)
|
||||
--i;
|
||||
fname.resize(i);
|
||||
if (fname.length() <= 1) {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
|
||||
std::size_t i = fname.length();
|
||||
while (i > 0 && fname[i - 1] == DIR_SEP_CHR) {
|
||||
--i;
|
||||
}
|
||||
fname.resize(i);
|
||||
}
|
||||
|
||||
// Returns true if file filename exists
|
||||
bool Exists(const std::string& filename) {
|
||||
struct stat file_info;
|
||||
|
||||
@@ -107,7 +108,6 @@ bool Exists(const std::string& filename) {
|
||||
return (result == 0);
|
||||
}
|
||||
|
||||
// Returns true if filename is a directory
|
||||
bool IsDirectory(const std::string& filename) {
|
||||
struct stat file_info;
|
||||
|
||||
@@ -132,8 +132,6 @@ bool IsDirectory(const std::string& filename) {
|
||||
return S_ISDIR(file_info.st_mode);
|
||||
}
|
||||
|
||||
// Deletes a given filename, return true on success
|
||||
// Doesn't supports deleting a directory
|
||||
bool Delete(const std::string& filename) {
|
||||
LOG_TRACE(Common_Filesystem, "file {}", filename);
|
||||
|
||||
@@ -165,7 +163,6 @@ bool Delete(const std::string& filename) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns true if successful, or path already exists.
|
||||
bool CreateDir(const std::string& path) {
|
||||
LOG_TRACE(Common_Filesystem, "directory {}", path);
|
||||
#ifdef _WIN32
|
||||
@@ -194,7 +191,6 @@ bool CreateDir(const std::string& path) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Creates the full path of fullPath returns true on success
|
||||
bool CreateFullPath(const std::string& fullPath) {
|
||||
int panicCounter = 100;
|
||||
LOG_TRACE(Common_Filesystem, "path {}", fullPath);
|
||||
@@ -230,7 +226,6 @@ bool CreateFullPath(const std::string& fullPath) {
|
||||
}
|
||||
}
|
||||
|
||||
// Deletes a directory filename, returns true on success
|
||||
bool DeleteDir(const std::string& filename) {
|
||||
LOG_TRACE(Common_Filesystem, "directory {}", filename);
|
||||
|
||||
@@ -252,7 +247,6 @@ bool DeleteDir(const std::string& filename) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// renames file srcFilename to destFilename, returns true on success
|
||||
bool Rename(const std::string& srcFilename, const std::string& destFilename) {
|
||||
LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename);
|
||||
#ifdef _WIN32
|
||||
@@ -268,7 +262,6 @@ bool Rename(const std::string& srcFilename, const std::string& destFilename) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// copies file srcFilename to destFilename, returns true on success
|
||||
bool Copy(const std::string& srcFilename, const std::string& destFilename) {
|
||||
LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename);
|
||||
#ifdef _WIN32
|
||||
@@ -324,7 +317,6 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns the size of filename (64bit)
|
||||
u64 GetSize(const std::string& filename) {
|
||||
if (!Exists(filename)) {
|
||||
LOG_ERROR(Common_Filesystem, "failed {}: No such file", filename);
|
||||
@@ -351,7 +343,6 @@ u64 GetSize(const std::string& filename) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Overloaded GetSize, accepts file descriptor
|
||||
u64 GetSize(const int fd) {
|
||||
struct stat buf;
|
||||
if (fstat(fd, &buf) != 0) {
|
||||
@@ -361,7 +352,6 @@ u64 GetSize(const int fd) {
|
||||
return buf.st_size;
|
||||
}
|
||||
|
||||
// Overloaded GetSize, accepts FILE*
|
||||
u64 GetSize(FILE* f) {
|
||||
// can't use off_t here because it can be 32-bit
|
||||
u64 pos = ftello(f);
|
||||
@@ -377,7 +367,6 @@ u64 GetSize(FILE* f) {
|
||||
return size;
|
||||
}
|
||||
|
||||
// creates an empty file filename, returns true on success
|
||||
bool CreateEmptyFile(const std::string& filename) {
|
||||
LOG_TRACE(Common_Filesystem, "{}", filename);
|
||||
|
||||
@@ -502,7 +491,6 @@ bool DeleteDirRecursively(const std::string& directory, unsigned int recursion)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create directory and copy contents (does not overwrite existing files)
|
||||
void CopyDir(const std::string& source_path, const std::string& dest_path) {
|
||||
#ifndef _WIN32
|
||||
if (source_path == dest_path)
|
||||
@@ -539,8 +527,7 @@ void CopyDir(const std::string& source_path, const std::string& dest_path) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns the current directory
|
||||
std::string GetCurrentDir() {
|
||||
std::optional<std::string> GetCurrentDir() {
|
||||
// Get the current working directory (getcwd uses malloc)
|
||||
#ifdef _WIN32
|
||||
wchar_t* dir;
|
||||
@@ -550,7 +537,7 @@ std::string GetCurrentDir() {
|
||||
if (!(dir = getcwd(nullptr, 0))) {
|
||||
#endif
|
||||
LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg());
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
#ifdef _WIN32
|
||||
std::string strDir = Common::UTF16ToUTF8(dir);
|
||||
@@ -561,7 +548,6 @@ std::string GetCurrentDir() {
|
||||
return strDir;
|
||||
}
|
||||
|
||||
// Sets the current directory to the given directory
|
||||
bool SetCurrentDir(const std::string& directory) {
|
||||
#ifdef _WIN32
|
||||
return _wchdir(Common::UTF8ToUTF16W(directory).c_str()) == 0;
|
||||
@@ -673,8 +659,6 @@ std::string GetSysDirectory() {
|
||||
return sysDir;
|
||||
}
|
||||
|
||||
// Returns a string with a yuzu data dir or file in the user's home
|
||||
// directory. To be used in "multi-user" mode (that is, installed).
|
||||
const std::string& GetUserPath(UserPath path, const std::string& new_path) {
|
||||
static std::unordered_map<UserPath, std::string> paths;
|
||||
auto& user_path = paths[UserPath::UserDir];
|
||||
@@ -762,11 +746,11 @@ std::string GetNANDRegistrationDir(bool system) {
|
||||
return GetUserPath(UserPath::NANDDir) + "user/Contents/registered/";
|
||||
}
|
||||
|
||||
std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) {
|
||||
return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size());
|
||||
std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str) {
|
||||
return IOFile(filename, text_file ? "w" : "wb").WriteString(str);
|
||||
}
|
||||
|
||||
std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str) {
|
||||
std::size_t ReadFileToString(bool text_file, const std::string& filename, std::string& str) {
|
||||
IOFile file(filename, text_file ? "r" : "rb");
|
||||
|
||||
if (!file.IsOpen())
|
||||
@@ -776,13 +760,6 @@ std::size_t ReadFileToString(bool text_file, const char* filename, std::string&
|
||||
return file.ReadArray(&str[0], str.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits the filename into 8.3 format
|
||||
* Loosely implemented following https://en.wikipedia.org/wiki/8.3_filename
|
||||
* @param filename The normal filename to use
|
||||
* @param short_name A 9-char array in which the short name will be written
|
||||
* @param extension A 4-char array in which the extension will be written
|
||||
*/
|
||||
void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
|
||||
std::array<char, 4>& extension) {
|
||||
const std::string forbidden_characters = ".\"/\\[]:;=, ";
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
@@ -118,7 +119,7 @@ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
|
||||
bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256);
|
||||
|
||||
// Returns the current directory
|
||||
std::string GetCurrentDir();
|
||||
std::optional<std::string> GetCurrentDir();
|
||||
|
||||
// Create directory and copy contents (does not overwrite existing files)
|
||||
void CopyDir(const std::string& source_path, const std::string& dest_path);
|
||||
@@ -146,9 +147,9 @@ const std::string& GetExeDirectory();
|
||||
std::string AppDataRoamingDirectory();
|
||||
#endif
|
||||
|
||||
std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename);
|
||||
std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str);
|
||||
|
||||
std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str);
|
||||
std::size_t ReadFileToString(bool text_file, const std::string& filename, std::string& str);
|
||||
|
||||
/**
|
||||
* Splits the filename into 8.3 format
|
||||
@@ -257,8 +258,8 @@ public:
|
||||
return WriteArray(&object, 1);
|
||||
}
|
||||
|
||||
std::size_t WriteString(const std::string& str) {
|
||||
return WriteArray(str.c_str(), str.length());
|
||||
std::size_t WriteString(std::string_view str) {
|
||||
return WriteArray(str.data(), str.length());
|
||||
}
|
||||
|
||||
bool IsOpen() const {
|
||||
@@ -286,8 +287,8 @@ private:
|
||||
template <typename T>
|
||||
void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) {
|
||||
#ifdef _MSC_VER
|
||||
fstream.open(Common::UTF8ToUTF16W(filename).c_str(), openmode);
|
||||
fstream.open(Common::UTF8ToUTF16W(filename), openmode);
|
||||
#else
|
||||
fstream.open(filename.c_str(), openmode);
|
||||
fstream.open(filename, openmode);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -41,4 +41,7 @@ struct Rectangle {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
Rectangle(T, T, T, T)->Rectangle<T>;
|
||||
|
||||
} // namespace Common
|
||||
|
||||
33
src/common/uuid.cpp
Normal file
33
src/common/uuid.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <random>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/uuid.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
UUID UUID::Generate() {
|
||||
std::random_device device;
|
||||
std::mt19937 gen(device());
|
||||
std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
|
||||
return UUID{distribution(gen), distribution(gen)};
|
||||
}
|
||||
|
||||
std::string UUID::Format() const {
|
||||
return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
|
||||
}
|
||||
|
||||
std::string UUID::FormatSwitch() const {
|
||||
std::array<u8, 16> s{};
|
||||
std::memcpy(s.data(), uuid.data(), sizeof(u128));
|
||||
return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
|
||||
":02x}{:02x}{:02x}{:02x}{:02x}",
|
||||
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
|
||||
s[12], s[13], s[14], s[15]);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
48
src/common/uuid.h
Normal file
48
src/common/uuid.h
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
constexpr u128 INVALID_UUID{{0, 0}};
|
||||
|
||||
struct UUID {
|
||||
// UUIDs which are 0 are considered invalid!
|
||||
u128 uuid = INVALID_UUID;
|
||||
constexpr UUID() = default;
|
||||
constexpr explicit UUID(const u128& id) : uuid{id} {}
|
||||
constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
|
||||
|
||||
constexpr explicit operator bool() const {
|
||||
return uuid[0] != INVALID_UUID[0] && uuid[1] != INVALID_UUID[1];
|
||||
}
|
||||
|
||||
constexpr bool operator==(const UUID& rhs) const {
|
||||
// TODO(DarkLordZach): Replace with uuid == rhs.uuid with C++20
|
||||
return uuid[0] == rhs.uuid[0] && uuid[1] == rhs.uuid[1];
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const UUID& rhs) const {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
// TODO(ogniK): Properly generate uuids based on RFC-4122
|
||||
static UUID Generate();
|
||||
|
||||
// Set the UUID to {0,0} to be considered an invalid user
|
||||
constexpr void Invalidate() {
|
||||
uuid = INVALID_UUID;
|
||||
}
|
||||
|
||||
std::string Format() const;
|
||||
std::string FormatSwitch() const;
|
||||
};
|
||||
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
|
||||
|
||||
} // namespace Common
|
||||
@@ -5,6 +5,8 @@ add_library(core STATIC
|
||||
arm/exclusive_monitor.h
|
||||
arm/unicorn/arm_unicorn.cpp
|
||||
arm/unicorn/arm_unicorn.h
|
||||
constants.cpp
|
||||
constants.h
|
||||
core.cpp
|
||||
core.h
|
||||
core_cpu.cpp
|
||||
@@ -310,6 +312,8 @@ add_library(core STATIC
|
||||
hle/service/mig/mig.h
|
||||
hle/service/mii/mii.cpp
|
||||
hle/service/mii/mii.h
|
||||
hle/service/mii/mii_manager.cpp
|
||||
hle/service/mii/mii_manager.h
|
||||
hle/service/mm/mm_u.cpp
|
||||
hle/service/mm/mm_u.h
|
||||
hle/service/ncm/ncm.cpp
|
||||
@@ -326,6 +330,9 @@ add_library(core STATIC
|
||||
hle/service/nim/nim.h
|
||||
hle/service/npns/npns.cpp
|
||||
hle/service/npns/npns.h
|
||||
hle/service/ns/errors.h
|
||||
hle/service/ns/language.cpp
|
||||
hle/service/ns/language.h
|
||||
hle/service/ns/ns.cpp
|
||||
hle/service/ns/ns.h
|
||||
hle/service/ns/pl_u.cpp
|
||||
|
||||
17
src/core/constants.cpp
Normal file
17
src/core/constants.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/constants.h"
|
||||
|
||||
namespace Core::Constants {
|
||||
const std::array<u8, 107> ACCOUNT_BACKUP_JPEG{{
|
||||
0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
|
||||
0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05,
|
||||
0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e,
|
||||
0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13,
|
||||
0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01,
|
||||
0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08,
|
||||
0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
|
||||
}};
|
||||
}
|
||||
17
src/core/constants.h
Normal file
17
src/core/constants.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
// This is to consolidate system-wide constants that are used by multiple components of yuzu.
|
||||
// This is especially to prevent the case of something in frontend duplicating a constexpr array or
|
||||
// directly including some service header for the sole purpose of data.
|
||||
namespace Core::Constants {
|
||||
|
||||
// ACC Service - Blank JPEG used as user icon in absentia of real one.
|
||||
extern const std::array<u8, 107> ACCOUNT_BACKUP_JPEG;
|
||||
|
||||
} // namespace Core::Constants
|
||||
@@ -18,11 +18,6 @@
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/vfs_concat.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
#include "core/frontend/applets/error.h"
|
||||
#include "core/frontend/applets/general_frontend.h"
|
||||
#include "core/frontend/applets/profile_select.h"
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
#include "core/frontend/applets/web_browser.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
@@ -37,9 +32,6 @@
|
||||
#include "core/settings.h"
|
||||
#include "core/telemetry_session.h"
|
||||
#include "file_sys/cheat_engine.h"
|
||||
#include "frontend/applets/profile_select.h"
|
||||
#include "frontend/applets/software_keyboard.h"
|
||||
#include "frontend/applets/web_browser.h"
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
@@ -144,20 +136,10 @@ struct System::Impl {
|
||||
ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
|
||||
const std::string& filepath) {
|
||||
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
|
||||
|
||||
if (!app_loader) {
|
||||
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
|
||||
return ResultStatus::ErrorGetLoader;
|
||||
}
|
||||
std::pair<std::optional<u32>, Loader::ResultStatus> system_mode =
|
||||
app_loader->LoadKernelSystemMode();
|
||||
|
||||
if (system_mode.second != Loader::ResultStatus::Success) {
|
||||
LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!",
|
||||
static_cast<int>(system_mode.second));
|
||||
|
||||
return ResultStatus::ErrorSystemMode;
|
||||
}
|
||||
|
||||
ResultStatus init_result{Init(system, emu_window)};
|
||||
if (init_result != ResultStatus::Success) {
|
||||
@@ -167,6 +149,7 @@ struct System::Impl {
|
||||
return init_result;
|
||||
}
|
||||
|
||||
telemetry_session->AddInitialInfo(*app_loader);
|
||||
auto main_process = Kernel::Process::Create(system, "main");
|
||||
const auto [load_result, load_parameters] = app_loader->Load(*main_process);
|
||||
if (load_result != Loader::ResultStatus::Success) {
|
||||
|
||||
@@ -98,7 +98,6 @@ public:
|
||||
Success, ///< Succeeded
|
||||
ErrorNotInitialized, ///< Error trying to use core prior to initialization
|
||||
ErrorGetLoader, ///< Error finding the correct application loader
|
||||
ErrorSystemMode, ///< Error determining the system mode
|
||||
ErrorSystemFiles, ///< Error in finding system files
|
||||
ErrorSharedFont, ///< Error in finding shared font
|
||||
ErrorVideoCore, ///< Error in the video core
|
||||
|
||||
@@ -13,52 +13,40 @@ namespace Core::Timing {
|
||||
|
||||
constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / BASE_CLOCK_RATE;
|
||||
|
||||
s64 usToCycles(s64 us) {
|
||||
if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) {
|
||||
s64 msToCycles(std::chrono::milliseconds ms) {
|
||||
if (static_cast<u64>(ms.count() / 1000) > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
|
||||
return std::numeric_limits<s64>::max();
|
||||
}
|
||||
if (us > MAX_VALUE_TO_MULTIPLY) {
|
||||
if (static_cast<u64>(ms.count()) > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
|
||||
return BASE_CLOCK_RATE * (us / 1000000);
|
||||
return BASE_CLOCK_RATE * (ms.count() / 1000);
|
||||
}
|
||||
return (BASE_CLOCK_RATE * us) / 1000000;
|
||||
return (BASE_CLOCK_RATE * ms.count()) / 1000;
|
||||
}
|
||||
|
||||
s64 usToCycles(u64 us) {
|
||||
if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) {
|
||||
s64 usToCycles(std::chrono::microseconds us) {
|
||||
if (static_cast<u64>(us.count() / 1000000) > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
|
||||
return std::numeric_limits<s64>::max();
|
||||
}
|
||||
if (us > MAX_VALUE_TO_MULTIPLY) {
|
||||
if (static_cast<u64>(us.count()) > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
|
||||
return BASE_CLOCK_RATE * static_cast<s64>(us / 1000000);
|
||||
return BASE_CLOCK_RATE * (us.count() / 1000000);
|
||||
}
|
||||
return (BASE_CLOCK_RATE * static_cast<s64>(us)) / 1000000;
|
||||
return (BASE_CLOCK_RATE * us.count()) / 1000000;
|
||||
}
|
||||
|
||||
s64 nsToCycles(s64 ns) {
|
||||
if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) {
|
||||
s64 nsToCycles(std::chrono::nanoseconds ns) {
|
||||
if (static_cast<u64>(ns.count() / 1000000000) > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
|
||||
return std::numeric_limits<s64>::max();
|
||||
}
|
||||
if (ns > MAX_VALUE_TO_MULTIPLY) {
|
||||
if (static_cast<u64>(ns.count()) > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
|
||||
return BASE_CLOCK_RATE * (ns / 1000000000);
|
||||
return BASE_CLOCK_RATE * (ns.count() / 1000000000);
|
||||
}
|
||||
return (BASE_CLOCK_RATE * ns) / 1000000000;
|
||||
}
|
||||
|
||||
s64 nsToCycles(u64 ns) {
|
||||
if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
|
||||
return std::numeric_limits<s64>::max();
|
||||
}
|
||||
if (ns > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
|
||||
return BASE_CLOCK_RATE * (static_cast<s64>(ns) / 1000000000);
|
||||
}
|
||||
return (BASE_CLOCK_RATE * static_cast<s64>(ns)) / 1000000000;
|
||||
return (BASE_CLOCK_RATE * ns.count()) / 1000000000;
|
||||
}
|
||||
|
||||
u64 CpuCyclesToClockCycles(u64 ticks) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
@@ -13,53 +14,20 @@ namespace Core::Timing {
|
||||
constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch clock speed is 1020MHz un/docked
|
||||
constexpr u64 CNTFREQ = 19200000; // Value from fusee.
|
||||
|
||||
inline s64 msToCycles(int ms) {
|
||||
// since ms is int there is no way to overflow
|
||||
return BASE_CLOCK_RATE * static_cast<s64>(ms) / 1000;
|
||||
s64 msToCycles(std::chrono::milliseconds ms);
|
||||
s64 usToCycles(std::chrono::microseconds us);
|
||||
s64 nsToCycles(std::chrono::nanoseconds ns);
|
||||
|
||||
inline std::chrono::milliseconds CyclesToMs(s64 cycles) {
|
||||
return std::chrono::milliseconds(cycles * 1000 / BASE_CLOCK_RATE);
|
||||
}
|
||||
|
||||
inline s64 msToCycles(float ms) {
|
||||
return static_cast<s64>(BASE_CLOCK_RATE * (0.001f) * ms);
|
||||
inline std::chrono::nanoseconds CyclesToNs(s64 cycles) {
|
||||
return std::chrono::nanoseconds(cycles * 1000000000 / BASE_CLOCK_RATE);
|
||||
}
|
||||
|
||||
inline s64 msToCycles(double ms) {
|
||||
return static_cast<s64>(BASE_CLOCK_RATE * (0.001) * ms);
|
||||
}
|
||||
|
||||
inline s64 usToCycles(float us) {
|
||||
return static_cast<s64>(BASE_CLOCK_RATE * (0.000001f) * us);
|
||||
}
|
||||
|
||||
inline s64 usToCycles(int us) {
|
||||
return (BASE_CLOCK_RATE * static_cast<s64>(us) / 1000000);
|
||||
}
|
||||
|
||||
s64 usToCycles(s64 us);
|
||||
|
||||
s64 usToCycles(u64 us);
|
||||
|
||||
inline s64 nsToCycles(float ns) {
|
||||
return static_cast<s64>(BASE_CLOCK_RATE * (0.000000001f) * ns);
|
||||
}
|
||||
|
||||
inline s64 nsToCycles(int ns) {
|
||||
return BASE_CLOCK_RATE * static_cast<s64>(ns) / 1000000000;
|
||||
}
|
||||
|
||||
s64 nsToCycles(s64 ns);
|
||||
|
||||
s64 nsToCycles(u64 ns);
|
||||
|
||||
inline u64 cyclesToNs(s64 cycles) {
|
||||
return cycles * 1000000000 / BASE_CLOCK_RATE;
|
||||
}
|
||||
|
||||
inline s64 cyclesToUs(s64 cycles) {
|
||||
return cycles * 1000000 / BASE_CLOCK_RATE;
|
||||
}
|
||||
|
||||
inline u64 cyclesToMs(s64 cycles) {
|
||||
return cycles * 1000 / BASE_CLOCK_RATE;
|
||||
inline std::chrono::microseconds CyclesToUs(s64 cycles) {
|
||||
return std::chrono::microseconds(cycles * 1000000 / BASE_CLOCK_RATE);
|
||||
}
|
||||
|
||||
u64 CpuCyclesToClockCycles(u64 ticks);
|
||||
|
||||
@@ -87,6 +87,10 @@ u64 NACP::GetDefaultJournalSaveSize() const {
|
||||
return raw.user_account_save_data_journal_size;
|
||||
}
|
||||
|
||||
u32 NACP::GetSupportedLanguages() const {
|
||||
return raw.supported_languages;
|
||||
}
|
||||
|
||||
std::vector<u8> NACP::GetRawBytes() const {
|
||||
std::vector<u8> out(sizeof(RawNACP));
|
||||
std::memcpy(out.data(), &raw, sizeof(RawNACP));
|
||||
|
||||
@@ -109,6 +109,7 @@ public:
|
||||
std::string GetVersionString() const;
|
||||
u64 GetDefaultNormalSaveSize() const;
|
||||
u64 GetDefaultJournalSaveSize() const;
|
||||
u32 GetSupportedLanguages() const;
|
||||
std::vector<u8> GetRawBytes() const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "common/common_funcs.h"
|
||||
@@ -69,11 +70,15 @@ struct CNMTHeader {
|
||||
u64_le title_id;
|
||||
u32_le title_version;
|
||||
TitleType type;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u8 reserved;
|
||||
u16_le table_offset;
|
||||
u16_le number_content_entries;
|
||||
u16_le number_meta_entries;
|
||||
INSERT_PADDING_BYTES(12);
|
||||
u8 attributes;
|
||||
std::array<u8, 2> reserved2;
|
||||
u8 is_committed;
|
||||
u32_le required_download_system_version;
|
||||
std::array<u8, 4> reserved3;
|
||||
};
|
||||
static_assert(sizeof(CNMTHeader) == 0x20, "CNMTHeader has incorrect size.");
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/frontend/applets/profile_select.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
@@ -10,9 +11,9 @@ namespace Core::Frontend {
|
||||
ProfileSelectApplet::~ProfileSelectApplet() = default;
|
||||
|
||||
void DefaultProfileSelectApplet::SelectProfile(
|
||||
std::function<void(std::optional<Service::Account::UUID>)> callback) const {
|
||||
std::function<void(std::optional<Common::UUID>)> callback) const {
|
||||
Service::Account::ProfileManager manager;
|
||||
callback(manager.GetUser(Settings::values.current_user).value_or(Service::Account::UUID{}));
|
||||
callback(manager.GetUser(Settings::values.current_user).value_or(Common::UUID{}));
|
||||
LOG_INFO(Service_ACC, "called, selecting current user instead of prompting...");
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "common/uuid.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
@@ -14,14 +14,12 @@ class ProfileSelectApplet {
|
||||
public:
|
||||
virtual ~ProfileSelectApplet();
|
||||
|
||||
virtual void SelectProfile(
|
||||
std::function<void(std::optional<Service::Account::UUID>)> callback) const = 0;
|
||||
virtual void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback) const = 0;
|
||||
};
|
||||
|
||||
class DefaultProfileSelectApplet final : public ProfileSelectApplet {
|
||||
public:
|
||||
void SelectProfile(
|
||||
std::function<void(std::optional<Service::Account::UUID>)> callback) const override;
|
||||
void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback) const override;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
||||
|
||||
@@ -169,8 +169,7 @@ private:
|
||||
* For the request to be honored, EmuWindow implementations will usually reimplement this
|
||||
* function.
|
||||
*/
|
||||
virtual void OnMinimalClientAreaChangeRequest(
|
||||
const std::pair<unsigned, unsigned>& minimal_size) {
|
||||
virtual void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned>) {
|
||||
// By default, ignore this request and do nothing.
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ static Common::Rectangle<T> MaxRectangle(Common::Rectangle<T> window_area,
|
||||
static_cast<T>(std::round(scale * screen_aspect_ratio))};
|
||||
}
|
||||
|
||||
FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height) {
|
||||
FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
|
||||
ASSERT(width > 0);
|
||||
ASSERT(height > 0);
|
||||
// The drawing code needs at least somewhat valid values for both screens
|
||||
@@ -29,22 +29,23 @@ FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height) {
|
||||
|
||||
const float emulation_aspect_ratio{static_cast<float>(ScreenUndocked::Height) /
|
||||
ScreenUndocked::Width};
|
||||
Common::Rectangle<unsigned> screen_window_area{0, 0, width, height};
|
||||
Common::Rectangle<unsigned> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio);
|
||||
const auto window_aspect_ratio = static_cast<float>(height) / width;
|
||||
|
||||
float window_aspect_ratio = static_cast<float>(height) / width;
|
||||
const Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
||||
Common::Rectangle<u32> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio);
|
||||
|
||||
if (window_aspect_ratio < emulation_aspect_ratio) {
|
||||
screen = screen.TranslateX((screen_window_area.GetWidth() - screen.GetWidth()) / 2);
|
||||
} else {
|
||||
screen = screen.TranslateY((height - screen.GetHeight()) / 2);
|
||||
}
|
||||
|
||||
res.screen = screen;
|
||||
return res;
|
||||
}
|
||||
|
||||
FramebufferLayout FrameLayoutFromResolutionScale(u16 res_scale) {
|
||||
int width, height;
|
||||
FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) {
|
||||
u32 width, height;
|
||||
|
||||
if (Settings::values.use_docked_mode) {
|
||||
width = ScreenDocked::WidthDocked * res_scale;
|
||||
|
||||
@@ -8,15 +8,22 @@
|
||||
|
||||
namespace Layout {
|
||||
|
||||
enum ScreenUndocked : unsigned { Width = 1280, Height = 720 };
|
||||
enum ScreenDocked : unsigned { WidthDocked = 1920, HeightDocked = 1080 };
|
||||
enum ScreenUndocked : u32 {
|
||||
Width = 1280,
|
||||
Height = 720,
|
||||
};
|
||||
|
||||
enum ScreenDocked : u32 {
|
||||
WidthDocked = 1920,
|
||||
HeightDocked = 1080,
|
||||
};
|
||||
|
||||
/// Describes the layout of the window framebuffer
|
||||
struct FramebufferLayout {
|
||||
unsigned width{ScreenUndocked::Width};
|
||||
unsigned height{ScreenUndocked::Height};
|
||||
u32 width{ScreenUndocked::Width};
|
||||
u32 height{ScreenUndocked::Height};
|
||||
|
||||
Common::Rectangle<unsigned> screen;
|
||||
Common::Rectangle<u32> screen;
|
||||
|
||||
/**
|
||||
* Returns the ration of pixel size of the screen, compared to the native size of the undocked
|
||||
@@ -33,12 +40,12 @@ struct FramebufferLayout {
|
||||
* @param height Window framebuffer height in pixels
|
||||
* @return Newly created FramebufferLayout object with default screen regions initialized
|
||||
*/
|
||||
FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height);
|
||||
FramebufferLayout DefaultFrameLayout(u32 width, u32 height);
|
||||
|
||||
/**
|
||||
* Convenience method to get frame layout by resolution scale
|
||||
* @param res_scale resolution scale factor
|
||||
*/
|
||||
FramebufferLayout FrameLayoutFromResolutionScale(u16 res_scale);
|
||||
FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale);
|
||||
|
||||
} // namespace Layout
|
||||
|
||||
@@ -43,7 +43,7 @@ void SessionRequestHandler::ClientDisconnected(const SharedPtr<ServerSession>& s
|
||||
}
|
||||
|
||||
SharedPtr<WritableEvent> HLERequestContext::SleepClientThread(
|
||||
SharedPtr<Thread> thread, const std::string& reason, u64 timeout, WakeupCallback&& callback,
|
||||
const std::string& reason, u64 timeout, WakeupCallback&& callback,
|
||||
SharedPtr<WritableEvent> writable_event) {
|
||||
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
|
||||
thread->SetWakeupCallback([context = *this, callback](
|
||||
@@ -76,8 +76,9 @@ SharedPtr<WritableEvent> HLERequestContext::SleepClientThread(
|
||||
return writable_event;
|
||||
}
|
||||
|
||||
HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session)
|
||||
: server_session(std::move(server_session)) {
|
||||
HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session,
|
||||
SharedPtr<Thread> thread)
|
||||
: server_session(std::move(server_session)), thread(std::move(thread)) {
|
||||
cmd_buf[0] = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ protected:
|
||||
*/
|
||||
class HLERequestContext {
|
||||
public:
|
||||
explicit HLERequestContext(SharedPtr<ServerSession> session);
|
||||
explicit HLERequestContext(SharedPtr<ServerSession> session, SharedPtr<Thread> thread);
|
||||
~HLERequestContext();
|
||||
|
||||
/// Returns a pointer to the IPC command buffer for this request.
|
||||
@@ -119,7 +119,6 @@ public:
|
||||
/**
|
||||
* Puts the specified guest thread to sleep until the returned event is signaled or until the
|
||||
* specified timeout expires.
|
||||
* @param thread Thread to be put to sleep.
|
||||
* @param reason Reason for pausing the thread, to be used for debugging purposes.
|
||||
* @param timeout Timeout in nanoseconds after which the thread will be awoken and the callback
|
||||
* invoked with a Timeout reason.
|
||||
@@ -130,8 +129,8 @@ public:
|
||||
* created.
|
||||
* @returns Event that when signaled will resume the thread and call the callback function.
|
||||
*/
|
||||
SharedPtr<WritableEvent> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason,
|
||||
u64 timeout, WakeupCallback&& callback,
|
||||
SharedPtr<WritableEvent> SleepClientThread(const std::string& reason, u64 timeout,
|
||||
WakeupCallback&& callback,
|
||||
SharedPtr<WritableEvent> writable_event = nullptr);
|
||||
|
||||
/// Populates this context with data from the requesting process/thread.
|
||||
@@ -268,6 +267,7 @@ private:
|
||||
|
||||
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
|
||||
SharedPtr<Kernel::ServerSession> server_session;
|
||||
SharedPtr<Thread> thread;
|
||||
// TODO(yuriks): Check common usage of this and optimize size accordingly
|
||||
boost::container::small_vector<SharedPtr<Object>, 8> move_objects;
|
||||
boost::container::small_vector<SharedPtr<Object>, 8> copy_objects;
|
||||
|
||||
@@ -48,7 +48,7 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) {
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
SharedPtr<Process> Process::Create(Core::System& system, std::string&& name) {
|
||||
SharedPtr<Process> Process::Create(Core::System& system, std::string name) {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
SharedPtr<Process> process(new Process(system));
|
||||
@@ -72,10 +72,26 @@ SharedPtr<ResourceLimit> Process::GetResourceLimit() const {
|
||||
return resource_limit;
|
||||
}
|
||||
|
||||
u64 Process::GetTotalPhysicalMemoryAvailable() const {
|
||||
return vm_manager.GetTotalPhysicalMemoryAvailable();
|
||||
}
|
||||
|
||||
u64 Process::GetTotalPhysicalMemoryAvailableWithoutMmHeap() const {
|
||||
// TODO: Subtract the personal heap size from this when the
|
||||
// personal heap is implemented.
|
||||
return GetTotalPhysicalMemoryAvailable();
|
||||
}
|
||||
|
||||
u64 Process::GetTotalPhysicalMemoryUsed() const {
|
||||
return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size;
|
||||
}
|
||||
|
||||
u64 Process::GetTotalPhysicalMemoryUsedWithoutMmHeap() const {
|
||||
// TODO: Subtract the personal heap size from this when the
|
||||
// personal heap is implemented.
|
||||
return GetTotalPhysicalMemoryUsed();
|
||||
}
|
||||
|
||||
void Process::RegisterThread(const Thread* thread) {
|
||||
thread_list.push_back(thread);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/address_arbiter.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
@@ -76,7 +75,7 @@ public:
|
||||
|
||||
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
|
||||
|
||||
static SharedPtr<Process> Create(Core::System& system, std::string&& name);
|
||||
static SharedPtr<Process> Create(Core::System& system, std::string name);
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "Process";
|
||||
@@ -187,9 +186,20 @@ public:
|
||||
return random_entropy.at(index);
|
||||
}
|
||||
|
||||
/// Retrieves the total physical memory available to this process in bytes.
|
||||
u64 GetTotalPhysicalMemoryAvailable() const;
|
||||
|
||||
/// Retrieves the total physical memory available to this process in bytes,
|
||||
/// without the size of the personal heap added to it.
|
||||
u64 GetTotalPhysicalMemoryAvailableWithoutMmHeap() const;
|
||||
|
||||
/// Retrieves the total physical memory used by this process in bytes.
|
||||
u64 GetTotalPhysicalMemoryUsed() const;
|
||||
|
||||
/// Retrieves the total physical memory used by this process in bytes,
|
||||
/// without the size of the personal heap added to it.
|
||||
u64 GetTotalPhysicalMemoryUsedWithoutMmHeap() const;
|
||||
|
||||
/// Gets the list of all threads created with this process as their owner.
|
||||
const std::list<const Thread*>& GetThreadList() const {
|
||||
return thread_list;
|
||||
|
||||
@@ -130,7 +130,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
|
||||
// The ServerSession received a sync request, this means that there's new data available
|
||||
// from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or
|
||||
// similar.
|
||||
Kernel::HLERequestContext context(this);
|
||||
Kernel::HLERequestContext context(this, thread);
|
||||
u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
|
||||
context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
|
||||
|
||||
|
||||
@@ -710,13 +710,13 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
|
||||
MapRegionSize = 3,
|
||||
HeapRegionBaseAddr = 4,
|
||||
HeapRegionSize = 5,
|
||||
TotalMemoryUsage = 6,
|
||||
TotalPhysicalMemoryAvailable = 6,
|
||||
TotalPhysicalMemoryUsed = 7,
|
||||
IsCurrentProcessBeingDebugged = 8,
|
||||
RegisterResourceLimit = 9,
|
||||
IdleTickCount = 10,
|
||||
RandomEntropy = 11,
|
||||
PerformanceCounter = 0xF0000002,
|
||||
ThreadTickCount = 0xF0000002,
|
||||
// 2.0.0+
|
||||
ASLRRegionBaseAddr = 12,
|
||||
ASLRRegionSize = 13,
|
||||
@@ -730,7 +730,9 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
|
||||
PrivilegedProcessId = 19,
|
||||
// 5.0.0+
|
||||
UserExceptionContextAddr = 20,
|
||||
ThreadTickCount = 0xF0000002,
|
||||
// 6.0.0+
|
||||
TotalPhysicalMemoryAvailableWithoutMmHeap = 21,
|
||||
TotalPhysicalMemoryUsedWithoutMmHeap = 22,
|
||||
};
|
||||
|
||||
const auto info_id_type = static_cast<GetInfoType>(info_id);
|
||||
@@ -746,12 +748,14 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
|
||||
case GetInfoType::ASLRRegionSize:
|
||||
case GetInfoType::NewMapRegionBaseAddr:
|
||||
case GetInfoType::NewMapRegionSize:
|
||||
case GetInfoType::TotalMemoryUsage:
|
||||
case GetInfoType::TotalPhysicalMemoryAvailable:
|
||||
case GetInfoType::TotalPhysicalMemoryUsed:
|
||||
case GetInfoType::IsVirtualAddressMemoryEnabled:
|
||||
case GetInfoType::PersonalMmHeapUsage:
|
||||
case GetInfoType::TitleId:
|
||||
case GetInfoType::UserExceptionContextAddr: {
|
||||
case GetInfoType::UserExceptionContextAddr:
|
||||
case GetInfoType::TotalPhysicalMemoryAvailableWithoutMmHeap:
|
||||
case GetInfoType::TotalPhysicalMemoryUsedWithoutMmHeap: {
|
||||
if (info_sub_id != 0) {
|
||||
return ERR_INVALID_ENUM_VALUE;
|
||||
}
|
||||
@@ -804,8 +808,8 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
|
||||
*result = process->VMManager().GetNewMapRegionSize();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::TotalMemoryUsage:
|
||||
*result = process->VMManager().GetTotalMemoryUsage();
|
||||
case GetInfoType::TotalPhysicalMemoryAvailable:
|
||||
*result = process->GetTotalPhysicalMemoryAvailable();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::TotalPhysicalMemoryUsed:
|
||||
@@ -826,6 +830,14 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
|
||||
*result = 0;
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::TotalPhysicalMemoryAvailableWithoutMmHeap:
|
||||
*result = process->GetTotalPhysicalMemoryAvailable();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::TotalPhysicalMemoryUsedWithoutMmHeap:
|
||||
*result = process->GetTotalPhysicalMemoryUsedWithoutMmHeap();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1342,7 +1354,7 @@ static void ExitProcess(Core::System& system) {
|
||||
/// Creates a new thread
|
||||
static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
|
||||
VAddr stack_top, u32 priority, s32 processor_id) {
|
||||
LOG_TRACE(Kernel_SVC,
|
||||
LOG_DEBUG(Kernel_SVC,
|
||||
"called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, "
|
||||
"threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}",
|
||||
entry_point, arg, stack_top, priority, processor_id, *out_handle);
|
||||
@@ -1402,7 +1414,7 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
|
||||
|
||||
/// Starts the thread for the provided handle
|
||||
static ResultCode StartThread(Core::System& system, Handle thread_handle) {
|
||||
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
|
||||
LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
|
||||
|
||||
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
|
||||
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
|
||||
@@ -1425,7 +1437,7 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) {
|
||||
|
||||
/// Called when a thread exits
|
||||
static void ExitThread(Core::System& system) {
|
||||
LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
|
||||
LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
|
||||
|
||||
auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
|
||||
current_thread->Stop();
|
||||
@@ -1435,7 +1447,7 @@ static void ExitThread(Core::System& system) {
|
||||
|
||||
/// Sleep the current thread
|
||||
static void SleepThread(Core::System& system, s64 nanoseconds) {
|
||||
LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
|
||||
LOG_DEBUG(Kernel_SVC, "called nanoseconds={}", nanoseconds);
|
||||
|
||||
enum class SleepType : s64 {
|
||||
YieldWithoutLoadBalancing = 0,
|
||||
@@ -1880,11 +1892,51 @@ static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle,
|
||||
}
|
||||
|
||||
static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core,
|
||||
u64 mask) {
|
||||
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:016X}, core=0x{:X}", thread_handle,
|
||||
mask, core);
|
||||
u64 affinity_mask) {
|
||||
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core=0x{:X}, affinity_mask=0x{:016X}",
|
||||
thread_handle, core, affinity_mask);
|
||||
|
||||
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
|
||||
const auto* const current_process = system.Kernel().CurrentProcess();
|
||||
|
||||
if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) {
|
||||
const u8 ideal_cpu_core = current_process->GetIdealCore();
|
||||
|
||||
ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL));
|
||||
|
||||
// Set the target CPU to the ideal core specified by the process.
|
||||
core = ideal_cpu_core;
|
||||
affinity_mask = 1ULL << core;
|
||||
} else {
|
||||
const u64 core_mask = current_process->GetCoreMask();
|
||||
|
||||
if ((core_mask | affinity_mask) != core_mask) {
|
||||
LOG_ERROR(
|
||||
Kernel_SVC,
|
||||
"Invalid processor ID specified (core_mask=0x{:08X}, affinity_mask=0x{:016X})",
|
||||
core_mask, affinity_mask);
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
|
||||
if (affinity_mask == 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Specfified affinity mask is zero.");
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
if (core < Core::NUM_CPU_CORES) {
|
||||
if ((affinity_mask & (1ULL << core)) == 0) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Core is not enabled for the current mask, core={}, mask={:016X}", core,
|
||||
affinity_mask);
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
} else if (core != static_cast<u32>(THREADPROCESSORID_DONT_CARE) &&
|
||||
core != static_cast<u32>(THREADPROCESSORID_DONT_UPDATE)) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid processor ID specified (core={}).", core);
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
}
|
||||
|
||||
const auto& handle_table = current_process->GetHandleTable();
|
||||
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
|
||||
if (!thread) {
|
||||
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
|
||||
@@ -1892,40 +1944,7 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle,
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) {
|
||||
const u8 ideal_cpu_core = thread->GetOwnerProcess()->GetIdealCore();
|
||||
|
||||
ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL));
|
||||
|
||||
// Set the target CPU to the ideal core specified by the process.
|
||||
core = ideal_cpu_core;
|
||||
mask = 1ULL << core;
|
||||
}
|
||||
|
||||
if (mask == 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Mask is 0");
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
/// This value is used to only change the affinity mask without changing the current ideal core.
|
||||
static constexpr u32 OnlyChangeMask = static_cast<u32>(-3);
|
||||
|
||||
if (core == OnlyChangeMask) {
|
||||
core = thread->GetIdealCore();
|
||||
} else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid core specified, got {}", core);
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
|
||||
// Error out if the input core isn't enabled in the input mask.
|
||||
if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Core is not enabled for the current mask, core={}, mask={:016X}",
|
||||
core, mask);
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
thread->ChangeCore(core, mask);
|
||||
|
||||
thread->ChangeCore(core, affinity_mask);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,9 +75,9 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
|
||||
|
||||
// This function might be called from any thread so we have to be cautious and use the
|
||||
// thread-safe version of ScheduleEvent.
|
||||
const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds});
|
||||
Core::System::GetInstance().CoreTiming().ScheduleEventThreadsafe(
|
||||
Core::Timing::nsToCycles(nanoseconds), kernel.ThreadWakeupCallbackEventType(),
|
||||
callback_handle);
|
||||
cycles, kernel.ThreadWakeupCallbackEventType(), callback_handle);
|
||||
}
|
||||
|
||||
void Thread::CancelWakeupTimer() {
|
||||
|
||||
@@ -30,12 +30,21 @@ enum ThreadPriority : u32 {
|
||||
};
|
||||
|
||||
enum ThreadProcessorId : s32 {
|
||||
THREADPROCESSORID_IDEAL = -2, ///< Run thread on the ideal core specified by the process.
|
||||
THREADPROCESSORID_0 = 0, ///< Run thread on core 0
|
||||
THREADPROCESSORID_1 = 1, ///< Run thread on core 1
|
||||
THREADPROCESSORID_2 = 2, ///< Run thread on core 2
|
||||
THREADPROCESSORID_3 = 3, ///< Run thread on core 3
|
||||
THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
|
||||
/// Indicates that no particular processor core is preferred.
|
||||
THREADPROCESSORID_DONT_CARE = -1,
|
||||
|
||||
/// Run thread on the ideal core specified by the process.
|
||||
THREADPROCESSORID_IDEAL = -2,
|
||||
|
||||
/// Indicates that the preferred processor ID shouldn't be updated in
|
||||
/// a core mask setting operation.
|
||||
THREADPROCESSORID_DONT_UPDATE = -3,
|
||||
|
||||
THREADPROCESSORID_0 = 0, ///< Run thread on core 0
|
||||
THREADPROCESSORID_1 = 1, ///< Run thread on core 1
|
||||
THREADPROCESSORID_2 = 2, ///< Run thread on core 2
|
||||
THREADPROCESSORID_3 = 3, ///< Run thread on core 3
|
||||
THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
|
||||
|
||||
/// Allowed CPU mask
|
||||
THREADPROCESSORID_DEFAULT_MASK = (1 << THREADPROCESSORID_0) | (1 << THREADPROCESSORID_1) |
|
||||
|
||||
@@ -68,9 +68,7 @@ VMManager::VMManager(Core::System& system) : system{system} {
|
||||
Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
|
||||
}
|
||||
|
||||
VMManager::~VMManager() {
|
||||
Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
|
||||
}
|
||||
VMManager::~VMManager() = default;
|
||||
|
||||
void VMManager::Reset(FileSys::ProgramAddressSpaceType type) {
|
||||
Clear();
|
||||
@@ -758,7 +756,7 @@ VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, Memo
|
||||
std::make_tuple(initial_state, initial_permissions, initial_attributes & ~ignore_mask));
|
||||
}
|
||||
|
||||
u64 VMManager::GetTotalMemoryUsage() const {
|
||||
u64 VMManager::GetTotalPhysicalMemoryAvailable() const {
|
||||
LOG_WARNING(Kernel, "(STUBBED) called");
|
||||
return 0xF8000000;
|
||||
}
|
||||
|
||||
@@ -499,7 +499,7 @@ public:
|
||||
void LogLayout() const;
|
||||
|
||||
/// Gets the total memory usage, used by svcGetInfo
|
||||
u64 GetTotalMemoryUsage() const;
|
||||
u64 GetTotalPhysicalMemoryAvailable() const;
|
||||
|
||||
/// Gets the address space base address
|
||||
VAddr GetAddressSpaceBaseAddress() const;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/constants.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/acc/acc.h"
|
||||
@@ -21,20 +22,7 @@
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
// Smallest JPEG https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
|
||||
// used as a backup should the one on disk not exist
|
||||
constexpr u32 backup_jpeg_size = 107;
|
||||
constexpr std::array<u8, backup_jpeg_size> backup_jpeg{{
|
||||
0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
|
||||
0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05,
|
||||
0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e,
|
||||
0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13,
|
||||
0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01,
|
||||
0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08,
|
||||
0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
|
||||
}};
|
||||
|
||||
static std::string GetImagePath(UUID uuid) {
|
||||
static std::string GetImagePath(Common::UUID uuid) {
|
||||
return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
|
||||
"/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
|
||||
}
|
||||
@@ -46,7 +34,7 @@ static constexpr u32 SanitizeJPEGSize(std::size_t size) {
|
||||
|
||||
class IProfile final : public ServiceFramework<IProfile> {
|
||||
public:
|
||||
explicit IProfile(UUID user_id, ProfileManager& profile_manager)
|
||||
explicit IProfile(Common::UUID user_id, ProfileManager& profile_manager)
|
||||
: ServiceFramework("IProfile"), profile_manager(profile_manager), user_id(user_id) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IProfile::Get, "Get"},
|
||||
@@ -101,8 +89,8 @@ private:
|
||||
if (!image.IsOpen()) {
|
||||
LOG_WARNING(Service_ACC,
|
||||
"Failed to load user provided image! Falling back to built-in backup...");
|
||||
ctx.WriteBuffer(backup_jpeg);
|
||||
rb.Push<u32>(backup_jpeg_size);
|
||||
ctx.WriteBuffer(Core::Constants::ACCOUNT_BACKUP_JPEG);
|
||||
rb.Push<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -124,14 +112,14 @@ private:
|
||||
if (!image.IsOpen()) {
|
||||
LOG_WARNING(Service_ACC,
|
||||
"Failed to load user provided image! Falling back to built-in backup...");
|
||||
rb.Push<u32>(backup_jpeg_size);
|
||||
rb.Push<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size());
|
||||
} else {
|
||||
rb.Push<u32>(SanitizeJPEGSize(image.GetSize()));
|
||||
}
|
||||
}
|
||||
|
||||
const ProfileManager& profile_manager;
|
||||
UUID user_id; ///< The user id this profile refers to.
|
||||
Common::UUID user_id; ///< The user id this profile refers to.
|
||||
};
|
||||
|
||||
class IManagerForApplication final : public ServiceFramework<IManagerForApplication> {
|
||||
@@ -179,7 +167,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
UUID user_id = rp.PopRaw<UUID>();
|
||||
Common::UUID user_id = rp.PopRaw<Common::UUID>();
|
||||
LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
@@ -205,12 +193,12 @@ void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_ACC, "called");
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<UUID>(profile_manager->GetLastOpenedUser());
|
||||
rb.PushRaw<Common::UUID>(profile_manager->GetLastOpenedUser());
|
||||
}
|
||||
|
||||
void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
UUID user_id = rp.PopRaw<UUID>();
|
||||
Common::UUID user_id = rp.PopRaw<Common::UUID>();
|
||||
LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
@@ -245,15 +233,15 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
if (profile_manager->GetUserCount() != 1) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<u128>(INVALID_UUID);
|
||||
rb.PushRaw<u128>(Common::INVALID_UUID);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto user_list = profile_manager->GetAllUsers();
|
||||
if (std::all_of(user_list.begin(), user_list.end(),
|
||||
[](const auto& user) { return user.uuid == INVALID_UUID; })) {
|
||||
[](const auto& user) { return user.uuid == Common::INVALID_UUID; })) {
|
||||
rb.Push(ResultCode(-1)); // TODO(ogniK): Find the correct error code
|
||||
rb.PushRaw<u128>(INVALID_UUID);
|
||||
rb.PushRaw<u128>(Common::INVALID_UUID);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
using Common::UUID;
|
||||
|
||||
struct UserRaw {
|
||||
UUID uuid;
|
||||
UUID uuid2;
|
||||
@@ -35,26 +37,6 @@ constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20);
|
||||
|
||||
constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "/system/save/8000000000000010/su/avators/";
|
||||
|
||||
UUID UUID::Generate() {
|
||||
std::random_device device;
|
||||
std::mt19937 gen(device());
|
||||
std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
|
||||
return UUID{distribution(gen), distribution(gen)};
|
||||
}
|
||||
|
||||
std::string UUID::Format() const {
|
||||
return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
|
||||
}
|
||||
|
||||
std::string UUID::FormatSwitch() const {
|
||||
std::array<u8, 16> s{};
|
||||
std::memcpy(s.data(), uuid.data(), sizeof(u128));
|
||||
return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
|
||||
":02x}{:02x}{:02x}{:02x}{:02x}",
|
||||
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
|
||||
s[12], s[13], s[14], s[15]);
|
||||
}
|
||||
|
||||
ProfileManager::ProfileManager() {
|
||||
ParseUserSaveFile();
|
||||
|
||||
@@ -217,7 +199,7 @@ bool ProfileManager::UserExists(UUID uuid) const {
|
||||
bool ProfileManager::UserExistsIndex(std::size_t index) const {
|
||||
if (index >= MAX_USERS)
|
||||
return false;
|
||||
return profiles[index].user_uuid.uuid != INVALID_UUID;
|
||||
return profiles[index].user_uuid.uuid != Common::INVALID_UUID;
|
||||
}
|
||||
|
||||
/// Opens a specific user
|
||||
@@ -311,7 +293,7 @@ bool ProfileManager::RemoveUser(UUID uuid) {
|
||||
|
||||
bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
|
||||
const auto index = GetUserIndex(uuid);
|
||||
if (!index || profile_new.user_uuid == UUID(INVALID_UUID)) {
|
||||
if (!index || profile_new.user_uuid == UUID(Common::INVALID_UUID)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -342,7 +324,7 @@ void ProfileManager::ParseUserSaveFile() {
|
||||
}
|
||||
|
||||
for (const auto& user : data.users) {
|
||||
if (user.uuid == UUID(INVALID_UUID)) {
|
||||
if (user.uuid == UUID(Common::INVALID_UUID)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,47 +9,15 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "common/uuid.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Service::Account {
|
||||
constexpr std::size_t MAX_USERS = 8;
|
||||
constexpr u128 INVALID_UUID{{0, 0}};
|
||||
|
||||
struct UUID {
|
||||
// UUIDs which are 0 are considered invalid!
|
||||
u128 uuid = INVALID_UUID;
|
||||
UUID() = default;
|
||||
explicit UUID(const u128& id) : uuid{id} {}
|
||||
explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
|
||||
|
||||
explicit operator bool() const {
|
||||
return uuid != INVALID_UUID;
|
||||
}
|
||||
|
||||
bool operator==(const UUID& rhs) const {
|
||||
return uuid == rhs.uuid;
|
||||
}
|
||||
|
||||
bool operator!=(const UUID& rhs) const {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
// TODO(ogniK): Properly generate uuids based on RFC-4122
|
||||
static UUID Generate();
|
||||
|
||||
// Set the UUID to {0,0} to be considered an invalid user
|
||||
void Invalidate() {
|
||||
uuid = INVALID_UUID;
|
||||
}
|
||||
|
||||
std::string Format() const;
|
||||
std::string FormatSwitch() const;
|
||||
};
|
||||
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
|
||||
|
||||
constexpr std::size_t profile_username_size = 32;
|
||||
using ProfileUsername = std::array<u8, profile_username_size>;
|
||||
using UserIDArray = std::array<UUID, MAX_USERS>;
|
||||
using UserIDArray = std::array<Common::UUID, MAX_USERS>;
|
||||
|
||||
/// Contains extra data related to a user.
|
||||
/// TODO: RE this structure
|
||||
@@ -66,7 +34,7 @@ static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect
|
||||
/// This holds general information about a users profile. This is where we store all the information
|
||||
/// based on a specific user
|
||||
struct ProfileInfo {
|
||||
UUID user_uuid;
|
||||
Common::UUID user_uuid;
|
||||
ProfileUsername username;
|
||||
u64 creation_time;
|
||||
ProfileData data; // TODO(ognik): Work out what this is
|
||||
@@ -74,7 +42,7 @@ struct ProfileInfo {
|
||||
};
|
||||
|
||||
struct ProfileBase {
|
||||
UUID user_uuid;
|
||||
Common::UUID user_uuid;
|
||||
u64_le timestamp;
|
||||
ProfileUsername username;
|
||||
|
||||
@@ -96,33 +64,33 @@ public:
|
||||
~ProfileManager();
|
||||
|
||||
ResultCode AddUser(const ProfileInfo& user);
|
||||
ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username);
|
||||
ResultCode CreateNewUser(UUID uuid, const std::string& username);
|
||||
std::optional<UUID> GetUser(std::size_t index) const;
|
||||
std::optional<std::size_t> GetUserIndex(const UUID& uuid) const;
|
||||
ResultCode CreateNewUser(Common::UUID uuid, const ProfileUsername& username);
|
||||
ResultCode CreateNewUser(Common::UUID uuid, const std::string& username);
|
||||
std::optional<Common::UUID> GetUser(std::size_t index) const;
|
||||
std::optional<std::size_t> GetUserIndex(const Common::UUID& uuid) const;
|
||||
std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
|
||||
bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const;
|
||||
bool GetProfileBase(UUID uuid, ProfileBase& profile) const;
|
||||
bool GetProfileBase(Common::UUID uuid, ProfileBase& profile) const;
|
||||
bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const;
|
||||
bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
|
||||
ProfileData& data) const;
|
||||
bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const;
|
||||
bool GetProfileBaseAndData(Common::UUID uuid, ProfileBase& profile, ProfileData& data) const;
|
||||
bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
|
||||
ProfileData& data) const;
|
||||
std::size_t GetUserCount() const;
|
||||
std::size_t GetOpenUserCount() const;
|
||||
bool UserExists(UUID uuid) const;
|
||||
bool UserExists(Common::UUID uuid) const;
|
||||
bool UserExistsIndex(std::size_t index) const;
|
||||
void OpenUser(UUID uuid);
|
||||
void CloseUser(UUID uuid);
|
||||
void OpenUser(Common::UUID uuid);
|
||||
void CloseUser(Common::UUID uuid);
|
||||
UserIDArray GetOpenUsers() const;
|
||||
UserIDArray GetAllUsers() const;
|
||||
UUID GetLastOpenedUser() const;
|
||||
Common::UUID GetLastOpenedUser() const;
|
||||
|
||||
bool CanSystemRegisterUser() const;
|
||||
|
||||
bool RemoveUser(UUID uuid);
|
||||
bool SetProfileBase(UUID uuid, const ProfileBase& profile_new);
|
||||
bool RemoveUser(Common::UUID uuid);
|
||||
bool SetProfileBase(Common::UUID uuid, const ProfileBase& profile_new);
|
||||
|
||||
private:
|
||||
void ParseUserSaveFile();
|
||||
@@ -132,7 +100,7 @@ private:
|
||||
|
||||
std::array<ProfileInfo, MAX_USERS> profiles{};
|
||||
std::size_t user_count = 0;
|
||||
UUID last_opened_user{INVALID_UUID};
|
||||
Common::UUID last_opened_user{Common::INVALID_UUID};
|
||||
};
|
||||
|
||||
}; // namespace Service::Account
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <cstring>
|
||||
#include "audio_core/audio_renderer.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
@@ -29,9 +31,11 @@
|
||||
#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/ns/ns.h"
|
||||
#include "core/hle/service/nvflinger/nvflinger.h"
|
||||
#include "core/hle/service/pm/pm.h"
|
||||
#include "core/hle/service/set/set.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
@@ -1100,10 +1104,42 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
|
||||
// TODO(bunnei): This should be configurable
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
// Get supported languages from NACP, if possible
|
||||
// Default to 0 (all languages supported)
|
||||
u32 supported_languages = 0;
|
||||
FileSys::PatchManager pm{Core::System::GetInstance().CurrentProcess()->GetTitleID()};
|
||||
|
||||
const auto res = pm.GetControlMetadata();
|
||||
if (res.first != nullptr) {
|
||||
supported_languages = res.first->GetSupportedLanguages();
|
||||
}
|
||||
|
||||
// Call IApplicationManagerInterface implementation.
|
||||
auto& service_manager = Core::System::GetInstance().ServiceManager();
|
||||
auto ns_am2 = service_manager.GetService<Service::NS::NS>("ns:am2");
|
||||
auto app_man = ns_am2->GetApplicationManagerInterface();
|
||||
|
||||
// Get desired application language
|
||||
const auto res_lang = app_man->GetApplicationDesiredLanguage(supported_languages);
|
||||
if (res_lang.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res_lang.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert to settings language code.
|
||||
const auto res_code = app_man->ConvertApplicationLanguageToLanguageCode(*res_lang);
|
||||
if (res_code.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res_code.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_AM, "got desired_language={:016X}", *res_code);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(
|
||||
static_cast<u64>(Service::Set::GetLanguageCodeFromIndex(Settings::values.language_index)));
|
||||
rb.Push(*res_code);
|
||||
}
|
||||
|
||||
void IApplicationFunctions::InitializeGamePlayRecording(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -121,6 +121,21 @@ void Applet::Initialize() {
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
AppletFrontendSet::AppletFrontendSet() = default;
|
||||
|
||||
AppletFrontendSet::AppletFrontendSet(ErrorApplet error, PhotoViewer photo_viewer,
|
||||
ProfileSelect profile_select,
|
||||
SoftwareKeyboard software_keyboard, WebBrowser web_browser)
|
||||
: error{std::move(error)}, photo_viewer{std::move(photo_viewer)}, profile_select{std::move(
|
||||
profile_select)},
|
||||
software_keyboard{std::move(software_keyboard)}, web_browser{std::move(web_browser)} {}
|
||||
|
||||
AppletFrontendSet::~AppletFrontendSet() = default;
|
||||
|
||||
AppletFrontendSet::AppletFrontendSet(AppletFrontendSet&&) noexcept = default;
|
||||
|
||||
AppletFrontendSet& AppletFrontendSet::operator=(AppletFrontendSet&&) noexcept = default;
|
||||
|
||||
AppletManager::AppletManager() = default;
|
||||
|
||||
AppletManager::~AppletManager() = default;
|
||||
|
||||
@@ -137,11 +137,28 @@ protected:
|
||||
};
|
||||
|
||||
struct AppletFrontendSet {
|
||||
std::unique_ptr<Core::Frontend::ErrorApplet> error;
|
||||
std::unique_ptr<Core::Frontend::PhotoViewerApplet> photo_viewer;
|
||||
std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_select;
|
||||
std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
|
||||
std::unique_ptr<Core::Frontend::WebBrowserApplet> web_browser;
|
||||
using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
|
||||
using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
|
||||
using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>;
|
||||
using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>;
|
||||
using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>;
|
||||
|
||||
AppletFrontendSet();
|
||||
AppletFrontendSet(ErrorApplet error, PhotoViewer photo_viewer, ProfileSelect profile_select,
|
||||
SoftwareKeyboard software_keyboard, WebBrowser web_browser);
|
||||
~AppletFrontendSet();
|
||||
|
||||
AppletFrontendSet(const AppletFrontendSet&) = delete;
|
||||
AppletFrontendSet& operator=(const AppletFrontendSet&) = delete;
|
||||
|
||||
AppletFrontendSet(AppletFrontendSet&&) noexcept;
|
||||
AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept;
|
||||
|
||||
ErrorApplet error;
|
||||
PhotoViewer photo_viewer;
|
||||
ProfileSelect profile_select;
|
||||
SoftwareKeyboard software_keyboard;
|
||||
WebBrowser web_browser;
|
||||
};
|
||||
|
||||
class AppletManager {
|
||||
|
||||
@@ -53,19 +53,19 @@ void ProfileSelect::Execute() {
|
||||
return;
|
||||
}
|
||||
|
||||
frontend.SelectProfile([this](std::optional<Account::UUID> uuid) { SelectionComplete(uuid); });
|
||||
frontend.SelectProfile([this](std::optional<Common::UUID> uuid) { SelectionComplete(uuid); });
|
||||
}
|
||||
|
||||
void ProfileSelect::SelectionComplete(std::optional<Account::UUID> uuid) {
|
||||
void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) {
|
||||
UserSelectionOutput output{};
|
||||
|
||||
if (uuid.has_value() && uuid->uuid != Account::INVALID_UUID) {
|
||||
if (uuid.has_value() && uuid->uuid != Common::INVALID_UUID) {
|
||||
output.result = 0;
|
||||
output.uuid_selected = uuid->uuid;
|
||||
} else {
|
||||
status = ERR_USER_CANCELLED_SELECTION;
|
||||
output.result = ERR_USER_CANCELLED_SELECTION.raw;
|
||||
output.uuid_selected = Account::INVALID_UUID;
|
||||
output.uuid_selected = Common::INVALID_UUID;
|
||||
}
|
||||
|
||||
final_data = std::vector<u8>(sizeof(UserSelectionOutput));
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "common/uuid.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
@@ -38,7 +39,7 @@ public:
|
||||
void ExecuteInteractive() override;
|
||||
void Execute() override;
|
||||
|
||||
void SelectionComplete(std::optional<Account::UUID> uuid);
|
||||
void SelectionComplete(std::optional<Common::UUID> uuid);
|
||||
|
||||
private:
|
||||
const Core::Frontend::ProfileSelectApplet& frontend;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/partition_filesystem.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
@@ -18,7 +17,6 @@
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/aoc/aoc_u.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
@@ -75,7 +73,15 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs
|
||||
AOC_U::~AOC_U() = default;
|
||||
|
||||
void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AOC, "called");
|
||||
struct Parameters {
|
||||
u64 process_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 8);
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto params = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_DEBUG(Service_AOC, "called. process_id={}", params.process_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -94,23 +100,32 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
u32 offset;
|
||||
u32 count;
|
||||
u64 process_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 16);
|
||||
|
||||
const auto offset = rp.PopRaw<u32>();
|
||||
auto count = rp.PopRaw<u32>();
|
||||
LOG_DEBUG(Service_AOC, "called with offset={}, count={}", offset, count);
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto [offset, count, process_id] = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
|
||||
process_id);
|
||||
|
||||
const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID();
|
||||
|
||||
std::vector<u32> out;
|
||||
for (size_t i = 0; i < add_on_content.size(); ++i) {
|
||||
if ((add_on_content[i] & DLC_BASE_TITLE_ID_MASK) == current)
|
||||
out.push_back(static_cast<u32>(add_on_content[i] & 0x7FF));
|
||||
}
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[current];
|
||||
if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end())
|
||||
out = {};
|
||||
if (std::find(disabled.begin(), disabled.end(), "DLC") == disabled.end()) {
|
||||
for (u64 content_id : add_on_content) {
|
||||
if ((content_id & DLC_BASE_TITLE_ID_MASK) != current) {
|
||||
continue;
|
||||
}
|
||||
|
||||
out.push_back(static_cast<u32>(content_id & 0x7FF));
|
||||
}
|
||||
}
|
||||
|
||||
if (out.size() < offset) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -119,22 +134,31 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
|
||||
const auto out_count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
|
||||
std::rotate(out.begin(), out.begin() + offset, out.end());
|
||||
out.resize(count);
|
||||
out.resize(out_count);
|
||||
|
||||
ctx.WriteBuffer(out);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(count);
|
||||
rb.Push(out_count);
|
||||
}
|
||||
|
||||
void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AOC, "called");
|
||||
struct Parameters {
|
||||
u64 process_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 8);
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto params = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_DEBUG(Service_AOC, "called. process_id={}", params.process_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
|
||||
FileSys::PatchManager pm{title_id};
|
||||
|
||||
@@ -148,10 +172,17 @@ void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
s32 addon_index;
|
||||
u64 process_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 16);
|
||||
|
||||
const auto aoc_id = rp.PopRaw<u32>();
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called with aoc_id={:08X}", aoc_id);
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto [addon_index, process_id] = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called with addon_index={}, process_id={}", addon_index,
|
||||
process_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "audio_core/audio_renderer.h"
|
||||
#include "common/alignment.h"
|
||||
#include "common/bit_util.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
@@ -262,64 +263,304 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
|
||||
OpenAudioRendererImpl(ctx);
|
||||
}
|
||||
|
||||
static u64 CalculateNumPerformanceEntries(const AudioCore::AudioRendererParameter& params) {
|
||||
// +1 represents the final mix.
|
||||
return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count +
|
||||
1;
|
||||
}
|
||||
|
||||
void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
u64 buffer_sz = Common::AlignUp(4 * params.mix_buffer_count, 0x40);
|
||||
buffer_sz += params.submix_count * 1024;
|
||||
buffer_sz += 0x940 * (params.submix_count + 1);
|
||||
buffer_sz += 0x3F0 * params.voice_count;
|
||||
buffer_sz += Common::AlignUp(8 * (params.submix_count + 1), 0x10);
|
||||
buffer_sz += Common::AlignUp(8 * params.voice_count, 0x10);
|
||||
buffer_sz += Common::AlignUp(
|
||||
(0x3C0 * (params.sink_count + params.submix_count) + 4 * params.sample_count) *
|
||||
(params.mix_buffer_count + 6),
|
||||
0x40);
|
||||
// Several calculations below align the sizes being calculated
|
||||
// onto a 64 byte boundary.
|
||||
static constexpr u64 buffer_alignment_size = 64;
|
||||
|
||||
if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
|
||||
const u32 count = params.submix_count + 1;
|
||||
u64 node_count = Common::AlignUp(count, 0x40);
|
||||
const u64 node_state_buffer_sz =
|
||||
4 * (node_count * node_count) + 0xC * node_count + 2 * (node_count / 8);
|
||||
u64 edge_matrix_buffer_sz = 0;
|
||||
node_count = Common::AlignUp(count * count, 0x40);
|
||||
if (node_count >> 31 != 0) {
|
||||
edge_matrix_buffer_sz = (node_count | 7) / 8;
|
||||
} else {
|
||||
edge_matrix_buffer_sz = node_count / 8;
|
||||
// Some calculations that calculate portions of the buffer
|
||||
// that will contain information, on the other hand, align
|
||||
// the result of some of their calcularions on a 16 byte boundary.
|
||||
static constexpr u64 info_field_alignment_size = 16;
|
||||
|
||||
// Maximum detail entries that may exist at one time for performance
|
||||
// frame statistics.
|
||||
static constexpr u64 max_perf_detail_entries = 100;
|
||||
|
||||
// Size of the data structure representing the bulk of the voice-related state.
|
||||
static constexpr u64 voice_state_size = 0x100;
|
||||
|
||||
// Size of the upsampler manager data structure
|
||||
constexpr u64 upsampler_manager_size = 0x48;
|
||||
|
||||
// Calculates the part of the size that relates to mix buffers.
|
||||
const auto calculate_mix_buffer_sizes = [](const AudioCore::AudioRendererParameter& params) {
|
||||
// As of 8.0.0 this is the maximum on voice channels.
|
||||
constexpr u64 max_voice_channels = 6;
|
||||
|
||||
// The service expects the sample_count member of the parameters to either be
|
||||
// a value of 160 or 240, so the maximum sample count is assumed in order
|
||||
// to adequately handle all values at runtime.
|
||||
constexpr u64 default_max_sample_count = 240;
|
||||
|
||||
const u64 total_mix_buffers = params.mix_buffer_count + max_voice_channels;
|
||||
|
||||
u64 size = 0;
|
||||
size += total_mix_buffers * (sizeof(s32) * params.sample_count);
|
||||
size += total_mix_buffers * (sizeof(s32) * default_max_sample_count);
|
||||
size += u64{params.submix_count} + params.sink_count;
|
||||
size = Common::AlignUp(size, buffer_alignment_size);
|
||||
size += Common::AlignUp(params.unknown_30, buffer_alignment_size);
|
||||
size += Common::AlignUp(sizeof(s32) * params.mix_buffer_count, buffer_alignment_size);
|
||||
return size;
|
||||
};
|
||||
|
||||
// Calculates the portion of the size related to the mix data (and the sorting thereof).
|
||||
const auto calculate_mix_info_size = [this](const AudioCore::AudioRendererParameter& params) {
|
||||
// The size of the mixing info data structure.
|
||||
constexpr u64 mix_info_size = 0x940;
|
||||
|
||||
// Consists of total submixes with the final mix included.
|
||||
const u64 total_mix_count = u64{params.submix_count} + 1;
|
||||
|
||||
// The total number of effects that may be available to the audio renderer at any time.
|
||||
constexpr u64 max_effects = 256;
|
||||
|
||||
// Calculates the part of the size related to the audio node state.
|
||||
// This will only be used if the audio revision supports the splitter.
|
||||
const auto calculate_node_state_size = [](std::size_t num_nodes) {
|
||||
// Internally within a nodestate, it appears to use a data structure
|
||||
// similar to a std::bitset<64> twice.
|
||||
constexpr u64 bit_size = Common::BitSize<u64>();
|
||||
constexpr u64 num_bitsets = 2;
|
||||
|
||||
// Node state instances have three states internally for performing
|
||||
// depth-first searches of nodes. Initialized, Found, and Done Sorting.
|
||||
constexpr u64 num_states = 3;
|
||||
|
||||
u64 size = 0;
|
||||
size += (num_nodes * num_nodes) * sizeof(s32);
|
||||
size += num_states * (num_nodes * sizeof(s32));
|
||||
size += num_bitsets * (Common::AlignUp(num_nodes, bit_size) / Common::BitSize<u8>());
|
||||
return size;
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to the adjacency (aka edge) matrix.
|
||||
const auto calculate_edge_matrix_size = [](std::size_t num_nodes) {
|
||||
return (num_nodes * num_nodes) * sizeof(s32);
|
||||
};
|
||||
|
||||
u64 size = 0;
|
||||
size += Common::AlignUp(sizeof(void*) * total_mix_count, info_field_alignment_size);
|
||||
size += Common::AlignUp(mix_info_size * total_mix_count, info_field_alignment_size);
|
||||
size += Common::AlignUp(sizeof(s32) * max_effects * params.submix_count,
|
||||
info_field_alignment_size);
|
||||
|
||||
if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
|
||||
size += Common::AlignUp(calculate_node_state_size(total_mix_count) +
|
||||
calculate_edge_matrix_size(total_mix_count),
|
||||
info_field_alignment_size);
|
||||
}
|
||||
buffer_sz += Common::AlignUp(node_state_buffer_sz + edge_matrix_buffer_sz, 0x10);
|
||||
}
|
||||
|
||||
buffer_sz += 0x20 * (params.effect_count + 4 * params.voice_count) + 0x50;
|
||||
if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
|
||||
buffer_sz += 0xE0 * params.num_splitter_send_channels;
|
||||
buffer_sz += 0x20 * params.splitter_count;
|
||||
buffer_sz += Common::AlignUp(4 * params.num_splitter_send_channels, 0x10);
|
||||
}
|
||||
buffer_sz = Common::AlignUp(buffer_sz, 0x40) + 0x170 * params.sink_count;
|
||||
u64 output_sz = buffer_sz + 0x280 * params.sink_count + 0x4B0 * params.effect_count +
|
||||
((params.voice_count * 256) | 0x40);
|
||||
return size;
|
||||
};
|
||||
|
||||
if (params.performance_frame_count >= 1) {
|
||||
output_sz = Common::AlignUp(((16 * params.sink_count + 16 * params.effect_count +
|
||||
16 * params.voice_count + 16) +
|
||||
0x658) *
|
||||
(params.performance_frame_count + 1) +
|
||||
0xc0,
|
||||
0x40) +
|
||||
output_sz;
|
||||
}
|
||||
output_sz = Common::AlignUp(output_sz + 0x1807e, 0x1000);
|
||||
// Calculates the part of the size related to voice channel info.
|
||||
const auto calculate_voice_info_size = [](const AudioCore::AudioRendererParameter& params) {
|
||||
constexpr u64 voice_info_size = 0x220;
|
||||
constexpr u64 voice_resource_size = 0xD0;
|
||||
|
||||
u64 size = 0;
|
||||
size += Common::AlignUp(sizeof(void*) * params.voice_count, info_field_alignment_size);
|
||||
size += Common::AlignUp(voice_info_size * params.voice_count, info_field_alignment_size);
|
||||
size +=
|
||||
Common::AlignUp(voice_resource_size * params.voice_count, info_field_alignment_size);
|
||||
size += Common::AlignUp(voice_state_size * params.voice_count, info_field_alignment_size);
|
||||
return size;
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to memory pools.
|
||||
const auto calculate_memory_pools_size = [](const AudioCore::AudioRendererParameter& params) {
|
||||
const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count);
|
||||
const u64 memory_pool_info_size = 0x20;
|
||||
return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size);
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to the splitter context.
|
||||
const auto calculate_splitter_context_size =
|
||||
[this](const AudioCore::AudioRendererParameter& params) -> u64 {
|
||||
if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
constexpr u64 splitter_info_size = 0x20;
|
||||
constexpr u64 splitter_destination_data_size = 0xE0;
|
||||
|
||||
u64 size = 0;
|
||||
size += params.num_splitter_send_channels;
|
||||
size +=
|
||||
Common::AlignUp(splitter_info_size * params.splitter_count, info_field_alignment_size);
|
||||
size += Common::AlignUp(splitter_destination_data_size * params.num_splitter_send_channels,
|
||||
info_field_alignment_size);
|
||||
|
||||
return size;
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to the upsampler info.
|
||||
const auto calculate_upsampler_info_size = [](const AudioCore::AudioRendererParameter& params) {
|
||||
constexpr u64 upsampler_info_size = 0x280;
|
||||
// Yes, using the buffer size over info alignment size is intentional here.
|
||||
return Common::AlignUp(upsampler_info_size * (u64{params.submix_count} + params.sink_count),
|
||||
buffer_alignment_size);
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to effect info.
|
||||
const auto calculate_effect_info_size = [](const AudioCore::AudioRendererParameter& params) {
|
||||
constexpr u64 effect_info_size = 0x2B0;
|
||||
return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size);
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to audio sink info.
|
||||
const auto calculate_sink_info_size = [](const AudioCore::AudioRendererParameter& params) {
|
||||
const u64 sink_info_size = 0x170;
|
||||
return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size);
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to voice state info.
|
||||
const auto calculate_voice_state_size = [](const AudioCore::AudioRendererParameter& params) {
|
||||
const u64 voice_state_size = 0x100;
|
||||
const u64 additional_size = buffer_alignment_size - 1;
|
||||
return Common::AlignUp(voice_state_size * params.voice_count + additional_size,
|
||||
info_field_alignment_size);
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to performance statistics.
|
||||
const auto calculate_perf_size = [this](const AudioCore::AudioRendererParameter& params) {
|
||||
// Extra size value appended to the end of the calculation.
|
||||
constexpr u64 appended = 128;
|
||||
|
||||
// Whether or not we assume the newer version of performance metrics data structures.
|
||||
const bool is_v2 =
|
||||
IsFeatureSupported(AudioFeatures::PerformanceMetricsVersion2, params.revision);
|
||||
|
||||
// Data structure sizes
|
||||
constexpr u64 perf_statistics_size = 0x0C;
|
||||
const u64 header_size = is_v2 ? 0x30 : 0x18;
|
||||
const u64 entry_size = is_v2 ? 0x18 : 0x10;
|
||||
const u64 detail_size = is_v2 ? 0x18 : 0x10;
|
||||
|
||||
const u64 entry_count = CalculateNumPerformanceEntries(params);
|
||||
const u64 size_per_frame =
|
||||
header_size + (entry_size * entry_count) + (detail_size * max_perf_detail_entries);
|
||||
|
||||
u64 size = 0;
|
||||
size += Common::AlignUp(size_per_frame * params.performance_frame_count + 1,
|
||||
buffer_alignment_size);
|
||||
size += Common::AlignUp(perf_statistics_size, buffer_alignment_size);
|
||||
size += appended;
|
||||
return size;
|
||||
};
|
||||
|
||||
// Calculates the part of the size that relates to the audio command buffer.
|
||||
const auto calculate_command_buffer_size =
|
||||
[this](const AudioCore::AudioRendererParameter& params) {
|
||||
constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
|
||||
|
||||
if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
|
||||
constexpr u64 command_buffer_size = 0x18000;
|
||||
|
||||
return command_buffer_size + alignment;
|
||||
}
|
||||
|
||||
// When the variadic command buffer is supported, this means
|
||||
// the command generator for the audio renderer can issue commands
|
||||
// that are (as one would expect), variable in size. So what we need to do
|
||||
// is determine the maximum possible size for a few command data structures
|
||||
// then multiply them by the amount of present commands indicated by the given
|
||||
// respective audio parameters.
|
||||
|
||||
constexpr u64 max_biquad_filters = 2;
|
||||
constexpr u64 max_mix_buffers = 24;
|
||||
|
||||
constexpr u64 biquad_filter_command_size = 0x2C;
|
||||
|
||||
constexpr u64 depop_mix_command_size = 0x24;
|
||||
constexpr u64 depop_setup_command_size = 0x50;
|
||||
|
||||
constexpr u64 effect_command_max_size = 0x540;
|
||||
|
||||
constexpr u64 mix_command_size = 0x1C;
|
||||
constexpr u64 mix_ramp_command_size = 0x24;
|
||||
constexpr u64 mix_ramp_grouped_command_size = 0x13C;
|
||||
|
||||
constexpr u64 perf_command_size = 0x28;
|
||||
|
||||
constexpr u64 sink_command_size = 0x130;
|
||||
|
||||
constexpr u64 submix_command_max_size =
|
||||
depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
|
||||
|
||||
constexpr u64 volume_command_size = 0x1C;
|
||||
constexpr u64 volume_ramp_command_size = 0x20;
|
||||
|
||||
constexpr u64 voice_biquad_filter_command_size =
|
||||
biquad_filter_command_size * max_biquad_filters;
|
||||
constexpr u64 voice_data_command_size = 0x9C;
|
||||
const u64 voice_command_max_size =
|
||||
(params.splitter_count * depop_setup_command_size) +
|
||||
(voice_data_command_size + voice_biquad_filter_command_size +
|
||||
volume_ramp_command_size + mix_ramp_grouped_command_size);
|
||||
|
||||
// Now calculate the individual elements that comprise the size and add them together.
|
||||
const u64 effect_commands_size = params.effect_count * effect_command_max_size;
|
||||
|
||||
const u64 final_mix_commands_size =
|
||||
depop_mix_command_size + volume_command_size * max_mix_buffers;
|
||||
|
||||
const u64 perf_commands_size =
|
||||
perf_command_size *
|
||||
(CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
|
||||
|
||||
const u64 sink_commands_size = params.sink_count * sink_command_size;
|
||||
|
||||
const u64 splitter_commands_size =
|
||||
params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size;
|
||||
|
||||
const u64 submix_commands_size = params.submix_count * submix_command_max_size;
|
||||
|
||||
const u64 voice_commands_size = params.voice_count * voice_command_max_size;
|
||||
|
||||
return effect_commands_size + final_mix_commands_size + perf_commands_size +
|
||||
sink_commands_size + splitter_commands_size + submix_commands_size +
|
||||
voice_commands_size + alignment;
|
||||
};
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
|
||||
|
||||
u64 size = 0;
|
||||
size += calculate_mix_buffer_sizes(params);
|
||||
size += calculate_mix_info_size(params);
|
||||
size += calculate_voice_info_size(params);
|
||||
size += upsampler_manager_size;
|
||||
size += calculate_memory_pools_size(params);
|
||||
size += calculate_splitter_context_size(params);
|
||||
|
||||
size = Common::AlignUp(size, buffer_alignment_size);
|
||||
|
||||
size += calculate_upsampler_info_size(params);
|
||||
size += calculate_effect_info_size(params);
|
||||
size += calculate_sink_info_size(params);
|
||||
size += calculate_voice_state_size(params);
|
||||
size += calculate_perf_size(params);
|
||||
size += calculate_command_buffer_size(params);
|
||||
|
||||
// finally, 4KB page align the size, and we're done.
|
||||
size = Common::AlignUp(size, 4096);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(output_sz);
|
||||
rb.Push<u64>(size);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", output_sz);
|
||||
LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", size);
|
||||
}
|
||||
|
||||
void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) {
|
||||
@@ -357,10 +598,15 @@ void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const {
|
||||
u32_be version_num = (revision - Common::MakeMagic('R', 'E', 'V', '0')); // Byte swap
|
||||
// Byte swap
|
||||
const u32_be version_num = revision - Common::MakeMagic('R', 'E', 'V', '0');
|
||||
|
||||
switch (feature) {
|
||||
case AudioFeatures::Splitter:
|
||||
return version_num >= 2u;
|
||||
return version_num >= 2U;
|
||||
case AudioFeatures::PerformanceMetricsVersion2:
|
||||
case AudioFeatures::VariadicCommandBuffer:
|
||||
return version_num >= 5U;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ private:
|
||||
|
||||
enum class AudioFeatures : u32 {
|
||||
Splitter,
|
||||
PerformanceMetricsVersion2,
|
||||
VariadicCommandBuffer,
|
||||
};
|
||||
|
||||
bool IsFeatureSupported(AudioFeatures feature, u32_le revision) const;
|
||||
|
||||
@@ -4,42 +4,50 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/mii/mii.h"
|
||||
#include "core/hle/service/mii/mii_manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::Mii {
|
||||
|
||||
constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1};
|
||||
constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
|
||||
constexpr ResultCode ERROR_NOT_IN_TEST_MODE{ErrorModule::Mii, 99};
|
||||
|
||||
class IDatabaseService final : public ServiceFramework<IDatabaseService> {
|
||||
public:
|
||||
explicit IDatabaseService() : ServiceFramework{"IDatabaseService"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "IsUpdated"},
|
||||
{1, nullptr, "IsFullDatabase"},
|
||||
{2, nullptr, "GetCount"},
|
||||
{3, nullptr, "Get"},
|
||||
{4, nullptr, "Get1"},
|
||||
{0, &IDatabaseService::IsUpdated, "IsUpdated"},
|
||||
{1, &IDatabaseService::IsFullDatabase, "IsFullDatabase"},
|
||||
{2, &IDatabaseService::GetCount, "GetCount"},
|
||||
{3, &IDatabaseService::Get, "Get"},
|
||||
{4, &IDatabaseService::Get1, "Get1"},
|
||||
{5, nullptr, "UpdateLatest"},
|
||||
{6, nullptr, "BuildRandom"},
|
||||
{7, nullptr, "BuildDefault"},
|
||||
{8, nullptr, "Get2"},
|
||||
{9, nullptr, "Get3"},
|
||||
{6, &IDatabaseService::BuildRandom, "BuildRandom"},
|
||||
{7, &IDatabaseService::BuildDefault, "BuildDefault"},
|
||||
{8, &IDatabaseService::Get2, "Get2"},
|
||||
{9, &IDatabaseService::Get3, "Get3"},
|
||||
{10, nullptr, "UpdateLatest1"},
|
||||
{11, nullptr, "FindIndex"},
|
||||
{12, nullptr, "Move"},
|
||||
{13, nullptr, "AddOrReplace"},
|
||||
{14, nullptr, "Delete"},
|
||||
{15, nullptr, "DestroyFile"},
|
||||
{16, nullptr, "DeleteFile"},
|
||||
{17, nullptr, "Format"},
|
||||
{11, &IDatabaseService::FindIndex, "FindIndex"},
|
||||
{12, &IDatabaseService::Move, "Move"},
|
||||
{13, &IDatabaseService::AddOrReplace, "AddOrReplace"},
|
||||
{14, &IDatabaseService::Delete, "Delete"},
|
||||
{15, &IDatabaseService::DestroyFile, "DestroyFile"},
|
||||
{16, &IDatabaseService::DeleteFile, "DeleteFile"},
|
||||
{17, &IDatabaseService::Format, "Format"},
|
||||
{18, nullptr, "Import"},
|
||||
{19, nullptr, "Export"},
|
||||
{20, nullptr, "IsBrokenDatabaseWithClearFlag"},
|
||||
{21, nullptr, "GetIndex"},
|
||||
{21, &IDatabaseService::GetIndex, "GetIndex"},
|
||||
{22, nullptr, "SetInterfaceVersion"},
|
||||
{23, nullptr, "Convert"},
|
||||
};
|
||||
@@ -47,6 +55,305 @@ public:
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename OutType>
|
||||
std::vector<u8> SerializeArray(OutType (MiiManager::*getter)(u32) const, u32 offset,
|
||||
u32 requested_size, u32& read_size) {
|
||||
read_size = std::min(requested_size, db.Size() - offset);
|
||||
|
||||
std::vector<u8> out(read_size * sizeof(OutType));
|
||||
|
||||
for (u32 i = 0; i < read_size; ++i) {
|
||||
const auto obj = (db.*getter)(offset + i);
|
||||
std::memcpy(out.data() + i * sizeof(OutType), &obj, sizeof(OutType));
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void IsUpdated(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto source{rp.PopRaw<Source>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source={}", source);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(db.CheckUpdatedFlag());
|
||||
db.ResetUpdatedFlag();
|
||||
}
|
||||
|
||||
void IsFullDatabase(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Mii, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(db.Full());
|
||||
}
|
||||
|
||||
void GetCount(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto source{rp.PopRaw<Source>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source={}", source);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(db.Size());
|
||||
}
|
||||
|
||||
// Gets Miis from database at offset and index in format MiiInfoElement
|
||||
void Get(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto size{rp.PopRaw<u32>()};
|
||||
const auto source{rp.PopRaw<Source>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
|
||||
offsets[0], source);
|
||||
|
||||
u32 read_size{};
|
||||
ctx.WriteBuffer(SerializeArray(&MiiManager::GetInfoElement, offsets[0], size, read_size));
|
||||
offsets[0] += read_size;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(read_size);
|
||||
}
|
||||
|
||||
// Gets Miis from database at offset and index in format MiiInfo
|
||||
void Get1(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto size{rp.PopRaw<u32>()};
|
||||
const auto source{rp.PopRaw<Source>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
|
||||
offsets[1], source);
|
||||
|
||||
u32 read_size{};
|
||||
ctx.WriteBuffer(SerializeArray(&MiiManager::GetInfo, offsets[1], size, read_size));
|
||||
offsets[1] += read_size;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(read_size);
|
||||
}
|
||||
|
||||
void BuildRandom(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto [unknown1, unknown2, unknown3] = rp.PopRaw<RandomParameters>();
|
||||
|
||||
if (unknown1 > 3) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||
LOG_ERROR(Service_Mii, "Invalid unknown1 value: {}", unknown1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (unknown2 > 2) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||
LOG_ERROR(Service_Mii, "Invalid unknown2 value: {}", unknown2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (unknown3 > 3) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||
LOG_ERROR(Service_Mii, "Invalid unknown3 value: {}", unknown3);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with param_1={:08X}, param_2={:08X}, param_3={:08X}",
|
||||
unknown1, unknown2, unknown3);
|
||||
|
||||
const auto info = db.CreateRandom({unknown1, unknown2, unknown3});
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<MiiInfo>(info);
|
||||
}
|
||||
|
||||
void BuildDefault(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto index{rp.PopRaw<u32>()};
|
||||
|
||||
if (index > 5) {
|
||||
LOG_ERROR(Service_Mii, "invalid argument, index cannot be greater than 5 but is {:08X}",
|
||||
index);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with index={:08X}", index);
|
||||
|
||||
const auto info = db.CreateDefault(index);
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<MiiInfo>(info);
|
||||
}
|
||||
|
||||
// Gets Miis from database at offset and index in format MiiStoreDataElement
|
||||
void Get2(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto size{rp.PopRaw<u32>()};
|
||||
const auto source{rp.PopRaw<Source>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
|
||||
offsets[2], source);
|
||||
|
||||
u32 read_size{};
|
||||
ctx.WriteBuffer(
|
||||
SerializeArray(&MiiManager::GetStoreDataElement, offsets[2], size, read_size));
|
||||
offsets[2] += read_size;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(read_size);
|
||||
}
|
||||
|
||||
// Gets Miis from database at offset and index in format MiiStoreData
|
||||
void Get3(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto size{rp.PopRaw<u32>()};
|
||||
const auto source{rp.PopRaw<Source>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
|
||||
offsets[3], source);
|
||||
|
||||
u32 read_size{};
|
||||
ctx.WriteBuffer(SerializeArray(&MiiManager::GetStoreData, offsets[3], size, read_size));
|
||||
offsets[3] += read_size;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(read_size);
|
||||
}
|
||||
|
||||
void FindIndex(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto uuid{rp.PopRaw<Common::UUID>()};
|
||||
const auto unknown{rp.PopRaw<bool>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with uuid={}, unknown={}", uuid.FormatSwitch(), unknown);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
||||
const auto index = db.IndexOf(uuid);
|
||||
if (index > MAX_MIIS) {
|
||||
// TODO(DarkLordZach): Find a better error code
|
||||
rb.Push(ResultCode(-1));
|
||||
rb.Push(index);
|
||||
} else {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(index);
|
||||
}
|
||||
}
|
||||
|
||||
void Move(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto uuid{rp.PopRaw<Common::UUID>()};
|
||||
const auto index{rp.PopRaw<s32>()};
|
||||
|
||||
if (index < 0) {
|
||||
LOG_ERROR(Service_Mii, "Index cannot be negative but is {:08X}!", index);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with uuid={}, index={:08X}", uuid.FormatSwitch(), index);
|
||||
|
||||
const auto success = db.Move(uuid, index);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(DarkLordZach): Find a better error code
|
||||
rb.Push(success ? RESULT_SUCCESS : ResultCode(-1));
|
||||
}
|
||||
|
||||
void AddOrReplace(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto data{rp.PopRaw<MiiStoreData>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with Mii data uuid={}, name={}", data.uuid.FormatSwitch(),
|
||||
Common::UTF16ToUTF8(data.Name()));
|
||||
|
||||
const auto success = db.AddOrReplace(data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(DarkLordZach): Find a better error code
|
||||
rb.Push(success ? RESULT_SUCCESS : ResultCode(-1));
|
||||
}
|
||||
|
||||
void Delete(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto uuid{rp.PopRaw<Common::UUID>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with uuid={}", uuid.FormatSwitch());
|
||||
|
||||
const auto success = db.Remove(uuid);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(success ? RESULT_SUCCESS : ERROR_CANNOT_FIND_ENTRY);
|
||||
}
|
||||
|
||||
void DestroyFile(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Mii, "called");
|
||||
|
||||
if (!db.IsTestModeEnabled()) {
|
||||
LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot destory database file.");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_NOT_IN_TEST_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(db.DestroyFile());
|
||||
}
|
||||
|
||||
void DeleteFile(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Mii, "called");
|
||||
|
||||
if (!db.IsTestModeEnabled()) {
|
||||
LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot delete database file.");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_NOT_IN_TEST_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(db.DeleteFile());
|
||||
}
|
||||
|
||||
void Format(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Mii, "called");
|
||||
|
||||
db.Clear();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetIndex(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto info{rp.PopRaw<MiiInfo>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with Mii info uuid={}, name={}", info.uuid.FormatSwitch(),
|
||||
Common::UTF16ToUTF8(info.Name()));
|
||||
|
||||
const auto index = db.IndexOf(info);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(index);
|
||||
}
|
||||
|
||||
MiiManager db;
|
||||
|
||||
// Last read offsets of Get functions
|
||||
std::array<u32, 4> offsets{};
|
||||
};
|
||||
|
||||
class MiiDBModule final : public ServiceFramework<MiiDBModule> {
|
||||
|
||||
416
src/core/hle/service/mii/mii_manager.cpp
Normal file
416
src/core/hle/service/mii/mii_manager.cpp
Normal file
@@ -0,0 +1,416 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include "common/assert.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/hle/service/mii/mii_manager.h"
|
||||
|
||||
namespace Service::Mii {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char MII_SAVE_DATABASE_PATH[] = "/system/save/8000000000000030/MiiDatabase.dat";
|
||||
constexpr std::array<char16_t, 11> DEFAULT_MII_NAME = {u'y', u'u', u'z', u'u', u'\0'};
|
||||
|
||||
// This value was retrieved from HW test
|
||||
constexpr MiiStoreData DEFAULT_MII = {
|
||||
{
|
||||
0x21, 0x40, 0x40, 0x01, 0x08, 0x01, 0x13, 0x08, 0x08, 0x02, 0x17, 0x8C, 0x06, 0x01,
|
||||
0x69, 0x6D, 0x8A, 0x6A, 0x82, 0x14, 0x00, 0x00, 0x00, 0x20, 0x64, 0x72, 0x44, 0x44,
|
||||
},
|
||||
{'y', 'u', 'z', 'u', '\0'},
|
||||
Common::UUID{1, 0},
|
||||
0,
|
||||
0,
|
||||
};
|
||||
|
||||
// Default values taken from multiple real databases
|
||||
const MiiDatabase DEFAULT_MII_DATABASE{Common::MakeMagic('N', 'F', 'D', 'B'), {}, {1}, 0, 0};
|
||||
|
||||
constexpr std::array<const char*, 4> SOURCE_NAMES{
|
||||
"Database",
|
||||
"Default",
|
||||
"Account",
|
||||
"Friend",
|
||||
};
|
||||
|
||||
template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize>
|
||||
std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) {
|
||||
std::array<T, DestArraySize> out{};
|
||||
std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize));
|
||||
return out;
|
||||
}
|
||||
|
||||
MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
|
||||
MiiStoreBitFields bf{};
|
||||
std::memcpy(&bf, data.data.data(), sizeof(MiiStoreBitFields));
|
||||
return {
|
||||
data.uuid,
|
||||
ResizeArray<char16_t, 10, 11>(data.name),
|
||||
static_cast<u8>(bf.font_region.Value()),
|
||||
static_cast<u8>(bf.favorite_color.Value()),
|
||||
static_cast<u8>(bf.gender.Value()),
|
||||
static_cast<u8>(bf.height.Value()),
|
||||
static_cast<u8>(bf.weight.Value()),
|
||||
static_cast<u8>(bf.mii_type.Value()),
|
||||
static_cast<u8>(bf.mii_region.Value()),
|
||||
static_cast<u8>(bf.face_type.Value()),
|
||||
static_cast<u8>(bf.face_color.Value()),
|
||||
static_cast<u8>(bf.face_wrinkle.Value()),
|
||||
static_cast<u8>(bf.face_makeup.Value()),
|
||||
static_cast<u8>(bf.hair_type.Value()),
|
||||
static_cast<u8>(bf.hair_color.Value()),
|
||||
static_cast<bool>(bf.hair_flip.Value()),
|
||||
static_cast<u8>(bf.eye_type.Value()),
|
||||
static_cast<u8>(bf.eye_color.Value()),
|
||||
static_cast<u8>(bf.eye_scale.Value()),
|
||||
static_cast<u8>(bf.eye_aspect.Value()),
|
||||
static_cast<u8>(bf.eye_rotate.Value()),
|
||||
static_cast<u8>(bf.eye_x.Value()),
|
||||
static_cast<u8>(bf.eye_y.Value()),
|
||||
static_cast<u8>(bf.eyebrow_type.Value()),
|
||||
static_cast<u8>(bf.eyebrow_color.Value()),
|
||||
static_cast<u8>(bf.eyebrow_scale.Value()),
|
||||
static_cast<u8>(bf.eyebrow_aspect.Value()),
|
||||
static_cast<u8>(bf.eyebrow_rotate.Value()),
|
||||
static_cast<u8>(bf.eyebrow_x.Value()),
|
||||
static_cast<u8>(bf.eyebrow_y.Value()),
|
||||
static_cast<u8>(bf.nose_type.Value()),
|
||||
static_cast<u8>(bf.nose_scale.Value()),
|
||||
static_cast<u8>(bf.nose_y.Value()),
|
||||
static_cast<u8>(bf.mouth_type.Value()),
|
||||
static_cast<u8>(bf.mouth_color.Value()),
|
||||
static_cast<u8>(bf.mouth_scale.Value()),
|
||||
static_cast<u8>(bf.mouth_aspect.Value()),
|
||||
static_cast<u8>(bf.mouth_y.Value()),
|
||||
static_cast<u8>(bf.facial_hair_color.Value()),
|
||||
static_cast<u8>(bf.beard_type.Value()),
|
||||
static_cast<u8>(bf.mustache_type.Value()),
|
||||
static_cast<u8>(bf.mustache_scale.Value()),
|
||||
static_cast<u8>(bf.mustache_y.Value()),
|
||||
static_cast<u8>(bf.glasses_type.Value()),
|
||||
static_cast<u8>(bf.glasses_color.Value()),
|
||||
static_cast<u8>(bf.glasses_scale.Value()),
|
||||
static_cast<u8>(bf.glasses_y.Value()),
|
||||
static_cast<u8>(bf.mole_type.Value()),
|
||||
static_cast<u8>(bf.mole_scale.Value()),
|
||||
static_cast<u8>(bf.mole_x.Value()),
|
||||
static_cast<u8>(bf.mole_y.Value()),
|
||||
0x00,
|
||||
};
|
||||
}
|
||||
MiiStoreData ConvertInfoToStoreData(const MiiInfo& info) {
|
||||
MiiStoreData out{};
|
||||
out.name = ResizeArray<char16_t, 11, 10>(info.name);
|
||||
out.uuid = info.uuid;
|
||||
|
||||
MiiStoreBitFields bf{};
|
||||
|
||||
bf.hair_type.Assign(info.hair_type);
|
||||
bf.mole_type.Assign(info.mole_type);
|
||||
bf.height.Assign(info.height);
|
||||
bf.hair_flip.Assign(info.hair_flip);
|
||||
bf.weight.Assign(info.weight);
|
||||
bf.hair_color.Assign(info.hair_color);
|
||||
|
||||
bf.gender.Assign(info.gender);
|
||||
bf.eye_color.Assign(info.eye_color);
|
||||
bf.eyebrow_color.Assign(info.eyebrow_color);
|
||||
bf.mouth_color.Assign(info.mouth_color);
|
||||
bf.facial_hair_color.Assign(info.facial_hair_color);
|
||||
|
||||
bf.mii_type.Assign(info.mii_type);
|
||||
bf.glasses_color.Assign(info.glasses_color);
|
||||
bf.font_region.Assign(info.font_region);
|
||||
bf.eye_type.Assign(info.eye_type);
|
||||
bf.mii_region.Assign(info.mii_region);
|
||||
bf.mouth_type.Assign(info.mouth_type);
|
||||
bf.glasses_scale.Assign(info.glasses_scale);
|
||||
bf.eye_y.Assign(info.eye_y);
|
||||
|
||||
bf.mustache_type.Assign(info.mustache_type);
|
||||
bf.eyebrow_type.Assign(info.eyebrow_type);
|
||||
bf.beard_type.Assign(info.beard_type);
|
||||
bf.nose_type.Assign(info.nose_type);
|
||||
bf.mouth_aspect.Assign(info.mouth_aspect_ratio);
|
||||
bf.nose_y.Assign(info.nose_y);
|
||||
bf.eyebrow_aspect.Assign(info.eyebrow_aspect_ratio);
|
||||
bf.mouth_y.Assign(info.mouth_y);
|
||||
|
||||
bf.eye_rotate.Assign(info.eye_rotate);
|
||||
bf.mustache_y.Assign(info.mustache_y);
|
||||
bf.eye_aspect.Assign(info.eye_aspect_ratio);
|
||||
bf.glasses_y.Assign(info.glasses_y);
|
||||
bf.eye_scale.Assign(info.eye_scale);
|
||||
bf.mole_x.Assign(info.mole_x);
|
||||
bf.mole_y.Assign(info.mole_y);
|
||||
|
||||
bf.glasses_type.Assign(info.glasses_type);
|
||||
bf.face_type.Assign(info.face_type);
|
||||
bf.favorite_color.Assign(info.favorite_color);
|
||||
bf.face_wrinkle.Assign(info.face_wrinkle);
|
||||
bf.face_color.Assign(info.face_color);
|
||||
bf.eye_x.Assign(info.eye_x);
|
||||
bf.face_makeup.Assign(info.face_makeup);
|
||||
|
||||
bf.eyebrow_rotate.Assign(info.eyebrow_rotate);
|
||||
bf.eyebrow_scale.Assign(info.eyebrow_scale);
|
||||
bf.eyebrow_y.Assign(info.eyebrow_y);
|
||||
bf.eyebrow_x.Assign(info.eyebrow_x);
|
||||
bf.mouth_scale.Assign(info.mouth_scale);
|
||||
bf.nose_scale.Assign(info.nose_scale);
|
||||
bf.mole_scale.Assign(info.mole_scale);
|
||||
bf.mustache_scale.Assign(info.mustache_scale);
|
||||
|
||||
std::memcpy(out.data.data(), &bf, sizeof(MiiStoreBitFields));
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, Source source) {
|
||||
os << SOURCE_NAMES.at(static_cast<std::size_t>(source));
|
||||
return os;
|
||||
}
|
||||
|
||||
std::u16string MiiInfo::Name() const {
|
||||
return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
|
||||
}
|
||||
|
||||
bool operator==(const MiiInfo& lhs, const MiiInfo& rhs) {
|
||||
return std::memcmp(&lhs, &rhs, sizeof(MiiInfo)) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
std::u16string MiiStoreData::Name() const {
|
||||
return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
|
||||
}
|
||||
|
||||
MiiManager::MiiManager() = default;
|
||||
|
||||
MiiManager::~MiiManager() = default;
|
||||
|
||||
MiiInfo MiiManager::CreateRandom(RandomParameters params) {
|
||||
LOG_WARNING(Service_Mii,
|
||||
"(STUBBED) called with params={:08X}{:08X}{:08X}, returning default Mii",
|
||||
params.unknown_1, params.unknown_2, params.unknown_3);
|
||||
|
||||
return ConvertStoreDataToInfo(CreateMiiWithUniqueUUID());
|
||||
}
|
||||
|
||||
MiiInfo MiiManager::CreateDefault(u32 index) {
|
||||
const auto new_mii = CreateMiiWithUniqueUUID();
|
||||
|
||||
database.miis.at(index) = new_mii;
|
||||
|
||||
EnsureDatabasePartition();
|
||||
return ConvertStoreDataToInfo(new_mii);
|
||||
}
|
||||
|
||||
bool MiiManager::CheckUpdatedFlag() const {
|
||||
return updated_flag;
|
||||
}
|
||||
|
||||
void MiiManager::ResetUpdatedFlag() {
|
||||
updated_flag = false;
|
||||
}
|
||||
|
||||
bool MiiManager::IsTestModeEnabled() const {
|
||||
return is_test_mode_enabled;
|
||||
}
|
||||
|
||||
bool MiiManager::Empty() const {
|
||||
return Size() == 0;
|
||||
}
|
||||
|
||||
bool MiiManager::Full() const {
|
||||
return Size() == MAX_MIIS;
|
||||
}
|
||||
|
||||
void MiiManager::Clear() {
|
||||
updated_flag = true;
|
||||
std::fill(database.miis.begin(), database.miis.end(), MiiStoreData{});
|
||||
}
|
||||
|
||||
u32 MiiManager::Size() const {
|
||||
return static_cast<u32>(std::count_if(database.miis.begin(), database.miis.end(),
|
||||
[](const MiiStoreData& elem) { return elem.uuid; }));
|
||||
}
|
||||
|
||||
MiiInfo MiiManager::GetInfo(u32 index) const {
|
||||
return ConvertStoreDataToInfo(GetStoreData(index));
|
||||
}
|
||||
|
||||
MiiInfoElement MiiManager::GetInfoElement(u32 index) const {
|
||||
return {GetInfo(index), Source::Database};
|
||||
}
|
||||
|
||||
MiiStoreData MiiManager::GetStoreData(u32 index) const {
|
||||
return database.miis.at(index);
|
||||
}
|
||||
|
||||
MiiStoreDataElement MiiManager::GetStoreDataElement(u32 index) const {
|
||||
return {GetStoreData(index), Source::Database};
|
||||
}
|
||||
|
||||
bool MiiManager::Remove(Common::UUID uuid) {
|
||||
const auto iter = std::find_if(database.miis.begin(), database.miis.end(),
|
||||
[uuid](const MiiStoreData& elem) { return elem.uuid == uuid; });
|
||||
|
||||
if (iter == database.miis.end())
|
||||
return false;
|
||||
|
||||
updated_flag = true;
|
||||
*iter = MiiStoreData{};
|
||||
EnsureDatabasePartition();
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 MiiManager::IndexOf(Common::UUID uuid) const {
|
||||
const auto iter = std::find_if(database.miis.begin(), database.miis.end(),
|
||||
[uuid](const MiiStoreData& elem) { return elem.uuid == uuid; });
|
||||
|
||||
if (iter == database.miis.end())
|
||||
return INVALID_INDEX;
|
||||
|
||||
return static_cast<u32>(std::distance(database.miis.begin(), iter));
|
||||
}
|
||||
|
||||
u32 MiiManager::IndexOf(const MiiInfo& info) const {
|
||||
const auto iter =
|
||||
std::find_if(database.miis.begin(), database.miis.end(), [&info](const MiiStoreData& elem) {
|
||||
return ConvertStoreDataToInfo(elem) == info;
|
||||
});
|
||||
|
||||
if (iter == database.miis.end())
|
||||
return INVALID_INDEX;
|
||||
|
||||
return static_cast<u32>(std::distance(database.miis.begin(), iter));
|
||||
}
|
||||
|
||||
bool MiiManager::Move(Common::UUID uuid, u32 new_index) {
|
||||
const auto index = IndexOf(uuid);
|
||||
|
||||
if (index == INVALID_INDEX || new_index >= MAX_MIIS)
|
||||
return false;
|
||||
|
||||
updated_flag = true;
|
||||
const auto moving = database.miis[index];
|
||||
const auto replacing = database.miis[new_index];
|
||||
if (replacing.uuid) {
|
||||
database.miis[index] = replacing;
|
||||
database.miis[new_index] = moving;
|
||||
} else {
|
||||
database.miis[index] = MiiStoreData{};
|
||||
database.miis[new_index] = moving;
|
||||
}
|
||||
|
||||
EnsureDatabasePartition();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MiiManager::AddOrReplace(const MiiStoreData& data) {
|
||||
const auto index = IndexOf(data.uuid);
|
||||
|
||||
updated_flag = true;
|
||||
if (index == INVALID_INDEX) {
|
||||
const auto size = Size();
|
||||
if (size == MAX_MIIS)
|
||||
return false;
|
||||
database.miis[size] = data;
|
||||
} else {
|
||||
database.miis[index] = data;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MiiManager::DestroyFile() {
|
||||
database = DEFAULT_MII_DATABASE;
|
||||
updated_flag = false;
|
||||
return DeleteFile();
|
||||
}
|
||||
|
||||
bool MiiManager::DeleteFile() {
|
||||
const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH;
|
||||
return FileUtil::Exists(path) && FileUtil::Delete(path);
|
||||
}
|
||||
|
||||
void MiiManager::WriteToFile() {
|
||||
const auto raw_path =
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000030";
|
||||
if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
|
||||
FileUtil::Delete(raw_path);
|
||||
|
||||
const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH;
|
||||
|
||||
if (!FileUtil::CreateFullPath(path)) {
|
||||
LOG_WARNING(Service_Mii,
|
||||
"Failed to create full path of MiiDatabase.dat. Create the directory "
|
||||
"nand/system/save/8000000000000030 to mitigate this "
|
||||
"issue.");
|
||||
return;
|
||||
}
|
||||
|
||||
FileUtil::IOFile save(path, "wb");
|
||||
|
||||
if (!save.IsOpen()) {
|
||||
LOG_WARNING(Service_Mii, "Failed to write save data to file... No changes to user data "
|
||||
"made in current session will be saved.");
|
||||
return;
|
||||
}
|
||||
|
||||
save.Resize(sizeof(MiiDatabase));
|
||||
if (save.WriteBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) {
|
||||
LOG_WARNING(Service_Mii, "Failed to write all data to save file... Data may be malformed "
|
||||
"and/or regenerated on next run.");
|
||||
save.Resize(0);
|
||||
}
|
||||
}
|
||||
|
||||
void MiiManager::ReadFromFile() {
|
||||
FileUtil::IOFile save(
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH, "rb");
|
||||
|
||||
if (!save.IsOpen()) {
|
||||
LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
|
||||
"blank Mii database with no Miis.");
|
||||
std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase));
|
||||
return;
|
||||
}
|
||||
|
||||
if (save.ReadBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) {
|
||||
LOG_WARNING(Service_ACC, "MiiDatabase.dat is smaller than expected... Generating new blank "
|
||||
"Mii database with no Miis.");
|
||||
std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase));
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureDatabasePartition();
|
||||
}
|
||||
|
||||
MiiStoreData MiiManager::CreateMiiWithUniqueUUID() const {
|
||||
auto new_mii = DEFAULT_MII;
|
||||
|
||||
do {
|
||||
new_mii.uuid = Common::UUID::Generate();
|
||||
} while (IndexOf(new_mii.uuid) != INVALID_INDEX);
|
||||
|
||||
return new_mii;
|
||||
}
|
||||
|
||||
void MiiManager::EnsureDatabasePartition() {
|
||||
std::stable_partition(database.miis.begin(), database.miis.end(),
|
||||
[](const MiiStoreData& elem) { return elem.uuid; });
|
||||
}
|
||||
|
||||
} // namespace Service::Mii
|
||||
273
src/core/hle/service/mii/mii_manager.h
Normal file
273
src/core/hle/service/mii/mii_manager.h
Normal file
@@ -0,0 +1,273 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/uuid.h"
|
||||
|
||||
namespace Service::Mii {
|
||||
|
||||
constexpr std::size_t MAX_MIIS = 100;
|
||||
constexpr u32 INVALID_INDEX = 0xFFFFFFFF;
|
||||
|
||||
struct RandomParameters {
|
||||
u32 unknown_1;
|
||||
u32 unknown_2;
|
||||
u32 unknown_3;
|
||||
};
|
||||
static_assert(sizeof(RandomParameters) == 0xC, "RandomParameters has incorrect size.");
|
||||
|
||||
enum class Source : u32 {
|
||||
Database = 0,
|
||||
Default = 1,
|
||||
Account = 2,
|
||||
Friend = 3,
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, Source source);
|
||||
|
||||
struct MiiInfo {
|
||||
Common::UUID uuid;
|
||||
std::array<char16_t, 11> name;
|
||||
u8 font_region;
|
||||
u8 favorite_color;
|
||||
u8 gender;
|
||||
u8 height;
|
||||
u8 weight;
|
||||
u8 mii_type;
|
||||
u8 mii_region;
|
||||
u8 face_type;
|
||||
u8 face_color;
|
||||
u8 face_wrinkle;
|
||||
u8 face_makeup;
|
||||
u8 hair_type;
|
||||
u8 hair_color;
|
||||
bool hair_flip;
|
||||
u8 eye_type;
|
||||
u8 eye_color;
|
||||
u8 eye_scale;
|
||||
u8 eye_aspect_ratio;
|
||||
u8 eye_rotate;
|
||||
u8 eye_x;
|
||||
u8 eye_y;
|
||||
u8 eyebrow_type;
|
||||
u8 eyebrow_color;
|
||||
u8 eyebrow_scale;
|
||||
u8 eyebrow_aspect_ratio;
|
||||
u8 eyebrow_rotate;
|
||||
u8 eyebrow_x;
|
||||
u8 eyebrow_y;
|
||||
u8 nose_type;
|
||||
u8 nose_scale;
|
||||
u8 nose_y;
|
||||
u8 mouth_type;
|
||||
u8 mouth_color;
|
||||
u8 mouth_scale;
|
||||
u8 mouth_aspect_ratio;
|
||||
u8 mouth_y;
|
||||
u8 facial_hair_color;
|
||||
u8 beard_type;
|
||||
u8 mustache_type;
|
||||
u8 mustache_scale;
|
||||
u8 mustache_y;
|
||||
u8 glasses_type;
|
||||
u8 glasses_color;
|
||||
u8 glasses_scale;
|
||||
u8 glasses_y;
|
||||
u8 mole_type;
|
||||
u8 mole_scale;
|
||||
u8 mole_x;
|
||||
u8 mole_y;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
|
||||
std::u16string Name() const;
|
||||
};
|
||||
static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
|
||||
static_assert(std::has_unique_object_representations_v<MiiInfo>,
|
||||
"All bits of MiiInfo must contribute to its value.");
|
||||
|
||||
bool operator==(const MiiInfo& lhs, const MiiInfo& rhs);
|
||||
bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs);
|
||||
|
||||
#pragma pack(push, 4)
|
||||
struct MiiInfoElement {
|
||||
MiiInfo info;
|
||||
Source source;
|
||||
};
|
||||
static_assert(sizeof(MiiInfoElement) == 0x5C, "MiiInfoElement has incorrect size.");
|
||||
|
||||
struct MiiStoreBitFields {
|
||||
union {
|
||||
u32 word_0;
|
||||
|
||||
BitField<24, 8, u32> hair_type;
|
||||
BitField<23, 1, u32> mole_type;
|
||||
BitField<16, 7, u32> height;
|
||||
BitField<15, 1, u32> hair_flip;
|
||||
BitField<8, 7, u32> weight;
|
||||
BitField<0, 7, u32> hair_color;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_1;
|
||||
|
||||
BitField<31, 1, u32> gender;
|
||||
BitField<24, 7, u32> eye_color;
|
||||
BitField<16, 7, u32> eyebrow_color;
|
||||
BitField<8, 7, u32> mouth_color;
|
||||
BitField<0, 7, u32> facial_hair_color;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_2;
|
||||
|
||||
BitField<31, 1, u32> mii_type;
|
||||
BitField<24, 7, u32> glasses_color;
|
||||
BitField<22, 2, u32> font_region;
|
||||
BitField<16, 6, u32> eye_type;
|
||||
BitField<14, 2, u32> mii_region;
|
||||
BitField<8, 6, u32> mouth_type;
|
||||
BitField<5, 3, u32> glasses_scale;
|
||||
BitField<0, 5, u32> eye_y;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_3;
|
||||
|
||||
BitField<29, 3, u32> mustache_type;
|
||||
BitField<24, 5, u32> eyebrow_type;
|
||||
BitField<21, 3, u32> beard_type;
|
||||
BitField<16, 5, u32> nose_type;
|
||||
BitField<13, 3, u32> mouth_aspect;
|
||||
BitField<8, 5, u32> nose_y;
|
||||
BitField<5, 3, u32> eyebrow_aspect;
|
||||
BitField<0, 5, u32> mouth_y;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_4;
|
||||
|
||||
BitField<29, 3, u32> eye_rotate;
|
||||
BitField<24, 5, u32> mustache_y;
|
||||
BitField<21, 3, u32> eye_aspect;
|
||||
BitField<16, 5, u32> glasses_y;
|
||||
BitField<13, 3, u32> eye_scale;
|
||||
BitField<8, 5, u32> mole_x;
|
||||
BitField<0, 5, u32> mole_y;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_5;
|
||||
|
||||
BitField<24, 5, u32> glasses_type;
|
||||
BitField<20, 4, u32> face_type;
|
||||
BitField<16, 4, u32> favorite_color;
|
||||
BitField<12, 4, u32> face_wrinkle;
|
||||
BitField<8, 4, u32> face_color;
|
||||
BitField<4, 4, u32> eye_x;
|
||||
BitField<0, 4, u32> face_makeup;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_6;
|
||||
|
||||
BitField<28, 4, u32> eyebrow_rotate;
|
||||
BitField<24, 4, u32> eyebrow_scale;
|
||||
BitField<20, 4, u32> eyebrow_y;
|
||||
BitField<16, 4, u32> eyebrow_x;
|
||||
BitField<12, 4, u32> mouth_scale;
|
||||
BitField<8, 4, u32> nose_scale;
|
||||
BitField<4, 4, u32> mole_scale;
|
||||
BitField<0, 4, u32> mustache_scale;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(MiiStoreBitFields) == 0x1C, "MiiStoreBitFields has incorrect size.");
|
||||
static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
|
||||
"MiiStoreBitFields is not trivially copyable.");
|
||||
|
||||
struct MiiStoreData {
|
||||
// This corresponds to the above structure MiiStoreBitFields. I did it like this because the
|
||||
// BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
|
||||
// not suitable for our uses.
|
||||
std::array<u8, 0x1C> data;
|
||||
static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
|
||||
|
||||
std::array<char16_t, 10> name;
|
||||
Common::UUID uuid;
|
||||
u16 crc_1;
|
||||
u16 crc_2;
|
||||
|
||||
std::u16string Name() const;
|
||||
};
|
||||
static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
|
||||
|
||||
struct MiiStoreDataElement {
|
||||
MiiStoreData data;
|
||||
Source source;
|
||||
};
|
||||
static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
|
||||
|
||||
struct MiiDatabase {
|
||||
u32 magic; // 'NFDB'
|
||||
std::array<MiiStoreData, MAX_MIIS> miis;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u8 count;
|
||||
u16 crc;
|
||||
};
|
||||
static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
|
||||
#pragma pack(pop)
|
||||
|
||||
// The Mii manager is responsible for loading and storing the Miis to the database in NAND along
|
||||
// with providing an easy interface for HLE emulation of the mii service.
|
||||
class MiiManager {
|
||||
public:
|
||||
MiiManager();
|
||||
~MiiManager();
|
||||
|
||||
MiiInfo CreateRandom(RandomParameters params);
|
||||
MiiInfo CreateDefault(u32 index);
|
||||
|
||||
bool CheckUpdatedFlag() const;
|
||||
void ResetUpdatedFlag();
|
||||
|
||||
bool IsTestModeEnabled() const;
|
||||
|
||||
bool Empty() const;
|
||||
bool Full() const;
|
||||
|
||||
void Clear();
|
||||
|
||||
u32 Size() const;
|
||||
|
||||
MiiInfo GetInfo(u32 index) const;
|
||||
MiiInfoElement GetInfoElement(u32 index) const;
|
||||
MiiStoreData GetStoreData(u32 index) const;
|
||||
MiiStoreDataElement GetStoreDataElement(u32 index) const;
|
||||
|
||||
bool Remove(Common::UUID uuid);
|
||||
u32 IndexOf(Common::UUID uuid) const;
|
||||
u32 IndexOf(const MiiInfo& info) const;
|
||||
|
||||
bool Move(Common::UUID uuid, u32 new_index);
|
||||
bool AddOrReplace(const MiiStoreData& data);
|
||||
|
||||
bool DestroyFile();
|
||||
bool DeleteFile();
|
||||
|
||||
private:
|
||||
void WriteToFile();
|
||||
void ReadFromFile();
|
||||
|
||||
MiiStoreData CreateMiiWithUniqueUUID() const;
|
||||
|
||||
void EnsureDatabasePartition();
|
||||
|
||||
MiiDatabase database;
|
||||
bool updated_flag = false;
|
||||
bool is_test_mode_enabled = false;
|
||||
};
|
||||
|
||||
}; // namespace Service::Mii
|
||||
@@ -4,15 +4,89 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/ncm/ncm.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::NCM {
|
||||
|
||||
class LocationResolver final : public ServiceFramework<LocationResolver> {
|
||||
class ILocationResolver final : public ServiceFramework<ILocationResolver> {
|
||||
public:
|
||||
explicit LocationResolver() : ServiceFramework{"lr"} {
|
||||
explicit ILocationResolver(FileSys::StorageId id)
|
||||
: ServiceFramework{"ILocationResolver"}, storage(id) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "ResolveProgramPath"},
|
||||
{1, nullptr, "RedirectProgramPath"},
|
||||
{2, nullptr, "ResolveApplicationControlPath"},
|
||||
{3, nullptr, "ResolveApplicationHtmlDocumentPath"},
|
||||
{4, nullptr, "ResolveDataPath"},
|
||||
{5, nullptr, "RedirectApplicationControlPath"},
|
||||
{6, nullptr, "RedirectApplicationHtmlDocumentPath"},
|
||||
{7, nullptr, "ResolveApplicationLegalInformationPath"},
|
||||
{8, nullptr, "RedirectApplicationLegalInformationPath"},
|
||||
{9, nullptr, "Refresh"},
|
||||
{10, nullptr, "RedirectProgramPath2"},
|
||||
{11, nullptr, "Refresh2"},
|
||||
{12, nullptr, "DeleteProgramPath"},
|
||||
{13, nullptr, "DeleteApplicationControlPath"},
|
||||
{14, nullptr, "DeleteApplicationHtmlDocumentPath"},
|
||||
{15, nullptr, "DeleteApplicationLegalInformationPath"},
|
||||
{16, nullptr, ""},
|
||||
{17, nullptr, ""},
|
||||
{18, nullptr, ""},
|
||||
{19, nullptr, ""},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
FileSys::StorageId storage;
|
||||
};
|
||||
|
||||
class IRegisteredLocationResolver final : public ServiceFramework<IRegisteredLocationResolver> {
|
||||
public:
|
||||
explicit IRegisteredLocationResolver() : ServiceFramework{"IRegisteredLocationResolver"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "ResolveProgramPath"},
|
||||
{1, nullptr, "RegisterProgramPath"},
|
||||
{2, nullptr, "UnregisterProgramPath"},
|
||||
{3, nullptr, "RedirectProgramPath"},
|
||||
{4, nullptr, "ResolveHtmlDocumentPath"},
|
||||
{5, nullptr, "RegisterHtmlDocumentPath"},
|
||||
{6, nullptr, "UnregisterHtmlDocumentPath"},
|
||||
{7, nullptr, "RedirectHtmlDocumentPath"},
|
||||
{8, nullptr, ""},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
class IAddOnContentLocationResolver final : public ServiceFramework<IAddOnContentLocationResolver> {
|
||||
public:
|
||||
explicit IAddOnContentLocationResolver() : ServiceFramework{"IAddOnContentLocationResolver"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "ResolveAddOnContentPath"},
|
||||
{1, nullptr, "RegisterAddOnContentStorage"},
|
||||
{2, nullptr, "UnregisterAllAddOnContentPath"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
class LR final : public ServiceFramework<LR> {
|
||||
public:
|
||||
explicit LR() : ServiceFramework{"lr"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "OpenLocationResolver"},
|
||||
@@ -52,7 +126,7 @@ public:
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm) {
|
||||
std::make_shared<LocationResolver>()->InstallAsService(sm);
|
||||
std::make_shared<LR>()->InstallAsService(sm);
|
||||
std::make_shared<NCM>()->InstallAsService(sm);
|
||||
}
|
||||
|
||||
|
||||
12
src/core/hle/service/ns/errors.h
Normal file
12
src/core/hle/service/ns/errors.h
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Service::NS {
|
||||
|
||||
constexpr ResultCode ERR_APPLICATION_LANGUAGE_NOT_FOUND{ErrorModule::NS, 300};
|
||||
}
|
||||
392
src/core/hle/service/ns/language.cpp
Normal file
392
src/core/hle/service/ns/language.cpp
Normal file
@@ -0,0 +1,392 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/ns/language.h"
|
||||
#include "core/hle/service/set/set.h"
|
||||
|
||||
namespace Service::NS {
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_american_english = {{
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_british_english = {{
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_japanese = {{
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_french = {{
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_german = {{
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_latin_american_spanish = {{
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_spanish = {{
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_italian = {{
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_dutch = {{
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_canadian_french = {{
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_portuguese = {{
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_russian = {{
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_korean = {{
|
||||
ApplicationLanguage::Korean,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_traditional_chinese = {{
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_simplified_chinese = {{
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(
|
||||
const ApplicationLanguage lang) {
|
||||
switch (lang) {
|
||||
case ApplicationLanguage::AmericanEnglish:
|
||||
return &priority_list_american_english;
|
||||
case ApplicationLanguage::BritishEnglish:
|
||||
return &priority_list_british_english;
|
||||
case ApplicationLanguage::Japanese:
|
||||
return &priority_list_japanese;
|
||||
case ApplicationLanguage::French:
|
||||
return &priority_list_french;
|
||||
case ApplicationLanguage::German:
|
||||
return &priority_list_german;
|
||||
case ApplicationLanguage::LatinAmericanSpanish:
|
||||
return &priority_list_latin_american_spanish;
|
||||
case ApplicationLanguage::Spanish:
|
||||
return &priority_list_spanish;
|
||||
case ApplicationLanguage::Italian:
|
||||
return &priority_list_italian;
|
||||
case ApplicationLanguage::Dutch:
|
||||
return &priority_list_dutch;
|
||||
case ApplicationLanguage::CanadianFrench:
|
||||
return &priority_list_canadian_french;
|
||||
case ApplicationLanguage::Portuguese:
|
||||
return &priority_list_portuguese;
|
||||
case ApplicationLanguage::Russian:
|
||||
return &priority_list_russian;
|
||||
case ApplicationLanguage::Korean:
|
||||
return &priority_list_korean;
|
||||
case ApplicationLanguage::TraditionalChinese:
|
||||
return &priority_list_traditional_chinese;
|
||||
case ApplicationLanguage::SimplifiedChinese:
|
||||
return &priority_list_simplified_chinese;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
|
||||
const Set::LanguageCode language_code) {
|
||||
switch (language_code) {
|
||||
case Set::LanguageCode::EN_US:
|
||||
return ApplicationLanguage::AmericanEnglish;
|
||||
case Set::LanguageCode::EN_GB:
|
||||
return ApplicationLanguage::BritishEnglish;
|
||||
case Set::LanguageCode::JA:
|
||||
return ApplicationLanguage::Japanese;
|
||||
case Set::LanguageCode::FR:
|
||||
return ApplicationLanguage::French;
|
||||
case Set::LanguageCode::DE:
|
||||
return ApplicationLanguage::German;
|
||||
case Set::LanguageCode::ES_419:
|
||||
return ApplicationLanguage::LatinAmericanSpanish;
|
||||
case Set::LanguageCode::ES:
|
||||
return ApplicationLanguage::Spanish;
|
||||
case Set::LanguageCode::IT:
|
||||
return ApplicationLanguage::Italian;
|
||||
case Set::LanguageCode::NL:
|
||||
return ApplicationLanguage::Dutch;
|
||||
case Set::LanguageCode::FR_CA:
|
||||
return ApplicationLanguage::CanadianFrench;
|
||||
case Set::LanguageCode::PT:
|
||||
return ApplicationLanguage::Portuguese;
|
||||
case Set::LanguageCode::RU:
|
||||
return ApplicationLanguage::Russian;
|
||||
case Set::LanguageCode::KO:
|
||||
return ApplicationLanguage::Korean;
|
||||
case Set::LanguageCode::ZH_HANT:
|
||||
return ApplicationLanguage::TraditionalChinese;
|
||||
case Set::LanguageCode::ZH_HANS:
|
||||
return ApplicationLanguage::SimplifiedChinese;
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Set::LanguageCode> ConvertToLanguageCode(const ApplicationLanguage lang) {
|
||||
switch (lang) {
|
||||
case ApplicationLanguage::AmericanEnglish:
|
||||
return Set::LanguageCode::EN_US;
|
||||
case ApplicationLanguage::BritishEnglish:
|
||||
return Set::LanguageCode::EN_GB;
|
||||
case ApplicationLanguage::Japanese:
|
||||
return Set::LanguageCode::JA;
|
||||
case ApplicationLanguage::French:
|
||||
return Set::LanguageCode::FR;
|
||||
case ApplicationLanguage::German:
|
||||
return Set::LanguageCode::DE;
|
||||
case ApplicationLanguage::LatinAmericanSpanish:
|
||||
return Set::LanguageCode::ES_419;
|
||||
case ApplicationLanguage::Spanish:
|
||||
return Set::LanguageCode::ES;
|
||||
case ApplicationLanguage::Italian:
|
||||
return Set::LanguageCode::IT;
|
||||
case ApplicationLanguage::Dutch:
|
||||
return Set::LanguageCode::NL;
|
||||
case ApplicationLanguage::CanadianFrench:
|
||||
return Set::LanguageCode::FR_CA;
|
||||
case ApplicationLanguage::Portuguese:
|
||||
return Set::LanguageCode::PT;
|
||||
case ApplicationLanguage::Russian:
|
||||
return Set::LanguageCode::RU;
|
||||
case ApplicationLanguage::Korean:
|
||||
return Set::LanguageCode::KO;
|
||||
case ApplicationLanguage::TraditionalChinese:
|
||||
return Set::LanguageCode::ZH_HANT;
|
||||
case ApplicationLanguage::SimplifiedChinese:
|
||||
return Set::LanguageCode::ZH_HANS;
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
} // namespace Service::NS
|
||||
45
src/core/hle/service/ns/language.h
Normal file
45
src/core/hle/service/ns/language.h
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::Set {
|
||||
enum class LanguageCode : u64;
|
||||
}
|
||||
|
||||
namespace Service::NS {
|
||||
/// This is nn::ns::detail::ApplicationLanguage
|
||||
enum class ApplicationLanguage : u8 {
|
||||
AmericanEnglish = 0,
|
||||
BritishEnglish,
|
||||
Japanese,
|
||||
French,
|
||||
German,
|
||||
LatinAmericanSpanish,
|
||||
Spanish,
|
||||
Italian,
|
||||
Dutch,
|
||||
CanadianFrench,
|
||||
Portuguese,
|
||||
Russian,
|
||||
Korean,
|
||||
TraditionalChinese,
|
||||
SimplifiedChinese,
|
||||
Count
|
||||
};
|
||||
using ApplicationLanguagePriorityList =
|
||||
const std::array<ApplicationLanguage, static_cast<std::size_t>(ApplicationLanguage::Count)>;
|
||||
|
||||
constexpr u32 GetSupportedLanguageFlag(const ApplicationLanguage lang) {
|
||||
return 1U << static_cast<u32>(lang);
|
||||
}
|
||||
|
||||
const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(ApplicationLanguage lang);
|
||||
std::optional<ApplicationLanguage> ConvertToApplicationLanguage(Set::LanguageCode language_code);
|
||||
std::optional<Set::LanguageCode> ConvertToLanguageCode(ApplicationLanguage lang);
|
||||
} // namespace Service::NS
|
||||
@@ -7,445 +7,507 @@
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/ns/errors.h"
|
||||
#include "core/hle/service/ns/language.h"
|
||||
#include "core/hle/service/ns/ns.h"
|
||||
#include "core/hle/service/ns/pl_u.h"
|
||||
#include "core/hle/service/set/set.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::NS {
|
||||
|
||||
class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> {
|
||||
public:
|
||||
explicit IAccountProxyInterface() : ServiceFramework{"IAccountProxyInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "CreateUserAccount"},
|
||||
};
|
||||
// clang-format on
|
||||
IAccountProxyInterface::IAccountProxyInterface() : ServiceFramework{"IAccountProxyInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "CreateUserAccount"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> {
|
||||
public:
|
||||
explicit IApplicationManagerInterface() : ServiceFramework{"IApplicationManagerInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "ListApplicationRecord"},
|
||||
{1, nullptr, "GenerateApplicationRecordCount"},
|
||||
{2, nullptr, "GetApplicationRecordUpdateSystemEvent"},
|
||||
{3, nullptr, "GetApplicationViewDeprecated"},
|
||||
{4, nullptr, "DeleteApplicationEntity"},
|
||||
{5, nullptr, "DeleteApplicationCompletely"},
|
||||
{6, nullptr, "IsAnyApplicationEntityRedundant"},
|
||||
{7, nullptr, "DeleteRedundantApplicationEntity"},
|
||||
{8, nullptr, "IsApplicationEntityMovable"},
|
||||
{9, nullptr, "MoveApplicationEntity"},
|
||||
{11, nullptr, "CalculateApplicationOccupiedSize"},
|
||||
{16, nullptr, "PushApplicationRecord"},
|
||||
{17, nullptr, "ListApplicationRecordContentMeta"},
|
||||
{19, nullptr, "LaunchApplicationOld"},
|
||||
{21, nullptr, "GetApplicationContentPath"},
|
||||
{22, nullptr, "TerminateApplication"},
|
||||
{23, nullptr, "ResolveApplicationContentPath"},
|
||||
{26, nullptr, "BeginInstallApplication"},
|
||||
{27, nullptr, "DeleteApplicationRecord"},
|
||||
{30, nullptr, "RequestApplicationUpdateInfo"},
|
||||
{32, nullptr, "CancelApplicationDownload"},
|
||||
{33, nullptr, "ResumeApplicationDownload"},
|
||||
{35, nullptr, "UpdateVersionList"},
|
||||
{36, nullptr, "PushLaunchVersion"},
|
||||
{37, nullptr, "ListRequiredVersion"},
|
||||
{38, nullptr, "CheckApplicationLaunchVersion"},
|
||||
{39, nullptr, "CheckApplicationLaunchRights"},
|
||||
{40, nullptr, "GetApplicationLogoData"},
|
||||
{41, nullptr, "CalculateApplicationDownloadRequiredSize"},
|
||||
{42, nullptr, "CleanupSdCard"},
|
||||
{43, nullptr, "CheckSdCardMountStatus"},
|
||||
{44, nullptr, "GetSdCardMountStatusChangedEvent"},
|
||||
{45, nullptr, "GetGameCardAttachmentEvent"},
|
||||
{46, nullptr, "GetGameCardAttachmentInfo"},
|
||||
{47, nullptr, "GetTotalSpaceSize"},
|
||||
{48, nullptr, "GetFreeSpaceSize"},
|
||||
{49, nullptr, "GetSdCardRemovedEvent"},
|
||||
{52, nullptr, "GetGameCardUpdateDetectionEvent"},
|
||||
{53, nullptr, "DisableApplicationAutoDelete"},
|
||||
{54, nullptr, "EnableApplicationAutoDelete"},
|
||||
{55, nullptr, "GetApplicationDesiredLanguage"},
|
||||
{56, nullptr, "SetApplicationTerminateResult"},
|
||||
{57, nullptr, "ClearApplicationTerminateResult"},
|
||||
{58, nullptr, "GetLastSdCardMountUnexpectedResult"},
|
||||
{59, nullptr, "ConvertApplicationLanguageToLanguageCode"},
|
||||
{60, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
|
||||
{61, nullptr, "GetBackgroundDownloadStressTaskInfo"},
|
||||
{62, nullptr, "GetGameCardStopper"},
|
||||
{63, nullptr, "IsSystemProgramInstalled"},
|
||||
{64, nullptr, "StartApplyDeltaTask"},
|
||||
{65, nullptr, "GetRequestServerStopper"},
|
||||
{66, nullptr, "GetBackgroundApplyDeltaStressTaskInfo"},
|
||||
{67, nullptr, "CancelApplicationApplyDelta"},
|
||||
{68, nullptr, "ResumeApplicationApplyDelta"},
|
||||
{69, nullptr, "CalculateApplicationApplyDeltaRequiredSize"},
|
||||
{70, nullptr, "ResumeAll"},
|
||||
{71, nullptr, "GetStorageSize"},
|
||||
{80, nullptr, "RequestDownloadApplication"},
|
||||
{81, nullptr, "RequestDownloadAddOnContent"},
|
||||
{82, nullptr, "DownloadApplication"},
|
||||
{83, nullptr, "CheckApplicationResumeRights"},
|
||||
{84, nullptr, "GetDynamicCommitEvent"},
|
||||
{85, nullptr, "RequestUpdateApplication2"},
|
||||
{86, nullptr, "EnableApplicationCrashReport"},
|
||||
{87, nullptr, "IsApplicationCrashReportEnabled"},
|
||||
{90, nullptr, "BoostSystemMemoryResourceLimit"},
|
||||
{91, nullptr, "DeprecatedLaunchApplication"},
|
||||
{92, nullptr, "GetRunningApplicationProgramId"},
|
||||
{93, nullptr, "GetMainApplicationProgramIndex"},
|
||||
{94, nullptr, "LaunchApplication"},
|
||||
{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"},
|
||||
{303, nullptr, "TerminateLibraryApplet"},
|
||||
{304, nullptr, "LaunchSystemApplet"},
|
||||
{305, nullptr, "TerminateSystemApplet"},
|
||||
{306, nullptr, "LaunchOverlayApplet"},
|
||||
{307, nullptr, "TerminateOverlayApplet"},
|
||||
{400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"},
|
||||
{401, nullptr, "InvalidateAllApplicationControlCache"},
|
||||
{402, nullptr, "RequestDownloadApplicationControlData"},
|
||||
{403, nullptr, "GetMaxApplicationControlCacheCount"},
|
||||
{404, nullptr, "InvalidateApplicationControlCache"},
|
||||
{405, nullptr, "ListApplicationControlCacheEntryInfo"},
|
||||
{406, nullptr, "GetApplicationControlProperty"},
|
||||
{502, nullptr, "RequestCheckGameCardRegistration"},
|
||||
{503, nullptr, "RequestGameCardRegistrationGoldPoint"},
|
||||
{504, nullptr, "RequestRegisterGameCard"},
|
||||
{505, nullptr, "GetGameCardMountFailureEvent"},
|
||||
{506, nullptr, "IsGameCardInserted"},
|
||||
{507, nullptr, "EnsureGameCardAccess"},
|
||||
{508, nullptr, "GetLastGameCardMountFailureResult"},
|
||||
{509, nullptr, "ListApplicationIdOnGameCard"},
|
||||
{600, nullptr, "CountApplicationContentMeta"},
|
||||
{601, nullptr, "ListApplicationContentMetaStatus"},
|
||||
{602, nullptr, "ListAvailableAddOnContent"},
|
||||
{603, nullptr, "GetOwnedApplicationContentMetaStatus"},
|
||||
{604, nullptr, "RegisterContentsExternalKey"},
|
||||
{605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
|
||||
{606, nullptr, "GetContentMetaStorage"},
|
||||
{607, nullptr, "ListAvailableAddOnContent"},
|
||||
{700, nullptr, "PushDownloadTaskList"},
|
||||
{701, nullptr, "ClearTaskStatusList"},
|
||||
{702, nullptr, "RequestDownloadTaskList"},
|
||||
{703, nullptr, "RequestEnsureDownloadTask"},
|
||||
{704, nullptr, "ListDownloadTaskStatus"},
|
||||
{705, nullptr, "RequestDownloadTaskListData"},
|
||||
{800, nullptr, "RequestVersionList"},
|
||||
{801, nullptr, "ListVersionList"},
|
||||
{802, nullptr, "RequestVersionListData"},
|
||||
{900, nullptr, "GetApplicationRecord"},
|
||||
{901, nullptr, "GetApplicationRecordProperty"},
|
||||
{902, nullptr, "EnableApplicationAutoUpdate"},
|
||||
{903, nullptr, "DisableApplicationAutoUpdate"},
|
||||
{904, nullptr, "TouchApplication"},
|
||||
{905, nullptr, "RequestApplicationUpdate"},
|
||||
{906, nullptr, "IsApplicationUpdateRequested"},
|
||||
{907, nullptr, "WithdrawApplicationUpdateRequest"},
|
||||
{908, nullptr, "ListApplicationRecordInstalledContentMeta"},
|
||||
{909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"},
|
||||
{910, nullptr, "HasApplicationRecord"},
|
||||
{911, nullptr, "SetPreInstalledApplication"},
|
||||
{912, nullptr, "ClearPreInstalledApplicationFlag"},
|
||||
{1000, nullptr, "RequestVerifyApplicationDeprecated"},
|
||||
{1001, nullptr, "CorruptApplicationForDebug"},
|
||||
{1002, nullptr, "RequestVerifyAddOnContentsRights"},
|
||||
{1003, nullptr, "RequestVerifyApplication"},
|
||||
{1004, nullptr, "CorruptContentForDebug"},
|
||||
{1200, nullptr, "NeedsUpdateVulnerability"},
|
||||
{1300, nullptr, "IsAnyApplicationEntityInstalled"},
|
||||
{1301, nullptr, "DeleteApplicationContentEntities"},
|
||||
{1302, nullptr, "CleanupUnrecordedApplicationEntity"},
|
||||
{1303, nullptr, "CleanupAddOnContentsWithNoRights"},
|
||||
{1304, nullptr, "DeleteApplicationContentEntity"},
|
||||
{1305, nullptr, "TryDeleteRunningApplicationEntity"},
|
||||
{1306, nullptr, "TryDeleteRunningApplicationCompletely"},
|
||||
{1307, nullptr, "TryDeleteRunningApplicationContentEntities"},
|
||||
{1308, nullptr, "DeleteApplicationCompletelyForDebug"},
|
||||
{1309, nullptr, "CleanupUnavailableAddOnContents"},
|
||||
{1400, nullptr, "PrepareShutdown"},
|
||||
{1500, nullptr, "FormatSdCard"},
|
||||
{1501, nullptr, "NeedsSystemUpdateToFormatSdCard"},
|
||||
{1502, nullptr, "GetLastSdCardFormatUnexpectedResult"},
|
||||
{1504, nullptr, "InsertSdCard"},
|
||||
{1505, nullptr, "RemoveSdCard"},
|
||||
{1600, nullptr, "GetSystemSeedForPseudoDeviceId"},
|
||||
{1601, nullptr, "ResetSystemSeedForPseudoDeviceId"},
|
||||
{1700, nullptr, "ListApplicationDownloadingContentMeta"},
|
||||
{1701, nullptr, "GetApplicationView"},
|
||||
{1702, nullptr, "GetApplicationDownloadTaskStatus"},
|
||||
{1703, nullptr, "GetApplicationViewDownloadErrorContext"},
|
||||
{1800, nullptr, "IsNotificationSetupCompleted"},
|
||||
{1801, nullptr, "GetLastNotificationInfoCount"},
|
||||
{1802, nullptr, "ListLastNotificationInfo"},
|
||||
{1803, nullptr, "ListNotificationTask"},
|
||||
{1900, nullptr, "IsActiveAccount"},
|
||||
{1901, nullptr, "RequestDownloadApplicationPrepurchasedRights"},
|
||||
{1902, nullptr, "GetApplicationTicketInfo"},
|
||||
{2000, nullptr, "GetSystemDeliveryInfo"},
|
||||
{2001, nullptr, "SelectLatestSystemDeliveryInfo"},
|
||||
{2002, nullptr, "VerifyDeliveryProtocolVersion"},
|
||||
{2003, nullptr, "GetApplicationDeliveryInfo"},
|
||||
{2004, nullptr, "HasAllContentsToDeliver"},
|
||||
{2005, nullptr, "CompareApplicationDeliveryInfo"},
|
||||
{2006, nullptr, "CanDeliverApplication"},
|
||||
{2007, nullptr, "ListContentMetaKeyToDeliverApplication"},
|
||||
{2008, nullptr, "NeedsSystemUpdateToDeliverApplication"},
|
||||
{2009, nullptr, "EstimateRequiredSize"},
|
||||
{2010, nullptr, "RequestReceiveApplication"},
|
||||
{2011, nullptr, "CommitReceiveApplication"},
|
||||
{2012, nullptr, "GetReceiveApplicationProgress"},
|
||||
{2013, nullptr, "RequestSendApplication"},
|
||||
{2014, nullptr, "GetSendApplicationProgress"},
|
||||
{2015, nullptr, "CompareSystemDeliveryInfo"},
|
||||
{2016, nullptr, "ListNotCommittedContentMeta"},
|
||||
{2017, nullptr, "CreateDownloadTask"},
|
||||
{2018, nullptr, "GetApplicationDeliveryInfoHash"},
|
||||
{2050, nullptr, "GetApplicationRightsOnClient"},
|
||||
{2100, nullptr, "GetApplicationTerminateResult"},
|
||||
{2101, nullptr, "GetRawApplicationTerminateResult"},
|
||||
{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, "GetGameCardApplicationCopyIdentifier"},
|
||||
{2201, nullptr, "GetInstalledApplicationCopyIdentifier"},
|
||||
{2250, nullptr, "RequestReportActiveELicence"},
|
||||
{2300, nullptr, "ListEventLog"},
|
||||
};
|
||||
// clang-format on
|
||||
IAccountProxyInterface::~IAccountProxyInterface() = default;
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
IApplicationManagerInterface::IApplicationManagerInterface()
|
||||
: ServiceFramework{"IApplicationManagerInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "ListApplicationRecord"},
|
||||
{1, nullptr, "GenerateApplicationRecordCount"},
|
||||
{2, nullptr, "GetApplicationRecordUpdateSystemEvent"},
|
||||
{3, nullptr, "GetApplicationViewDeprecated"},
|
||||
{4, nullptr, "DeleteApplicationEntity"},
|
||||
{5, nullptr, "DeleteApplicationCompletely"},
|
||||
{6, nullptr, "IsAnyApplicationEntityRedundant"},
|
||||
{7, nullptr, "DeleteRedundantApplicationEntity"},
|
||||
{8, nullptr, "IsApplicationEntityMovable"},
|
||||
{9, nullptr, "MoveApplicationEntity"},
|
||||
{11, nullptr, "CalculateApplicationOccupiedSize"},
|
||||
{16, nullptr, "PushApplicationRecord"},
|
||||
{17, nullptr, "ListApplicationRecordContentMeta"},
|
||||
{19, nullptr, "LaunchApplicationOld"},
|
||||
{21, nullptr, "GetApplicationContentPath"},
|
||||
{22, nullptr, "TerminateApplication"},
|
||||
{23, nullptr, "ResolveApplicationContentPath"},
|
||||
{26, nullptr, "BeginInstallApplication"},
|
||||
{27, nullptr, "DeleteApplicationRecord"},
|
||||
{30, nullptr, "RequestApplicationUpdateInfo"},
|
||||
{32, nullptr, "CancelApplicationDownload"},
|
||||
{33, nullptr, "ResumeApplicationDownload"},
|
||||
{35, nullptr, "UpdateVersionList"},
|
||||
{36, nullptr, "PushLaunchVersion"},
|
||||
{37, nullptr, "ListRequiredVersion"},
|
||||
{38, nullptr, "CheckApplicationLaunchVersion"},
|
||||
{39, nullptr, "CheckApplicationLaunchRights"},
|
||||
{40, nullptr, "GetApplicationLogoData"},
|
||||
{41, nullptr, "CalculateApplicationDownloadRequiredSize"},
|
||||
{42, nullptr, "CleanupSdCard"},
|
||||
{43, nullptr, "CheckSdCardMountStatus"},
|
||||
{44, nullptr, "GetSdCardMountStatusChangedEvent"},
|
||||
{45, nullptr, "GetGameCardAttachmentEvent"},
|
||||
{46, nullptr, "GetGameCardAttachmentInfo"},
|
||||
{47, nullptr, "GetTotalSpaceSize"},
|
||||
{48, nullptr, "GetFreeSpaceSize"},
|
||||
{49, nullptr, "GetSdCardRemovedEvent"},
|
||||
{52, nullptr, "GetGameCardUpdateDetectionEvent"},
|
||||
{53, nullptr, "DisableApplicationAutoDelete"},
|
||||
{54, nullptr, "EnableApplicationAutoDelete"},
|
||||
{55, &IApplicationManagerInterface::GetApplicationDesiredLanguage, "GetApplicationDesiredLanguage"},
|
||||
{56, nullptr, "SetApplicationTerminateResult"},
|
||||
{57, nullptr, "ClearApplicationTerminateResult"},
|
||||
{58, nullptr, "GetLastSdCardMountUnexpectedResult"},
|
||||
{59, &IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode, "ConvertApplicationLanguageToLanguageCode"},
|
||||
{60, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
|
||||
{61, nullptr, "GetBackgroundDownloadStressTaskInfo"},
|
||||
{62, nullptr, "GetGameCardStopper"},
|
||||
{63, nullptr, "IsSystemProgramInstalled"},
|
||||
{64, nullptr, "StartApplyDeltaTask"},
|
||||
{65, nullptr, "GetRequestServerStopper"},
|
||||
{66, nullptr, "GetBackgroundApplyDeltaStressTaskInfo"},
|
||||
{67, nullptr, "CancelApplicationApplyDelta"},
|
||||
{68, nullptr, "ResumeApplicationApplyDelta"},
|
||||
{69, nullptr, "CalculateApplicationApplyDeltaRequiredSize"},
|
||||
{70, nullptr, "ResumeAll"},
|
||||
{71, nullptr, "GetStorageSize"},
|
||||
{80, nullptr, "RequestDownloadApplication"},
|
||||
{81, nullptr, "RequestDownloadAddOnContent"},
|
||||
{82, nullptr, "DownloadApplication"},
|
||||
{83, nullptr, "CheckApplicationResumeRights"},
|
||||
{84, nullptr, "GetDynamicCommitEvent"},
|
||||
{85, nullptr, "RequestUpdateApplication2"},
|
||||
{86, nullptr, "EnableApplicationCrashReport"},
|
||||
{87, nullptr, "IsApplicationCrashReportEnabled"},
|
||||
{90, nullptr, "BoostSystemMemoryResourceLimit"},
|
||||
{91, nullptr, "DeprecatedLaunchApplication"},
|
||||
{92, nullptr, "GetRunningApplicationProgramId"},
|
||||
{93, nullptr, "GetMainApplicationProgramIndex"},
|
||||
{94, nullptr, "LaunchApplication"},
|
||||
{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"},
|
||||
{303, nullptr, "TerminateLibraryApplet"},
|
||||
{304, nullptr, "LaunchSystemApplet"},
|
||||
{305, nullptr, "TerminateSystemApplet"},
|
||||
{306, nullptr, "LaunchOverlayApplet"},
|
||||
{307, nullptr, "TerminateOverlayApplet"},
|
||||
{400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"},
|
||||
{401, nullptr, "InvalidateAllApplicationControlCache"},
|
||||
{402, nullptr, "RequestDownloadApplicationControlData"},
|
||||
{403, nullptr, "GetMaxApplicationControlCacheCount"},
|
||||
{404, nullptr, "InvalidateApplicationControlCache"},
|
||||
{405, nullptr, "ListApplicationControlCacheEntryInfo"},
|
||||
{406, nullptr, "GetApplicationControlProperty"},
|
||||
{502, nullptr, "RequestCheckGameCardRegistration"},
|
||||
{503, nullptr, "RequestGameCardRegistrationGoldPoint"},
|
||||
{504, nullptr, "RequestRegisterGameCard"},
|
||||
{505, nullptr, "GetGameCardMountFailureEvent"},
|
||||
{506, nullptr, "IsGameCardInserted"},
|
||||
{507, nullptr, "EnsureGameCardAccess"},
|
||||
{508, nullptr, "GetLastGameCardMountFailureResult"},
|
||||
{509, nullptr, "ListApplicationIdOnGameCard"},
|
||||
{600, nullptr, "CountApplicationContentMeta"},
|
||||
{601, nullptr, "ListApplicationContentMetaStatus"},
|
||||
{602, nullptr, "ListAvailableAddOnContent"},
|
||||
{603, nullptr, "GetOwnedApplicationContentMetaStatus"},
|
||||
{604, nullptr, "RegisterContentsExternalKey"},
|
||||
{605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
|
||||
{606, nullptr, "GetContentMetaStorage"},
|
||||
{607, nullptr, "ListAvailableAddOnContent"},
|
||||
{700, nullptr, "PushDownloadTaskList"},
|
||||
{701, nullptr, "ClearTaskStatusList"},
|
||||
{702, nullptr, "RequestDownloadTaskList"},
|
||||
{703, nullptr, "RequestEnsureDownloadTask"},
|
||||
{704, nullptr, "ListDownloadTaskStatus"},
|
||||
{705, nullptr, "RequestDownloadTaskListData"},
|
||||
{800, nullptr, "RequestVersionList"},
|
||||
{801, nullptr, "ListVersionList"},
|
||||
{802, nullptr, "RequestVersionListData"},
|
||||
{900, nullptr, "GetApplicationRecord"},
|
||||
{901, nullptr, "GetApplicationRecordProperty"},
|
||||
{902, nullptr, "EnableApplicationAutoUpdate"},
|
||||
{903, nullptr, "DisableApplicationAutoUpdate"},
|
||||
{904, nullptr, "TouchApplication"},
|
||||
{905, nullptr, "RequestApplicationUpdate"},
|
||||
{906, nullptr, "IsApplicationUpdateRequested"},
|
||||
{907, nullptr, "WithdrawApplicationUpdateRequest"},
|
||||
{908, nullptr, "ListApplicationRecordInstalledContentMeta"},
|
||||
{909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"},
|
||||
{910, nullptr, "HasApplicationRecord"},
|
||||
{911, nullptr, "SetPreInstalledApplication"},
|
||||
{912, nullptr, "ClearPreInstalledApplicationFlag"},
|
||||
{1000, nullptr, "RequestVerifyApplicationDeprecated"},
|
||||
{1001, nullptr, "CorruptApplicationForDebug"},
|
||||
{1002, nullptr, "RequestVerifyAddOnContentsRights"},
|
||||
{1003, nullptr, "RequestVerifyApplication"},
|
||||
{1004, nullptr, "CorruptContentForDebug"},
|
||||
{1200, nullptr, "NeedsUpdateVulnerability"},
|
||||
{1300, nullptr, "IsAnyApplicationEntityInstalled"},
|
||||
{1301, nullptr, "DeleteApplicationContentEntities"},
|
||||
{1302, nullptr, "CleanupUnrecordedApplicationEntity"},
|
||||
{1303, nullptr, "CleanupAddOnContentsWithNoRights"},
|
||||
{1304, nullptr, "DeleteApplicationContentEntity"},
|
||||
{1305, nullptr, "TryDeleteRunningApplicationEntity"},
|
||||
{1306, nullptr, "TryDeleteRunningApplicationCompletely"},
|
||||
{1307, nullptr, "TryDeleteRunningApplicationContentEntities"},
|
||||
{1308, nullptr, "DeleteApplicationCompletelyForDebug"},
|
||||
{1309, nullptr, "CleanupUnavailableAddOnContents"},
|
||||
{1400, nullptr, "PrepareShutdown"},
|
||||
{1500, nullptr, "FormatSdCard"},
|
||||
{1501, nullptr, "NeedsSystemUpdateToFormatSdCard"},
|
||||
{1502, nullptr, "GetLastSdCardFormatUnexpectedResult"},
|
||||
{1504, nullptr, "InsertSdCard"},
|
||||
{1505, nullptr, "RemoveSdCard"},
|
||||
{1600, nullptr, "GetSystemSeedForPseudoDeviceId"},
|
||||
{1601, nullptr, "ResetSystemSeedForPseudoDeviceId"},
|
||||
{1700, nullptr, "ListApplicationDownloadingContentMeta"},
|
||||
{1701, nullptr, "GetApplicationView"},
|
||||
{1702, nullptr, "GetApplicationDownloadTaskStatus"},
|
||||
{1703, nullptr, "GetApplicationViewDownloadErrorContext"},
|
||||
{1800, nullptr, "IsNotificationSetupCompleted"},
|
||||
{1801, nullptr, "GetLastNotificationInfoCount"},
|
||||
{1802, nullptr, "ListLastNotificationInfo"},
|
||||
{1803, nullptr, "ListNotificationTask"},
|
||||
{1900, nullptr, "IsActiveAccount"},
|
||||
{1901, nullptr, "RequestDownloadApplicationPrepurchasedRights"},
|
||||
{1902, nullptr, "GetApplicationTicketInfo"},
|
||||
{2000, nullptr, "GetSystemDeliveryInfo"},
|
||||
{2001, nullptr, "SelectLatestSystemDeliveryInfo"},
|
||||
{2002, nullptr, "VerifyDeliveryProtocolVersion"},
|
||||
{2003, nullptr, "GetApplicationDeliveryInfo"},
|
||||
{2004, nullptr, "HasAllContentsToDeliver"},
|
||||
{2005, nullptr, "CompareApplicationDeliveryInfo"},
|
||||
{2006, nullptr, "CanDeliverApplication"},
|
||||
{2007, nullptr, "ListContentMetaKeyToDeliverApplication"},
|
||||
{2008, nullptr, "NeedsSystemUpdateToDeliverApplication"},
|
||||
{2009, nullptr, "EstimateRequiredSize"},
|
||||
{2010, nullptr, "RequestReceiveApplication"},
|
||||
{2011, nullptr, "CommitReceiveApplication"},
|
||||
{2012, nullptr, "GetReceiveApplicationProgress"},
|
||||
{2013, nullptr, "RequestSendApplication"},
|
||||
{2014, nullptr, "GetSendApplicationProgress"},
|
||||
{2015, nullptr, "CompareSystemDeliveryInfo"},
|
||||
{2016, nullptr, "ListNotCommittedContentMeta"},
|
||||
{2017, nullptr, "CreateDownloadTask"},
|
||||
{2018, nullptr, "GetApplicationDeliveryInfoHash"},
|
||||
{2050, nullptr, "GetApplicationRightsOnClient"},
|
||||
{2100, nullptr, "GetApplicationTerminateResult"},
|
||||
{2101, nullptr, "GetRawApplicationTerminateResult"},
|
||||
{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, "GetGameCardApplicationCopyIdentifier"},
|
||||
{2201, nullptr, "GetInstalledApplicationCopyIdentifier"},
|
||||
{2250, nullptr, "RequestReportActiveELicence"},
|
||||
{2300, nullptr, "ListEventLog"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
void GetApplicationControlData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto flag = rp.PopRaw<u64>();
|
||||
LOG_DEBUG(Service_NS, "called with flag={:016X}", flag);
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
IApplicationManagerInterface::~IApplicationManagerInterface() = default;
|
||||
|
||||
const auto size = ctx.GetWriteBufferSize();
|
||||
void IApplicationManagerInterface::GetApplicationControlData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto flag = rp.PopRaw<u64>();
|
||||
LOG_DEBUG(Service_NS, "called with flag={:016X}", flag);
|
||||
|
||||
const FileSys::PatchManager pm{title_id};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
std::vector<u8> out;
|
||||
const auto size = ctx.GetWriteBufferSize();
|
||||
|
||||
if (control.first != nullptr) {
|
||||
if (size < 0x4000) {
|
||||
LOG_ERROR(Service_NS,
|
||||
"output buffer is too small! (actual={:016X}, expected_min=0x4000)",
|
||||
size);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(DarkLordZach): Find a better error code for this.
|
||||
rb.Push(ResultCode(-1));
|
||||
return;
|
||||
}
|
||||
const FileSys::PatchManager pm{title_id};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
|
||||
out.resize(0x4000);
|
||||
const auto bytes = control.first->GetRawBytes();
|
||||
std::memcpy(out.data(), bytes.data(), bytes.size());
|
||||
} else {
|
||||
LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.",
|
||||
title_id);
|
||||
out.resize(std::min<u64>(0x4000, size));
|
||||
std::vector<u8> out;
|
||||
|
||||
if (control.first != nullptr) {
|
||||
if (size < 0x4000) {
|
||||
LOG_ERROR(Service_NS,
|
||||
"output buffer is too small! (actual={:016X}, expected_min=0x4000)", size);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(DarkLordZach): Find a better error code for this.
|
||||
rb.Push(ResultCode(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
if (control.second != nullptr) {
|
||||
if (size < 0x4000 + control.second->GetSize()) {
|
||||
LOG_ERROR(Service_NS,
|
||||
"output buffer is too small! (actual={:016X}, expected_min={:016X})",
|
||||
size, 0x4000 + control.second->GetSize());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(DarkLordZach): Find a better error code for this.
|
||||
rb.Push(ResultCode(-1));
|
||||
return;
|
||||
}
|
||||
out.resize(0x4000);
|
||||
const auto bytes = control.first->GetRawBytes();
|
||||
std::memcpy(out.data(), bytes.data(), bytes.size());
|
||||
} else {
|
||||
LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.",
|
||||
title_id);
|
||||
out.resize(std::min<u64>(0x4000, size));
|
||||
}
|
||||
|
||||
out.resize(0x4000 + control.second->GetSize());
|
||||
control.second->Read(out.data() + 0x4000, control.second->GetSize());
|
||||
} else {
|
||||
LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.",
|
||||
title_id);
|
||||
if (control.second != nullptr) {
|
||||
if (size < 0x4000 + control.second->GetSize()) {
|
||||
LOG_ERROR(Service_NS,
|
||||
"output buffer is too small! (actual={:016X}, expected_min={:016X})", size,
|
||||
0x4000 + control.second->GetSize());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(DarkLordZach): Find a better error code for this.
|
||||
rb.Push(ResultCode(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(out);
|
||||
out.resize(0x4000 + control.second->GetSize());
|
||||
control.second->Read(out.data() + 0x4000, control.second->GetSize());
|
||||
} else {
|
||||
LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.",
|
||||
title_id);
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(out);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(static_cast<u32>(out.size()));
|
||||
}
|
||||
|
||||
void IApplicationManagerInterface::GetApplicationDesiredLanguage(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto supported_languages = rp.Pop<u32>();
|
||||
|
||||
const auto res = GetApplicationDesiredLanguage(supported_languages);
|
||||
if (res.Succeeded()) {
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(static_cast<u32>(out.size()));
|
||||
rb.Push<u32>(*res);
|
||||
} else {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res.Code());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
|
||||
public:
|
||||
explicit IApplicationVersionInterface() : ServiceFramework{"IApplicationVersionInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetLaunchRequiredVersion"},
|
||||
{1, nullptr, "UpgradeLaunchRequiredVersion"},
|
||||
{35, nullptr, "UpdateVersionList"},
|
||||
{36, nullptr, "PushLaunchVersion"},
|
||||
{37, nullptr, "ListRequiredVersion"},
|
||||
{800, nullptr, "RequestVersionList"},
|
||||
{801, nullptr, "ListVersionList"},
|
||||
{802, nullptr, "RequestVersionListData"},
|
||||
{1000, nullptr, "PerformAutoUpdate"},
|
||||
};
|
||||
// clang-format on
|
||||
ResultVal<u8> IApplicationManagerInterface::GetApplicationDesiredLanguage(
|
||||
const u32 supported_languages) {
|
||||
LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages);
|
||||
|
||||
RegisterHandlers(functions);
|
||||
// Get language code from settings
|
||||
const auto language_code = Set::GetLanguageCodeFromIndex(Settings::values.language_index);
|
||||
|
||||
// Convert to application language, get priority list
|
||||
const auto application_language = ConvertToApplicationLanguage(language_code);
|
||||
if (application_language == std::nullopt) {
|
||||
return ERR_APPLICATION_LANGUAGE_NOT_FOUND;
|
||||
}
|
||||
};
|
||||
|
||||
class IContentManagerInterface final : public ServiceFramework<IContentManagerInterface> {
|
||||
public:
|
||||
explicit IContentManagerInterface() : ServiceFramework{"IContentManagerInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{11, nullptr, "CalculateApplicationOccupiedSize"},
|
||||
{43, nullptr, "CheckSdCardMountStatus"},
|
||||
{47, nullptr, "GetTotalSpaceSize"},
|
||||
{48, nullptr, "GetFreeSpaceSize"},
|
||||
{600, nullptr, "CountApplicationContentMeta"},
|
||||
{601, nullptr, "ListApplicationContentMetaStatus"},
|
||||
{605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
|
||||
{607, nullptr, "IsAnyApplicationRunning"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
const auto priority_list = GetApplicationLanguagePriorityList(*application_language);
|
||||
if (!priority_list) {
|
||||
return ERR_APPLICATION_LANGUAGE_NOT_FOUND;
|
||||
}
|
||||
};
|
||||
|
||||
class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
|
||||
public:
|
||||
explicit IDocumentInterface() : ServiceFramework{"IDocumentInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{21, nullptr, "GetApplicationContentPath"},
|
||||
{23, nullptr, "ResolveApplicationContentPath"},
|
||||
{93, nullptr, "GetRunningApplicationProgramId"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
// Try to find a valid language.
|
||||
for (const auto lang : *priority_list) {
|
||||
const auto supported_flag = GetSupportedLanguageFlag(lang);
|
||||
if (supported_languages == 0 || (supported_languages & supported_flag) == supported_flag) {
|
||||
return MakeResult(static_cast<u8>(lang));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> {
|
||||
public:
|
||||
explicit IDownloadTaskInterface() : ServiceFramework{"IDownloadTaskInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{701, nullptr, "ClearTaskStatusList"},
|
||||
{702, nullptr, "RequestDownloadTaskList"},
|
||||
{703, nullptr, "RequestEnsureDownloadTask"},
|
||||
{704, nullptr, "ListDownloadTaskStatus"},
|
||||
{705, nullptr, "RequestDownloadTaskListData"},
|
||||
{706, nullptr, "TryCommitCurrentApplicationDownloadTask"},
|
||||
{707, nullptr, "EnableAutoCommit"},
|
||||
{708, nullptr, "DisableAutoCommit"},
|
||||
{709, nullptr, "TriggerDynamicCommitEvent"},
|
||||
};
|
||||
// clang-format on
|
||||
return ERR_APPLICATION_LANGUAGE_NOT_FOUND;
|
||||
}
|
||||
|
||||
RegisterHandlers(functions);
|
||||
void IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode(
|
||||
Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto application_language = rp.Pop<u8>();
|
||||
|
||||
const auto res = ConvertApplicationLanguageToLanguageCode(application_language);
|
||||
if (res.Succeeded()) {
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(*res);
|
||||
} else {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res.Code());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class IECommerceInterface final : public ServiceFramework<IECommerceInterface> {
|
||||
public:
|
||||
explicit IECommerceInterface() : ServiceFramework{"IECommerceInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "RequestLinkDevice"},
|
||||
{1, nullptr, "RequestCleanupAllPreInstalledApplications"},
|
||||
{2, nullptr, "RequestCleanupPreInstalledApplication"},
|
||||
{3, nullptr, "RequestSyncRights"},
|
||||
{4, nullptr, "RequestUnlinkDevice"},
|
||||
{5, nullptr, "RequestRevokeAllELicense"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
ResultVal<u64> IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode(
|
||||
u8 application_language) {
|
||||
const auto language_code =
|
||||
ConvertToLanguageCode(static_cast<ApplicationLanguage>(application_language));
|
||||
if (language_code == std::nullopt) {
|
||||
return ERR_APPLICATION_LANGUAGE_NOT_FOUND;
|
||||
}
|
||||
};
|
||||
|
||||
class IFactoryResetInterface final : public ServiceFramework<IFactoryResetInterface> {
|
||||
public:
|
||||
explicit IFactoryResetInterface() : ServiceFramework{"IFactoryResetInterface"} {
|
||||
// clang-format off
|
||||
return MakeResult(static_cast<u64>(*language_code));
|
||||
}
|
||||
|
||||
IApplicationVersionInterface::IApplicationVersionInterface()
|
||||
: ServiceFramework{"IApplicationVersionInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetLaunchRequiredVersion"},
|
||||
{1, nullptr, "UpgradeLaunchRequiredVersion"},
|
||||
{35, nullptr, "UpdateVersionList"},
|
||||
{36, nullptr, "PushLaunchVersion"},
|
||||
{37, nullptr, "ListRequiredVersion"},
|
||||
{800, nullptr, "RequestVersionList"},
|
||||
{801, nullptr, "ListVersionList"},
|
||||
{802, nullptr, "RequestVersionListData"},
|
||||
{1000, nullptr, "PerformAutoUpdate"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IApplicationVersionInterface::~IApplicationVersionInterface() = default;
|
||||
|
||||
IContentManagerInterface::IContentManagerInterface()
|
||||
: ServiceFramework{"IContentManagerInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{11, nullptr, "CalculateApplicationOccupiedSize"},
|
||||
{43, nullptr, "CheckSdCardMountStatus"},
|
||||
{47, nullptr, "GetTotalSpaceSize"},
|
||||
{48, nullptr, "GetFreeSpaceSize"},
|
||||
{600, nullptr, "CountApplicationContentMeta"},
|
||||
{601, nullptr, "ListApplicationContentMetaStatus"},
|
||||
{605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
|
||||
{607, nullptr, "IsAnyApplicationRunning"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IContentManagerInterface::~IContentManagerInterface() = default;
|
||||
|
||||
IDocumentInterface::IDocumentInterface() : ServiceFramework{"IDocumentInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{21, nullptr, "GetApplicationContentPath"},
|
||||
{23, nullptr, "ResolveApplicationContentPath"},
|
||||
{93, nullptr, "GetRunningApplicationProgramId"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IDocumentInterface::~IDocumentInterface() = default;
|
||||
|
||||
IDownloadTaskInterface::IDownloadTaskInterface() : ServiceFramework{"IDownloadTaskInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{701, nullptr, "ClearTaskStatusList"},
|
||||
{702, nullptr, "RequestDownloadTaskList"},
|
||||
{703, nullptr, "RequestEnsureDownloadTask"},
|
||||
{704, nullptr, "ListDownloadTaskStatus"},
|
||||
{705, nullptr, "RequestDownloadTaskListData"},
|
||||
{706, nullptr, "TryCommitCurrentApplicationDownloadTask"},
|
||||
{707, nullptr, "EnableAutoCommit"},
|
||||
{708, nullptr, "DisableAutoCommit"},
|
||||
{709, nullptr, "TriggerDynamicCommitEvent"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IDownloadTaskInterface::~IDownloadTaskInterface() = default;
|
||||
|
||||
IECommerceInterface::IECommerceInterface() : ServiceFramework{"IECommerceInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "RequestLinkDevice"},
|
||||
{1, nullptr, "RequestCleanupAllPreInstalledApplications"},
|
||||
{2, nullptr, "RequestCleanupPreInstalledApplication"},
|
||||
{3, nullptr, "RequestSyncRights"},
|
||||
{4, nullptr, "RequestUnlinkDevice"},
|
||||
{5, nullptr, "RequestRevokeAllELicense"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IECommerceInterface::~IECommerceInterface() = default;
|
||||
|
||||
IFactoryResetInterface::IFactoryResetInterface::IFactoryResetInterface()
|
||||
: ServiceFramework{"IFactoryResetInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{100, nullptr, "ResetToFactorySettings"},
|
||||
{101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
|
||||
{102, nullptr, "ResetToFactorySettingsForRefurbishment"},
|
||||
};
|
||||
// clang-format on
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
class NS final : public ServiceFramework<NS> {
|
||||
public:
|
||||
explicit NS(const char* name) : ServiceFramework{name} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
|
||||
{7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"},
|
||||
{7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"},
|
||||
{7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"},
|
||||
{7996, &NS::PushInterface<IApplicationManagerInterface>, "GetApplicationManagerInterface"},
|
||||
{7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"},
|
||||
{7998, &NS::PushInterface<IContentManagerInterface>, "GetContentManagementInterface"},
|
||||
{7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"},
|
||||
};
|
||||
// clang-format on
|
||||
IFactoryResetInterface::~IFactoryResetInterface() = default;
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
NS::NS(const char* name) : ServiceFramework{name} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
|
||||
{7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"},
|
||||
{7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"},
|
||||
{7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"},
|
||||
{7996, &NS::PushInterface<IApplicationManagerInterface>, "GetApplicationManagerInterface"},
|
||||
{7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"},
|
||||
{7998, &NS::PushInterface<IContentManagerInterface>, "GetContentManagementInterface"},
|
||||
{7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void PushInterface(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<T>();
|
||||
}
|
||||
};
|
||||
NS::~NS() = default;
|
||||
|
||||
std::shared_ptr<IApplicationManagerInterface> NS::GetApplicationManagerInterface() const {
|
||||
return GetInterface<IApplicationManagerInterface>();
|
||||
}
|
||||
|
||||
class NS_DEV final : public ServiceFramework<NS_DEV> {
|
||||
public:
|
||||
|
||||
@@ -8,6 +8,88 @@
|
||||
|
||||
namespace Service::NS {
|
||||
|
||||
class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> {
|
||||
public:
|
||||
explicit IAccountProxyInterface();
|
||||
~IAccountProxyInterface() override;
|
||||
};
|
||||
|
||||
class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> {
|
||||
public:
|
||||
explicit IApplicationManagerInterface();
|
||||
~IApplicationManagerInterface() override;
|
||||
|
||||
ResultVal<u8> GetApplicationDesiredLanguage(u32 supported_languages);
|
||||
ResultVal<u64> ConvertApplicationLanguageToLanguageCode(u8 application_language);
|
||||
|
||||
private:
|
||||
void GetApplicationControlData(Kernel::HLERequestContext& ctx);
|
||||
void GetApplicationDesiredLanguage(Kernel::HLERequestContext& ctx);
|
||||
void ConvertApplicationLanguageToLanguageCode(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
|
||||
public:
|
||||
explicit IApplicationVersionInterface();
|
||||
~IApplicationVersionInterface() override;
|
||||
};
|
||||
|
||||
class IContentManagerInterface final : public ServiceFramework<IContentManagerInterface> {
|
||||
public:
|
||||
explicit IContentManagerInterface();
|
||||
~IContentManagerInterface() override;
|
||||
};
|
||||
|
||||
class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
|
||||
public:
|
||||
explicit IDocumentInterface();
|
||||
~IDocumentInterface() override;
|
||||
};
|
||||
|
||||
class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> {
|
||||
public:
|
||||
explicit IDownloadTaskInterface();
|
||||
~IDownloadTaskInterface() override;
|
||||
};
|
||||
|
||||
class IECommerceInterface final : public ServiceFramework<IECommerceInterface> {
|
||||
public:
|
||||
explicit IECommerceInterface();
|
||||
~IECommerceInterface() override;
|
||||
};
|
||||
|
||||
class IFactoryResetInterface final : public ServiceFramework<IFactoryResetInterface> {
|
||||
public:
|
||||
explicit IFactoryResetInterface();
|
||||
~IFactoryResetInterface() override;
|
||||
};
|
||||
|
||||
class NS final : public ServiceFramework<NS> {
|
||||
public:
|
||||
explicit NS(const char* name);
|
||||
~NS() override;
|
||||
|
||||
std::shared_ptr<IApplicationManagerInterface> GetApplicationManagerInterface() const;
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void PushInterface(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::shared_ptr<T> GetInterface() const {
|
||||
static_assert(std::is_base_of_v<Kernel::SessionRequestHandler, T>,
|
||||
"Not a base of ServiceFrameworkBase");
|
||||
|
||||
return std::make_shared<T>();
|
||||
}
|
||||
};
|
||||
|
||||
/// Registers all NS services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
|
||||
|
||||
42
src/core/hle/service/ns/ns_language.h
Normal file
42
src/core/hle/service/ns/ns_language.h
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/set/set.h"
|
||||
|
||||
namespace Service::NS {
|
||||
/// This is nn::ns::detail::ApplicationLanguage
|
||||
enum class ApplicationLanguage : u8 {
|
||||
AmericanEnglish = 0,
|
||||
BritishEnglish,
|
||||
Japanese,
|
||||
French,
|
||||
German,
|
||||
LatinAmericanSpanish,
|
||||
Spanish,
|
||||
Italian,
|
||||
Dutch,
|
||||
CanadianFrench,
|
||||
Portuguese,
|
||||
Russian,
|
||||
Korean,
|
||||
TraditionalChinese,
|
||||
SimplifiedChinese,
|
||||
Count
|
||||
};
|
||||
using ApplicationLanguagePriorityList =
|
||||
const std::array<ApplicationLanguage, static_cast<std::size_t>(ApplicationLanguage::Count)>;
|
||||
|
||||
constexpr u32 GetSupportedLanguageFlag(const ApplicationLanguage lang) {
|
||||
return 1U << static_cast<u32>(lang);
|
||||
}
|
||||
|
||||
const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(ApplicationLanguage lang);
|
||||
std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
|
||||
Service::Set::LanguageCode language_code);
|
||||
std::optional<Service::Set::LanguageCode> ConvertToLanguageCode(ApplicationLanguage lang);
|
||||
} // namespace Service::NS
|
||||
@@ -185,7 +185,8 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o
|
||||
|
||||
IoctlGetGpuTime params{};
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
params.gpu_time = Core::Timing::cyclesToNs(Core::System::GetInstance().CoreTiming().GetTicks());
|
||||
const auto ns = Core::Timing::CyclesToNs(Core::System::GetInstance().CoreTiming().GetTicks());
|
||||
params.gpu_time = static_cast<u64_le>(ns.count());
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -108,8 +108,9 @@ private:
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
const auto& core_timing = Core::System::GetInstance().CoreTiming();
|
||||
const SteadyClockTimePoint steady_clock_time_point{
|
||||
Core::Timing::cyclesToMs(core_timing.GetTicks()) / 1000};
|
||||
const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
|
||||
const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000),
|
||||
{}};
|
||||
IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(steady_clock_time_point);
|
||||
@@ -284,8 +285,8 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
const auto& core_timing = Core::System::GetInstance().CoreTiming();
|
||||
const SteadyClockTimePoint steady_clock_time_point{
|
||||
Core::Timing::cyclesToMs(core_timing.GetTicks()) / 1000, {}};
|
||||
const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
|
||||
const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000), {}};
|
||||
|
||||
CalendarTime calendar_time{};
|
||||
calendar_time.year = tm->tm_year + 1900;
|
||||
|
||||
@@ -556,7 +556,7 @@ private:
|
||||
} else {
|
||||
// Wait the current thread until a buffer becomes available
|
||||
ctx.SleepClientThread(
|
||||
Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1,
|
||||
"IHOSBinderDriver::DequeueBuffer", -1,
|
||||
[=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
|
||||
Kernel::ThreadWakeupReason reason) {
|
||||
// Repeat TransactParcel DequeueBuffer when a buffer is available
|
||||
|
||||
@@ -153,17 +153,6 @@ public:
|
||||
*/
|
||||
virtual LoadResult Load(Kernel::Process& process) = 0;
|
||||
|
||||
/**
|
||||
* Loads the system mode that this application needs.
|
||||
* This function defaults to 2 (96MB allocated to the application) if it can't read the
|
||||
* information.
|
||||
* @returns A pair with the optional system mode, and and the status.
|
||||
*/
|
||||
virtual std::pair<std::optional<u32>, ResultStatus> LoadKernelSystemMode() {
|
||||
// 96MB allocated to the application.
|
||||
return std::make_pair(2, ResultStatus::Success);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the code (typically .code section) of the application
|
||||
* @param buffer Reference to buffer to store data
|
||||
|
||||
@@ -39,7 +39,7 @@ std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
|
||||
const std::vector<u8> uncompressed_data =
|
||||
Common::Compression::DecompressDataLZ4(compressed_data, header.size);
|
||||
|
||||
ASSERT_MSG(uncompressed_data.size() == static_cast<int>(header.size), "{} != {}", header.size,
|
||||
ASSERT_MSG(uncompressed_data.size() == header.size, "{} != {}", header.size,
|
||||
uncompressed_data.size());
|
||||
|
||||
return uncompressed_data;
|
||||
|
||||
@@ -90,7 +90,6 @@ void LogSettings() {
|
||||
LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor);
|
||||
LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit);
|
||||
LogSetting("Renderer_FrameLimit", Settings::values.frame_limit);
|
||||
LogSetting("Renderer_UseCompatibilityProfile", Settings::values.use_compatibility_profile);
|
||||
LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache);
|
||||
LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation);
|
||||
LogSetting("Renderer_UseAsynchronousGpuEmulation",
|
||||
|
||||
@@ -390,7 +390,6 @@ struct Values {
|
||||
float resolution_factor;
|
||||
bool use_frame_limit;
|
||||
u16 frame_limit;
|
||||
bool use_compatibility_profile;
|
||||
bool use_disk_shader_cache;
|
||||
bool use_accurate_gpu_emulation;
|
||||
bool use_asynchronous_gpu_emulation;
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/loader/loader.h"
|
||||
@@ -101,7 +100,30 @@ bool VerifyLogin(const std::string& username, const std::string& token) {
|
||||
#endif
|
||||
}
|
||||
|
||||
TelemetrySession::TelemetrySession() {
|
||||
TelemetrySession::TelemetrySession() = default;
|
||||
|
||||
TelemetrySession::~TelemetrySession() {
|
||||
// Log one-time session end information
|
||||
const s64 shutdown_time{std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count()};
|
||||
AddField(Telemetry::FieldType::Session, "Shutdown_Time", shutdown_time);
|
||||
|
||||
#ifdef ENABLE_WEB_SERVICE
|
||||
auto backend = std::make_unique<WebService::TelemetryJson>(
|
||||
Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token);
|
||||
#else
|
||||
auto backend = std::make_unique<Telemetry::NullVisitor>();
|
||||
#endif
|
||||
|
||||
// Complete the session, submitting to the web service backend if necessary
|
||||
field_collection.Accept(*backend);
|
||||
if (Settings::values.enable_telemetry) {
|
||||
backend->Complete();
|
||||
}
|
||||
}
|
||||
|
||||
void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
|
||||
// Log one-time top-level information
|
||||
AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId());
|
||||
|
||||
@@ -112,26 +134,28 @@ TelemetrySession::TelemetrySession() {
|
||||
AddField(Telemetry::FieldType::Session, "Init_Time", init_time);
|
||||
|
||||
u64 program_id{};
|
||||
const Loader::ResultStatus res{System::GetInstance().GetAppLoader().ReadProgramId(program_id)};
|
||||
const Loader::ResultStatus res{app_loader.ReadProgramId(program_id)};
|
||||
if (res == Loader::ResultStatus::Success) {
|
||||
const std::string formatted_program_id{fmt::format("{:016X}", program_id)};
|
||||
AddField(Telemetry::FieldType::Session, "ProgramId", formatted_program_id);
|
||||
|
||||
std::string name;
|
||||
System::GetInstance().GetAppLoader().ReadTitle(name);
|
||||
app_loader.ReadTitle(name);
|
||||
|
||||
if (name.empty()) {
|
||||
auto [nacp, icon_file] = FileSys::PatchManager(program_id).GetControlMetadata();
|
||||
if (nacp != nullptr)
|
||||
if (nacp != nullptr) {
|
||||
name = nacp->GetApplicationName();
|
||||
}
|
||||
}
|
||||
|
||||
if (!name.empty())
|
||||
if (!name.empty()) {
|
||||
AddField(Telemetry::FieldType::Session, "ProgramName", name);
|
||||
}
|
||||
}
|
||||
|
||||
AddField(Telemetry::FieldType::Session, "ProgramFormat",
|
||||
static_cast<u8>(System::GetInstance().GetAppLoader().GetFileType()));
|
||||
static_cast<u8>(app_loader.GetFileType()));
|
||||
|
||||
// Log application information
|
||||
Telemetry::AppendBuildInfo(field_collection);
|
||||
@@ -162,27 +186,6 @@ TelemetrySession::TelemetrySession() {
|
||||
Settings::values.use_docked_mode);
|
||||
}
|
||||
|
||||
TelemetrySession::~TelemetrySession() {
|
||||
// Log one-time session end information
|
||||
const s64 shutdown_time{std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count()};
|
||||
AddField(Telemetry::FieldType::Session, "Shutdown_Time", shutdown_time);
|
||||
|
||||
#ifdef ENABLE_WEB_SERVICE
|
||||
auto backend = std::make_unique<WebService::TelemetryJson>(
|
||||
Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token);
|
||||
#else
|
||||
auto backend = std::make_unique<Telemetry::NullVisitor>();
|
||||
#endif
|
||||
|
||||
// Complete the session, submitting to web service if necessary
|
||||
field_collection.Accept(*backend);
|
||||
if (Settings::values.enable_telemetry)
|
||||
backend->Complete();
|
||||
backend = nullptr;
|
||||
}
|
||||
|
||||
bool TelemetrySession::SubmitTestcase() {
|
||||
#ifdef ENABLE_WEB_SERVICE
|
||||
auto backend = std::make_unique<WebService::TelemetryJson>(
|
||||
|
||||
@@ -4,10 +4,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "common/telemetry.h"
|
||||
|
||||
namespace Loader {
|
||||
class AppLoader;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
/**
|
||||
@@ -15,11 +18,33 @@ namespace Core {
|
||||
* session, logging any one-time fields. Interfaces with the telemetry backend used for submitting
|
||||
* data to the web service. Submits session data on close.
|
||||
*/
|
||||
class TelemetrySession : NonCopyable {
|
||||
class TelemetrySession {
|
||||
public:
|
||||
TelemetrySession();
|
||||
explicit TelemetrySession();
|
||||
~TelemetrySession();
|
||||
|
||||
TelemetrySession(const TelemetrySession&) = delete;
|
||||
TelemetrySession& operator=(const TelemetrySession&) = delete;
|
||||
|
||||
TelemetrySession(TelemetrySession&&) = delete;
|
||||
TelemetrySession& operator=(TelemetrySession&&) = delete;
|
||||
|
||||
/**
|
||||
* Adds the initial telemetry info necessary when starting up a title.
|
||||
*
|
||||
* This includes information such as:
|
||||
* - Telemetry ID
|
||||
* - Initialization time
|
||||
* - Title ID
|
||||
* - Title name
|
||||
* - Title file format
|
||||
* - Miscellaneous settings values.
|
||||
*
|
||||
* @param app_loader The application loader to use to retrieve
|
||||
* title-specific information.
|
||||
*/
|
||||
void AddInitialInfo(Loader::AppLoader& app_loader);
|
||||
|
||||
/**
|
||||
* Wrapper around the Telemetry::FieldCollection::AddField method.
|
||||
* @param type Type of the field to add.
|
||||
|
||||
@@ -6,15 +6,8 @@
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "core/frontend/input.h"
|
||||
#include "input_common/main.h"
|
||||
|
||||
union SDL_Event;
|
||||
|
||||
namespace Common {
|
||||
class ParamPackage;
|
||||
} // namespace Common
|
||||
|
||||
namespace InputCommon::Polling {
|
||||
class DevicePoller;
|
||||
enum class DeviceType;
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
@@ -15,7 +14,6 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <SDL.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/param_package.h"
|
||||
@@ -23,12 +21,10 @@
|
||||
#include "core/frontend/input.h"
|
||||
#include "input_common/sdl/sdl_impl.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
namespace SDL {
|
||||
namespace InputCommon::SDL {
|
||||
|
||||
static std::string GetGUID(SDL_Joystick* joystick) {
|
||||
SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
|
||||
const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
|
||||
char guid_str[33];
|
||||
SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
|
||||
return guid_str;
|
||||
@@ -37,26 +33,27 @@ static std::string GetGUID(SDL_Joystick* joystick) {
|
||||
/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
|
||||
static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event);
|
||||
|
||||
static int SDLEventWatcher(void* userdata, SDL_Event* event) {
|
||||
SDLState* sdl_state = reinterpret_cast<SDLState*>(userdata);
|
||||
static int SDLEventWatcher(void* user_data, SDL_Event* event) {
|
||||
auto* const sdl_state = static_cast<SDLState*>(user_data);
|
||||
|
||||
// Don't handle the event if we are configuring
|
||||
if (sdl_state->polling) {
|
||||
sdl_state->event_queue.Push(*event);
|
||||
} else {
|
||||
sdl_state->HandleGameControllerEvent(*event);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
class SDLJoystick {
|
||||
public:
|
||||
SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
|
||||
decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose)
|
||||
: guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, deleter} {}
|
||||
SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick)
|
||||
: guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose} {}
|
||||
|
||||
void SetButton(int button, bool value) {
|
||||
std::lock_guard lock{mutex};
|
||||
state.buttons[button] = value;
|
||||
state.buttons.insert_or_assign(button, value);
|
||||
}
|
||||
|
||||
bool GetButton(int button) const {
|
||||
@@ -66,7 +63,7 @@ public:
|
||||
|
||||
void SetAxis(int axis, Sint16 value) {
|
||||
std::lock_guard lock{mutex};
|
||||
state.axes[axis] = value;
|
||||
state.axes.insert_or_assign(axis, value);
|
||||
}
|
||||
|
||||
float GetAxis(int axis) const {
|
||||
@@ -93,7 +90,7 @@ public:
|
||||
|
||||
void SetHat(int hat, Uint8 direction) {
|
||||
std::lock_guard lock{mutex};
|
||||
state.hats[hat] = direction;
|
||||
state.hats.insert_or_assign(hat, direction);
|
||||
}
|
||||
|
||||
bool GetHatDirection(int hat, Uint8 direction) const {
|
||||
@@ -118,10 +115,8 @@ public:
|
||||
return sdl_joystick.get();
|
||||
}
|
||||
|
||||
void SetSDLJoystick(SDL_Joystick* joystick,
|
||||
decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose) {
|
||||
sdl_joystick =
|
||||
std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)>(joystick, deleter);
|
||||
void SetSDLJoystick(SDL_Joystick* joystick) {
|
||||
sdl_joystick.reset(joystick);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -136,59 +131,57 @@ private:
|
||||
mutable std::mutex mutex;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the nth joystick with the corresponding GUID
|
||||
*/
|
||||
std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) {
|
||||
std::lock_guard lock{joystick_map_mutex};
|
||||
const auto it = joystick_map.find(guid);
|
||||
if (it != joystick_map.end()) {
|
||||
while (it->second.size() <= port) {
|
||||
auto joystick = std::make_shared<SDLJoystick>(guid, it->second.size(), nullptr,
|
||||
[](SDL_Joystick*) {});
|
||||
while (it->second.size() <= static_cast<std::size_t>(port)) {
|
||||
auto joystick =
|
||||
std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()), nullptr);
|
||||
it->second.emplace_back(std::move(joystick));
|
||||
}
|
||||
return it->second[port];
|
||||
}
|
||||
auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, [](SDL_Joystick*) {});
|
||||
auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr);
|
||||
return joystick_map[guid].emplace_back(std::move(joystick));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check how many identical joysticks (by guid) were connected before the one with sdl_id and so tie
|
||||
* it to a SDLJoystick with the same guid and that port
|
||||
*/
|
||||
std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
|
||||
auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
|
||||
const std::string guid = GetGUID(sdl_joystick);
|
||||
|
||||
std::lock_guard lock{joystick_map_mutex};
|
||||
auto map_it = joystick_map.find(guid);
|
||||
const auto map_it = joystick_map.find(guid);
|
||||
if (map_it != joystick_map.end()) {
|
||||
auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
|
||||
[&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
|
||||
return sdl_joystick == joystick->GetSDLJoystick();
|
||||
});
|
||||
const auto vec_it =
|
||||
std::find_if(map_it->second.begin(), map_it->second.end(),
|
||||
[&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
|
||||
return sdl_joystick == joystick->GetSDLJoystick();
|
||||
});
|
||||
if (vec_it != map_it->second.end()) {
|
||||
// This is the common case: There is already an existing SDL_Joystick maped to a
|
||||
// SDLJoystick. return the SDLJoystick
|
||||
return *vec_it;
|
||||
}
|
||||
|
||||
// Search for a SDLJoystick without a mapped SDL_Joystick...
|
||||
auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(),
|
||||
[](const std::shared_ptr<SDLJoystick>& joystick) {
|
||||
return !joystick->GetSDLJoystick();
|
||||
});
|
||||
const auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(),
|
||||
[](const std::shared_ptr<SDLJoystick>& joystick) {
|
||||
return !joystick->GetSDLJoystick();
|
||||
});
|
||||
if (nullptr_it != map_it->second.end()) {
|
||||
// ... and map it
|
||||
(*nullptr_it)->SetSDLJoystick(sdl_joystick);
|
||||
return *nullptr_it;
|
||||
}
|
||||
|
||||
// There is no SDLJoystick without a mapped SDL_Joystick
|
||||
// Create a new SDLJoystick
|
||||
auto joystick = std::make_shared<SDLJoystick>(guid, map_it->second.size(), sdl_joystick);
|
||||
const int port = static_cast<int>(map_it->second.size());
|
||||
auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick);
|
||||
return map_it->second.emplace_back(std::move(joystick));
|
||||
}
|
||||
|
||||
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
|
||||
return joystick_map[guid].emplace_back(std::move(joystick));
|
||||
}
|
||||
@@ -215,17 +208,19 @@ void SDLState::InitJoystick(int joystick_index) {
|
||||
(*it)->SetSDLJoystick(sdl_joystick);
|
||||
return;
|
||||
}
|
||||
auto joystick = std::make_shared<SDLJoystick>(guid, joystick_guid_list.size(), sdl_joystick);
|
||||
const int port = static_cast<int>(joystick_guid_list.size());
|
||||
auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick);
|
||||
joystick_guid_list.emplace_back(std::move(joystick));
|
||||
}
|
||||
|
||||
void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
|
||||
std::string guid = GetGUID(sdl_joystick);
|
||||
const std::string guid = GetGUID(sdl_joystick);
|
||||
|
||||
std::shared_ptr<SDLJoystick> joystick;
|
||||
{
|
||||
std::lock_guard lock{joystick_map_mutex};
|
||||
// This call to guid is safe since the joystick is guaranteed to be in the map
|
||||
auto& joystick_guid_list = joystick_map[guid];
|
||||
const auto& joystick_guid_list = joystick_map[guid];
|
||||
const auto joystick_it =
|
||||
std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
|
||||
[&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
|
||||
@@ -233,9 +228,10 @@ void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
|
||||
});
|
||||
joystick = *joystick_it;
|
||||
}
|
||||
// Destruct SDL_Joystick outside the lock guard because SDL can internally call event calback
|
||||
// which locks the mutex again
|
||||
joystick->SetSDLJoystick(nullptr, [](SDL_Joystick*) {});
|
||||
|
||||
// Destruct SDL_Joystick outside the lock guard because SDL can internally call the
|
||||
// event callback which locks the mutex again.
|
||||
joystick->SetSDLJoystick(nullptr);
|
||||
}
|
||||
|
||||
void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
|
||||
@@ -317,9 +313,10 @@ public:
|
||||
trigger_if_greater(trigger_if_greater_) {}
|
||||
|
||||
bool GetStatus() const override {
|
||||
float axis_value = joystick->GetAxis(axis);
|
||||
if (trigger_if_greater)
|
||||
const float axis_value = joystick->GetAxis(axis);
|
||||
if (trigger_if_greater) {
|
||||
return axis_value > threshold;
|
||||
}
|
||||
return axis_value < threshold;
|
||||
}
|
||||
|
||||
@@ -444,7 +441,7 @@ public:
|
||||
const int port = params.Get("port", 0);
|
||||
const int axis_x = params.Get("axis_x", 0);
|
||||
const int axis_y = params.Get("axis_y", 1);
|
||||
float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f);
|
||||
const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f);
|
||||
|
||||
auto joystick = state.GetSDLJoystickByGUID(guid, port);
|
||||
|
||||
@@ -470,7 +467,7 @@ SDLState::SDLState() {
|
||||
return;
|
||||
}
|
||||
if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) {
|
||||
LOG_ERROR(Input, "Failed to set Hint for background events", SDL_GetError());
|
||||
LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError());
|
||||
}
|
||||
|
||||
SDL_AddEventWatch(&SDLEventWatcher, this);
|
||||
@@ -507,12 +504,12 @@ SDLState::~SDLState() {
|
||||
}
|
||||
}
|
||||
|
||||
Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
|
||||
static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
|
||||
Common::ParamPackage params({{"engine", "sdl"}});
|
||||
|
||||
switch (event.type) {
|
||||
case SDL_JOYAXISMOTION: {
|
||||
auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
|
||||
const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
|
||||
params.Set("port", joystick->GetPort());
|
||||
params.Set("guid", joystick->GetGUID());
|
||||
params.Set("axis", event.jaxis.axis);
|
||||
@@ -526,14 +523,14 @@ Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Eve
|
||||
break;
|
||||
}
|
||||
case SDL_JOYBUTTONUP: {
|
||||
auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
|
||||
const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
|
||||
params.Set("port", joystick->GetPort());
|
||||
params.Set("guid", joystick->GetGUID());
|
||||
params.Set("button", event.jbutton.button);
|
||||
break;
|
||||
}
|
||||
case SDL_JOYHATMOTION: {
|
||||
auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
|
||||
const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
|
||||
params.Set("port", joystick->GetPort());
|
||||
params.Set("guid", joystick->GetGUID());
|
||||
params.Set("hat", event.jhat.hat);
|
||||
@@ -607,8 +604,8 @@ public:
|
||||
SDLPoller::Start();
|
||||
|
||||
// Reset stored axes
|
||||
analog_xaxis = -1;
|
||||
analog_yaxis = -1;
|
||||
analog_x_axis = -1;
|
||||
analog_y_axis = -1;
|
||||
analog_axes_joystick = -1;
|
||||
}
|
||||
|
||||
@@ -620,25 +617,25 @@ public:
|
||||
}
|
||||
// An analog device needs two axes, so we need to store the axis for later and wait for
|
||||
// a second SDL event. The axes also must be from the same joystick.
|
||||
int axis = event.jaxis.axis;
|
||||
if (analog_xaxis == -1) {
|
||||
analog_xaxis = axis;
|
||||
const int axis = event.jaxis.axis;
|
||||
if (analog_x_axis == -1) {
|
||||
analog_x_axis = axis;
|
||||
analog_axes_joystick = event.jaxis.which;
|
||||
} else if (analog_yaxis == -1 && analog_xaxis != axis &&
|
||||
} else if (analog_y_axis == -1 && analog_x_axis != axis &&
|
||||
analog_axes_joystick == event.jaxis.which) {
|
||||
analog_yaxis = axis;
|
||||
analog_y_axis = axis;
|
||||
}
|
||||
}
|
||||
Common::ParamPackage params;
|
||||
if (analog_xaxis != -1 && analog_yaxis != -1) {
|
||||
auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
|
||||
if (analog_x_axis != -1 && analog_y_axis != -1) {
|
||||
const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
|
||||
params.Set("engine", "sdl");
|
||||
params.Set("port", joystick->GetPort());
|
||||
params.Set("guid", joystick->GetGUID());
|
||||
params.Set("axis_x", analog_xaxis);
|
||||
params.Set("axis_y", analog_yaxis);
|
||||
analog_xaxis = -1;
|
||||
analog_yaxis = -1;
|
||||
params.Set("axis_x", analog_x_axis);
|
||||
params.Set("axis_y", analog_y_axis);
|
||||
analog_x_axis = -1;
|
||||
analog_y_axis = -1;
|
||||
analog_axes_joystick = -1;
|
||||
return params;
|
||||
}
|
||||
@@ -646,8 +643,8 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
int analog_xaxis = -1;
|
||||
int analog_yaxis = -1;
|
||||
int analog_x_axis = -1;
|
||||
int analog_y_axis = -1;
|
||||
SDL_JoystickID analog_axes_joystick = -1;
|
||||
};
|
||||
} // namespace Polling
|
||||
@@ -667,5 +664,4 @@ SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
|
||||
return pollers;
|
||||
}
|
||||
|
||||
} // namespace SDL
|
||||
} // namespace InputCommon
|
||||
} // namespace InputCommon::SDL
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include "common/common_types.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "input_common/sdl/sdl.h"
|
||||
|
||||
@@ -16,9 +19,9 @@ using SDL_JoystickID = s32;
|
||||
|
||||
namespace InputCommon::SDL {
|
||||
|
||||
class SDLJoystick;
|
||||
class SDLButtonFactory;
|
||||
class SDLAnalogFactory;
|
||||
class SDLButtonFactory;
|
||||
class SDLJoystick;
|
||||
|
||||
class SDLState : public State {
|
||||
public:
|
||||
@@ -31,7 +34,13 @@ public:
|
||||
/// Handle SDL_Events for joysticks from SDL_PollEvent
|
||||
void HandleGameControllerEvent(const SDL_Event& event);
|
||||
|
||||
/// Get the nth joystick with the corresponding GUID
|
||||
std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id);
|
||||
|
||||
/**
|
||||
* Check how many identical joysticks (by guid) were connected before the one with sdl_id and so
|
||||
* tie it to a SDLJoystick with the same guid and that port
|
||||
*/
|
||||
std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port);
|
||||
|
||||
/// Get all DevicePoller that use the SDL backend for a specific device type
|
||||
|
||||
@@ -42,8 +42,6 @@ add_library(video_core STATIC
|
||||
renderer_opengl/gl_device.h
|
||||
renderer_opengl/gl_global_cache.cpp
|
||||
renderer_opengl/gl_global_cache.h
|
||||
renderer_opengl/gl_primitive_assembler.cpp
|
||||
renderer_opengl/gl_primitive_assembler.h
|
||||
renderer_opengl/gl_rasterizer.cpp
|
||||
renderer_opengl/gl_rasterizer.h
|
||||
renderer_opengl/gl_rasterizer_cache.cpp
|
||||
@@ -102,6 +100,9 @@ add_library(video_core STATIC
|
||||
shader/decode/xmad.cpp
|
||||
shader/decode/other.cpp
|
||||
shader/decode.cpp
|
||||
shader/node_helper.cpp
|
||||
shader/node_helper.h
|
||||
shader/node.h
|
||||
shader/shader_ir.cpp
|
||||
shader/shader_ir.h
|
||||
shader/track.cpp
|
||||
|
||||
@@ -40,6 +40,13 @@ bool DmaPusher::Step() {
|
||||
}
|
||||
|
||||
const CommandList& command_list{dma_pushbuffer.front()};
|
||||
ASSERT_OR_EXECUTE(!command_list.empty(), {
|
||||
// Somehow the command_list is empty, in order to avoid a crash
|
||||
// We ignore it and assume its size is 0.
|
||||
dma_pushbuffer.pop();
|
||||
dma_pushbuffer_subindex = 0;
|
||||
return true;
|
||||
});
|
||||
const CommandListHeader command_list_header{command_list[dma_pushbuffer_subindex++]};
|
||||
GPUVAddr dma_get = command_list_header.addr;
|
||||
GPUVAddr dma_put = dma_get + command_list_header.size * sizeof(u32);
|
||||
|
||||
@@ -140,7 +140,7 @@ public:
|
||||
|
||||
BitField<0, 16, u32> shared_alloc;
|
||||
|
||||
BitField<0, 31, u32> block_dim_x;
|
||||
BitField<16, 16, u32> block_dim_x;
|
||||
union {
|
||||
BitField<0, 16, u32> block_dim_y;
|
||||
BitField<16, 16, u32> block_dim_z;
|
||||
@@ -153,7 +153,7 @@ public:
|
||||
|
||||
INSERT_PADDING_WORDS(0x8);
|
||||
|
||||
struct {
|
||||
struct ConstBufferConfig {
|
||||
u32 address_low;
|
||||
union {
|
||||
BitField<0, 8, u32> address_high;
|
||||
@@ -163,7 +163,8 @@ public:
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high.Value()) << 32) |
|
||||
address_low);
|
||||
}
|
||||
} const_buffer_config[8];
|
||||
};
|
||||
std::array<ConstBufferConfig, NumConstBuffers> const_buffer_config;
|
||||
|
||||
union {
|
||||
BitField<0, 20, u32> local_pos_alloc;
|
||||
|
||||
@@ -59,6 +59,7 @@ public:
|
||||
static constexpr std::size_t NumCBData = 16;
|
||||
static constexpr std::size_t NumVertexArrays = 32;
|
||||
static constexpr std::size_t NumVertexAttributes = 32;
|
||||
static constexpr std::size_t NumVaryings = 31;
|
||||
static constexpr std::size_t NumTextureSamplers = 32;
|
||||
static constexpr std::size_t NumClipDistances = 8;
|
||||
static constexpr std::size_t MaxShaderProgram = 6;
|
||||
|
||||
@@ -98,6 +98,10 @@ union Attribute {
|
||||
BitField<22, 2, u64> element;
|
||||
BitField<24, 6, Index> index;
|
||||
BitField<47, 3, AttributeSize> size;
|
||||
|
||||
bool IsPhysical() const {
|
||||
return element == 0 && static_cast<u64>(index.Value()) == 0;
|
||||
}
|
||||
} fmt20;
|
||||
|
||||
union {
|
||||
@@ -499,6 +503,11 @@ enum class SystemVariable : u64 {
|
||||
CircularQueueEntryAddressHigh = 0x63,
|
||||
};
|
||||
|
||||
enum class PhysicalAttributeDirection : u64 {
|
||||
Input = 0,
|
||||
Output = 1,
|
||||
};
|
||||
|
||||
union Instruction {
|
||||
Instruction& operator=(const Instruction& instr) {
|
||||
value = instr.value;
|
||||
@@ -520,6 +529,11 @@ union Instruction {
|
||||
BitField<39, 8, Register> gpr39;
|
||||
BitField<48, 16, u64> opcode;
|
||||
|
||||
union {
|
||||
BitField<8, 8, Register> gpr;
|
||||
BitField<20, 24, s64> offset;
|
||||
} gmem;
|
||||
|
||||
union {
|
||||
BitField<20, 16, u64> imm20_16;
|
||||
BitField<20, 19, u64> imm20_19;
|
||||
@@ -587,6 +601,7 @@ union Instruction {
|
||||
} alu;
|
||||
|
||||
union {
|
||||
BitField<38, 1, u64> idx;
|
||||
BitField<51, 1, u64> saturate;
|
||||
BitField<52, 2, IpaSampleMode> sample_mode;
|
||||
BitField<54, 2, IpaInterpMode> interp_mode;
|
||||
@@ -802,15 +817,24 @@ union Instruction {
|
||||
union {
|
||||
BitField<48, 3, UniformType> type;
|
||||
BitField<46, 2, u64> cache_mode;
|
||||
BitField<20, 24, s64> immediate_offset;
|
||||
} ldg;
|
||||
|
||||
union {
|
||||
BitField<48, 3, UniformType> type;
|
||||
BitField<46, 2, u64> cache_mode;
|
||||
BitField<20, 24, s64> immediate_offset;
|
||||
} stg;
|
||||
|
||||
union {
|
||||
BitField<32, 1, PhysicalAttributeDirection> direction;
|
||||
BitField<47, 3, AttributeSize> size;
|
||||
BitField<20, 11, u64> address;
|
||||
} al2p;
|
||||
|
||||
union {
|
||||
BitField<53, 3, UniformType> type;
|
||||
BitField<52, 1, u64> extended;
|
||||
} generic;
|
||||
|
||||
union {
|
||||
BitField<0, 3, u64> pred0;
|
||||
BitField<3, 3, u64> pred3;
|
||||
@@ -1371,11 +1395,14 @@ public:
|
||||
LD_L,
|
||||
LD_S,
|
||||
LD_C,
|
||||
LD, // Load from generic memory
|
||||
LDG, // Load from global memory
|
||||
ST_A,
|
||||
ST_L,
|
||||
ST_S,
|
||||
LDG, // Load from global memory
|
||||
STG, // Store in global memory
|
||||
ST, // Store in generic memory
|
||||
STG, // Store in global memory
|
||||
AL2P, // Transforms attribute memory into physical memory
|
||||
TEX,
|
||||
TEX_B, // Texture Load Bindless
|
||||
TXQ, // Texture Query
|
||||
@@ -1636,16 +1663,20 @@ private:
|
||||
INST("111000100100----", Id::BRA, Type::Flow, "BRA"),
|
||||
INST("1111000011111---", Id::SYNC, Type::Flow, "SYNC"),
|
||||
INST("111000110100---", Id::BRK, Type::Flow, "BRK"),
|
||||
INST("111000110000----", Id::EXIT, Type::Flow, "EXIT"),
|
||||
INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"),
|
||||
INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"),
|
||||
INST("1110111101001---", Id::LD_S, Type::Memory, "LD_S"),
|
||||
INST("1110111101000---", Id::LD_L, Type::Memory, "LD_L"),
|
||||
INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"),
|
||||
INST("100-------------", Id::LD, Type::Memory, "LD"),
|
||||
INST("1110111011010---", Id::LDG, Type::Memory, "LDG"),
|
||||
INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"),
|
||||
INST("1110111101011---", Id::ST_S, Type::Memory, "ST_S"),
|
||||
INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"),
|
||||
INST("1110111011010---", Id::LDG, Type::Memory, "LDG"),
|
||||
INST("101-------------", Id::ST, Type::Memory, "ST"),
|
||||
INST("1110111011011---", Id::STG, Type::Memory, "STG"),
|
||||
INST("1110111110100---", Id::AL2P, Type::Memory, "AL2P"),
|
||||
INST("110000----111---", Id::TEX, Type::Texture, "TEX"),
|
||||
INST("1101111010111---", Id::TEX_B, Type::Texture, "TEX_B"),
|
||||
INST("1101111101001---", Id::TXQ, Type::Texture, "TXQ"),
|
||||
@@ -1656,7 +1687,6 @@ private:
|
||||
INST("1101111100------", Id::TLD4S, Type::Texture, "TLD4S"),
|
||||
INST("110111110110----", Id::TMML_B, Type::Texture, "TMML_B"),
|
||||
INST("1101111101011---", Id::TMML, Type::Texture, "TMML"),
|
||||
INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"),
|
||||
INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
|
||||
INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"),
|
||||
INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"),
|
||||
|
||||
@@ -75,7 +75,7 @@ void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPus
|
||||
|
||||
void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
|
||||
const u64 fence{PushCommand(SubmitListCommand(std::move(entries)))};
|
||||
const s64 synchronization_ticks{Core::Timing::usToCycles(9000)};
|
||||
const s64 synchronization_ticks{Core::Timing::usToCycles(std::chrono::microseconds{9000})};
|
||||
system.CoreTiming().ScheduleEvent(synchronization_ticks, synchronization_event, fence);
|
||||
}
|
||||
|
||||
|
||||
@@ -81,12 +81,6 @@ struct CommandDataContainer {
|
||||
CommandDataContainer(CommandData&& data, u64 next_fence)
|
||||
: data{std::move(data)}, fence{next_fence} {}
|
||||
|
||||
CommandDataContainer& operator=(const CommandDataContainer& t) {
|
||||
data = std::move(t.data);
|
||||
fence = t.fence;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CommandData data;
|
||||
u64 fence{};
|
||||
};
|
||||
|
||||
@@ -120,7 +120,9 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) {
|
||||
|
||||
// An instruction with the Exit flag will not actually
|
||||
// cause an exit if it's executed inside a delay slot.
|
||||
if (opcode.is_exit && !is_delay_slot) {
|
||||
// TODO(Blinkhawk): Reversed to always exit. The behavior explained above requires further
|
||||
// testing on the MME code.
|
||||
if (opcode.is_exit) {
|
||||
// Exit has a delay slot, execute the next instruction
|
||||
Step(offset, true);
|
||||
return false;
|
||||
|
||||
@@ -163,8 +163,8 @@ private:
|
||||
static constexpr u64 page_size{1 << page_bits};
|
||||
static constexpr u64 page_mask{page_size - 1};
|
||||
|
||||
/// Address space in bits, this is fairly arbitrary but sufficiently large.
|
||||
static constexpr u32 address_space_width{39};
|
||||
/// Address space in bits, according to Tegra X1 TRM
|
||||
static constexpr u32 address_space_width{40};
|
||||
/// Start address for mapping, this is fairly arbitrary but must be non-zero.
|
||||
static constexpr GPUVAddr address_space_base{0x100000};
|
||||
/// End of address space, based on address space in bits.
|
||||
|
||||
@@ -144,8 +144,9 @@ protected:
|
||||
|
||||
object->SetIsRegistered(false);
|
||||
rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), -1);
|
||||
const CacheAddr addr = object->GetCacheAddr();
|
||||
interval_cache.subtract({GetInterval(object), ObjectSet{object}});
|
||||
map_cache.erase(object->GetCacheAddr());
|
||||
map_cache.erase(addr);
|
||||
}
|
||||
|
||||
/// Returns a ticks counter used for tracking when cached objects were last modified
|
||||
|
||||
@@ -71,16 +71,6 @@ GLintptr OGLBufferCache::UploadHostMemory(const void* raw_pointer, std::size_t s
|
||||
return uploaded_offset;
|
||||
}
|
||||
|
||||
std::tuple<u8*, GLintptr> OGLBufferCache::ReserveMemory(std::size_t size, std::size_t alignment) {
|
||||
AlignBuffer(alignment);
|
||||
u8* const uploaded_ptr = buffer_ptr;
|
||||
const GLintptr uploaded_offset = buffer_offset;
|
||||
|
||||
buffer_ptr += size;
|
||||
buffer_offset += size;
|
||||
return std::make_tuple(uploaded_ptr, uploaded_offset);
|
||||
}
|
||||
|
||||
bool OGLBufferCache::Map(std::size_t max_size) {
|
||||
bool invalidate;
|
||||
std::tie(buffer_ptr, buffer_offset_base, invalidate) =
|
||||
|
||||
@@ -61,9 +61,6 @@ public:
|
||||
/// Uploads from a host memory. Returns host's buffer offset where it's been allocated.
|
||||
GLintptr UploadHostMemory(const void* raw_pointer, std::size_t size, std::size_t alignment = 4);
|
||||
|
||||
/// Reserves memory to be used by host's CPU. Returns mapped address and offset.
|
||||
std::tuple<u8*, GLintptr> ReserveMemory(std::size_t size, std::size_t alignment = 4);
|
||||
|
||||
bool Map(std::size_t max_size);
|
||||
void Unmap();
|
||||
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "video_core/renderer_opengl/gl_device.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
@@ -21,11 +24,23 @@ T GetInteger(GLenum pname) {
|
||||
|
||||
Device::Device() {
|
||||
uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
|
||||
max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS);
|
||||
max_varyings = GetInteger<u32>(GL_MAX_VARYING_VECTORS);
|
||||
has_variable_aoffi = TestVariableAoffi();
|
||||
has_component_indexing_bug = TestComponentIndexingBug();
|
||||
}
|
||||
|
||||
Device::Device(std::nullptr_t) {
|
||||
uniform_buffer_alignment = 0;
|
||||
max_vertex_attributes = 16;
|
||||
max_varyings = 15;
|
||||
has_variable_aoffi = true;
|
||||
has_component_indexing_bug = false;
|
||||
}
|
||||
|
||||
bool Device::TestVariableAoffi() {
|
||||
const GLchar* AOFFI_TEST = R"(#version 430 core
|
||||
// This is a unit test, please ignore me on apitrace bug reports.
|
||||
uniform sampler2D tex;
|
||||
uniform ivec2 variable_offset;
|
||||
void main() {
|
||||
@@ -42,4 +57,53 @@ void main() {
|
||||
return supported;
|
||||
}
|
||||
|
||||
bool Device::TestComponentIndexingBug() {
|
||||
constexpr char log_message[] = "Renderer_ComponentIndexingBug: {}";
|
||||
const GLchar* COMPONENT_TEST = R"(#version 430 core
|
||||
layout (std430, binding = 0) buffer OutputBuffer {
|
||||
uint output_value;
|
||||
};
|
||||
layout (std140, binding = 0) uniform InputBuffer {
|
||||
uvec4 input_value[4096];
|
||||
};
|
||||
layout (location = 0) uniform uint idx;
|
||||
void main() {
|
||||
output_value = input_value[idx >> 2][idx & 3];
|
||||
})";
|
||||
const GLuint shader{glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &COMPONENT_TEST)};
|
||||
SCOPE_EXIT({ glDeleteProgram(shader); });
|
||||
glUseProgram(shader);
|
||||
|
||||
OGLVertexArray vao;
|
||||
vao.Create();
|
||||
glBindVertexArray(vao.handle);
|
||||
|
||||
constexpr std::array<GLuint, 8> values{0, 0, 0, 0, 0x1236327, 0x985482, 0x872753, 0x2378432};
|
||||
OGLBuffer ubo;
|
||||
ubo.Create();
|
||||
glNamedBufferData(ubo.handle, sizeof(values), values.data(), GL_STATIC_DRAW);
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo.handle);
|
||||
|
||||
OGLBuffer ssbo;
|
||||
ssbo.Create();
|
||||
glNamedBufferStorage(ssbo.handle, sizeof(GLuint), nullptr, GL_CLIENT_STORAGE_BIT);
|
||||
|
||||
for (GLuint index = 4; index < 8; ++index) {
|
||||
glInvalidateBufferData(ssbo.handle);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssbo.handle);
|
||||
|
||||
glProgramUniform1ui(shader, 0, index);
|
||||
glDrawArrays(GL_POINTS, 0, 1);
|
||||
|
||||
GLuint result;
|
||||
glGetNamedBufferSubData(ssbo.handle, 0, sizeof(result), &result);
|
||||
if (result != values.at(index)) {
|
||||
LOG_INFO(Render_OpenGL, log_message, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
LOG_INFO(Render_OpenGL, log_message, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -5,26 +5,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class Device {
|
||||
public:
|
||||
Device();
|
||||
explicit Device();
|
||||
explicit Device(std::nullptr_t);
|
||||
|
||||
std::size_t GetUniformBufferAlignment() const {
|
||||
return uniform_buffer_alignment;
|
||||
}
|
||||
|
||||
u32 GetMaxVertexAttributes() const {
|
||||
return max_vertex_attributes;
|
||||
}
|
||||
|
||||
u32 GetMaxVaryings() const {
|
||||
return max_varyings;
|
||||
}
|
||||
|
||||
bool HasVariableAoffi() const {
|
||||
return has_variable_aoffi;
|
||||
}
|
||||
|
||||
bool HasComponentIndexingBug() const {
|
||||
return has_component_indexing_bug;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool TestVariableAoffi();
|
||||
static bool TestComponentIndexingBug();
|
||||
|
||||
std::size_t uniform_buffer_alignment{};
|
||||
u32 max_vertex_attributes{};
|
||||
u32 max_varyings{};
|
||||
bool has_variable_aoffi{};
|
||||
bool has_component_indexing_bug{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_buffer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_primitive_assembler.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
constexpr u32 TRIANGLES_PER_QUAD = 6;
|
||||
constexpr std::array<u32, TRIANGLES_PER_QUAD> QUAD_MAP = {0, 1, 2, 0, 2, 3};
|
||||
|
||||
PrimitiveAssembler::PrimitiveAssembler(OGLBufferCache& buffer_cache) : buffer_cache(buffer_cache) {}
|
||||
|
||||
PrimitiveAssembler::~PrimitiveAssembler() = default;
|
||||
|
||||
std::size_t PrimitiveAssembler::CalculateQuadSize(u32 count) const {
|
||||
ASSERT_MSG(count % 4 == 0, "Quad count is expected to be a multiple of 4");
|
||||
return (count / 4) * TRIANGLES_PER_QUAD * sizeof(GLuint);
|
||||
}
|
||||
|
||||
GLintptr PrimitiveAssembler::MakeQuadArray(u32 first, u32 count) {
|
||||
const std::size_t size{CalculateQuadSize(count)};
|
||||
auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(size);
|
||||
|
||||
for (u32 primitive = 0; primitive < count / 4; ++primitive) {
|
||||
for (u32 i = 0; i < TRIANGLES_PER_QUAD; ++i) {
|
||||
const u32 index = first + primitive * 4 + QUAD_MAP[i];
|
||||
std::memcpy(dst_pointer, &index, sizeof(index));
|
||||
dst_pointer += sizeof(index);
|
||||
}
|
||||
}
|
||||
|
||||
return index_offset;
|
||||
}
|
||||
|
||||
GLintptr PrimitiveAssembler::MakeQuadIndexed(GPUVAddr gpu_addr, std::size_t index_size, u32 count) {
|
||||
const std::size_t map_size{CalculateQuadSize(count)};
|
||||
auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(map_size);
|
||||
|
||||
auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
|
||||
const u8* source{memory_manager.GetPointer(gpu_addr)};
|
||||
|
||||
for (u32 primitive = 0; primitive < count / 4; ++primitive) {
|
||||
for (std::size_t i = 0; i < TRIANGLES_PER_QUAD; ++i) {
|
||||
const u32 index = primitive * 4 + QUAD_MAP[i];
|
||||
const u8* src_offset = source + (index * index_size);
|
||||
|
||||
std::memcpy(dst_pointer, src_offset, index_size);
|
||||
dst_pointer += index_size;
|
||||
}
|
||||
}
|
||||
|
||||
return index_offset;
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
@@ -1,31 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class OGLBufferCache;
|
||||
|
||||
class PrimitiveAssembler {
|
||||
public:
|
||||
explicit PrimitiveAssembler(OGLBufferCache& buffer_cache);
|
||||
~PrimitiveAssembler();
|
||||
|
||||
/// Calculates the size required by MakeQuadArray and MakeQuadIndexed.
|
||||
std::size_t CalculateQuadSize(u32 count) const;
|
||||
|
||||
GLintptr MakeQuadArray(u32 first, u32 count);
|
||||
|
||||
GLintptr MakeQuadIndexed(GPUVAddr gpu_addr, std::size_t index_size, u32 count);
|
||||
|
||||
private:
|
||||
OGLBufferCache& buffer_cache;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
@@ -98,9 +98,11 @@ struct FramebufferCacheKey {
|
||||
}
|
||||
};
|
||||
|
||||
RasterizerOpenGL::RasterizerOpenGL(Core::System& system, ScreenInfo& info)
|
||||
: res_cache{*this}, shader_cache{*this, system, device}, global_cache{*this}, system{system},
|
||||
screen_info{info}, buffer_cache(*this, STREAM_BUFFER_SIZE) {
|
||||
RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
|
||||
ScreenInfo& info)
|
||||
: res_cache{*this}, shader_cache{*this, system, emu_window, device},
|
||||
global_cache{*this}, system{system}, screen_info{info},
|
||||
buffer_cache(*this, STREAM_BUFFER_SIZE) {
|
||||
OpenGLState::ApplyDefaultState();
|
||||
|
||||
shader_program_manager = std::make_unique<GLShader::ProgramManager>();
|
||||
@@ -244,29 +246,6 @@ DrawParameters RasterizerOpenGL::SetupDraw() {
|
||||
DrawParameters params{};
|
||||
params.current_instance = gpu.state.current_instance;
|
||||
|
||||
if (regs.draw.topology == Maxwell::PrimitiveTopology::Quads) {
|
||||
MICROPROFILE_SCOPE(OpenGL_PrimitiveAssembly);
|
||||
|
||||
params.use_indexed = true;
|
||||
params.primitive_mode = GL_TRIANGLES;
|
||||
|
||||
if (is_indexed) {
|
||||
params.index_format = MaxwellToGL::IndexFormat(regs.index_array.format);
|
||||
params.count = (regs.index_array.count / 4) * 6;
|
||||
params.index_buffer_offset = primitive_assembler.MakeQuadIndexed(
|
||||
regs.index_array.IndexStart(), regs.index_array.FormatSizeInBytes(),
|
||||
regs.index_array.count);
|
||||
params.base_vertex = static_cast<GLint>(regs.vb_element_base);
|
||||
} else {
|
||||
// MakeQuadArray always generates u32 indexes
|
||||
params.index_format = GL_UNSIGNED_INT;
|
||||
params.count = (regs.vertex_buffer.count / 4) * 6;
|
||||
params.index_buffer_offset = primitive_assembler.MakeQuadArray(
|
||||
regs.vertex_buffer.first, regs.vertex_buffer.count);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
params.use_indexed = is_indexed;
|
||||
params.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology);
|
||||
|
||||
@@ -684,30 +663,19 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
SyncCullMode();
|
||||
SyncPrimitiveRestart();
|
||||
SyncScissorTest(state);
|
||||
// Alpha Testing is synced on shaders.
|
||||
SyncTransformFeedback();
|
||||
SyncPointState();
|
||||
CheckAlphaTests();
|
||||
SyncPolygonOffset();
|
||||
// TODO(bunnei): Sync framebuffer_scale uniform here
|
||||
// TODO(bunnei): Sync scissorbox uniform(s) here
|
||||
SyncAlphaTest();
|
||||
|
||||
// Draw the vertex batch
|
||||
const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
|
||||
|
||||
std::size_t buffer_size = CalculateVertexArraysSize();
|
||||
|
||||
// Add space for index buffer (keeping in mind non-core primitives)
|
||||
switch (regs.draw.topology) {
|
||||
case Maxwell::PrimitiveTopology::Quads:
|
||||
buffer_size = Common::AlignUp(buffer_size, 4) +
|
||||
primitive_assembler.CalculateQuadSize(regs.vertex_buffer.count);
|
||||
break;
|
||||
default:
|
||||
if (is_indexed) {
|
||||
buffer_size = Common::AlignUp(buffer_size, 4) + CalculateIndexBufferSize();
|
||||
}
|
||||
break;
|
||||
// Add space for index buffer
|
||||
if (is_indexed) {
|
||||
buffer_size = Common::AlignUp(buffer_size, 4) + CalculateIndexBufferSize();
|
||||
}
|
||||
|
||||
// Uniform space for the 5 shader stages
|
||||
@@ -1150,10 +1118,17 @@ void RasterizerOpenGL::SyncPolygonOffset() {
|
||||
state.polygon_offset.clamp = regs.polygon_offset_clamp;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::CheckAlphaTests() {
|
||||
void RasterizerOpenGL::SyncAlphaTest() {
|
||||
const auto& regs = system.GPU().Maxwell3D().regs;
|
||||
UNIMPLEMENTED_IF_MSG(regs.alpha_test_enabled != 0 && regs.rt_control.count > 1,
|
||||
"Alpha Testing is enabled with more than one rendertarget");
|
||||
|
||||
state.alpha_test.enabled = regs.alpha_test_enabled;
|
||||
if (!state.alpha_test.enabled) {
|
||||
return;
|
||||
}
|
||||
state.alpha_test.func = MaxwellToGL::ComparisonOp(regs.alpha_test_func);
|
||||
state.alpha_test.ref = regs.alpha_test_ref;
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "video_core/renderer_opengl/gl_buffer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_device.h"
|
||||
#include "video_core/renderer_opengl/gl_global_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_primitive_assembler.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_sampler_cache.h"
|
||||
@@ -48,7 +47,8 @@ struct FramebufferCacheKey;
|
||||
|
||||
class RasterizerOpenGL : public VideoCore::RasterizerInterface {
|
||||
public:
|
||||
explicit RasterizerOpenGL(Core::System& system, ScreenInfo& info);
|
||||
explicit RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
|
||||
ScreenInfo& info);
|
||||
~RasterizerOpenGL() override;
|
||||
|
||||
void DrawArrays() override;
|
||||
@@ -166,8 +166,8 @@ private:
|
||||
/// Syncs the polygon offsets
|
||||
void SyncPolygonOffset();
|
||||
|
||||
/// Check asserts for alpha testing.
|
||||
void CheckAlphaTests();
|
||||
/// Syncs the alpha test state to match the guest state
|
||||
void SyncAlphaTest();
|
||||
|
||||
/// Check for extension that are not strictly required
|
||||
/// but are needed for correct emulation
|
||||
@@ -196,7 +196,6 @@ private:
|
||||
|
||||
static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
|
||||
OGLBufferCache buffer_cache;
|
||||
PrimitiveAssembler primitive_assembler{buffer_cache};
|
||||
|
||||
BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER};
|
||||
BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER};
|
||||
|
||||
@@ -2,10 +2,14 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include "common/assert.h"
|
||||
#include "common/hash.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||
@@ -31,8 +35,8 @@ struct UnspecializedShader {
|
||||
namespace {
|
||||
|
||||
/// Gets the address for the specified shader stage program
|
||||
GPUVAddr GetShaderAddress(Maxwell::ShaderProgram program) {
|
||||
const auto& gpu{Core::System::GetInstance().GPU().Maxwell3D()};
|
||||
GPUVAddr GetShaderAddress(Core::System& system, Maxwell::ShaderProgram program) {
|
||||
const auto& gpu{system.GPU().Maxwell3D()};
|
||||
const auto& shader_config{gpu.regs.shader_config[static_cast<std::size_t>(program)]};
|
||||
return gpu.regs.code_address.CodeAddress() + shader_config.offset;
|
||||
}
|
||||
@@ -166,7 +170,8 @@ GLShader::ProgramResult CreateProgram(const Device& device, Maxwell::ShaderProgr
|
||||
CachedProgram SpecializeShader(const std::string& code, const GLShader::ShaderEntries& entries,
|
||||
Maxwell::ShaderProgram program_type, BaseBindings base_bindings,
|
||||
GLenum primitive_mode, bool hint_retrievable = false) {
|
||||
std::string source = "#version 430 core\n";
|
||||
std::string source = "#version 430 core\n"
|
||||
"#extension GL_ARB_separate_shader_objects : enable\n\n";
|
||||
source += fmt::format("#define EMULATION_UBO_BINDING {}\n", base_bindings.cbuf++);
|
||||
|
||||
for (const auto& cbuf : entries.const_buffers) {
|
||||
@@ -344,8 +349,9 @@ ShaderDiskCacheUsage CachedShader::GetUsage(GLenum primitive_mode,
|
||||
}
|
||||
|
||||
ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system,
|
||||
const Device& device)
|
||||
: RasterizerCache{rasterizer}, device{device}, disk_cache{system} {}
|
||||
Core::Frontend::EmuWindow& emu_window, const Device& device)
|
||||
: RasterizerCache{rasterizer}, system{system}, emu_window{emu_window}, device{device},
|
||||
disk_cache{system} {}
|
||||
|
||||
void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
|
||||
const VideoCore::DiskResourceLoadCallback& callback) {
|
||||
@@ -353,62 +359,107 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
|
||||
if (!transferable) {
|
||||
return;
|
||||
}
|
||||
const auto [raws, usages] = *transferable;
|
||||
const auto [raws, shader_usages] = *transferable;
|
||||
|
||||
auto [decompiled, dumps] = disk_cache.LoadPrecompiled();
|
||||
|
||||
const auto supported_formats{GetSupportedFormats()};
|
||||
const auto unspecialized{
|
||||
const auto unspecialized_shaders{
|
||||
GenerateUnspecializedShaders(stop_loading, callback, raws, decompiled)};
|
||||
if (stop_loading)
|
||||
if (stop_loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Track if precompiled cache was altered during loading to know if we have to serialize the
|
||||
// virtual precompiled cache file back to the hard drive
|
||||
bool precompiled_cache_altered = false;
|
||||
|
||||
// Build shaders
|
||||
if (callback)
|
||||
callback(VideoCore::LoadCallbackStage::Build, 0, usages.size());
|
||||
for (std::size_t i = 0; i < usages.size(); ++i) {
|
||||
if (stop_loading)
|
||||
return;
|
||||
// Inform the frontend about shader build initialization
|
||||
if (callback) {
|
||||
callback(VideoCore::LoadCallbackStage::Build, 0, shader_usages.size());
|
||||
}
|
||||
|
||||
const auto& usage{usages[i]};
|
||||
LOG_INFO(Render_OpenGL, "Building shader {:016x} ({} of {})", usage.unique_identifier,
|
||||
i + 1, usages.size());
|
||||
std::mutex mutex;
|
||||
std::size_t built_shaders = 0; // It doesn't have be atomic since it's used behind a mutex
|
||||
std::atomic_bool compilation_failed = false;
|
||||
|
||||
const auto& unspec{unspecialized.at(usage.unique_identifier)};
|
||||
const auto dump_it = dumps.find(usage);
|
||||
const auto Worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin,
|
||||
std::size_t end, const std::vector<ShaderDiskCacheUsage>& shader_usages,
|
||||
const ShaderDumpsMap& dumps) {
|
||||
context->MakeCurrent();
|
||||
SCOPE_EXIT({ return context->DoneCurrent(); });
|
||||
|
||||
CachedProgram shader;
|
||||
if (dump_it != dumps.end()) {
|
||||
// If the shader is dumped, attempt to load it with
|
||||
shader = GeneratePrecompiledProgram(dump_it->second, supported_formats);
|
||||
if (!shader) {
|
||||
// Invalidate the precompiled cache if a shader dumped shader was rejected
|
||||
disk_cache.InvalidatePrecompiled();
|
||||
precompiled_cache_altered = true;
|
||||
dumps.clear();
|
||||
for (std::size_t i = begin; i < end; ++i) {
|
||||
if (stop_loading || compilation_failed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!shader) {
|
||||
shader = SpecializeShader(unspec.code, unspec.entries, unspec.program_type,
|
||||
usage.bindings, usage.primitive, true);
|
||||
}
|
||||
precompiled_programs.insert({usage, std::move(shader)});
|
||||
const auto& usage{shader_usages[i]};
|
||||
LOG_INFO(Render_OpenGL, "Building shader {:016x} (index {} of {})",
|
||||
usage.unique_identifier, i, shader_usages.size());
|
||||
|
||||
if (callback)
|
||||
callback(VideoCore::LoadCallbackStage::Build, i + 1, usages.size());
|
||||
const auto& unspecialized{unspecialized_shaders.at(usage.unique_identifier)};
|
||||
const auto dump{dumps.find(usage)};
|
||||
|
||||
CachedProgram shader;
|
||||
if (dump != dumps.end()) {
|
||||
// If the shader is dumped, attempt to load it with
|
||||
shader = GeneratePrecompiledProgram(dump->second, supported_formats);
|
||||
if (!shader) {
|
||||
compilation_failed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!shader) {
|
||||
shader = SpecializeShader(unspecialized.code, unspecialized.entries,
|
||||
unspecialized.program_type, usage.bindings,
|
||||
usage.primitive, true);
|
||||
}
|
||||
|
||||
std::scoped_lock lock(mutex);
|
||||
if (callback) {
|
||||
callback(VideoCore::LoadCallbackStage::Build, ++built_shaders,
|
||||
shader_usages.size());
|
||||
}
|
||||
|
||||
precompiled_programs.emplace(usage, std::move(shader));
|
||||
}
|
||||
};
|
||||
|
||||
const auto num_workers{static_cast<std::size_t>(std::thread::hardware_concurrency() + 1)};
|
||||
const std::size_t bucket_size{shader_usages.size() / num_workers};
|
||||
std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> contexts(num_workers);
|
||||
std::vector<std::thread> threads(num_workers);
|
||||
for (std::size_t i = 0; i < num_workers; ++i) {
|
||||
const bool is_last_worker = i + 1 == num_workers;
|
||||
const std::size_t start{bucket_size * i};
|
||||
const std::size_t end{is_last_worker ? shader_usages.size() : start + bucket_size};
|
||||
|
||||
// On some platforms the shared context has to be created from the GUI thread
|
||||
contexts[i] = emu_window.CreateSharedContext();
|
||||
threads[i] = std::thread(Worker, contexts[i].get(), start, end, shader_usages, dumps);
|
||||
}
|
||||
for (auto& thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
if (compilation_failed) {
|
||||
// Invalidate the precompiled cache if a shader dumped shader was rejected
|
||||
disk_cache.InvalidatePrecompiled();
|
||||
dumps.clear();
|
||||
precompiled_cache_altered = true;
|
||||
return;
|
||||
}
|
||||
if (stop_loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(Rodrigo): Do state tracking for transferable shaders and do a dummy draw before
|
||||
// precompiling them
|
||||
|
||||
for (std::size_t i = 0; i < usages.size(); ++i) {
|
||||
const auto& usage{usages[i]};
|
||||
for (std::size_t i = 0; i < shader_usages.size(); ++i) {
|
||||
const auto& usage{shader_usages[i]};
|
||||
if (dumps.find(usage) == dumps.end()) {
|
||||
const auto& program = precompiled_programs.at(usage);
|
||||
const auto& program{precompiled_programs.at(usage)};
|
||||
disk_cache.SaveDump(usage, program->handle);
|
||||
precompiled_cache_altered = true;
|
||||
}
|
||||
@@ -496,42 +547,45 @@ std::unordered_map<u64, UnspecializedShader> ShaderCacheOpenGL::GenerateUnspecia
|
||||
}
|
||||
|
||||
Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
|
||||
if (!Core::System::GetInstance().GPU().Maxwell3D().dirty_flags.shaders) {
|
||||
return last_shaders[static_cast<u32>(program)];
|
||||
if (!system.GPU().Maxwell3D().dirty_flags.shaders) {
|
||||
return last_shaders[static_cast<std::size_t>(program)];
|
||||
}
|
||||
|
||||
auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
|
||||
const GPUVAddr program_addr{GetShaderAddress(program)};
|
||||
auto& memory_manager{system.GPU().MemoryManager()};
|
||||
const GPUVAddr program_addr{GetShaderAddress(system, program)};
|
||||
|
||||
// Look up shader in the cache based on address
|
||||
const auto& host_ptr{memory_manager.GetPointer(program_addr)};
|
||||
const auto host_ptr{memory_manager.GetPointer(program_addr)};
|
||||
Shader shader{TryGet(host_ptr)};
|
||||
|
||||
if (!shader) {
|
||||
// No shader found - create a new one
|
||||
ProgramCode program_code{GetShaderCode(memory_manager, program_addr, host_ptr)};
|
||||
ProgramCode program_code_b;
|
||||
if (program == Maxwell::ShaderProgram::VertexA) {
|
||||
const GPUVAddr program_addr_b{GetShaderAddress(Maxwell::ShaderProgram::VertexB)};
|
||||
program_code_b = GetShaderCode(memory_manager, program_addr_b,
|
||||
memory_manager.GetPointer(program_addr_b));
|
||||
}
|
||||
const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b);
|
||||
const VAddr cpu_addr{*memory_manager.GpuToCpuAddress(program_addr)};
|
||||
const auto found = precompiled_shaders.find(unique_identifier);
|
||||
if (found != precompiled_shaders.end()) {
|
||||
shader =
|
||||
std::make_shared<CachedShader>(cpu_addr, unique_identifier, program, disk_cache,
|
||||
precompiled_programs, found->second, host_ptr);
|
||||
} else {
|
||||
shader = std::make_shared<CachedShader>(
|
||||
device, cpu_addr, unique_identifier, program, disk_cache, precompiled_programs,
|
||||
std::move(program_code), std::move(program_code_b), host_ptr);
|
||||
}
|
||||
Register(shader);
|
||||
if (shader) {
|
||||
return last_shaders[static_cast<std::size_t>(program)] = shader;
|
||||
}
|
||||
|
||||
return last_shaders[static_cast<u32>(program)] = shader;
|
||||
// No shader found - create a new one
|
||||
ProgramCode program_code{GetShaderCode(memory_manager, program_addr, host_ptr)};
|
||||
ProgramCode program_code_b;
|
||||
if (program == Maxwell::ShaderProgram::VertexA) {
|
||||
const GPUVAddr program_addr_b{GetShaderAddress(system, Maxwell::ShaderProgram::VertexB)};
|
||||
program_code_b = GetShaderCode(memory_manager, program_addr_b,
|
||||
memory_manager.GetPointer(program_addr_b));
|
||||
}
|
||||
|
||||
const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b);
|
||||
const VAddr cpu_addr{*memory_manager.GpuToCpuAddress(program_addr)};
|
||||
const auto found = precompiled_shaders.find(unique_identifier);
|
||||
if (found != precompiled_shaders.end()) {
|
||||
// Create a shader from the cache
|
||||
shader = std::make_shared<CachedShader>(cpu_addr, unique_identifier, program, disk_cache,
|
||||
precompiled_programs, found->second, host_ptr);
|
||||
} else {
|
||||
// Create a shader from guest memory
|
||||
shader = std::make_shared<CachedShader>(
|
||||
device, cpu_addr, unique_identifier, program, disk_cache, precompiled_programs,
|
||||
std::move(program_code), std::move(program_code_b), host_ptr);
|
||||
}
|
||||
Register(shader);
|
||||
|
||||
return last_shaders[static_cast<std::size_t>(program)] = shader;
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -22,7 +22,11 @@
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
} // namespace Core
|
||||
}
|
||||
|
||||
namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
}
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
@@ -111,7 +115,7 @@ private:
|
||||
class ShaderCacheOpenGL final : public RasterizerCache<Shader> {
|
||||
public:
|
||||
explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system,
|
||||
const Device& device);
|
||||
Core::Frontend::EmuWindow& emu_window, const Device& device);
|
||||
|
||||
/// Loads disk cache for the current game
|
||||
void LoadDiskCache(const std::atomic_bool& stop_loading,
|
||||
@@ -133,13 +137,14 @@ private:
|
||||
CachedProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump,
|
||||
const std::set<GLenum>& supported_formats);
|
||||
|
||||
Core::System& system;
|
||||
Core::Frontend::EmuWindow& emu_window;
|
||||
const Device& device;
|
||||
|
||||
std::array<Shader, Maxwell::MaxShaderProgram> last_shaders;
|
||||
|
||||
ShaderDiskCacheOpenGL disk_cache;
|
||||
|
||||
PrecompiledShaders precompiled_shaders;
|
||||
PrecompiledPrograms precompiled_programs;
|
||||
std::array<Shader, Maxwell::MaxShaderProgram> last_shaders;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -104,8 +104,9 @@ bool ShaderDiskCacheRaw::Save(FileUtil::IOFile& file) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system)
|
||||
: system{system}, precompiled_cache_virtual_file_offset{0} {}
|
||||
ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system) : system{system} {}
|
||||
|
||||
ShaderDiskCacheOpenGL::~ShaderDiskCacheOpenGL() = default;
|
||||
|
||||
std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>>
|
||||
ShaderDiskCacheOpenGL::LoadTransferable() {
|
||||
@@ -182,8 +183,7 @@ ShaderDiskCacheOpenGL::LoadTransferable() {
|
||||
return {{raws, usages}};
|
||||
}
|
||||
|
||||
std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>,
|
||||
std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>
|
||||
std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, ShaderDumpsMap>
|
||||
ShaderDiskCacheOpenGL::LoadPrecompiled() {
|
||||
if (!IsUsable())
|
||||
return {};
|
||||
@@ -207,8 +207,7 @@ ShaderDiskCacheOpenGL::LoadPrecompiled() {
|
||||
return *result;
|
||||
}
|
||||
|
||||
std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>,
|
||||
std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>>
|
||||
std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, ShaderDumpsMap>>
|
||||
ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) {
|
||||
// Read compressed file from disk and decompress to virtual precompiled cache file
|
||||
std::vector<u8> compressed(file.GetSize());
|
||||
@@ -229,7 +228,7 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) {
|
||||
}
|
||||
|
||||
std::unordered_map<u64, ShaderDiskCacheDecompiled> decompiled;
|
||||
std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump> dumps;
|
||||
ShaderDumpsMap dumps;
|
||||
while (precompiled_cache_virtual_file_offset < precompiled_cache_virtual_file.GetSize()) {
|
||||
PrecompiledEntryKind kind{};
|
||||
if (!LoadObjectFromPrecompiled(kind)) {
|
||||
@@ -243,7 +242,7 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto entry = LoadDecompiledEntry();
|
||||
auto entry = LoadDecompiledEntry();
|
||||
if (!entry) {
|
||||
return {};
|
||||
}
|
||||
@@ -287,13 +286,13 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<u8> code(code_size);
|
||||
std::string code(code_size, '\0');
|
||||
if (!LoadArrayFromPrecompiled(code.data(), code.size())) {
|
||||
return {};
|
||||
}
|
||||
|
||||
ShaderDiskCacheDecompiled entry;
|
||||
entry.code = std::string(reinterpret_cast<const char*>(code.data()), code_size);
|
||||
entry.code = std::move(code);
|
||||
|
||||
u32 const_buffers_count{};
|
||||
if (!LoadObjectFromPrecompiled(const_buffers_count)) {
|
||||
@@ -303,12 +302,12 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
|
||||
for (u32 i = 0; i < const_buffers_count; ++i) {
|
||||
u32 max_offset{};
|
||||
u32 index{};
|
||||
u8 is_indirect{};
|
||||
bool is_indirect{};
|
||||
if (!LoadObjectFromPrecompiled(max_offset) || !LoadObjectFromPrecompiled(index) ||
|
||||
!LoadObjectFromPrecompiled(is_indirect)) {
|
||||
return {};
|
||||
}
|
||||
entry.entries.const_buffers.emplace_back(max_offset, is_indirect != 0, index);
|
||||
entry.entries.const_buffers.emplace_back(max_offset, is_indirect, index);
|
||||
}
|
||||
|
||||
u32 samplers_count{};
|
||||
@@ -320,18 +319,17 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
|
||||
u64 offset{};
|
||||
u64 index{};
|
||||
u32 type{};
|
||||
u8 is_array{};
|
||||
u8 is_shadow{};
|
||||
u8 is_bindless{};
|
||||
bool is_array{};
|
||||
bool is_shadow{};
|
||||
bool is_bindless{};
|
||||
if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) ||
|
||||
!LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_array) ||
|
||||
!LoadObjectFromPrecompiled(is_shadow) || !LoadObjectFromPrecompiled(is_bindless)) {
|
||||
return {};
|
||||
}
|
||||
entry.entries.samplers.emplace_back(static_cast<std::size_t>(offset),
|
||||
static_cast<std::size_t>(index),
|
||||
static_cast<Tegra::Shader::TextureType>(type),
|
||||
is_array != 0, is_shadow != 0, is_bindless != 0);
|
||||
entry.entries.samplers.emplace_back(
|
||||
static_cast<std::size_t>(offset), static_cast<std::size_t>(index),
|
||||
static_cast<Tegra::Shader::TextureType>(type), is_array, is_shadow, is_bindless);
|
||||
}
|
||||
|
||||
u32 global_memory_count{};
|
||||
@@ -342,21 +340,20 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
|
||||
for (u32 i = 0; i < global_memory_count; ++i) {
|
||||
u32 cbuf_index{};
|
||||
u32 cbuf_offset{};
|
||||
u8 is_read{};
|
||||
u8 is_written{};
|
||||
bool is_read{};
|
||||
bool is_written{};
|
||||
if (!LoadObjectFromPrecompiled(cbuf_index) || !LoadObjectFromPrecompiled(cbuf_offset) ||
|
||||
!LoadObjectFromPrecompiled(is_read) || !LoadObjectFromPrecompiled(is_written)) {
|
||||
return {};
|
||||
}
|
||||
entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset, is_read != 0,
|
||||
is_written != 0);
|
||||
entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset, is_read,
|
||||
is_written);
|
||||
}
|
||||
|
||||
for (auto& clip_distance : entry.entries.clip_distances) {
|
||||
u8 clip_distance_raw{};
|
||||
if (!LoadObjectFromPrecompiled(clip_distance_raw))
|
||||
if (!LoadObjectFromPrecompiled(clip_distance)) {
|
||||
return {};
|
||||
clip_distance = clip_distance_raw != 0;
|
||||
}
|
||||
}
|
||||
|
||||
u64 shader_length{};
|
||||
@@ -384,7 +381,7 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std:
|
||||
for (const auto& cbuf : entries.const_buffers) {
|
||||
if (!SaveObjectToPrecompiled(static_cast<u32>(cbuf.GetMaxOffset())) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u32>(cbuf.GetIndex())) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u8>(cbuf.IsIndirect() ? 1 : 0))) {
|
||||
!SaveObjectToPrecompiled(cbuf.IsIndirect())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -396,9 +393,9 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std:
|
||||
if (!SaveObjectToPrecompiled(static_cast<u64>(sampler.GetOffset())) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u64>(sampler.GetIndex())) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u32>(sampler.GetType())) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u8>(sampler.IsArray() ? 1 : 0)) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u8>(sampler.IsShadow() ? 1 : 0)) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u8>(sampler.IsBindless() ? 1 : 0))) {
|
||||
!SaveObjectToPrecompiled(sampler.IsArray()) ||
|
||||
!SaveObjectToPrecompiled(sampler.IsShadow()) ||
|
||||
!SaveObjectToPrecompiled(sampler.IsBindless())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -409,14 +406,13 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std:
|
||||
for (const auto& gmem : entries.global_memory_entries) {
|
||||
if (!SaveObjectToPrecompiled(static_cast<u32>(gmem.GetCbufIndex())) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u32>(gmem.GetCbufOffset())) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u8>(gmem.IsRead() ? 1 : 0)) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u8>(gmem.IsWritten() ? 1 : 0))) {
|
||||
!SaveObjectToPrecompiled(gmem.IsRead()) || !SaveObjectToPrecompiled(gmem.IsWritten())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const bool clip_distance : entries.clip_distances) {
|
||||
if (!SaveObjectToPrecompiled(static_cast<u8>(clip_distance ? 1 : 0))) {
|
||||
if (!SaveObjectToPrecompiled(clip_distance)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,11 @@ namespace OpenGL {
|
||||
using ProgramCode = std::vector<u64>;
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
|
||||
struct ShaderDiskCacheUsage;
|
||||
struct ShaderDiskCacheDump;
|
||||
|
||||
using ShaderDumpsMap = std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>;
|
||||
|
||||
/// Allocated bindings used by an OpenGL shader program
|
||||
struct BaseBindings {
|
||||
u32 cbuf{};
|
||||
@@ -70,14 +75,14 @@ namespace std {
|
||||
|
||||
template <>
|
||||
struct hash<OpenGL::BaseBindings> {
|
||||
std::size_t operator()(const OpenGL::BaseBindings& bindings) const {
|
||||
std::size_t operator()(const OpenGL::BaseBindings& bindings) const noexcept {
|
||||
return bindings.cbuf | bindings.gmem << 8 | bindings.sampler << 16;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<OpenGL::ShaderDiskCacheUsage> {
|
||||
std::size_t operator()(const OpenGL::ShaderDiskCacheUsage& usage) const {
|
||||
std::size_t operator()(const OpenGL::ShaderDiskCacheUsage& usage) const noexcept {
|
||||
return static_cast<std::size_t>(usage.unique_identifier) ^
|
||||
std::hash<OpenGL::BaseBindings>()(usage.bindings) ^ usage.primitive << 16;
|
||||
}
|
||||
@@ -162,6 +167,7 @@ struct ShaderDiskCacheDump {
|
||||
class ShaderDiskCacheOpenGL {
|
||||
public:
|
||||
explicit ShaderDiskCacheOpenGL(Core::System& system);
|
||||
~ShaderDiskCacheOpenGL();
|
||||
|
||||
/// Loads transferable cache. If file has a old version or on failure, it deletes the file.
|
||||
std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>>
|
||||
@@ -259,23 +265,38 @@ private:
|
||||
return SaveArrayToPrecompiled(&object, 1);
|
||||
}
|
||||
|
||||
bool SaveObjectToPrecompiled(bool object) {
|
||||
const auto value = static_cast<u8>(object);
|
||||
return SaveArrayToPrecompiled(&value, 1);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool LoadObjectFromPrecompiled(T& object) {
|
||||
return LoadArrayFromPrecompiled(&object, 1);
|
||||
}
|
||||
|
||||
// Copre system
|
||||
bool LoadObjectFromPrecompiled(bool& object) {
|
||||
u8 value;
|
||||
const bool read_ok = LoadArrayFromPrecompiled(&value, 1);
|
||||
if (!read_ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
object = value != 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Core system
|
||||
Core::System& system;
|
||||
// Stored transferable shaders
|
||||
std::map<u64, std::unordered_set<ShaderDiskCacheUsage>> transferable;
|
||||
// Stores whole precompiled cache which will be read from or saved to the precompiled chache
|
||||
// file
|
||||
// Stores whole precompiled cache which will be read from/saved to the precompiled cache file
|
||||
FileSys::VectorVfsFile precompiled_cache_virtual_file;
|
||||
// Stores the current offset of the precompiled cache file for IO purposes
|
||||
std::size_t precompiled_cache_virtual_file_offset;
|
||||
std::size_t precompiled_cache_virtual_file_offset = 0;
|
||||
|
||||
// The cache has been loaded at boot
|
||||
bool tried_to_load{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -19,17 +19,13 @@ static constexpr u32 PROGRAM_OFFSET{10};
|
||||
ProgramResult GenerateVertexShader(const Device& device, const ShaderSetup& setup) {
|
||||
const std::string id = fmt::format("{:016x}", setup.program.unique_identifier);
|
||||
|
||||
std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n";
|
||||
out += "// Shader Unique Id: VS" + id + "\n\n";
|
||||
std::string out = "// Shader Unique Id: VS" + id + "\n\n";
|
||||
out += GetCommonDeclarations();
|
||||
|
||||
out += R"(
|
||||
layout (location = 0) out vec4 position;
|
||||
|
||||
layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config {
|
||||
vec4 viewport_flip;
|
||||
uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
|
||||
uvec4 alpha_test;
|
||||
};
|
||||
|
||||
)";
|
||||
@@ -49,7 +45,6 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config {
|
||||
|
||||
out += R"(
|
||||
void main() {
|
||||
position = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
execute_vertex();
|
||||
)";
|
||||
|
||||
@@ -60,19 +55,12 @@ void main() {
|
||||
out += R"(
|
||||
|
||||
// Set Position Y direction
|
||||
position.y *= utof(config_pack[2]);
|
||||
gl_Position.y *= utof(config_pack[2]);
|
||||
// Check if the flip stage is VertexB
|
||||
// Config pack's second value is flip_stage
|
||||
if (config_pack[1] == 1) {
|
||||
// Viewport can be flipped, which is unsupported by glViewport
|
||||
position.xy *= viewport_flip.xy;
|
||||
}
|
||||
gl_Position = position;
|
||||
|
||||
// TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0
|
||||
// For now, this is here to bring order in lieu of proper emulation
|
||||
if (config_pack[1] == 1) {
|
||||
position.w = 1.0;
|
||||
gl_Position.xy *= viewport_flip.xy;
|
||||
}
|
||||
})";
|
||||
|
||||
@@ -82,18 +70,13 @@ void main() {
|
||||
ProgramResult GenerateGeometryShader(const Device& device, const ShaderSetup& setup) {
|
||||
const std::string id = fmt::format("{:016x}", setup.program.unique_identifier);
|
||||
|
||||
std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n";
|
||||
out += "// Shader Unique Id: GS" + id + "\n\n";
|
||||
std::string out = "// Shader Unique Id: GS" + id + "\n\n";
|
||||
out += GetCommonDeclarations();
|
||||
|
||||
out += R"(
|
||||
layout (location = 0) in vec4 gs_position[];
|
||||
layout (location = 0) out vec4 position;
|
||||
|
||||
layout (std140, binding = EMULATION_UBO_BINDING) uniform gs_config {
|
||||
vec4 viewport_flip;
|
||||
uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
|
||||
uvec4 alpha_test;
|
||||
};
|
||||
|
||||
)";
|
||||
@@ -113,8 +96,7 @@ void main() {
|
||||
ProgramResult GenerateFragmentShader(const Device& device, const ShaderSetup& setup) {
|
||||
const std::string id = fmt::format("{:016x}", setup.program.unique_identifier);
|
||||
|
||||
std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n";
|
||||
out += "// Shader Unique Id: FS" + id + "\n\n";
|
||||
std::string out = "// Shader Unique Id: FS" + id + "\n\n";
|
||||
out += GetCommonDeclarations();
|
||||
|
||||
out += R"(
|
||||
@@ -127,38 +109,11 @@ layout (location = 5) out vec4 FragColor5;
|
||||
layout (location = 6) out vec4 FragColor6;
|
||||
layout (location = 7) out vec4 FragColor7;
|
||||
|
||||
layout (location = 0) in noperspective vec4 position;
|
||||
|
||||
layout (std140, binding = EMULATION_UBO_BINDING) uniform fs_config {
|
||||
vec4 viewport_flip;
|
||||
uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
)";
|
||||
const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET);
|
||||
ProgramResult program =
|
||||
|
||||
@@ -48,17 +48,6 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell, std::size_t shade
|
||||
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;
|
||||
|
||||
auto func{static_cast<u32>(regs.alpha_test_func)};
|
||||
// Normalize the gl variants of opCompare to be the same as the normal variants
|
||||
const u32 op_gl_variant_base = static_cast<u32>(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;
|
||||
|
||||
instance_id = state.current_instance;
|
||||
|
||||
// Assign in which stage the position has to be flipped
|
||||
|
||||
@@ -27,14 +27,8 @@ struct MaxwellUniformData {
|
||||
GLuint flip_stage;
|
||||
GLfloat y_direction;
|
||||
};
|
||||
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) == 32, "MaxwellUniformData structure size is incorrect");
|
||||
static_assert(sizeof(MaxwellUniformData) < 16384,
|
||||
"MaxwellUniformData structure must be less than 16kb as per the OpenGL spec");
|
||||
|
||||
|
||||
@@ -156,6 +156,10 @@ OpenGLState::OpenGLState() {
|
||||
polygon_offset.factor = 0.0f;
|
||||
polygon_offset.units = 0.0f;
|
||||
polygon_offset.clamp = 0.0f;
|
||||
|
||||
alpha_test.enabled = false;
|
||||
alpha_test.func = GL_ALWAYS;
|
||||
alpha_test.ref = 0.0f;
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyDefaultState() {
|
||||
@@ -461,6 +465,14 @@ void OpenGLState::ApplyPolygonOffset() const {
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyAlphaTest() const {
|
||||
Enable(GL_ALPHA_TEST, cur_state.alpha_test.enabled, alpha_test.enabled);
|
||||
if (UpdateTie(std::tie(cur_state.alpha_test.func, cur_state.alpha_test.ref),
|
||||
std::tie(alpha_test.func, alpha_test.ref))) {
|
||||
glAlphaFunc(alpha_test.func, alpha_test.ref);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyTextures() const {
|
||||
bool has_delta{};
|
||||
std::size_t first{};
|
||||
@@ -533,6 +545,7 @@ void OpenGLState::Apply() const {
|
||||
ApplyTextures();
|
||||
ApplySamplers();
|
||||
ApplyPolygonOffset();
|
||||
ApplyAlphaTest();
|
||||
}
|
||||
|
||||
void OpenGLState::EmulateViewportWithScissor() {
|
||||
|
||||
@@ -172,6 +172,12 @@ public:
|
||||
GLfloat clamp;
|
||||
} polygon_offset;
|
||||
|
||||
struct {
|
||||
bool enabled; // GL_ALPHA_TEST
|
||||
GLenum func; // GL_ALPHA_TEST_FUNC
|
||||
GLfloat ref; // GL_ALPHA_TEST_REF
|
||||
} alpha_test;
|
||||
|
||||
std::array<bool, 8> clip_distance; // GL_CLIP_DISTANCE
|
||||
|
||||
OpenGLState();
|
||||
@@ -215,6 +221,7 @@ public:
|
||||
void ApplySamplers() const;
|
||||
void ApplyDepthClamp() const;
|
||||
void ApplyPolygonOffset() const;
|
||||
void ApplyAlphaTest() const;
|
||||
|
||||
/// Set the initial OpenGL state
|
||||
static void ApplyDefaultState();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user