Compare commits
134 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c09ff382a4 | |||
| 8d7a012297 | |||
| 0bcc305797 | |||
| 799302bc9d | |||
| 81e09bb121 | |||
| 6aff1005ef | |||
| e1d755bdda | |||
| 2beaaa35c5 | |||
| 1eb979221f | |||
| dd4fe0dab1 | |||
| 433ca686a8 | |||
| 6ce5f3e1bf | |||
| dfc40a3f9e | |||
| 4f7a1f6c8c | |||
| 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 | |||
| e70f16fff7 | |||
| 1edf018319 | |||
| d0d97de1e4 | |||
| de2533d389 | |||
| ed74a3cb8b | |||
| 75561d190a | |||
| e72b9044a0 | |||
| ad321564ed | |||
| 838b6d2ff8 | |||
| 9b2d38582f | |||
| e9772a2539 | |||
| 3f11d1c821 | |||
| d9c1b94f03 | |||
| e3608578e4 | |||
| 665b7e8e18 | |||
| cfc9d92b38 | |||
| 802dd3cc95 | |||
| 139301c5a1 | |||
| 8bbe930fac | |||
| c6f05b586f | |||
| 84a8fb9264 | |||
| b1a4ab2ccc | |||
| 215fd82738 | |||
| 05af9d915c | |||
| 2fb3b9b951 | |||
| cfd885163f | |||
| 2eb4d27c48 | |||
| 21c0b4dec8 | |||
| 84928e6d67 | |||
| 46e2ca5475 | |||
| 944c07ac7d | |||
| f95bdb5088 | |||
| 180f22f17e | |||
| 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 | |||
| 016f2eab73 | |||
| 2ed896075e | |||
| d81b58f320 | |||
| 7fba9c7224 | |||
| 7dbf4c1ae5 | |||
| b6dcb1ae4d | |||
| 228e58d0a5 | |||
| 87b4c1ac5e | |||
| 3e7d37301a | |||
| d0e200a894 | |||
| 819d229e76 | |||
| f4c15db9e8 | |||
| 69215b5a55 | |||
| c03b8c4c19 | |||
| 75e7b45d69 | |||
| f78ef617b6 | |||
| f49a04ba39 | |||
| 938d6dca30 | |||
| 9c3461604c | |||
| ada79fa8ad | |||
| 5993133d5e | |||
| 8d6342384b | |||
| 4e462d1fd7 | |||
| 851c01c45e | |||
| 1aa2b99a98 | |||
| c40cff454d | |||
| f0db2e3ef3 | |||
| e25a7891e9 | |||
| daf5b8c61b | |||
| ca5638a142 |
@@ -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.
|
||||
|
||||
|
||||
+10
-2
@@ -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
|
||||
|
||||
Vendored
+1976
-412
File diff suppressed because one or more lines are too long
Vendored
+997
-128
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) {
|
||||
|
||||
@@ -123,6 +123,8 @@ add_library(common STATIC
|
||||
timer.h
|
||||
uint128.cpp
|
||||
uint128.h
|
||||
uuid.cpp
|
||||
uuid.h
|
||||
vector_math.h
|
||||
web_result.h
|
||||
zstd_compression.cpp
|
||||
|
||||
@@ -41,4 +41,7 @@ struct Rectangle {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
Rectangle(T, T, T, T)->Rectangle<T>;
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -310,6 +310,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 +328,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
|
||||
|
||||
+1
-18
@@ -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) {
|
||||
|
||||
+11
-43
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -34,7 +34,7 @@ constexpr std::array<u8, backup_jpeg_size> backup_jpeg{{
|
||||
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 +46,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"},
|
||||
@@ -131,7 +131,7 @@ private:
|
||||
}
|
||||
|
||||
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 +179,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 +205,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 +245,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);
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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};
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
+455
-393
@@ -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();
|
||||
};
|
||||
|
||||
class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> {
|
||||
public:
|
||||
explicit IApplicationManagerInterface();
|
||||
~IApplicationManagerInterface();
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
class IContentManagerInterface final : public ServiceFramework<IContentManagerInterface> {
|
||||
public:
|
||||
explicit IContentManagerInterface();
|
||||
~IContentManagerInterface();
|
||||
};
|
||||
|
||||
class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
|
||||
public:
|
||||
explicit IDocumentInterface();
|
||||
~IDocumentInterface();
|
||||
};
|
||||
|
||||
class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> {
|
||||
public:
|
||||
explicit IDownloadTaskInterface();
|
||||
~IDownloadTaskInterface();
|
||||
};
|
||||
|
||||
class IECommerceInterface final : public ServiceFramework<IECommerceInterface> {
|
||||
public:
|
||||
explicit IECommerceInterface();
|
||||
~IECommerceInterface();
|
||||
};
|
||||
|
||||
class IFactoryResetInterface final : public ServiceFramework<IFactoryResetInterface> {
|
||||
public:
|
||||
explicit IFactoryResetInterface();
|
||||
~IFactoryResetInterface();
|
||||
};
|
||||
|
||||
class NS final : public ServiceFramework<NS> {
|
||||
public:
|
||||
explicit NS(const char* name);
|
||||
~NS();
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -529,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;
|
||||
@@ -812,13 +817,11 @@ 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 {
|
||||
@@ -827,6 +830,11 @@ union Instruction {
|
||||
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;
|
||||
@@ -1387,10 +1395,12 @@ 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
|
||||
ST, // Store in generic memory
|
||||
STG, // Store in global memory
|
||||
AL2P, // Transforms attribute memory into physical memory
|
||||
TEX,
|
||||
@@ -1653,15 +1663,18 @@ 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"),
|
||||
@@ -1674,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -24,6 +27,7 @@ Device::Device() {
|
||||
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) {
|
||||
@@ -31,10 +35,12 @@ Device::Device(std::nullptr_t) {
|
||||
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() {
|
||||
@@ -51,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
|
||||
|
||||
@@ -30,13 +30,19 @@ public:
|
||||
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
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -48,7 +48,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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -31,6 +31,8 @@ using Tegra::Shader::IpaInterpMode;
|
||||
using Tegra::Shader::IpaMode;
|
||||
using Tegra::Shader::IpaSampleMode;
|
||||
using Tegra::Shader::Register;
|
||||
|
||||
using namespace std::string_literals;
|
||||
using namespace VideoCommon::Shader;
|
||||
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
@@ -93,11 +95,9 @@ private:
|
||||
};
|
||||
|
||||
/// Generates code to use for a swizzle operation.
|
||||
std::string GetSwizzle(u32 elem) {
|
||||
ASSERT(elem <= 3);
|
||||
std::string swizzle = ".";
|
||||
swizzle += "xyzw"[elem];
|
||||
return swizzle;
|
||||
constexpr const char* GetSwizzle(u32 element) {
|
||||
constexpr std::array<const char*, 4> swizzle = {".x", ".y", ".z", ".w"};
|
||||
return swizzle.at(element);
|
||||
}
|
||||
|
||||
/// Translate topology
|
||||
@@ -577,9 +577,26 @@ private:
|
||||
if (std::holds_alternative<OperationNode>(*offset)) {
|
||||
// Indirect access
|
||||
const std::string final_offset = code.GenerateTemporary();
|
||||
code.AddLine("uint {} = (ftou({}) / 4);", final_offset, Visit(offset));
|
||||
return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()),
|
||||
final_offset, final_offset);
|
||||
code.AddLine("uint {} = ftou({}) >> 2;", final_offset, Visit(offset));
|
||||
|
||||
if (!device.HasComponentIndexingBug()) {
|
||||
return fmt::format("{}[{} >> 2][{} & 3]", GetConstBuffer(cbuf->GetIndex()),
|
||||
final_offset, final_offset);
|
||||
}
|
||||
|
||||
// AMD's proprietary GLSL compiler emits ill code for variable component access.
|
||||
// To bypass this driver bug generate 4 ifs, one per each component.
|
||||
const std::string pack = code.GenerateTemporary();
|
||||
code.AddLine("vec4 {} = {}[{} >> 2];", pack, GetConstBuffer(cbuf->GetIndex()),
|
||||
final_offset);
|
||||
|
||||
const std::string result = code.GenerateTemporary();
|
||||
code.AddLine("float {};", result);
|
||||
for (u32 swizzle = 0; swizzle < 4; ++swizzle) {
|
||||
code.AddLine("if (({} & 3) == {}) {} = {}{};", final_offset, swizzle, result,
|
||||
pack, GetSwizzle(swizzle));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
UNREACHABLE_MSG("Unmanaged offset node type");
|
||||
@@ -636,7 +653,7 @@ private:
|
||||
if (stage != ShaderStage::Fragment) {
|
||||
return GeometryPass("position") + GetSwizzle(element);
|
||||
} else {
|
||||
return element == 3 ? "1.0f" : "gl_FragCoord" + GetSwizzle(element);
|
||||
return element == 3 ? "1.0f" : ("gl_FragCoord"s + GetSwizzle(element));
|
||||
}
|
||||
case Attribute::Index::PointCoord:
|
||||
switch (element) {
|
||||
@@ -921,7 +938,7 @@ private:
|
||||
target = [&]() -> std::string {
|
||||
switch (const auto attribute = abuf->GetIndex(); abuf->GetIndex()) {
|
||||
case Attribute::Index::Position:
|
||||
return "position" + GetSwizzle(abuf->GetElement());
|
||||
return "position"s + GetSwizzle(abuf->GetElement());
|
||||
case Attribute::Index::PointSize:
|
||||
return "gl_PointSize";
|
||||
case Attribute::Index::ClipDistances0123:
|
||||
@@ -1526,6 +1543,16 @@ private:
|
||||
return "uintBitsToFloat(config_pack[2])";
|
||||
}
|
||||
|
||||
template <u32 element>
|
||||
std::string LocalInvocationId(Operation) {
|
||||
return "utof(gl_LocalInvocationID"s + GetSwizzle(element) + ')';
|
||||
}
|
||||
|
||||
template <u32 element>
|
||||
std::string WorkGroupId(Operation) {
|
||||
return "utof(gl_WorkGroupID"s + GetSwizzle(element) + ')';
|
||||
}
|
||||
|
||||
static constexpr OperationDecompilersArray operation_decompilers = {
|
||||
&GLSLDecompiler::Assign,
|
||||
|
||||
@@ -1665,6 +1692,12 @@ private:
|
||||
&GLSLDecompiler::EndPrimitive,
|
||||
|
||||
&GLSLDecompiler::YNegate,
|
||||
&GLSLDecompiler::LocalInvocationId<0>,
|
||||
&GLSLDecompiler::LocalInvocationId<1>,
|
||||
&GLSLDecompiler::LocalInvocationId<2>,
|
||||
&GLSLDecompiler::WorkGroupId<0>,
|
||||
&GLSLDecompiler::WorkGroupId<1>,
|
||||
&GLSLDecompiler::WorkGroupId<2>,
|
||||
};
|
||||
|
||||
std::string GetRegister(u32 index) const {
|
||||
|
||||
@@ -183,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 {};
|
||||
@@ -208,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());
|
||||
@@ -230,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)) {
|
||||
|
||||
@@ -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{};
|
||||
@@ -294,4 +299,4 @@ private:
|
||||
bool tried_to_load{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -19,8 +19,7 @@ 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"(
|
||||
@@ -82,8 +81,7 @@ 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"(
|
||||
@@ -113,8 +111,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"(
|
||||
|
||||
@@ -97,8 +97,8 @@ static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, cons
|
||||
return matrix;
|
||||
}
|
||||
|
||||
RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& window, Core::System& system)
|
||||
: VideoCore::RendererBase{window}, system{system} {}
|
||||
RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system)
|
||||
: VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system} {}
|
||||
|
||||
RendererOpenGL::~RendererOpenGL() = default;
|
||||
|
||||
@@ -265,7 +265,7 @@ void RendererOpenGL::CreateRasterizer() {
|
||||
}
|
||||
// Initialize sRGB Usage
|
||||
OpenGLState::ClearsRGBUsed();
|
||||
rasterizer = std::make_unique<RasterizerOpenGL>(system, screen_info);
|
||||
rasterizer = std::make_unique<RasterizerOpenGL>(system, emu_window, screen_info);
|
||||
}
|
||||
|
||||
void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
|
||||
|
||||
@@ -45,7 +45,7 @@ struct ScreenInfo {
|
||||
|
||||
class RendererOpenGL : public VideoCore::RendererBase {
|
||||
public:
|
||||
explicit RendererOpenGL(Core::Frontend::EmuWindow& window, Core::System& system);
|
||||
explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system);
|
||||
~RendererOpenGL() override;
|
||||
|
||||
/// Swap buffers (render frame)
|
||||
@@ -77,6 +77,7 @@ private:
|
||||
void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a,
|
||||
const TextureInfo& texture);
|
||||
|
||||
Core::Frontend::EmuWindow& emu_window;
|
||||
Core::System& system;
|
||||
|
||||
OpenGLState state;
|
||||
|
||||
@@ -38,27 +38,27 @@ void BindBuffersRangePushBuffer::Bind() const {
|
||||
sizes.data());
|
||||
}
|
||||
|
||||
void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info) {
|
||||
void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string_view extra_info) {
|
||||
if (!GLAD_GL_KHR_debug) {
|
||||
return; // We don't need to throw an error as this is just for debugging
|
||||
// We don't need to throw an error as this is just for debugging
|
||||
return;
|
||||
}
|
||||
const std::string nice_addr = fmt::format("0x{:016x}", addr);
|
||||
std::string object_label;
|
||||
|
||||
std::string object_label;
|
||||
if (extra_info.empty()) {
|
||||
switch (identifier) {
|
||||
case GL_TEXTURE:
|
||||
object_label = "Texture@" + nice_addr;
|
||||
object_label = fmt::format("Texture@0x{:016X}", addr);
|
||||
break;
|
||||
case GL_PROGRAM:
|
||||
object_label = "Shader@" + nice_addr;
|
||||
object_label = fmt::format("Shader@0x{:016X}", addr);
|
||||
break;
|
||||
default:
|
||||
object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr);
|
||||
object_label = fmt::format("Object(0x{:X})@0x{:016X}", identifier, addr);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
object_label = extra_info + '@' + nice_addr;
|
||||
object_label = fmt::format("{}@0x{:016X}", extra_info, addr);
|
||||
}
|
||||
glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str()));
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <glad/glad.h>
|
||||
#include "common/common_types.h"
|
||||
@@ -30,6 +30,6 @@ private:
|
||||
std::vector<GLsizeiptr> sizes;
|
||||
};
|
||||
|
||||
void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info = "");
|
||||
void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string_view extra_info = {});
|
||||
|
||||
} // namespace OpenGL
|
||||
@@ -1035,6 +1035,18 @@ private:
|
||||
return {};
|
||||
}
|
||||
|
||||
template <u32 element>
|
||||
Id LocalInvocationId(Operation) {
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
}
|
||||
|
||||
template <u32 element>
|
||||
Id WorkGroupId(Operation) {
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
}
|
||||
|
||||
Id DeclareBuiltIn(spv::BuiltIn builtin, spv::StorageClass storage, Id type,
|
||||
const std::string& name) {
|
||||
const Id id = OpVariable(type, storage);
|
||||
@@ -1291,6 +1303,12 @@ private:
|
||||
&SPIRVDecompiler::EndPrimitive,
|
||||
|
||||
&SPIRVDecompiler::YNegate,
|
||||
&SPIRVDecompiler::LocalInvocationId<0>,
|
||||
&SPIRVDecompiler::LocalInvocationId<1>,
|
||||
&SPIRVDecompiler::LocalInvocationId<2>,
|
||||
&SPIRVDecompiler::WorkGroupId<0>,
|
||||
&SPIRVDecompiler::WorkGroupId<1>,
|
||||
&SPIRVDecompiler::WorkGroupId<2>,
|
||||
};
|
||||
|
||||
const ShaderIR& ir;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
@@ -152,4 +153,4 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
@@ -47,4 +48,4 @@ u32 ShaderIR::DecodeArithmeticHalfImmediate(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -49,4 +49,4 @@ u32 ShaderIR::DecodeArithmeticImmediate(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -93,4 +93,4 @@ void ShaderIR::WriteLogicOperation(NodeBlock& bb, Register dest, LogicOperation
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -46,4 +46,4 @@ u32 ShaderIR::DecodeBfe(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -38,4 +38,4 @@ u32 ShaderIR::DecodeBfi(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -56,4 +56,4 @@ u32 ShaderIR::DecodeFfma(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -55,4 +55,4 @@ u32 ShaderIR::DecodeFloatSet(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -53,4 +53,4 @@ u32 ShaderIR::DecodeFloatSetPredicate(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
@@ -64,4 +65,4 @@ u32 ShaderIR::DecodeHalfSet(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -59,4 +59,4 @@ u32 ShaderIR::DecodeHalfSetPredicate(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
@@ -47,4 +46,4 @@ u32 ShaderIR::DecodeIntegerSet(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -50,4 +50,4 @@ u32 ShaderIR::DecodeIntegerSetPredicate(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -146,12 +146,25 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::LD:
|
||||
case OpCode::Id::LDG: {
|
||||
const auto [real_address_base, base_address, descriptor] =
|
||||
TrackAndGetGlobalMemory(bb, GetRegister(instr.gpr8),
|
||||
static_cast<u32>(instr.ldg.immediate_offset.Value()), false);
|
||||
const auto type = [instr, &opcode]() -> Tegra::Shader::UniformType {
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::LD:
|
||||
UNIMPLEMENTED_IF_MSG(!instr.generic.extended, "Unextended LD is not implemented");
|
||||
return instr.generic.type;
|
||||
case OpCode::Id::LDG:
|
||||
return instr.ldg.type;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
}
|
||||
}();
|
||||
|
||||
const u32 count = GetUniformTypeElementsCount(instr.ldg.type);
|
||||
const auto [real_address_base, base_address, descriptor] =
|
||||
TrackAndGetGlobalMemory(bb, instr, false);
|
||||
|
||||
const u32 count = GetUniformTypeElementsCount(type);
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
const Node it_offset = Immediate(i * 4);
|
||||
const Node real_address =
|
||||
@@ -165,28 +178,6 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::STG: {
|
||||
const auto [real_address_base, base_address, descriptor] =
|
||||
TrackAndGetGlobalMemory(bb, GetRegister(instr.gpr8),
|
||||
static_cast<u32>(instr.stg.immediate_offset.Value()), true);
|
||||
|
||||
// Encode in temporary registers like this: real_base_address, {registers_to_be_written...}
|
||||
SetTemporal(bb, 0, real_address_base);
|
||||
|
||||
const u32 count = GetUniformTypeElementsCount(instr.stg.type);
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
SetTemporal(bb, i + 1, GetRegister(instr.gpr0.Value() + i));
|
||||
}
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
const Node it_offset = Immediate(i * 4);
|
||||
const Node real_address =
|
||||
Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset);
|
||||
const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor));
|
||||
|
||||
bb.push_back(Operation(OperationCode::Assign, gmem, GetTemporal(i + 1)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::ST_A: {
|
||||
UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex,
|
||||
"Indirect attribute loads are not supported");
|
||||
@@ -242,6 +233,41 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::ST:
|
||||
case OpCode::Id::STG: {
|
||||
const auto type = [instr, &opcode]() -> Tegra::Shader::UniformType {
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::ST:
|
||||
UNIMPLEMENTED_IF_MSG(!instr.generic.extended, "Unextended ST is not implemented");
|
||||
return instr.generic.type;
|
||||
case OpCode::Id::STG:
|
||||
return instr.stg.type;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
}
|
||||
}();
|
||||
|
||||
const auto [real_address_base, base_address, descriptor] =
|
||||
TrackAndGetGlobalMemory(bb, instr, true);
|
||||
|
||||
// Encode in temporary registers like this: real_base_address, {registers_to_be_written...}
|
||||
SetTemporal(bb, 0, real_address_base);
|
||||
|
||||
const u32 count = GetUniformTypeElementsCount(type);
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
SetTemporal(bb, i + 1, GetRegister(instr.gpr0.Value() + i));
|
||||
}
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
const Node it_offset = Immediate(i * 4);
|
||||
const Node real_address =
|
||||
Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset);
|
||||
const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor));
|
||||
|
||||
bb.push_back(Operation(OperationCode::Assign, gmem, GetTemporal(i + 1)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::AL2P: {
|
||||
// Ignore al2p.direction since we don't care about it.
|
||||
|
||||
@@ -265,9 +291,11 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
}
|
||||
|
||||
std::tuple<Node, Node, GlobalMemoryBase> ShaderIR::TrackAndGetGlobalMemory(NodeBlock& bb,
|
||||
Node addr_register,
|
||||
u32 immediate_offset,
|
||||
Instruction instr,
|
||||
bool is_write) {
|
||||
const auto addr_register{GetRegister(instr.gmem.gpr)};
|
||||
const auto immediate_offset{static_cast<u32>(instr.gmem.offset)};
|
||||
|
||||
const Node base_address{
|
||||
TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size()))};
|
||||
const auto cbuf = std::get_if<CbufNode>(base_address);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
@@ -13,6 +14,7 @@ using Tegra::Shader::ConditionCode;
|
||||
using Tegra::Shader::Instruction;
|
||||
using Tegra::Shader::OpCode;
|
||||
using Tegra::Shader::Register;
|
||||
using Tegra::Shader::SystemVariable;
|
||||
|
||||
u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||
const Instruction instr = {program_code[pc]};
|
||||
@@ -58,20 +60,33 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::MOV_SYS: {
|
||||
switch (instr.sys20) {
|
||||
case Tegra::Shader::SystemVariable::InvocationInfo: {
|
||||
LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete");
|
||||
SetRegister(bb, instr.gpr0, Immediate(0u));
|
||||
break;
|
||||
}
|
||||
case Tegra::Shader::SystemVariable::Ydirection: {
|
||||
// Config pack's third value is Y_NEGATE's state.
|
||||
SetRegister(bb, instr.gpr0, Operation(OperationCode::YNegate));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unhandled system move: {}", static_cast<u32>(instr.sys20.Value()));
|
||||
}
|
||||
const Node value = [&]() {
|
||||
switch (instr.sys20) {
|
||||
case SystemVariable::Ydirection:
|
||||
return Operation(OperationCode::YNegate);
|
||||
case SystemVariable::InvocationInfo:
|
||||
LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete");
|
||||
return Immediate(0u);
|
||||
case SystemVariable::TidX:
|
||||
return Operation(OperationCode::LocalInvocationIdX);
|
||||
case SystemVariable::TidY:
|
||||
return Operation(OperationCode::LocalInvocationIdY);
|
||||
case SystemVariable::TidZ:
|
||||
return Operation(OperationCode::LocalInvocationIdZ);
|
||||
case SystemVariable::CtaIdX:
|
||||
return Operation(OperationCode::WorkGroupIdX);
|
||||
case SystemVariable::CtaIdY:
|
||||
return Operation(OperationCode::WorkGroupIdY);
|
||||
case SystemVariable::CtaIdZ:
|
||||
return Operation(OperationCode::WorkGroupIdZ);
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unhandled system move: {}",
|
||||
static_cast<u32>(instr.sys20.Value()));
|
||||
return Immediate(0u);
|
||||
}
|
||||
}();
|
||||
SetRegister(bb, instr.gpr0, value);
|
||||
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::BRA: {
|
||||
|
||||
@@ -64,4 +64,4 @@ u32 ShaderIR::DecodePredicateSetPredicate(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -43,4 +43,4 @@ u32 ShaderIR::DecodePredicateSetRegister(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -48,4 +48,4 @@ u32 ShaderIR::DecodeRegisterSetPredicate(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -52,4 +52,4 @@ u32 ShaderIR::DecodeShift(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -108,4 +108,4 @@ Node ShaderIR::GetVideoOperand(Node op, bool is_chunk, bool is_signed,
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -39,8 +39,8 @@ Node ShaderIR::Conditional(Node condition, std::vector<Node>&& code) {
|
||||
return StoreNode(ConditionalNode(condition, std::move(code)));
|
||||
}
|
||||
|
||||
Node ShaderIR::Comment(const std::string& text) {
|
||||
return StoreNode(CommentNode(text));
|
||||
Node ShaderIR::Comment(std::string text) {
|
||||
return StoreNode(CommentNode(std::move(text)));
|
||||
}
|
||||
|
||||
Node ShaderIR::Immediate(u32 value) {
|
||||
|
||||
@@ -181,7 +181,13 @@ enum class OperationCode {
|
||||
EmitVertex, /// () -> void
|
||||
EndPrimitive, /// () -> void
|
||||
|
||||
YNegate, /// () -> float
|
||||
YNegate, /// () -> float
|
||||
LocalInvocationIdX, /// () -> uint
|
||||
LocalInvocationIdY, /// () -> uint
|
||||
LocalInvocationIdZ, /// () -> uint
|
||||
WorkGroupIdX, /// () -> uint
|
||||
WorkGroupIdY, /// () -> uint
|
||||
WorkGroupIdZ, /// () -> uint
|
||||
|
||||
Amount,
|
||||
};
|
||||
@@ -663,7 +669,7 @@ private:
|
||||
/// Creates a conditional node
|
||||
Node Conditional(Node condition, std::vector<Node>&& code);
|
||||
/// Creates a commentary
|
||||
Node Comment(const std::string& text);
|
||||
Node Comment(std::string text);
|
||||
/// Creates an u32 immediate
|
||||
Node Immediate(u32 value);
|
||||
/// Creates a s32 immediate
|
||||
@@ -818,10 +824,8 @@ private:
|
||||
std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code,
|
||||
s64 cursor) const;
|
||||
|
||||
std::tuple<Node, Node, GlobalMemoryBase> TrackAndGetGlobalMemory(NodeBlock& bb,
|
||||
Node addr_register,
|
||||
u32 immediate_offset,
|
||||
bool is_write);
|
||||
std::tuple<Node, Node, GlobalMemoryBase> TrackAndGetGlobalMemory(
|
||||
NodeBlock& bb, Tegra::Shader::Instruction instr, bool is_write);
|
||||
|
||||
template <typename... T>
|
||||
Node Operation(OperationCode code, const T*... operands) {
|
||||
|
||||
@@ -155,6 +155,10 @@ target_compile_definitions(yuzu PRIVATE
|
||||
# Use QStringBuilder for string concatenation to reduce
|
||||
# the overall number of temporary strings created.
|
||||
-DQT_USE_QSTRINGBUILDER
|
||||
|
||||
# Disable implicit conversions from/to C strings
|
||||
-DQT_NO_CAST_FROM_ASCII
|
||||
-DQT_NO_CAST_TO_ASCII
|
||||
)
|
||||
|
||||
if (YUZU_ENABLE_COMPATIBILITY_REPORTING)
|
||||
|
||||
@@ -29,11 +29,13 @@ void QtErrorDisplay::ShowError(ResultCode error, std::function<void()> finished)
|
||||
void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
|
||||
std::function<void()> finished) const {
|
||||
this->callback = std::move(finished);
|
||||
|
||||
const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count());
|
||||
emit MainWindowDisplayError(
|
||||
tr("An error occured on %1 at %2.\nPlease try again or contact the "
|
||||
"developer of the software.\n\nError Code: %3-%4 (0x%5)")
|
||||
.arg(QDateTime::fromSecsSinceEpoch(time.count()).toString("dddd, MMMM d, yyyy"))
|
||||
.arg(QDateTime::fromSecsSinceEpoch(time.count()).toString("h:mm:ss A"))
|
||||
.arg(date_time.toString(QStringLiteral("dddd, MMMM d, yyyy")))
|
||||
.arg(date_time.toString(QStringLiteral("h:mm:ss A")))
|
||||
.arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
|
||||
.arg(error.description, 4, 10, QChar::fromLatin1('0'))
|
||||
.arg(error.raw, 8, 16, QChar::fromLatin1('0')));
|
||||
|
||||
@@ -27,20 +27,20 @@ constexpr std::array<u8, 107> backup_jpeg{
|
||||
0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
|
||||
};
|
||||
|
||||
QString FormatUserEntryText(const QString& username, Service::Account::UUID uuid) {
|
||||
QString FormatUserEntryText(const QString& username, Common::UUID uuid) {
|
||||
return QtProfileSelectionDialog::tr(
|
||||
"%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. "
|
||||
"00112233-4455-6677-8899-AABBCCDDEEFF))")
|
||||
.arg(username, QString::fromStdString(uuid.FormatSwitch()));
|
||||
}
|
||||
|
||||
QString GetImagePath(Service::Account::UUID uuid) {
|
||||
QString GetImagePath(Common::UUID uuid) {
|
||||
const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
|
||||
"/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
|
||||
return QString::fromStdString(path);
|
||||
}
|
||||
|
||||
QPixmap GetIcon(Service::Account::UUID uuid) {
|
||||
QPixmap GetIcon(Common::UUID uuid) {
|
||||
QPixmap icon{GetImagePath(uuid)};
|
||||
|
||||
if (!icon) {
|
||||
@@ -122,21 +122,15 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
|
||||
QtProfileSelectionDialog::~QtProfileSelectionDialog() = default;
|
||||
|
||||
void QtProfileSelectionDialog::accept() {
|
||||
ok = true;
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void QtProfileSelectionDialog::reject() {
|
||||
ok = false;
|
||||
user_index = 0;
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
bool QtProfileSelectionDialog::GetStatus() const {
|
||||
return ok;
|
||||
}
|
||||
|
||||
u32 QtProfileSelectionDialog::GetIndex() const {
|
||||
int QtProfileSelectionDialog::GetIndex() const {
|
||||
return user_index;
|
||||
}
|
||||
|
||||
@@ -154,12 +148,12 @@ QtProfileSelector::QtProfileSelector(GMainWindow& parent) {
|
||||
QtProfileSelector::~QtProfileSelector() = default;
|
||||
|
||||
void QtProfileSelector::SelectProfile(
|
||||
std::function<void(std::optional<Service::Account::UUID>)> callback) const {
|
||||
std::function<void(std::optional<Common::UUID>)> callback) const {
|
||||
this->callback = std::move(callback);
|
||||
emit MainWindowSelectProfile();
|
||||
}
|
||||
|
||||
void QtProfileSelector::MainWindowFinishedSelection(std::optional<Service::Account::UUID> uuid) {
|
||||
void QtProfileSelector::MainWindowFinishedSelection(std::optional<Common::UUID> uuid) {
|
||||
// Acquire the HLE mutex
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
callback(uuid);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <QList>
|
||||
#include <QTreeView>
|
||||
#include "core/frontend/applets/profile_select.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
|
||||
class GMainWindow;
|
||||
class QDialogButtonBox;
|
||||
@@ -29,15 +30,13 @@ public:
|
||||
void accept() override;
|
||||
void reject() override;
|
||||
|
||||
bool GetStatus() const;
|
||||
u32 GetIndex() const;
|
||||
int GetIndex() const;
|
||||
|
||||
private:
|
||||
bool ok = false;
|
||||
u32 user_index = 0;
|
||||
|
||||
void SelectUser(const QModelIndex& index);
|
||||
|
||||
int user_index = 0;
|
||||
|
||||
QVBoxLayout* layout;
|
||||
QTreeView* tree_view;
|
||||
QStandardItemModel* item_model;
|
||||
@@ -60,14 +59,13 @@ public:
|
||||
explicit QtProfileSelector(GMainWindow& parent);
|
||||
~QtProfileSelector() override;
|
||||
|
||||
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;
|
||||
|
||||
signals:
|
||||
void MainWindowSelectProfile() const;
|
||||
|
||||
private:
|
||||
void MainWindowFinishedSelection(std::optional<Service::Account::UUID> uuid);
|
||||
void MainWindowFinishedSelection(std::optional<Common::UUID> uuid);
|
||||
|
||||
mutable std::function<void(std::optional<Service::Account::UUID>)> callback;
|
||||
mutable std::function<void(std::optional<Common::UUID>)> callback;
|
||||
};
|
||||
|
||||
@@ -18,23 +18,30 @@ QtSoftwareKeyboardValidator::QtSoftwareKeyboardValidator(
|
||||
: parameters(std::move(parameters)) {}
|
||||
|
||||
QValidator::State QtSoftwareKeyboardValidator::validate(QString& input, int& pos) const {
|
||||
if (input.size() > parameters.max_length)
|
||||
if (input.size() > static_cast<s64>(parameters.max_length)) {
|
||||
return Invalid;
|
||||
if (parameters.disable_space && input.contains(' '))
|
||||
}
|
||||
if (parameters.disable_space && input.contains(QLatin1Char{' '})) {
|
||||
return Invalid;
|
||||
if (parameters.disable_address && input.contains('@'))
|
||||
}
|
||||
if (parameters.disable_address && input.contains(QLatin1Char{'@'})) {
|
||||
return Invalid;
|
||||
if (parameters.disable_percent && input.contains('%'))
|
||||
}
|
||||
if (parameters.disable_percent && input.contains(QLatin1Char{'%'})) {
|
||||
return Invalid;
|
||||
if (parameters.disable_slash && (input.contains('/') || input.contains('\\')))
|
||||
}
|
||||
if (parameters.disable_slash &&
|
||||
(input.contains(QLatin1Char{'/'}) || input.contains(QLatin1Char{'\\'}))) {
|
||||
return Invalid;
|
||||
}
|
||||
if (parameters.disable_number &&
|
||||
std::any_of(input.begin(), input.end(), [](QChar c) { return c.isDigit(); })) {
|
||||
return Invalid;
|
||||
}
|
||||
|
||||
if (parameters.disable_download_code &&
|
||||
std::any_of(input.begin(), input.end(), [](QChar c) { return c == 'O' || c == 'I'; })) {
|
||||
if (parameters.disable_download_code && std::any_of(input.begin(), input.end(), [](QChar c) {
|
||||
return c == QLatin1Char{'O'} || c == QLatin1Char{'I'};
|
||||
})) {
|
||||
return Invalid;
|
||||
}
|
||||
|
||||
@@ -97,13 +104,11 @@ QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog(
|
||||
QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() = default;
|
||||
|
||||
void QtSoftwareKeyboardDialog::accept() {
|
||||
ok = true;
|
||||
text = line_edit->text().toStdU16String();
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void QtSoftwareKeyboardDialog::reject() {
|
||||
ok = false;
|
||||
text.clear();
|
||||
QDialog::reject();
|
||||
}
|
||||
@@ -112,10 +117,6 @@ std::u16string QtSoftwareKeyboardDialog::GetText() const {
|
||||
return text;
|
||||
}
|
||||
|
||||
bool QtSoftwareKeyboardDialog::GetStatus() const {
|
||||
return ok;
|
||||
}
|
||||
|
||||
QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& main_window) {
|
||||
connect(this, &QtSoftwareKeyboard::MainWindowGetText, &main_window,
|
||||
&GMainWindow::SoftwareKeyboardGetText, Qt::QueuedConnection);
|
||||
@@ -142,7 +143,7 @@ void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message,
|
||||
void QtSoftwareKeyboard::MainWindowFinishedText(std::optional<std::u16string> text) {
|
||||
// Acquire the HLE mutex
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
text_output(text);
|
||||
text_output(std::move(text));
|
||||
}
|
||||
|
||||
void QtSoftwareKeyboard::MainWindowFinishedCheckDialog() {
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include <QDialog>
|
||||
#include <QValidator>
|
||||
#include "common/assert.h"
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
|
||||
class GMainWindow;
|
||||
@@ -37,10 +36,8 @@ public:
|
||||
void reject() override;
|
||||
|
||||
std::u16string GetText() const;
|
||||
bool GetStatus() const;
|
||||
|
||||
private:
|
||||
bool ok = false;
|
||||
std::u16string text;
|
||||
|
||||
QDialogButtonBox* buttons;
|
||||
|
||||
+42
-35
@@ -26,6 +26,8 @@
|
||||
|
||||
EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {}
|
||||
|
||||
EmuThread::~EmuThread() = default;
|
||||
|
||||
void EmuThread::run() {
|
||||
render_window->MakeCurrent();
|
||||
|
||||
@@ -91,25 +93,25 @@ void EmuThread::run() {
|
||||
|
||||
class GGLContext : public Core::Frontend::GraphicsContext {
|
||||
public:
|
||||
explicit GGLContext(QOpenGLContext* shared_context)
|
||||
: context{std::make_unique<QOpenGLContext>(shared_context)} {
|
||||
surface.setFormat(shared_context->format());
|
||||
surface.create();
|
||||
explicit GGLContext(QOpenGLContext* shared_context) : shared_context{shared_context} {
|
||||
context.setFormat(shared_context->format());
|
||||
context.setShareContext(shared_context);
|
||||
context.create();
|
||||
}
|
||||
|
||||
void MakeCurrent() override {
|
||||
context->makeCurrent(&surface);
|
||||
context.makeCurrent(shared_context->surface());
|
||||
}
|
||||
|
||||
void DoneCurrent() override {
|
||||
context->doneCurrent();
|
||||
context.doneCurrent();
|
||||
}
|
||||
|
||||
void SwapBuffers() override {}
|
||||
|
||||
private:
|
||||
std::unique_ptr<QOpenGLContext> context;
|
||||
QOffscreenSurface surface;
|
||||
QOpenGLContext* shared_context;
|
||||
QOpenGLContext context;
|
||||
};
|
||||
|
||||
// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL
|
||||
@@ -185,7 +187,7 @@ private:
|
||||
bool do_painting;
|
||||
};
|
||||
|
||||
GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
|
||||
GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread)
|
||||
: QWidget(parent), emu_thread(emu_thread) {
|
||||
setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
|
||||
.arg(QString::fromUtf8(Common::g_build_name),
|
||||
@@ -194,8 +196,7 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
|
||||
setAttribute(Qt::WA_AcceptTouchEvents);
|
||||
|
||||
InputCommon::Init();
|
||||
connect(this, &GRenderWindow::FirstFrameDisplayed, static_cast<GMainWindow*>(parent),
|
||||
&GMainWindow::OnLoadComplete);
|
||||
connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
|
||||
}
|
||||
|
||||
GRenderWindow::~GRenderWindow() {
|
||||
@@ -247,9 +248,9 @@ void GRenderWindow::PollEvents() {}
|
||||
void GRenderWindow::OnFramebufferSizeChanged() {
|
||||
// Screen changes potentially incur a change in screen DPI, hence we should update the
|
||||
// framebuffer size
|
||||
qreal pixelRatio = GetWindowPixelRatio();
|
||||
unsigned width = child->QPaintDevice::width() * pixelRatio;
|
||||
unsigned height = child->QPaintDevice::height() * pixelRatio;
|
||||
const qreal pixel_ratio = GetWindowPixelRatio();
|
||||
const u32 width = child->QPaintDevice::width() * pixel_ratio;
|
||||
const u32 height = child->QPaintDevice::height() * pixel_ratio;
|
||||
UpdateCurrentFramebufferLayout(width, height);
|
||||
}
|
||||
|
||||
@@ -266,7 +267,7 @@ void GRenderWindow::ForwardKeyReleaseEvent(QKeyEvent* event) {
|
||||
}
|
||||
|
||||
void GRenderWindow::BackupGeometry() {
|
||||
geometry = ((QWidget*)this)->saveGeometry();
|
||||
geometry = QWidget::saveGeometry();
|
||||
}
|
||||
|
||||
void GRenderWindow::RestoreGeometry() {
|
||||
@@ -283,10 +284,11 @@ void GRenderWindow::restoreGeometry(const QByteArray& geometry) {
|
||||
QByteArray GRenderWindow::saveGeometry() {
|
||||
// If we are a top-level widget, store the current geometry
|
||||
// otherwise, store the last backup
|
||||
if (parent() == nullptr)
|
||||
return ((QWidget*)this)->saveGeometry();
|
||||
else
|
||||
return geometry;
|
||||
if (parent() == nullptr) {
|
||||
return QWidget::saveGeometry();
|
||||
}
|
||||
|
||||
return geometry;
|
||||
}
|
||||
|
||||
qreal GRenderWindow::GetWindowPixelRatio() const {
|
||||
@@ -294,10 +296,10 @@ qreal GRenderWindow::GetWindowPixelRatio() const {
|
||||
return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f;
|
||||
}
|
||||
|
||||
std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const {
|
||||
std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const {
|
||||
const qreal pixel_ratio = GetWindowPixelRatio();
|
||||
return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
|
||||
static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
|
||||
return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
|
||||
static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
|
||||
}
|
||||
|
||||
void GRenderWindow::closeEvent(QCloseEvent* event) {
|
||||
@@ -353,12 +355,12 @@ void GRenderWindow::focusOutEvent(QFocusEvent* event) {
|
||||
InputCommon::GetKeyboard()->ReleaseAllKeys();
|
||||
}
|
||||
|
||||
void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) {
|
||||
void GRenderWindow::OnClientAreaResized(u32 width, u32 height) {
|
||||
NotifyClientAreaSizeChanged(std::make_pair(width, height));
|
||||
}
|
||||
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
|
||||
return std::make_unique<GGLContext>(shared_context.get());
|
||||
return std::make_unique<GGLContext>(context.get());
|
||||
}
|
||||
|
||||
void GRenderWindow::InitRenderTarget() {
|
||||
@@ -394,7 +396,7 @@ void GRenderWindow::InitRenderTarget() {
|
||||
context->setShareContext(shared_context.get());
|
||||
context->setFormat(fmt);
|
||||
context->create();
|
||||
fmt.setSwapInterval(false);
|
||||
fmt.setSwapInterval(0);
|
||||
|
||||
child = new GGLWidgetInternal(this, shared_context.get());
|
||||
container = QWidget::createWindowContainer(child, this);
|
||||
@@ -424,24 +426,29 @@ void GRenderWindow::InitRenderTarget() {
|
||||
BackupGeometry();
|
||||
}
|
||||
|
||||
void GRenderWindow::CaptureScreenshot(u16 res_scale, const QString& screenshot_path) {
|
||||
void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) {
|
||||
auto& renderer = Core::System::GetInstance().Renderer();
|
||||
|
||||
if (!res_scale)
|
||||
if (res_scale == 0) {
|
||||
res_scale = VideoCore::GetResolutionScaleFactor(renderer);
|
||||
}
|
||||
|
||||
const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)};
|
||||
screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
|
||||
renderer.RequestScreenshot(screenshot_image.bits(),
|
||||
[=] {
|
||||
screenshot_image.mirrored(false, true).save(screenshot_path);
|
||||
LOG_INFO(Frontend, "The screenshot is saved.");
|
||||
},
|
||||
layout);
|
||||
renderer.RequestScreenshot(
|
||||
screenshot_image.bits(),
|
||||
[=] {
|
||||
const std::string std_screenshot_path = screenshot_path.toStdString();
|
||||
if (screenshot_image.mirrored(false, true).save(screenshot_path)) {
|
||||
LOG_INFO(Frontend, "Screenshot saved to \"{}\"", std_screenshot_path);
|
||||
} else {
|
||||
LOG_ERROR(Frontend, "Failed to save screenshot to \"{}\"", std_screenshot_path);
|
||||
}
|
||||
},
|
||||
layout);
|
||||
}
|
||||
|
||||
void GRenderWindow::OnMinimalClientAreaChangeRequest(
|
||||
const std::pair<unsigned, unsigned>& minimal_size) {
|
||||
void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) {
|
||||
setMinimumSize(minimal_size.first, minimal_size.second);
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user