Compare commits

...

87 Commits

Author SHA1 Message Date
Lioncash
c09ff382a4 yuzu/configuration: Make all widgets and dialogs aware of language changes
To prepare for translation support, this makes all of the widgets
cognizant of the language change event that occurs whenever
installTranslator() is called and automatically retranslates their text
where necessary.

This is important as calling the backing UI's retranslateUi() is often
not enough, particularly in cases where we add our own strings that
aren't controlled by it. In that case we need to manually refresh the
strings ourselves.
2019-06-05 21:57:21 -04:00
bunnei
8d7a012297 Merge pull request #2521 from lioncash/naming
yuzu/configuration: Make function naming consistent
2019-06-05 18:03:05 -04:00
bunnei
0bcc305797 Merge pull request #2512 from ReinUsesLisp/comp-indexing
gl_shader_decompiler: Pessimize uniform buffer access on AMD's prorpietary driver
2019-06-05 18:02:30 -04:00
Zach Hilman
799302bc9d Merge pull request #2526 from lioncash/global
core/telemetry_session: Remove usages of the global system accessor
2019-06-05 15:57:48 -04:00
Zach Hilman
81e09bb121 Merge pull request #2545 from lioncash/timing
core/core_timing_util: Use std::chrono types for specifying time units
2019-06-05 15:52:37 -04:00
Zach Hilman
6aff1005ef Merge pull request #2541 from lioncash/input
input_common/sdl/sdl_impl: Minor cleanup
2019-06-05 15:51:03 -04:00
Lioncash
e1d755bdda yuzu/configuration: Make function naming consistent 2019-06-05 15:40:33 -04:00
Zach Hilman
2beaaa35c5 Merge pull request #2510 from SciresM/desired_language
Implement/Fix IApplicationFunctions::GetDesiredLanguage
2019-06-05 15:39:33 -04:00
Zach Hilman
1eb979221f Merge pull request #2527 from lioncash/index
yuzu/{profile_select, software_keyboard}: Tidy up interface
2019-06-05 15:30:51 -04:00
Zach Hilman
dd4fe0dab1 Merge pull request #2534 from ReinUsesLisp/shader-cleanup
gl_shader_cache: Minor style changes
2019-06-05 15:28:34 -04:00
Zach Hilman
433ca686a8 Merge pull request #2531 from ReinUsesLisp/qt-warnings
qt: Silence name collision warnings
2019-06-05 15:27:12 -04:00
Zach Hilman
6ce5f3e1bf Merge pull request #2515 from lioncash/narrowing
yuzu/configuration/configure_graphics: Eliminate type narrowing in a connect call
2019-06-05 15:26:13 -04:00
Zach Hilman
dfc40a3f9e Merge pull request #2532 from ShalokShalom/patch-2
Remove outdated info about compability
2019-06-05 15:04:57 -04:00
Zach Hilman
4f7a1f6c8c Merge pull request #2536 from lioncash/cache
game_list_worker: Use QFile over our own IOFile instance or std streams for the game list cache
2019-06-05 15:03:59 -04:00
Rodrigo Locatti
2ba4aa8a3b Merge pull request #2529 from lioncash/boot
yuzu/bootmanager: Minor interface tidying
2019-06-04 21:35:56 -03:00
Lioncash
2548661c08 core/core_timing_util: Amend casing of cyclesTo* functions
Makes the casing consistent with all of our general function naming
conventions.
2019-06-04 20:31:46 -04:00
Lioncash
42f5fd0ab3 core/core_timing_util: Use std::chrono types for specifying time units
Makes the interface more type-safe and consistent in terms of return
values.
2019-06-04 20:31:24 -04:00
Lioncash
79189c7e3e core/core_timing_utils: Simplify overload set
Removes unused overloads, simplifying the overall interface,
deduplicating some code.
2019-06-04 19:44:05 -04:00
Mat M
55f8111543 Merge pull request #2525 from FearlessTobi/remove-unused-settings
yuzu: Remove unused birthday setting
2019-06-04 13:39:24 -04:00
Hexagon12
b35953e4fd Merge pull request #2543 from FernandoS27/exit-flow
shader_bytecode: Mark EXIT as flow instruction
2019-06-04 19:52:59 +03:00
Fernando Sahmkow
a32c52b1d8 shader_bytecode: Mark EXIT as flow instruction 2019-06-04 12:18:35 -04:00
Lioncash
5ccf2a7b82 input_common/sdl/sdl_impl: Correct logging string in SDLState constructor
If this path was ever taken, a runtime exception would occur due to the
lack of a formatting specifier to insert the error code into the format
string.
2019-06-03 16:56:47 -04:00
Lioncash
cfac942e63 input_common/sdl/sdl_impl: Move documentation comments to header where applicable
Places the documentation comments with the rest of SDLState's member
function documentation.
2019-06-03 16:56:47 -04:00
Lioncash
b9b23c98ff input_common/sdl/sdl_impl: Amend names for axes for SDLAnalogPoller
Adds another underscore to clearly indicate the axis names.
2019-06-03 16:56:47 -04:00
Lioncash
50048d9f5a input_common/sdl/sdl_impl: Mark variables const where applicable
Make it explicit that these aren't modified elsewhere (either through
functions by reference, or by other operations).
2019-06-03 16:56:47 -04:00
Lioncash
ca7ca2919c input_common/sdl/sdl_impl: Mark SDLEventToButtonParamPackage() as static
Its prototype declared at the top of the translation unit contains the
static qualifier, so the function itself should also contain it to make
it a proper internally linked function.
2019-06-03 16:56:47 -04:00
Lioncash
b73ea457cc input_common/sdl/sdl_impl: Convert reinterpret_cast into a static_cast
It's valid to static_cast a void pointer back into its proper type.
2019-06-03 16:56:46 -04:00
Lioncash
2c679cda51 input_common/sdl/sdl_impl: Use insert_or_assign() where applicable
Same behavior, but without a potential need to unnecessarily default
construct a value.
2019-06-03 16:56:46 -04:00
Lioncash
b46e615551 input_common/sdl/sdl_impl: Simplify SDL_Joystick deleter handling
The deleter can just be set in the constructor and maintained throughout
the lifetime of the object.

If a contained pointer is null, then the deleter won't execute, so this
is safe to do. We don't need to swap it out with a version of a deleter
that does nothing.
2019-06-03 16:56:46 -04:00
Lioncash
7ea07c6063 input_common/sdl/sdl_impl: Resolve two sign conversion warnings
Silences the final two warnings in SDL code.
2019-06-03 16:56:46 -04:00
Lioncash
cf0d01a5d7 input_common/sdl: Remove unused header includes and forward declarations
Gets rid of a few unnecessary inclusion dependencies. It also uncovered
a few indirect inclusion dependencies being relied upon.
2019-06-03 16:56:42 -04:00
Lioncash
00f0827a26 input_common/sdl/sdl_impl: Use nested namespace specifiers where applicable 2019-06-03 15:49:04 -04:00
Lioncash
77ce85f51d yuzu/bootmanager: Log out screenshot destination path
We can make this message more meaningful by indicating the location the
screenshot has been saved to. We can also log out whenever a screenshot
could not be saved (e.g. due to filesystem permissions or some other
reason).
2019-06-03 15:34:32 -04:00
Lioncash
e32bf646cf yuzu/bootmanager: Treat the resolution factor as a u32
Treating it as a u16 can result in a sign-conversion warning when
performing arithmetic with it, as u16 promotes to an int when aritmetic
is performed on it, not unsigned int.

This also makes the interface more uniform, as the layout interface now
operates on u32 across the board.
2019-06-03 15:34:31 -04:00
Lioncash
536c9cf006 yuzu/bootmanager: Default EmuThread's destructor in the cpp file
This class contains non-trivial members, so we should default the
destructor's definition within the cpp file.
2019-06-03 15:34:31 -04:00
Lioncash
0a650ec99e yuzu/bootmanager: unsigned -> u32
Same thing (for platforms we support), less reading.
2019-06-03 15:34:31 -04:00
Lioncash
2575403acf yuzu/bootmanager: Change false literal to 0 for setSwapInterval()
This function is defined as taking an int, not a bool.
2019-06-03 15:31:52 -04:00
Lioncash
cfb59aad3f yuzu/bootmanager: Remove pointer downcast in GRenderWindow's constructor
We can just pass a pointer to GMainWindow directly and make it a
requirement of the interface. This makes the interface a little safer,
since this would technically otherwise allow any random QWidget to be
the parent of a render window, downcasting it to GMainWindow (which is
undefined behavior).
2019-06-03 15:31:52 -04:00
Lioncash
49e3a6e924 yuzu/bootmanager: Remove unnecessary pointer casts
We can just invoke these functions by qualifying the object name before
the function.
2019-06-03 15:31:51 -04:00
Lioncash
e70f16fff7 input_common/sdl/sdl_impl: Silence sign conversion warnings
Makes the conversions explicit, as opposed to implicit.
2019-05-31 04:47:02 -03:00
Lioncash
1edf018319 common/math_util: Provide a template deduction guide for Common::Rectangle
Allows for things such as:

auto rect = Common::Rectangle{0, 0, 0, 0};

as opposed to being required to explicitly write out the underlying
type, such as:

auto rect = Common::Rectangle<int>{0, 0, 0, 0};

The only requirement for the deduction is that all constructor arguments
be the same type.
2019-05-31 04:44:02 -03:00
Lioncash
d0d97de1e4 game_list_worker: Use QFile over our own IOFile instance or std streams
Stays consistent in our code with using Qt's provided mechanisms, and
also properly handles Unicode paths (which file streams on Windows don't
do very well).
2019-05-30 22:15:13 -04:00
Lioncash
de2533d389 game_list_worker: Remove template specializations
This is equivalent to specifying two separate functions, so we can just
do that.
2019-05-30 18:56:06 -04:00
bunnei
ed74a3cb8b Merge pull request #1931 from DarkLordZach/mii-database-1
mii: Implement MiiManager backend and several mii service commands
2019-05-30 13:26:40 -04:00
bunnei
75561d190a Merge pull request #2431 from DarkLordZach/game-list-cache
yuzu: Implement a caching mechanism for the game list
2019-05-30 13:04:40 -04:00
ReinUsesLisp
e72b9044a0 gl_shader_cache: Store a system class and drop global accessors 2019-05-30 14:01:40 -03:00
ReinUsesLisp
ad321564ed gl_shader_cache: Add commentaries explaining the intention in shaders creation 2019-05-30 13:58:38 -03:00
ReinUsesLisp
838b6d2ff8 gl_shader_cache: Flip if condition in GetStageProgram to reduce indentation 2019-05-30 13:56:03 -03:00
Zach Hilman
9b2d38582f main: Remove extraneous comment 2019-05-30 10:47:56 -04:00
ShalokShalom
e9772a2539 Remove outdated info about compability 2019-05-30 05:38:27 +02:00
ReinUsesLisp
3f11d1c821 qt: Silence name collision warnings 2019-05-29 21:35:05 -03:00
bunnei
e3608578e4 Merge pull request #2446 from ReinUsesLisp/tid
shader: Implement S2R Tid{XYZ} and CtaId{XYZ}
2019-05-29 12:21:17 -04:00
bunnei
665b7e8e18 Merge pull request #2518 from ReinUsesLisp/sdl2-window
yuzu_cmd: Split emu_window OpenGL implementation into its own file
2019-05-29 11:01:12 -04:00
Lioncash
cfc9d92b38 yuzu/software_keyboard: Remove unnecessary GetStatus() member function
Like with the profile selection dialog, we can just use the result of
QDialog's exec() function to determine whether or not a dialog was
accepted.
2019-05-29 00:56:45 -04:00
Lioncash
802dd3cc95 profile_select: Remove unnecessary GetStatus() member function
This behavior is already provided by the built-in exec() function. We
just need to check the return value of it.
2019-05-29 00:56:41 -04:00
Lioncash
139301c5a1 profile_select: Return int instead of u32 for GetIndex()
Qt uses a signed value to represent indices. We should follow this
convention where applicable to avoid unnecessary sign-conversion
warnings, as well as making it easier to interoperate with other aspects
of Qt.

While we're at it, we can also make a sign-conversion explicit.
2019-05-29 00:29:09 -04:00
Lioncash
8bbe930fac core/core: Remove unnecessary includes
The contents of these includes aren't used anywhere in this translation
unit.
2019-05-29 00:00:27 -04:00
Lioncash
c6f05b586f yuzu_cmd/yuzu: Correct formatting specifier
Amends the formatting specifier to obey libfmt. Prevents the application
from terminating due to a formatting issue in the error case.
2019-05-28 22:28:46 -04:00
Lioncash
84a8fb9264 core/loader: Remove LoadKernelSystemMode
This is a hold-over from Citra and doesn't apply to yuzu.
2019-05-28 22:28:44 -04:00
Lioncash
b1a4ab2ccc core/telemetry_session: Remove unnecessary web service nulling out in destructor
This will automatically occur when the backend instance goes out of
scope at the end of the destructor's execution.
2019-05-28 22:28:18 -04:00
Lioncash
215fd82738 core/telemetry_session: Remove usages of the global system accessor
Makes the dependency explicit in the TelemetrySession's interface
instead of making it a hidden dependency.

This also revealed a hidden issue with the way the telemetry session was
being initialized. It was attempting to retrieve the app loader and log
out title-specific information. However, this isn't always guaranteed to
be possible.

During the initialization phase, everything is being constructed. It
doesn't mean an actual title has been selected. This is what the Load()
function is for. This potentially results in dead code paths involving
the app loader. Instead, we explicitly add this information when we know
the app loader instance is available.
2019-05-28 22:28:15 -04:00
Lioncash
05af9d915c core/telemetry_session: Explicitly delete copy and move constructors
NonCopyable is misleading here. It also makes the class non-moveable as
well, so we can be explicit about this.
2019-05-28 21:07:38 -04:00
Lioncash
2fb3b9b951 core/telemetry_session: Remove unused include 2019-05-28 20:56:22 -04:00
Zach Hilman
46e2ca5475 game_list_worker: Add better error handling to caching 2019-05-26 17:14:09 -04:00
Zach Hilman
944c07ac7d yuzu: Clear partial/full game list cache when data is updated 2019-05-26 15:12:12 -04:00
Zach Hilman
f95bdb5088 game_list: Implement caching for game list
Preserves list of add ons and the icon, which are the two costliest parts of game list population.
2019-05-26 15:12:12 -04:00
Zach Hilman
180f22f17e ui_settings: Add option to cache game list 2019-05-26 15:12:12 -04:00
ReinUsesLisp
37eaf39b44 emu_window: Pass OnMinimalClientAreaChangeRequest argument by copy
There's no performance improvement in passing an unsigned pair by
reference.
2019-05-26 00:54:13 -03:00
ReinUsesLisp
4b80dd23a4 yuzu_cmd: Split emu_window OpenGL implementation into its own file 2019-05-25 17:47:13 -03:00
Lioncash
d623e38d18 yuzu/configuration/configure_graphics: Eliminate type narrowing in a connect call
A checkbox is able to be tri-state, giving it three possible activity
types, so in the connect call here, it would actually be truncating an
int into a bool.

Instead, we can just listen on the toggled() signal, which passes along
a bool, not an int.
2019-05-24 22:24:40 -04:00
ReinUsesLisp
d8827b07b5 gl_shader_decompiler: Use an if based cbuf indexing for broken drivers
The following code is broken on AMD's proprietary GLSL compiler:
```glsl
uint idx = ...;
vec4 values = ...;
float some_value = values[idx & 3];
```

It index the wrong components, to fix this the following pessimized code
is emitted when that bug is present:
```glsl
uint idx = ...;
vec4 values = ...;
float some_value;
if ((idx & 3) == 0) some_value = values.x;
if ((idx & 3) == 1) some_value = values.y;
if ((idx & 3) == 2) some_value = values.z;
if ((idx & 3) == 3) some_value = values.w;
```
2019-05-24 02:47:56 -03:00
ReinUsesLisp
46177901b8 gl_device: Add test to detect broken component indexing
Component indexing on AMD's proprietary driver is broken. This commit adds
a test to detect when we are on a driver that can't successfully manage
component indexing.

It dispatches a dummy draw with just one vertex shader that writes to an
indexed SSBO from the GPU with data sent through uniforms, it then reads
that data from the CPU and compares the expected output.
2019-05-24 02:47:56 -03:00
Michael Scire
016f2eab73 Fix bitmask logic inversion 2019-05-23 02:37:13 -07:00
Michael Scire
2ed896075e fix introduced clang-format errors 2019-05-23 01:39:22 -07:00
Michael Scire
d81b58f320 Address review comments 2019-05-23 01:28:27 -07:00
Michael Scire
7fba9c7224 clang-format fixes 2019-05-23 01:14:11 -07:00
Michael Scire
7dbf4c1ae5 Implement IApplicationFunctions::GetDesiredLanguage 2019-05-23 00:55:56 -07:00
ReinUsesLisp
9c3461604c shader: Implement S2R Tid{XYZ} and CtaId{XYZ} 2019-05-20 16:36:49 -03:00
ReinUsesLisp
ada79fa8ad gl_shader_decompiler: Make GetSwizzle constexpr 2019-05-20 16:36:48 -03:00
Zach Hilman
4e462d1fd7 mii_manager: Fix incorrect loop condition in mii UUID generation code 2019-04-25 08:57:23 -04:00
Zach Hilman
851c01c45e profile_select: Port Service::Account::UUID to Common::UUID 2019-04-25 08:13:11 -04:00
Zach Hilman
1aa2b99a98 mii: Implement Delete and Destroy file 2019-04-25 08:07:57 -04:00
Zach Hilman
c40cff454d mii: Implement IsUpdated command (IPC 0) 2019-04-25 08:07:57 -04:00
Zach Hilman
f0db2e3ef3 mii_manager: Cleanup and optimization 2019-04-25 08:07:57 -04:00
Zach Hilman
e25a7891e9 mii: Implement IDatabaseService commands using MiiManager
Since the MiiManager was designed around the IPC interface, this is quite easy. Only functions that were clearly defined were implemented.
2019-04-25 08:07:57 -04:00
Zach Hilman
daf5b8c61b mii: Add MiiManager class to manage Mii database
Provides serialization/deserialization to the database in system save files, accessors for database state and proper handling of both major Mii formats (MiiInfo and MiiStoreData)
2019-04-25 08:07:57 -04:00
Zach Hilman
ca5638a142 common: Extract UUID to its own class
Since the Mii database uses UUIDs very similar to the Accounts database, it makes no sense to not share code between them.
2019-04-25 08:07:57 -04:00
106 changed files with 3631 additions and 1369 deletions

View File

@@ -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.

View File

@@ -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) {

View File

@@ -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

View File

@@ -41,4 +41,7 @@ struct Rectangle {
}
};
template <typename T>
Rectangle(T, T, T, T)->Rectangle<T>;
} // namespace Common

33
src/common/uuid.cpp Normal file
View File

@@ -0,0 +1,33 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <random>
#include <fmt/format.h>
#include "common/uuid.h"
namespace Common {
UUID UUID::Generate() {
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
return UUID{distribution(gen), distribution(gen)};
}
std::string UUID::Format() const {
return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
}
std::string UUID::FormatSwitch() const {
std::array<u8, 16> s{};
std::memcpy(s.data(), uuid.data(), sizeof(u128));
return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
":02x}{:02x}{:02x}{:02x}{:02x}",
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
s[12], s[13], s[14], s[15]);
}
} // namespace Common

48
src/common/uuid.h Normal file
View File

@@ -0,0 +1,48 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include "common/common_types.h"
namespace Common {
constexpr u128 INVALID_UUID{{0, 0}};
struct UUID {
// UUIDs which are 0 are considered invalid!
u128 uuid = INVALID_UUID;
constexpr UUID() = default;
constexpr explicit UUID(const u128& id) : uuid{id} {}
constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
constexpr explicit operator bool() const {
return uuid[0] != INVALID_UUID[0] && uuid[1] != INVALID_UUID[1];
}
constexpr bool operator==(const UUID& rhs) const {
// TODO(DarkLordZach): Replace with uuid == rhs.uuid with C++20
return uuid[0] == rhs.uuid[0] && uuid[1] == rhs.uuid[1];
}
constexpr bool operator!=(const UUID& rhs) const {
return !operator==(rhs);
}
// TODO(ogniK): Properly generate uuids based on RFC-4122
static UUID Generate();
// Set the UUID to {0,0} to be considered an invalid user
constexpr void Invalidate() {
uuid = INVALID_UUID;
}
std::string Format() const;
std::string FormatSwitch() const;
};
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
} // namespace Common

View File

@@ -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

View File

@@ -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) {

View File

@@ -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

View File

@@ -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 (static_cast<u64>(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 (static_cast<u64>(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 (static_cast<u64>(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 (static_cast<u64>(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) {

View File

@@ -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);

View File

@@ -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));

View File

@@ -109,6 +109,7 @@ public:
std::string GetVersionString() const;
u64 GetDefaultNormalSaveSize() const;
u64 GetDefaultJournalSaveSize() const;
u32 GetSupportedLanguages() const;
std::vector<u8> GetRawBytes() const;
private:

View File

@@ -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...");
}

View File

@@ -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

View File

@@ -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.
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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() {

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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));

View File

@@ -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;

View File

@@ -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> {

View File

@@ -0,0 +1,416 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cstring>
#include "common/assert.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/hle/service/mii/mii_manager.h"
namespace Service::Mii {
namespace {
constexpr char MII_SAVE_DATABASE_PATH[] = "/system/save/8000000000000030/MiiDatabase.dat";
constexpr std::array<char16_t, 11> DEFAULT_MII_NAME = {u'y', u'u', u'z', u'u', u'\0'};
// This value was retrieved from HW test
constexpr MiiStoreData DEFAULT_MII = {
{
0x21, 0x40, 0x40, 0x01, 0x08, 0x01, 0x13, 0x08, 0x08, 0x02, 0x17, 0x8C, 0x06, 0x01,
0x69, 0x6D, 0x8A, 0x6A, 0x82, 0x14, 0x00, 0x00, 0x00, 0x20, 0x64, 0x72, 0x44, 0x44,
},
{'y', 'u', 'z', 'u', '\0'},
Common::UUID{1, 0},
0,
0,
};
// Default values taken from multiple real databases
const MiiDatabase DEFAULT_MII_DATABASE{Common::MakeMagic('N', 'F', 'D', 'B'), {}, {1}, 0, 0};
constexpr std::array<const char*, 4> SOURCE_NAMES{
"Database",
"Default",
"Account",
"Friend",
};
template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize>
std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) {
std::array<T, DestArraySize> out{};
std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize));
return out;
}
MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
MiiStoreBitFields bf{};
std::memcpy(&bf, data.data.data(), sizeof(MiiStoreBitFields));
return {
data.uuid,
ResizeArray<char16_t, 10, 11>(data.name),
static_cast<u8>(bf.font_region.Value()),
static_cast<u8>(bf.favorite_color.Value()),
static_cast<u8>(bf.gender.Value()),
static_cast<u8>(bf.height.Value()),
static_cast<u8>(bf.weight.Value()),
static_cast<u8>(bf.mii_type.Value()),
static_cast<u8>(bf.mii_region.Value()),
static_cast<u8>(bf.face_type.Value()),
static_cast<u8>(bf.face_color.Value()),
static_cast<u8>(bf.face_wrinkle.Value()),
static_cast<u8>(bf.face_makeup.Value()),
static_cast<u8>(bf.hair_type.Value()),
static_cast<u8>(bf.hair_color.Value()),
static_cast<bool>(bf.hair_flip.Value()),
static_cast<u8>(bf.eye_type.Value()),
static_cast<u8>(bf.eye_color.Value()),
static_cast<u8>(bf.eye_scale.Value()),
static_cast<u8>(bf.eye_aspect.Value()),
static_cast<u8>(bf.eye_rotate.Value()),
static_cast<u8>(bf.eye_x.Value()),
static_cast<u8>(bf.eye_y.Value()),
static_cast<u8>(bf.eyebrow_type.Value()),
static_cast<u8>(bf.eyebrow_color.Value()),
static_cast<u8>(bf.eyebrow_scale.Value()),
static_cast<u8>(bf.eyebrow_aspect.Value()),
static_cast<u8>(bf.eyebrow_rotate.Value()),
static_cast<u8>(bf.eyebrow_x.Value()),
static_cast<u8>(bf.eyebrow_y.Value()),
static_cast<u8>(bf.nose_type.Value()),
static_cast<u8>(bf.nose_scale.Value()),
static_cast<u8>(bf.nose_y.Value()),
static_cast<u8>(bf.mouth_type.Value()),
static_cast<u8>(bf.mouth_color.Value()),
static_cast<u8>(bf.mouth_scale.Value()),
static_cast<u8>(bf.mouth_aspect.Value()),
static_cast<u8>(bf.mouth_y.Value()),
static_cast<u8>(bf.facial_hair_color.Value()),
static_cast<u8>(bf.beard_type.Value()),
static_cast<u8>(bf.mustache_type.Value()),
static_cast<u8>(bf.mustache_scale.Value()),
static_cast<u8>(bf.mustache_y.Value()),
static_cast<u8>(bf.glasses_type.Value()),
static_cast<u8>(bf.glasses_color.Value()),
static_cast<u8>(bf.glasses_scale.Value()),
static_cast<u8>(bf.glasses_y.Value()),
static_cast<u8>(bf.mole_type.Value()),
static_cast<u8>(bf.mole_scale.Value()),
static_cast<u8>(bf.mole_x.Value()),
static_cast<u8>(bf.mole_y.Value()),
0x00,
};
}
MiiStoreData ConvertInfoToStoreData(const MiiInfo& info) {
MiiStoreData out{};
out.name = ResizeArray<char16_t, 11, 10>(info.name);
out.uuid = info.uuid;
MiiStoreBitFields bf{};
bf.hair_type.Assign(info.hair_type);
bf.mole_type.Assign(info.mole_type);
bf.height.Assign(info.height);
bf.hair_flip.Assign(info.hair_flip);
bf.weight.Assign(info.weight);
bf.hair_color.Assign(info.hair_color);
bf.gender.Assign(info.gender);
bf.eye_color.Assign(info.eye_color);
bf.eyebrow_color.Assign(info.eyebrow_color);
bf.mouth_color.Assign(info.mouth_color);
bf.facial_hair_color.Assign(info.facial_hair_color);
bf.mii_type.Assign(info.mii_type);
bf.glasses_color.Assign(info.glasses_color);
bf.font_region.Assign(info.font_region);
bf.eye_type.Assign(info.eye_type);
bf.mii_region.Assign(info.mii_region);
bf.mouth_type.Assign(info.mouth_type);
bf.glasses_scale.Assign(info.glasses_scale);
bf.eye_y.Assign(info.eye_y);
bf.mustache_type.Assign(info.mustache_type);
bf.eyebrow_type.Assign(info.eyebrow_type);
bf.beard_type.Assign(info.beard_type);
bf.nose_type.Assign(info.nose_type);
bf.mouth_aspect.Assign(info.mouth_aspect_ratio);
bf.nose_y.Assign(info.nose_y);
bf.eyebrow_aspect.Assign(info.eyebrow_aspect_ratio);
bf.mouth_y.Assign(info.mouth_y);
bf.eye_rotate.Assign(info.eye_rotate);
bf.mustache_y.Assign(info.mustache_y);
bf.eye_aspect.Assign(info.eye_aspect_ratio);
bf.glasses_y.Assign(info.glasses_y);
bf.eye_scale.Assign(info.eye_scale);
bf.mole_x.Assign(info.mole_x);
bf.mole_y.Assign(info.mole_y);
bf.glasses_type.Assign(info.glasses_type);
bf.face_type.Assign(info.face_type);
bf.favorite_color.Assign(info.favorite_color);
bf.face_wrinkle.Assign(info.face_wrinkle);
bf.face_color.Assign(info.face_color);
bf.eye_x.Assign(info.eye_x);
bf.face_makeup.Assign(info.face_makeup);
bf.eyebrow_rotate.Assign(info.eyebrow_rotate);
bf.eyebrow_scale.Assign(info.eyebrow_scale);
bf.eyebrow_y.Assign(info.eyebrow_y);
bf.eyebrow_x.Assign(info.eyebrow_x);
bf.mouth_scale.Assign(info.mouth_scale);
bf.nose_scale.Assign(info.nose_scale);
bf.mole_scale.Assign(info.mole_scale);
bf.mustache_scale.Assign(info.mustache_scale);
std::memcpy(out.data.data(), &bf, sizeof(MiiStoreBitFields));
return out;
}
} // namespace
std::ostream& operator<<(std::ostream& os, Source source) {
os << SOURCE_NAMES.at(static_cast<std::size_t>(source));
return os;
}
std::u16string MiiInfo::Name() const {
return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
}
bool operator==(const MiiInfo& lhs, const MiiInfo& rhs) {
return std::memcmp(&lhs, &rhs, sizeof(MiiInfo)) == 0;
}
bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs) {
return !operator==(lhs, rhs);
}
std::u16string MiiStoreData::Name() const {
return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
}
MiiManager::MiiManager() = default;
MiiManager::~MiiManager() = default;
MiiInfo MiiManager::CreateRandom(RandomParameters params) {
LOG_WARNING(Service_Mii,
"(STUBBED) called with params={:08X}{:08X}{:08X}, returning default Mii",
params.unknown_1, params.unknown_2, params.unknown_3);
return ConvertStoreDataToInfo(CreateMiiWithUniqueUUID());
}
MiiInfo MiiManager::CreateDefault(u32 index) {
const auto new_mii = CreateMiiWithUniqueUUID();
database.miis.at(index) = new_mii;
EnsureDatabasePartition();
return ConvertStoreDataToInfo(new_mii);
}
bool MiiManager::CheckUpdatedFlag() const {
return updated_flag;
}
void MiiManager::ResetUpdatedFlag() {
updated_flag = false;
}
bool MiiManager::IsTestModeEnabled() const {
return is_test_mode_enabled;
}
bool MiiManager::Empty() const {
return Size() == 0;
}
bool MiiManager::Full() const {
return Size() == MAX_MIIS;
}
void MiiManager::Clear() {
updated_flag = true;
std::fill(database.miis.begin(), database.miis.end(), MiiStoreData{});
}
u32 MiiManager::Size() const {
return static_cast<u32>(std::count_if(database.miis.begin(), database.miis.end(),
[](const MiiStoreData& elem) { return elem.uuid; }));
}
MiiInfo MiiManager::GetInfo(u32 index) const {
return ConvertStoreDataToInfo(GetStoreData(index));
}
MiiInfoElement MiiManager::GetInfoElement(u32 index) const {
return {GetInfo(index), Source::Database};
}
MiiStoreData MiiManager::GetStoreData(u32 index) const {
return database.miis.at(index);
}
MiiStoreDataElement MiiManager::GetStoreDataElement(u32 index) const {
return {GetStoreData(index), Source::Database};
}
bool MiiManager::Remove(Common::UUID uuid) {
const auto iter = std::find_if(database.miis.begin(), database.miis.end(),
[uuid](const MiiStoreData& elem) { return elem.uuid == uuid; });
if (iter == database.miis.end())
return false;
updated_flag = true;
*iter = MiiStoreData{};
EnsureDatabasePartition();
return true;
}
u32 MiiManager::IndexOf(Common::UUID uuid) const {
const auto iter = std::find_if(database.miis.begin(), database.miis.end(),
[uuid](const MiiStoreData& elem) { return elem.uuid == uuid; });
if (iter == database.miis.end())
return INVALID_INDEX;
return static_cast<u32>(std::distance(database.miis.begin(), iter));
}
u32 MiiManager::IndexOf(const MiiInfo& info) const {
const auto iter =
std::find_if(database.miis.begin(), database.miis.end(), [&info](const MiiStoreData& elem) {
return ConvertStoreDataToInfo(elem) == info;
});
if (iter == database.miis.end())
return INVALID_INDEX;
return static_cast<u32>(std::distance(database.miis.begin(), iter));
}
bool MiiManager::Move(Common::UUID uuid, u32 new_index) {
const auto index = IndexOf(uuid);
if (index == INVALID_INDEX || new_index >= MAX_MIIS)
return false;
updated_flag = true;
const auto moving = database.miis[index];
const auto replacing = database.miis[new_index];
if (replacing.uuid) {
database.miis[index] = replacing;
database.miis[new_index] = moving;
} else {
database.miis[index] = MiiStoreData{};
database.miis[new_index] = moving;
}
EnsureDatabasePartition();
return true;
}
bool MiiManager::AddOrReplace(const MiiStoreData& data) {
const auto index = IndexOf(data.uuid);
updated_flag = true;
if (index == INVALID_INDEX) {
const auto size = Size();
if (size == MAX_MIIS)
return false;
database.miis[size] = data;
} else {
database.miis[index] = data;
}
return true;
}
bool MiiManager::DestroyFile() {
database = DEFAULT_MII_DATABASE;
updated_flag = false;
return DeleteFile();
}
bool MiiManager::DeleteFile() {
const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH;
return FileUtil::Exists(path) && FileUtil::Delete(path);
}
void MiiManager::WriteToFile() {
const auto raw_path =
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000030";
if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
FileUtil::Delete(raw_path);
const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH;
if (!FileUtil::CreateFullPath(path)) {
LOG_WARNING(Service_Mii,
"Failed to create full path of MiiDatabase.dat. Create the directory "
"nand/system/save/8000000000000030 to mitigate this "
"issue.");
return;
}
FileUtil::IOFile save(path, "wb");
if (!save.IsOpen()) {
LOG_WARNING(Service_Mii, "Failed to write save data to file... No changes to user data "
"made in current session will be saved.");
return;
}
save.Resize(sizeof(MiiDatabase));
if (save.WriteBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) {
LOG_WARNING(Service_Mii, "Failed to write all data to save file... Data may be malformed "
"and/or regenerated on next run.");
save.Resize(0);
}
}
void MiiManager::ReadFromFile() {
FileUtil::IOFile save(
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH, "rb");
if (!save.IsOpen()) {
LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
"blank Mii database with no Miis.");
std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase));
return;
}
if (save.ReadBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) {
LOG_WARNING(Service_ACC, "MiiDatabase.dat is smaller than expected... Generating new blank "
"Mii database with no Miis.");
std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase));
return;
}
EnsureDatabasePartition();
}
MiiStoreData MiiManager::CreateMiiWithUniqueUUID() const {
auto new_mii = DEFAULT_MII;
do {
new_mii.uuid = Common::UUID::Generate();
} while (IndexOf(new_mii.uuid) != INVALID_INDEX);
return new_mii;
}
void MiiManager::EnsureDatabasePartition() {
std::stable_partition(database.miis.begin(), database.miis.end(),
[](const MiiStoreData& elem) { return elem.uuid; });
}
} // namespace Service::Mii

View File

@@ -0,0 +1,273 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/uuid.h"
namespace Service::Mii {
constexpr std::size_t MAX_MIIS = 100;
constexpr u32 INVALID_INDEX = 0xFFFFFFFF;
struct RandomParameters {
u32 unknown_1;
u32 unknown_2;
u32 unknown_3;
};
static_assert(sizeof(RandomParameters) == 0xC, "RandomParameters has incorrect size.");
enum class Source : u32 {
Database = 0,
Default = 1,
Account = 2,
Friend = 3,
};
std::ostream& operator<<(std::ostream& os, Source source);
struct MiiInfo {
Common::UUID uuid;
std::array<char16_t, 11> name;
u8 font_region;
u8 favorite_color;
u8 gender;
u8 height;
u8 weight;
u8 mii_type;
u8 mii_region;
u8 face_type;
u8 face_color;
u8 face_wrinkle;
u8 face_makeup;
u8 hair_type;
u8 hair_color;
bool hair_flip;
u8 eye_type;
u8 eye_color;
u8 eye_scale;
u8 eye_aspect_ratio;
u8 eye_rotate;
u8 eye_x;
u8 eye_y;
u8 eyebrow_type;
u8 eyebrow_color;
u8 eyebrow_scale;
u8 eyebrow_aspect_ratio;
u8 eyebrow_rotate;
u8 eyebrow_x;
u8 eyebrow_y;
u8 nose_type;
u8 nose_scale;
u8 nose_y;
u8 mouth_type;
u8 mouth_color;
u8 mouth_scale;
u8 mouth_aspect_ratio;
u8 mouth_y;
u8 facial_hair_color;
u8 beard_type;
u8 mustache_type;
u8 mustache_scale;
u8 mustache_y;
u8 glasses_type;
u8 glasses_color;
u8 glasses_scale;
u8 glasses_y;
u8 mole_type;
u8 mole_scale;
u8 mole_x;
u8 mole_y;
INSERT_PADDING_BYTES(1);
std::u16string Name() const;
};
static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
static_assert(std::has_unique_object_representations_v<MiiInfo>,
"All bits of MiiInfo must contribute to its value.");
bool operator==(const MiiInfo& lhs, const MiiInfo& rhs);
bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs);
#pragma pack(push, 4)
struct MiiInfoElement {
MiiInfo info;
Source source;
};
static_assert(sizeof(MiiInfoElement) == 0x5C, "MiiInfoElement has incorrect size.");
struct MiiStoreBitFields {
union {
u32 word_0;
BitField<24, 8, u32> hair_type;
BitField<23, 1, u32> mole_type;
BitField<16, 7, u32> height;
BitField<15, 1, u32> hair_flip;
BitField<8, 7, u32> weight;
BitField<0, 7, u32> hair_color;
};
union {
u32 word_1;
BitField<31, 1, u32> gender;
BitField<24, 7, u32> eye_color;
BitField<16, 7, u32> eyebrow_color;
BitField<8, 7, u32> mouth_color;
BitField<0, 7, u32> facial_hair_color;
};
union {
u32 word_2;
BitField<31, 1, u32> mii_type;
BitField<24, 7, u32> glasses_color;
BitField<22, 2, u32> font_region;
BitField<16, 6, u32> eye_type;
BitField<14, 2, u32> mii_region;
BitField<8, 6, u32> mouth_type;
BitField<5, 3, u32> glasses_scale;
BitField<0, 5, u32> eye_y;
};
union {
u32 word_3;
BitField<29, 3, u32> mustache_type;
BitField<24, 5, u32> eyebrow_type;
BitField<21, 3, u32> beard_type;
BitField<16, 5, u32> nose_type;
BitField<13, 3, u32> mouth_aspect;
BitField<8, 5, u32> nose_y;
BitField<5, 3, u32> eyebrow_aspect;
BitField<0, 5, u32> mouth_y;
};
union {
u32 word_4;
BitField<29, 3, u32> eye_rotate;
BitField<24, 5, u32> mustache_y;
BitField<21, 3, u32> eye_aspect;
BitField<16, 5, u32> glasses_y;
BitField<13, 3, u32> eye_scale;
BitField<8, 5, u32> mole_x;
BitField<0, 5, u32> mole_y;
};
union {
u32 word_5;
BitField<24, 5, u32> glasses_type;
BitField<20, 4, u32> face_type;
BitField<16, 4, u32> favorite_color;
BitField<12, 4, u32> face_wrinkle;
BitField<8, 4, u32> face_color;
BitField<4, 4, u32> eye_x;
BitField<0, 4, u32> face_makeup;
};
union {
u32 word_6;
BitField<28, 4, u32> eyebrow_rotate;
BitField<24, 4, u32> eyebrow_scale;
BitField<20, 4, u32> eyebrow_y;
BitField<16, 4, u32> eyebrow_x;
BitField<12, 4, u32> mouth_scale;
BitField<8, 4, u32> nose_scale;
BitField<4, 4, u32> mole_scale;
BitField<0, 4, u32> mustache_scale;
};
};
static_assert(sizeof(MiiStoreBitFields) == 0x1C, "MiiStoreBitFields has incorrect size.");
static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
"MiiStoreBitFields is not trivially copyable.");
struct MiiStoreData {
// This corresponds to the above structure MiiStoreBitFields. I did it like this because the
// BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
// not suitable for our uses.
std::array<u8, 0x1C> data;
static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
std::array<char16_t, 10> name;
Common::UUID uuid;
u16 crc_1;
u16 crc_2;
std::u16string Name() const;
};
static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
struct MiiStoreDataElement {
MiiStoreData data;
Source source;
};
static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
struct MiiDatabase {
u32 magic; // 'NFDB'
std::array<MiiStoreData, MAX_MIIS> miis;
INSERT_PADDING_BYTES(1);
u8 count;
u16 crc;
};
static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
#pragma pack(pop)
// The Mii manager is responsible for loading and storing the Miis to the database in NAND along
// with providing an easy interface for HLE emulation of the mii service.
class MiiManager {
public:
MiiManager();
~MiiManager();
MiiInfo CreateRandom(RandomParameters params);
MiiInfo CreateDefault(u32 index);
bool CheckUpdatedFlag() const;
void ResetUpdatedFlag();
bool IsTestModeEnabled() const;
bool Empty() const;
bool Full() const;
void Clear();
u32 Size() const;
MiiInfo GetInfo(u32 index) const;
MiiInfoElement GetInfoElement(u32 index) const;
MiiStoreData GetStoreData(u32 index) const;
MiiStoreDataElement GetStoreDataElement(u32 index) const;
bool Remove(Common::UUID uuid);
u32 IndexOf(Common::UUID uuid) const;
u32 IndexOf(const MiiInfo& info) const;
bool Move(Common::UUID uuid, u32 new_index);
bool AddOrReplace(const MiiStoreData& data);
bool DestroyFile();
bool DeleteFile();
private:
void WriteToFile();
void ReadFromFile();
MiiStoreData CreateMiiWithUniqueUUID() const;
void EnsureDatabasePartition();
MiiDatabase database;
bool updated_flag = false;
bool is_test_mode_enabled = false;
};
}; // namespace Service::Mii

View File

@@ -0,0 +1,12 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/result.h"
namespace Service::NS {
constexpr ResultCode ERR_APPLICATION_LANGUAGE_NOT_FOUND{ErrorModule::NS, 300};
}

View File

@@ -0,0 +1,392 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/ns/language.h"
#include "core/hle/service/set/set.h"
namespace Service::NS {
constexpr ApplicationLanguagePriorityList priority_list_american_english = {{
ApplicationLanguage::AmericanEnglish,
ApplicationLanguage::BritishEnglish,
ApplicationLanguage::LatinAmericanSpanish,
ApplicationLanguage::CanadianFrench,
ApplicationLanguage::French,
ApplicationLanguage::German,
ApplicationLanguage::Spanish,
ApplicationLanguage::Italian,
ApplicationLanguage::Dutch,
ApplicationLanguage::Portuguese,
ApplicationLanguage::Russian,
ApplicationLanguage::Japanese,
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
}};
constexpr ApplicationLanguagePriorityList priority_list_british_english = {{
ApplicationLanguage::BritishEnglish,
ApplicationLanguage::AmericanEnglish,
ApplicationLanguage::French,
ApplicationLanguage::German,
ApplicationLanguage::Spanish,
ApplicationLanguage::Italian,
ApplicationLanguage::Dutch,
ApplicationLanguage::Portuguese,
ApplicationLanguage::Russian,
ApplicationLanguage::LatinAmericanSpanish,
ApplicationLanguage::CanadianFrench,
ApplicationLanguage::Japanese,
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
}};
constexpr ApplicationLanguagePriorityList priority_list_japanese = {{
ApplicationLanguage::Japanese,
ApplicationLanguage::AmericanEnglish,
ApplicationLanguage::BritishEnglish,
ApplicationLanguage::LatinAmericanSpanish,
ApplicationLanguage::CanadianFrench,
ApplicationLanguage::French,
ApplicationLanguage::German,
ApplicationLanguage::Spanish,
ApplicationLanguage::Italian,
ApplicationLanguage::Dutch,
ApplicationLanguage::Portuguese,
ApplicationLanguage::Russian,
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
}};
constexpr ApplicationLanguagePriorityList priority_list_french = {{
ApplicationLanguage::French,
ApplicationLanguage::CanadianFrench,
ApplicationLanguage::BritishEnglish,
ApplicationLanguage::AmericanEnglish,
ApplicationLanguage::German,
ApplicationLanguage::Spanish,
ApplicationLanguage::Italian,
ApplicationLanguage::Dutch,
ApplicationLanguage::Portuguese,
ApplicationLanguage::Russian,
ApplicationLanguage::LatinAmericanSpanish,
ApplicationLanguage::Japanese,
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
}};
constexpr ApplicationLanguagePriorityList priority_list_german = {{
ApplicationLanguage::German,
ApplicationLanguage::BritishEnglish,
ApplicationLanguage::AmericanEnglish,
ApplicationLanguage::French,
ApplicationLanguage::Spanish,
ApplicationLanguage::Italian,
ApplicationLanguage::Dutch,
ApplicationLanguage::Portuguese,
ApplicationLanguage::Russian,
ApplicationLanguage::LatinAmericanSpanish,
ApplicationLanguage::CanadianFrench,
ApplicationLanguage::Japanese,
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
}};
constexpr ApplicationLanguagePriorityList priority_list_latin_american_spanish = {{
ApplicationLanguage::LatinAmericanSpanish,
ApplicationLanguage::Spanish,
ApplicationLanguage::AmericanEnglish,
ApplicationLanguage::BritishEnglish,
ApplicationLanguage::Portuguese,
ApplicationLanguage::CanadianFrench,
ApplicationLanguage::French,
ApplicationLanguage::Italian,
ApplicationLanguage::German,
ApplicationLanguage::Dutch,
ApplicationLanguage::Russian,
ApplicationLanguage::Japanese,
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
}};
constexpr ApplicationLanguagePriorityList priority_list_spanish = {{
ApplicationLanguage::Spanish,
ApplicationLanguage::LatinAmericanSpanish,
ApplicationLanguage::BritishEnglish,
ApplicationLanguage::AmericanEnglish,
ApplicationLanguage::French,
ApplicationLanguage::German,
ApplicationLanguage::Italian,
ApplicationLanguage::Dutch,
ApplicationLanguage::Portuguese,
ApplicationLanguage::Russian,
ApplicationLanguage::CanadianFrench,
ApplicationLanguage::Japanese,
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
}};
constexpr ApplicationLanguagePriorityList priority_list_italian = {{
ApplicationLanguage::Italian,
ApplicationLanguage::BritishEnglish,
ApplicationLanguage::AmericanEnglish,
ApplicationLanguage::French,
ApplicationLanguage::German,
ApplicationLanguage::Spanish,
ApplicationLanguage::Dutch,
ApplicationLanguage::Portuguese,
ApplicationLanguage::Russian,
ApplicationLanguage::LatinAmericanSpanish,
ApplicationLanguage::CanadianFrench,
ApplicationLanguage::Japanese,
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
}};
constexpr ApplicationLanguagePriorityList priority_list_dutch = {{
ApplicationLanguage::Dutch,
ApplicationLanguage::BritishEnglish,
ApplicationLanguage::AmericanEnglish,
ApplicationLanguage::French,
ApplicationLanguage::German,
ApplicationLanguage::Spanish,
ApplicationLanguage::Italian,
ApplicationLanguage::Portuguese,
ApplicationLanguage::Russian,
ApplicationLanguage::LatinAmericanSpanish,
ApplicationLanguage::CanadianFrench,
ApplicationLanguage::Japanese,
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
}};
constexpr ApplicationLanguagePriorityList priority_list_canadian_french = {{
ApplicationLanguage::CanadianFrench,
ApplicationLanguage::French,
ApplicationLanguage::AmericanEnglish,
ApplicationLanguage::BritishEnglish,
ApplicationLanguage::LatinAmericanSpanish,
ApplicationLanguage::Spanish,
ApplicationLanguage::German,
ApplicationLanguage::Italian,
ApplicationLanguage::Dutch,
ApplicationLanguage::Portuguese,
ApplicationLanguage::Russian,
ApplicationLanguage::Japanese,
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
}};
constexpr ApplicationLanguagePriorityList priority_list_portuguese = {{
ApplicationLanguage::Portuguese,
ApplicationLanguage::BritishEnglish,
ApplicationLanguage::AmericanEnglish,
ApplicationLanguage::French,
ApplicationLanguage::German,
ApplicationLanguage::Spanish,
ApplicationLanguage::Italian,
ApplicationLanguage::Dutch,
ApplicationLanguage::Russian,
ApplicationLanguage::LatinAmericanSpanish,
ApplicationLanguage::CanadianFrench,
ApplicationLanguage::Japanese,
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
}};
constexpr ApplicationLanguagePriorityList priority_list_russian = {{
ApplicationLanguage::Russian,
ApplicationLanguage::BritishEnglish,
ApplicationLanguage::AmericanEnglish,
ApplicationLanguage::French,
ApplicationLanguage::German,
ApplicationLanguage::Spanish,
ApplicationLanguage::Italian,
ApplicationLanguage::Dutch,
ApplicationLanguage::Portuguese,
ApplicationLanguage::LatinAmericanSpanish,
ApplicationLanguage::CanadianFrench,
ApplicationLanguage::Japanese,
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
}};
constexpr ApplicationLanguagePriorityList priority_list_korean = {{
ApplicationLanguage::Korean,
ApplicationLanguage::AmericanEnglish,
ApplicationLanguage::BritishEnglish,
ApplicationLanguage::LatinAmericanSpanish,
ApplicationLanguage::CanadianFrench,
ApplicationLanguage::French,
ApplicationLanguage::German,
ApplicationLanguage::Spanish,
ApplicationLanguage::Italian,
ApplicationLanguage::Dutch,
ApplicationLanguage::Portuguese,
ApplicationLanguage::Russian,
ApplicationLanguage::Japanese,
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
}};
constexpr ApplicationLanguagePriorityList priority_list_traditional_chinese = {{
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::AmericanEnglish,
ApplicationLanguage::BritishEnglish,
ApplicationLanguage::Japanese,
ApplicationLanguage::LatinAmericanSpanish,
ApplicationLanguage::CanadianFrench,
ApplicationLanguage::French,
ApplicationLanguage::German,
ApplicationLanguage::Spanish,
ApplicationLanguage::Italian,
ApplicationLanguage::Dutch,
ApplicationLanguage::Portuguese,
ApplicationLanguage::Russian,
ApplicationLanguage::Korean,
}};
constexpr ApplicationLanguagePriorityList priority_list_simplified_chinese = {{
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::AmericanEnglish,
ApplicationLanguage::BritishEnglish,
ApplicationLanguage::Japanese,
ApplicationLanguage::LatinAmericanSpanish,
ApplicationLanguage::CanadianFrench,
ApplicationLanguage::French,
ApplicationLanguage::German,
ApplicationLanguage::Spanish,
ApplicationLanguage::Italian,
ApplicationLanguage::Dutch,
ApplicationLanguage::Portuguese,
ApplicationLanguage::Russian,
ApplicationLanguage::Korean,
}};
const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(
const ApplicationLanguage lang) {
switch (lang) {
case ApplicationLanguage::AmericanEnglish:
return &priority_list_american_english;
case ApplicationLanguage::BritishEnglish:
return &priority_list_british_english;
case ApplicationLanguage::Japanese:
return &priority_list_japanese;
case ApplicationLanguage::French:
return &priority_list_french;
case ApplicationLanguage::German:
return &priority_list_german;
case ApplicationLanguage::LatinAmericanSpanish:
return &priority_list_latin_american_spanish;
case ApplicationLanguage::Spanish:
return &priority_list_spanish;
case ApplicationLanguage::Italian:
return &priority_list_italian;
case ApplicationLanguage::Dutch:
return &priority_list_dutch;
case ApplicationLanguage::CanadianFrench:
return &priority_list_canadian_french;
case ApplicationLanguage::Portuguese:
return &priority_list_portuguese;
case ApplicationLanguage::Russian:
return &priority_list_russian;
case ApplicationLanguage::Korean:
return &priority_list_korean;
case ApplicationLanguage::TraditionalChinese:
return &priority_list_traditional_chinese;
case ApplicationLanguage::SimplifiedChinese:
return &priority_list_simplified_chinese;
default:
return nullptr;
}
}
std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
const Set::LanguageCode language_code) {
switch (language_code) {
case Set::LanguageCode::EN_US:
return ApplicationLanguage::AmericanEnglish;
case Set::LanguageCode::EN_GB:
return ApplicationLanguage::BritishEnglish;
case Set::LanguageCode::JA:
return ApplicationLanguage::Japanese;
case Set::LanguageCode::FR:
return ApplicationLanguage::French;
case Set::LanguageCode::DE:
return ApplicationLanguage::German;
case Set::LanguageCode::ES_419:
return ApplicationLanguage::LatinAmericanSpanish;
case Set::LanguageCode::ES:
return ApplicationLanguage::Spanish;
case Set::LanguageCode::IT:
return ApplicationLanguage::Italian;
case Set::LanguageCode::NL:
return ApplicationLanguage::Dutch;
case Set::LanguageCode::FR_CA:
return ApplicationLanguage::CanadianFrench;
case Set::LanguageCode::PT:
return ApplicationLanguage::Portuguese;
case Set::LanguageCode::RU:
return ApplicationLanguage::Russian;
case Set::LanguageCode::KO:
return ApplicationLanguage::Korean;
case Set::LanguageCode::ZH_HANT:
return ApplicationLanguage::TraditionalChinese;
case Set::LanguageCode::ZH_HANS:
return ApplicationLanguage::SimplifiedChinese;
default:
return std::nullopt;
}
}
std::optional<Set::LanguageCode> ConvertToLanguageCode(const ApplicationLanguage lang) {
switch (lang) {
case ApplicationLanguage::AmericanEnglish:
return Set::LanguageCode::EN_US;
case ApplicationLanguage::BritishEnglish:
return Set::LanguageCode::EN_GB;
case ApplicationLanguage::Japanese:
return Set::LanguageCode::JA;
case ApplicationLanguage::French:
return Set::LanguageCode::FR;
case ApplicationLanguage::German:
return Set::LanguageCode::DE;
case ApplicationLanguage::LatinAmericanSpanish:
return Set::LanguageCode::ES_419;
case ApplicationLanguage::Spanish:
return Set::LanguageCode::ES;
case ApplicationLanguage::Italian:
return Set::LanguageCode::IT;
case ApplicationLanguage::Dutch:
return Set::LanguageCode::NL;
case ApplicationLanguage::CanadianFrench:
return Set::LanguageCode::FR_CA;
case ApplicationLanguage::Portuguese:
return Set::LanguageCode::PT;
case ApplicationLanguage::Russian:
return Set::LanguageCode::RU;
case ApplicationLanguage::Korean:
return Set::LanguageCode::KO;
case ApplicationLanguage::TraditionalChinese:
return Set::LanguageCode::ZH_HANT;
case ApplicationLanguage::SimplifiedChinese:
return Set::LanguageCode::ZH_HANS;
default:
return std::nullopt;
}
}
} // namespace Service::NS

View File

@@ -0,0 +1,45 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <optional>
#include <string>
#include "common/common_types.h"
namespace Service::Set {
enum class LanguageCode : u64;
}
namespace Service::NS {
/// This is nn::ns::detail::ApplicationLanguage
enum class ApplicationLanguage : u8 {
AmericanEnglish = 0,
BritishEnglish,
Japanese,
French,
German,
LatinAmericanSpanish,
Spanish,
Italian,
Dutch,
CanadianFrench,
Portuguese,
Russian,
Korean,
TraditionalChinese,
SimplifiedChinese,
Count
};
using ApplicationLanguagePriorityList =
const std::array<ApplicationLanguage, static_cast<std::size_t>(ApplicationLanguage::Count)>;
constexpr u32 GetSupportedLanguageFlag(const ApplicationLanguage lang) {
return 1U << static_cast<u32>(lang);
}
const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(ApplicationLanguage lang);
std::optional<ApplicationLanguage> ConvertToApplicationLanguage(Set::LanguageCode language_code);
std::optional<Set::LanguageCode> ConvertToLanguageCode(ApplicationLanguage lang);
} // namespace Service::NS

View File

@@ -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:

View File

@@ -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);

View File

@@ -0,0 +1,42 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <optional>
#include <string>
#include "common/common_types.h"
#include "core/hle/service/set/set.h"
namespace Service::NS {
/// This is nn::ns::detail::ApplicationLanguage
enum class ApplicationLanguage : u8 {
AmericanEnglish = 0,
BritishEnglish,
Japanese,
French,
German,
LatinAmericanSpanish,
Spanish,
Italian,
Dutch,
CanadianFrench,
Portuguese,
Russian,
Korean,
TraditionalChinese,
SimplifiedChinese,
Count
};
using ApplicationLanguagePriorityList =
const std::array<ApplicationLanguage, static_cast<std::size_t>(ApplicationLanguage::Count)>;
constexpr u32 GetSupportedLanguageFlag(const ApplicationLanguage lang) {
return 1U << static_cast<u32>(lang);
}
const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(ApplicationLanguage lang);
std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
Service::Set::LanguageCode language_code);
std::optional<Service::Set::LanguageCode> ConvertToLanguageCode(ApplicationLanguage lang);
} // namespace Service::NS

View File

@@ -185,7 +185,8 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o
IoctlGetGpuTime params{};
std::memcpy(&params, 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(), &params, output.size());
return 0;
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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>(

View File

@@ -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.

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -1663,6 +1663,7 @@ 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"),
@@ -1686,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"),

View File

@@ -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);
}

View File

@@ -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,6 +35,7 @@ Device::Device(std::nullptr_t) {
max_vertex_attributes = 16;
max_varyings = 15;
has_variable_aoffi = true;
has_component_indexing_bug = false;
}
bool Device::TestVariableAoffi() {
@@ -52,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

View File

@@ -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

View File

@@ -35,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;
}
@@ -350,7 +350,8 @@ ShaderDiskCacheUsage CachedShader::GetUsage(GLenum primitive_mode,
ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system,
Core::Frontend::EmuWindow& emu_window, const Device& device)
: RasterizerCache{rasterizer}, emu_window{emu_window}, device{device}, disk_cache{system} {}
: 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) {
@@ -546,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

View File

@@ -137,6 +137,7 @@ private:
CachedProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump,
const std::set<GLenum>& supported_formats);
Core::System& system;
Core::Frontend::EmuWindow& emu_window;
const Device& device;
ShaderDiskCacheOpenGL disk_cache;

View File

@@ -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 {

View File

@@ -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;

View File

@@ -14,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]};
@@ -59,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: {

View File

@@ -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,
};

View File

@@ -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);

View File

@@ -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;
};

View File

@@ -104,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();
}
@@ -119,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);

View File

@@ -36,10 +36,8 @@ public:
void reject() override;
std::u16string GetText() const;
bool GetStatus() const;
private:
bool ok = false;
std::u16string text;
QDialogButtonBox* buttons;

View File

@@ -26,6 +26,8 @@
EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {}
EmuThread::~EmuThread() = default;
void EmuThread::run() {
render_window->MakeCurrent();
@@ -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,7 +355,7 @@ 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));
}
@@ -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);
}

View File

@@ -27,11 +27,12 @@ namespace VideoCore {
enum class LoadCallbackStage;
}
class EmuThread : public QThread {
class EmuThread final : public QThread {
Q_OBJECT
public:
explicit EmuThread(GRenderWindow* render_window);
~EmuThread() override;
/**
* Start emulation (on new thread)
@@ -114,7 +115,7 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
Q_OBJECT
public:
GRenderWindow(QWidget* parent, EmuThread* emu_thread);
GRenderWindow(GMainWindow* parent, EmuThread* emu_thread);
~GRenderWindow() override;
// EmuWindow implementation
@@ -133,17 +134,17 @@ public:
QByteArray saveGeometry(); // overridden
qreal GetWindowPixelRatio() const;
std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const;
std::pair<u32, u32> ScaleTouch(QPointF pos) const;
void closeEvent(QCloseEvent* event) override;
bool event(QEvent* event) override;
void focusOutEvent(QFocusEvent* event) override;
void OnClientAreaResized(unsigned width, unsigned height);
void OnClientAreaResized(u32 width, u32 height);
void InitRenderTarget();
void CaptureScreenshot(u16 res_scale, const QString& screenshot_path);
void CaptureScreenshot(u32 res_scale, const QString& screenshot_path);
public slots:
void moveContext(); // overridden
@@ -162,8 +163,7 @@ private:
void TouchUpdateEvent(const QTouchEvent* event);
void TouchEndEvent();
void OnMinimalClientAreaChangeRequest(
const std::pair<unsigned, unsigned>& minimal_size) override;
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
QWidget* container = nullptr;
GGLWidgetInternal* child = nullptr;

View File

@@ -645,6 +645,8 @@ void Config::ReadUIGamelistValues() {
UISettings::values.icon_size = ReadSetting(QStringLiteral("icon_size"), 64).toUInt();
UISettings::values.row_1_text_id = ReadSetting(QStringLiteral("row_1_text_id"), 3).toUInt();
UISettings::values.row_2_text_id = ReadSetting(QStringLiteral("row_2_text_id"), 2).toUInt();
UISettings::values.cache_game_list =
ReadSetting(QStringLiteral("cache_game_list"), true).toBool();
qt_config->endGroup();
}
@@ -1009,6 +1011,7 @@ void Config::SaveUIGamelistValues() {
WriteSetting(QStringLiteral("icon_size"), UISettings::values.icon_size, 64);
WriteSetting(QStringLiteral("row_1_text_id"), UISettings::values.row_1_text_id, 3);
WriteSetting(QStringLiteral("row_2_text_id"), UISettings::values.row_2_text_id, 2);
WriteSetting(QStringLiteral("cache_game_list"), UISettings::values.cache_game_list, true);
qt_config->endGroup();
}

View File

@@ -4,6 +4,8 @@
#include <memory>
#include <QSignalBlocker>
#include "audio_core/sink.h"
#include "audio_core/sink_details.h"
#include "core/core.h"
@@ -15,18 +17,14 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureAudio>()) {
ui->setupUi(this);
ui->output_sink_combo_box->clear();
ui->output_sink_combo_box->addItem(QString::fromUtf8(AudioCore::auto_device_name));
for (const char* id : AudioCore::GetSinkIDs()) {
ui->output_sink_combo_box->addItem(QString::fromUtf8(id));
}
InitializeAudioOutputSinkComboBox();
connect(ui->volume_slider, &QSlider::valueChanged, this,
&ConfigureAudio::setVolumeIndicatorText);
this->setConfiguration();
&ConfigureAudio::SetVolumeIndicatorText);
connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
&ConfigureAudio::updateAudioDevices);
&ConfigureAudio::UpdateAudioDevices);
SetConfiguration();
const bool is_powered_on = Core::System::GetInstance().IsPoweredOn();
ui->output_sink_combo_box->setEnabled(!is_powered_on);
@@ -35,22 +33,23 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
ConfigureAudio::~ConfigureAudio() = default;
void ConfigureAudio::setConfiguration() {
setOutputSinkFromSinkID();
void ConfigureAudio::SetConfiguration() {
SetOutputSinkFromSinkID();
// The device list cannot be pre-populated (nor listed) until the output sink is known.
updateAudioDevices(ui->output_sink_combo_box->currentIndex());
UpdateAudioDevices(ui->output_sink_combo_box->currentIndex());
setAudioDeviceFromDeviceID();
SetAudioDeviceFromDeviceID();
ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching);
ui->volume_slider->setValue(Settings::values.volume * ui->volume_slider->maximum());
setVolumeIndicatorText(ui->volume_slider->sliderPosition());
SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
}
void ConfigureAudio::setOutputSinkFromSinkID() {
int new_sink_index = 0;
void ConfigureAudio::SetOutputSinkFromSinkID() {
[[maybe_unused]] const QSignalBlocker blocker(ui->output_sink_combo_box);
int new_sink_index = 0;
const QString sink_id = QString::fromStdString(Settings::values.sink_id);
for (int index = 0; index < ui->output_sink_combo_box->count(); index++) {
if (ui->output_sink_combo_box->itemText(index) == sink_id) {
@@ -62,7 +61,7 @@ void ConfigureAudio::setOutputSinkFromSinkID() {
ui->output_sink_combo_box->setCurrentIndex(new_sink_index);
}
void ConfigureAudio::setAudioDeviceFromDeviceID() {
void ConfigureAudio::SetAudioDeviceFromDeviceID() {
int new_device_index = -1;
const QString device_id = QString::fromStdString(Settings::values.audio_device_id);
@@ -76,11 +75,11 @@ void ConfigureAudio::setAudioDeviceFromDeviceID() {
ui->audio_device_combo_box->setCurrentIndex(new_device_index);
}
void ConfigureAudio::setVolumeIndicatorText(int percentage) {
void ConfigureAudio::SetVolumeIndicatorText(int percentage) {
ui->volume_indicator->setText(tr("%1%", "Volume percentage (e.g. 50%)").arg(percentage));
}
void ConfigureAudio::applyConfiguration() {
void ConfigureAudio::ApplyConfiguration() {
Settings::values.sink_id =
ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
.toStdString();
@@ -92,7 +91,15 @@ void ConfigureAudio::applyConfiguration() {
static_cast<float>(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum();
}
void ConfigureAudio::updateAudioDevices(int sink_index) {
void ConfigureAudio::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QWidget::changeEvent(event);
}
void ConfigureAudio::UpdateAudioDevices(int sink_index) {
ui->audio_device_combo_box->clear();
ui->audio_device_combo_box->addItem(QString::fromUtf8(AudioCore::auto_device_name));
@@ -102,6 +109,16 @@ void ConfigureAudio::updateAudioDevices(int sink_index) {
}
}
void ConfigureAudio::retranslateUi() {
ui->retranslateUi(this);
void ConfigureAudio::InitializeAudioOutputSinkComboBox() {
ui->output_sink_combo_box->clear();
ui->output_sink_combo_box->addItem(QString::fromUtf8(AudioCore::auto_device_name));
for (const char* id : AudioCore::GetSinkIDs()) {
ui->output_sink_combo_box->addItem(QString::fromUtf8(id));
}
}
void ConfigureAudio::RetranslateUI() {
ui->retranslateUi(this);
SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
}

View File

@@ -18,16 +18,21 @@ public:
explicit ConfigureAudio(QWidget* parent = nullptr);
~ConfigureAudio() override;
void applyConfiguration();
void retranslateUi();
void ApplyConfiguration();
private:
void updateAudioDevices(int sink_index);
void changeEvent(QEvent* event) override;
void setConfiguration();
void setOutputSinkFromSinkID();
void setAudioDeviceFromDeviceID();
void setVolumeIndicatorText(int percentage);
void InitializeAudioOutputSinkComboBox();
void RetranslateUI();
void UpdateAudioDevices(int sink_index);
void SetConfiguration();
void SetOutputSinkFromSinkID();
void SetAudioDeviceFromDeviceID();
void SetVolumeIndicatorText(int percentage);
std::unique_ptr<Ui::ConfigureAudio> ui;
};

View File

@@ -20,7 +20,7 @@
<item>
<layout class="QHBoxLayout">
<item>
<widget class="QLabel" name="label">
<widget class="QLabel" name="label_1">
<property name="text">
<string>Output Engine:</string>
</property>
@@ -44,7 +44,7 @@
<item>
<layout class="QHBoxLayout">
<item>
<widget class="QLabel" name="label">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Audio Device:</string>
</property>
@@ -61,7 +61,7 @@
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Volume:</string>
</property>

View File

@@ -16,7 +16,8 @@
ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureDebug) {
ui->setupUi(this);
this->setConfiguration();
SetConfiguration();
connect(ui->open_log_button, &QPushButton::pressed, []() {
QString path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LogDir));
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
@@ -25,7 +26,7 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::Co
ConfigureDebug::~ConfigureDebug() = default;
void ConfigureDebug::setConfiguration() {
void ConfigureDebug::SetConfiguration() {
ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub);
ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub);
ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port);
@@ -37,7 +38,7 @@ void ConfigureDebug::setConfiguration() {
ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso);
}
void ConfigureDebug::applyConfiguration() {
void ConfigureDebug::ApplyConfiguration() {
Settings::values.use_gdbstub = ui->toggle_gdbstub->isChecked();
Settings::values.gdbstub_port = ui->gdbport_spinbox->value();
UISettings::values.show_console = ui->toggle_console->isChecked();
@@ -50,3 +51,15 @@ void ConfigureDebug::applyConfiguration() {
filter.ParseFilterString(Settings::values.log_filter);
Log::SetGlobalFilter(filter);
}
void ConfigureDebug::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QWidget::changeEvent(event);
}
void ConfigureDebug::RetranslateUI() {
ui->retranslateUi(this);
}

View File

@@ -18,10 +18,13 @@ public:
explicit ConfigureDebug(QWidget* parent = nullptr);
~ConfigureDebug() override;
void applyConfiguration();
void ApplyConfiguration();
private:
void setConfiguration();
void changeEvent(QEvent* event) override;
void RetranslateUI();
void SetConfiguration();
std::unique_ptr<Ui::ConfigureDebug> ui;
};

View File

@@ -45,7 +45,7 @@
</spacer>
</item>
<item>
<widget class="QLabel" name="label_2">
<widget class="QLabel" name="label_1">
<property name="text">
<string>Port:</string>
</property>
@@ -70,11 +70,11 @@
<property name="title">
<string>Logging</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Global Log Filter</string>
</property>
@@ -86,7 +86,7 @@
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="toggle_console">
<property name="text">
@@ -111,11 +111,11 @@
<property name="title">
<string>Homebrew</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Arguments String</string>
</property>
@@ -134,7 +134,7 @@
<property name="title">
<string>Dump</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QCheckBox" name="dump_decompressed_nso">
<property name="whatsThis">

View File

@@ -4,6 +4,7 @@
#include <QHash>
#include <QListWidgetItem>
#include <QSignalBlocker>
#include "core/settings.h"
#include "ui_configure.h"
#include "yuzu/configuration/config.h"
@@ -15,11 +16,11 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
: QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) {
ui->setupUi(this);
ui->hotkeysTab->Populate(registry);
this->setConfiguration();
this->PopulateSelectionList();
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
SetConfiguration();
PopulateSelectionList();
connect(ui->selectorList, &QListWidget::itemSelectionChanged, this,
&ConfigureDialog::UpdateVisibleTabs);
@@ -29,30 +30,55 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
ConfigureDialog::~ConfigureDialog() = default;
void ConfigureDialog::setConfiguration() {}
void ConfigureDialog::SetConfiguration() {}
void ConfigureDialog::applyConfiguration() {
ui->generalTab->applyConfiguration();
ui->gameListTab->applyConfiguration();
ui->systemTab->applyConfiguration();
ui->profileManagerTab->applyConfiguration();
ui->inputTab->applyConfiguration();
ui->hotkeysTab->applyConfiguration(registry);
ui->graphicsTab->applyConfiguration();
ui->audioTab->applyConfiguration();
ui->debugTab->applyConfiguration();
ui->webTab->applyConfiguration();
void ConfigureDialog::ApplyConfiguration() {
ui->generalTab->ApplyConfiguration();
ui->gameListTab->ApplyConfiguration();
ui->systemTab->ApplyConfiguration();
ui->profileManagerTab->ApplyConfiguration();
ui->inputTab->ApplyConfiguration();
ui->hotkeysTab->ApplyConfiguration(registry);
ui->graphicsTab->ApplyConfiguration();
ui->audioTab->ApplyConfiguration();
ui->debugTab->ApplyConfiguration();
ui->webTab->ApplyConfiguration();
Settings::Apply();
Settings::LogSettings();
}
void ConfigureDialog::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QDialog::changeEvent(event);
}
void ConfigureDialog::RetranslateUI() {
const int old_row = ui->selectorList->currentRow();
const int old_index = ui->tabWidget->currentIndex();
ui->retranslateUi(this);
PopulateSelectionList();
ui->selectorList->setCurrentRow(old_row);
UpdateVisibleTabs();
ui->tabWidget->setCurrentIndex(old_index);
}
void ConfigureDialog::PopulateSelectionList() {
const std::array<std::pair<QString, QStringList>, 4> items{
{{tr("General"), {tr("General"), tr("Web"), tr("Debug"), tr("Game List")}},
{tr("System"), {tr("System"), tr("Profiles"), tr("Audio")}},
{tr("Graphics"), {tr("Graphics")}},
{tr("Controls"), {tr("Input"), tr("Hotkeys")}}}};
{tr("Controls"), {tr("Input"), tr("Hotkeys")}}},
};
[[maybe_unused]] const QSignalBlocker blocker(ui->selectorList);
ui->selectorList->clear();
for (const auto& entry : items) {
auto* const item = new QListWidgetItem(entry.first);
item->setData(Qt::UserRole, entry.second);
@@ -63,24 +89,28 @@ void ConfigureDialog::PopulateSelectionList() {
void ConfigureDialog::UpdateVisibleTabs() {
const auto items = ui->selectorList->selectedItems();
if (items.isEmpty())
if (items.isEmpty()) {
return;
}
const std::map<QString, QWidget*> widgets = {{tr("General"), ui->generalTab},
{tr("System"), ui->systemTab},
{tr("Profiles"), ui->profileManagerTab},
{tr("Input"), ui->inputTab},
{tr("Hotkeys"), ui->hotkeysTab},
{tr("Graphics"), ui->graphicsTab},
{tr("Audio"), ui->audioTab},
{tr("Debug"), ui->debugTab},
{tr("Web"), ui->webTab},
{tr("Game List"), ui->gameListTab}};
const std::map<QString, QWidget*> widgets = {
{tr("General"), ui->generalTab},
{tr("System"), ui->systemTab},
{tr("Profiles"), ui->profileManagerTab},
{tr("Input"), ui->inputTab},
{tr("Hotkeys"), ui->hotkeysTab},
{tr("Graphics"), ui->graphicsTab},
{tr("Audio"), ui->audioTab},
{tr("Debug"), ui->debugTab},
{tr("Web"), ui->webTab},
{tr("Game List"), ui->gameListTab},
};
[[maybe_unused]] const QSignalBlocker blocker(ui->tabWidget);
ui->tabWidget->clear();
const QStringList tabs = items[0]->data(Qt::UserRole).toStringList();
for (const auto& tab : tabs)
for (const auto& tab : tabs) {
ui->tabWidget->addTab(widgets.find(tab)->second, tab);
}
}

View File

@@ -20,10 +20,14 @@ public:
explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry);
~ConfigureDialog() override;
void applyConfiguration();
void ApplyConfiguration();
private:
void setConfiguration();
void changeEvent(QEvent* event) override;
void RetranslateUI();
void SetConfiguration();
void UpdateVisibleTabs();
void PopulateSelectionList();

View File

@@ -12,20 +12,20 @@
#include "yuzu/ui_settings.h"
namespace {
constexpr std::array<std::pair<u32, const char*>, 5> default_icon_sizes{{
constexpr std::array default_icon_sizes{
std::make_pair(0, QT_TR_NOOP("None")),
std::make_pair(32, QT_TR_NOOP("Small (32x32)")),
std::make_pair(64, QT_TR_NOOP("Standard (64x64)")),
std::make_pair(128, QT_TR_NOOP("Large (128x128)")),
std::make_pair(256, QT_TR_NOOP("Full Size (256x256)")),
}};
};
constexpr std::array<const char*, 4> row_text_names{{
constexpr std::array row_text_names{
QT_TR_NOOP("Filename"),
QT_TR_NOOP("Filetype"),
QT_TR_NOOP("Title ID"),
QT_TR_NOOP("Title Name"),
}};
};
} // Anonymous namespace
ConfigureGameList::ConfigureGameList(QWidget* parent)
@@ -35,7 +35,7 @@ ConfigureGameList::ConfigureGameList(QWidget* parent)
InitializeIconSizeComboBox();
InitializeRowComboBoxes();
this->setConfiguration();
SetConfiguration();
// Force game list reload if any of the relevant settings are changed.
connect(ui->show_unknown, &QCheckBox::stateChanged, this,
@@ -50,7 +50,7 @@ ConfigureGameList::ConfigureGameList(QWidget* parent)
ConfigureGameList::~ConfigureGameList() = default;
void ConfigureGameList::applyConfiguration() {
void ConfigureGameList::ApplyConfiguration() {
UISettings::values.show_unknown = ui->show_unknown->isChecked();
UISettings::values.show_add_ons = ui->show_add_ons->isChecked();
UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt();
@@ -63,7 +63,7 @@ void ConfigureGameList::RequestGameListUpdate() {
UISettings::values.is_game_list_reload_pending.exchange(true);
}
void ConfigureGameList::setConfiguration() {
void ConfigureGameList::SetConfiguration() {
ui->show_unknown->setChecked(UISettings::values.show_unknown);
ui->show_add_ons->setChecked(UISettings::values.show_add_ons);
ui->icon_size_combobox->setCurrentIndex(
@@ -77,7 +77,6 @@ void ConfigureGameList::setConfiguration() {
void ConfigureGameList::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
return;
}
QWidget::changeEvent(event);

View File

@@ -18,12 +18,12 @@ public:
explicit ConfigureGameList(QWidget* parent = nullptr);
~ConfigureGameList() override;
void applyConfiguration();
void ApplyConfiguration();
private:
void RequestGameListUpdate();
void setConfiguration();
void SetConfiguration();
void changeEvent(QEvent*) override;
void RetranslateUI();

View File

@@ -18,7 +18,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
QString::fromUtf8(theme.second));
}
this->setConfiguration();
SetConfiguration();
connect(ui->toggle_deepscan, &QCheckBox::stateChanged, this,
[] { UISettings::values.is_game_list_reload_pending.exchange(true); });
@@ -28,7 +28,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
ConfigureGeneral::~ConfigureGeneral() = default;
void ConfigureGeneral::setConfiguration() {
void ConfigureGeneral::SetConfiguration() {
ui->toggle_deepscan->setChecked(UISettings::values.game_directory_deepscan);
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot);
@@ -36,7 +36,7 @@ void ConfigureGeneral::setConfiguration() {
ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit);
}
void ConfigureGeneral::applyConfiguration() {
void ConfigureGeneral::ApplyConfiguration() {
UISettings::values.game_directory_deepscan = ui->toggle_deepscan->isChecked();
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
@@ -45,3 +45,15 @@ void ConfigureGeneral::applyConfiguration() {
Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked();
}
void ConfigureGeneral::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QWidget::changeEvent(event);
}
void ConfigureGeneral::RetranslateUI() {
ui->retranslateUi(this);
}

View File

@@ -20,10 +20,13 @@ public:
explicit ConfigureGeneral(QWidget* parent = nullptr);
~ConfigureGeneral() override;
void applyConfiguration();
void ApplyConfiguration();
private:
void setConfiguration();
void changeEvent(QEvent* event) override;
void RetranslateUI();
void SetConfiguration();
std::unique_ptr<Ui::ConfigureGeneral> ui;
};

View File

@@ -51,29 +51,29 @@ Resolution FromResolutionFactor(float factor) {
ConfigureGraphics::ConfigureGraphics(QWidget* parent)
: QWidget(parent), ui(new Ui::ConfigureGraphics) {
ui->setupUi(this);
this->setConfiguration();
ui->frame_limit->setEnabled(Settings::values.use_frame_limit);
connect(ui->toggle_frame_limit, &QCheckBox::stateChanged, ui->frame_limit,
&QSpinBox::setEnabled);
SetConfiguration();
connect(ui->toggle_frame_limit, &QCheckBox::toggled, ui->frame_limit, &QSpinBox::setEnabled);
connect(ui->bg_button, &QPushButton::clicked, this, [this] {
const QColor new_bg_color = QColorDialog::getColor(bg_color);
if (!new_bg_color.isValid())
if (!new_bg_color.isValid()) {
return;
}
UpdateBackgroundColorButton(new_bg_color);
});
}
ConfigureGraphics::~ConfigureGraphics() = default;
void ConfigureGraphics::setConfiguration() {
void ConfigureGraphics::SetConfiguration() {
const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
ui->resolution_factor_combobox->setCurrentIndex(
static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor)));
ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit);
ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked());
ui->frame_limit->setValue(Settings::values.frame_limit);
ui->use_compatibility_profile->setEnabled(runtime_lock);
ui->use_compatibility_profile->setChecked(Settings::values.use_compatibility_profile);
@@ -88,7 +88,7 @@ void ConfigureGraphics::setConfiguration() {
Settings::values.bg_blue));
}
void ConfigureGraphics::applyConfiguration() {
void ConfigureGraphics::ApplyConfiguration() {
Settings::values.resolution_factor =
ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex()));
Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked();
@@ -104,6 +104,18 @@ void ConfigureGraphics::applyConfiguration() {
Settings::values.bg_blue = static_cast<float>(bg_color.blueF());
}
void ConfigureGraphics::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QWidget::changeEvent(event);
}
void ConfigureGraphics::RetranslateUI() {
ui->retranslateUi(this);
}
void ConfigureGraphics::UpdateBackgroundColorButton(QColor color) {
bg_color = color;

View File

@@ -18,10 +18,13 @@ public:
explicit ConfigureGraphics(QWidget* parent = nullptr);
~ConfigureGraphics() override;
void applyConfiguration();
void ApplyConfiguration();
private:
void setConfiguration();
void changeEvent(QEvent* event) override;
void RetranslateUI();
void SetConfiguration();
void UpdateBackgroundColorButton(QColor color);

View File

@@ -17,7 +17,6 @@ ConfigureHotkeys::ConfigureHotkeys(QWidget* parent)
model = new QStandardItemModel(this);
model->setColumnCount(3);
model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Context")});
connect(ui->hotkey_list, &QTreeView::doubleClicked, this, &ConfigureHotkeys::Configure);
ui->hotkey_list->setModel(model);
@@ -27,6 +26,8 @@ ConfigureHotkeys::ConfigureHotkeys(QWidget* parent)
ui->hotkey_list->setColumnWidth(0, 200);
ui->hotkey_list->resizeColumnToContents(1);
RetranslateUI();
}
ConfigureHotkeys::~ConfigureHotkeys() = default;
@@ -49,6 +50,20 @@ void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
ui->hotkey_list->expandAll();
}
void ConfigureHotkeys::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QWidget::changeEvent(event);
}
void ConfigureHotkeys::RetranslateUI() {
ui->retranslateUi(this);
model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Context")});
}
void ConfigureHotkeys::Configure(QModelIndex index) {
if (!index.parent().isValid()) {
return;
@@ -92,7 +107,7 @@ bool ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) const {
return false;
}
void ConfigureHotkeys::applyConfiguration(HotkeyRegistry& registry) {
void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) {
for (int key_id = 0; key_id < model->rowCount(); key_id++) {
const QStandardItem* parent = model->item(key_id, 0);
for (int key_column_id = 0; key_column_id < parent->rowCount(); key_column_id++) {
@@ -112,7 +127,3 @@ void ConfigureHotkeys::applyConfiguration(HotkeyRegistry& registry) {
registry.SaveHotkeys();
}
void ConfigureHotkeys::retranslateUi() {
ui->retranslateUi(this);
}

View File

@@ -21,8 +21,7 @@ public:
explicit ConfigureHotkeys(QWidget* parent = nullptr);
~ConfigureHotkeys() override;
void applyConfiguration(HotkeyRegistry& registry);
void retranslateUi();
void ApplyConfiguration(HotkeyRegistry& registry);
/**
* Populates the hotkey list widget using data from the provided registry.
@@ -32,6 +31,9 @@ public:
void Populate(const HotkeyRegistry& registry);
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
void Configure(QModelIndex index);
bool IsUsedKey(QKeySequence key_sequence) const;

View File

@@ -5,6 +5,7 @@
#include <algorithm>
#include <memory>
#include <QSignalBlocker>
#include <QTimer>
#include "configuration/configure_touchscreen_advanced.h"
@@ -50,12 +51,12 @@ void OnDockedModeChanged(bool last_state, bool new_state) {
namespace {
template <typename Dialog, typename... Args>
void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
parent.applyConfiguration();
parent.ApplyConfiguration();
Dialog dialog(&parent, std::forward<Args>(args)...);
const auto res = dialog.exec();
if (res == QDialog::Accepted) {
dialog.applyConfiguration();
dialog.ApplyConfiguration();
}
}
} // Anonymous namespace
@@ -74,29 +75,25 @@ ConfigureInput::ConfigureInput(QWidget* parent)
ui->player5_configure, ui->player6_configure, ui->player7_configure, ui->player8_configure,
};
for (auto* controller_box : players_controller) {
controller_box->addItems({tr("None"), tr("Pro Controller"), tr("Dual Joycons"),
tr("Single Right Joycon"), tr("Single Left Joycon")});
}
this->loadConfiguration();
updateUIEnabled();
RetranslateUI();
LoadConfiguration();
UpdateUIEnabled();
connect(ui->restore_defaults_button, &QPushButton::pressed, this,
&ConfigureInput::restoreDefaults);
&ConfigureInput::RestoreDefaults);
for (auto* enabled : players_controller) {
connect(enabled, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&ConfigureInput::updateUIEnabled);
&ConfigureInput::UpdateUIEnabled);
}
connect(ui->use_docked_mode, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
connect(ui->use_docked_mode, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled);
connect(ui->handheld_connected, &QCheckBox::stateChanged, this,
&ConfigureInput::updateUIEnabled);
connect(ui->mouse_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
connect(ui->keyboard_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
connect(ui->debug_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
&ConfigureInput::UpdateUIEnabled);
connect(ui->mouse_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled);
connect(ui->keyboard_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled);
connect(ui->debug_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled);
connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this,
&ConfigureInput::updateUIEnabled);
&ConfigureInput::UpdateUIEnabled);
for (std::size_t i = 0; i < players_configure.size(); ++i) {
connect(players_configure[i], &QPushButton::pressed, this,
@@ -118,7 +115,7 @@ ConfigureInput::ConfigureInput(QWidget* parent)
ConfigureInput::~ConfigureInput() = default;
void ConfigureInput::applyConfiguration() {
void ConfigureInput::ApplyConfiguration() {
for (std::size_t i = 0; i < players_controller.size(); ++i) {
const auto controller_type_index = players_controller[i]->currentIndex();
@@ -144,7 +141,32 @@ void ConfigureInput::applyConfiguration() {
Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
}
void ConfigureInput::updateUIEnabled() {
void ConfigureInput::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QDialog::changeEvent(event);
}
void ConfigureInput::RetranslateUI() {
ui->retranslateUi(this);
RetranslateControllerComboBoxes();
}
void ConfigureInput::RetranslateControllerComboBoxes() {
for (auto* controller_box : players_controller) {
[[maybe_unused]] const QSignalBlocker blocker(controller_box);
controller_box->clear();
controller_box->addItems({tr("None"), tr("Pro Controller"), tr("Dual Joycons"),
tr("Single Right Joycon"), tr("Single Left Joycon")});
}
LoadPlayerControllerIndices();
}
void ConfigureInput::UpdateUIEnabled() {
bool hit_disabled = false;
for (auto* player : players_controller) {
player->setDisabled(hit_disabled);
@@ -168,18 +190,14 @@ void ConfigureInput::updateUIEnabled() {
ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
}
void ConfigureInput::loadConfiguration() {
void ConfigureInput::LoadConfiguration() {
std::stable_partition(
Settings::values.players.begin(),
Settings::values.players.begin() +
Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD),
[](const auto& player) { return player.connected; });
for (std::size_t i = 0; i < players_controller.size(); ++i) {
const auto connected = Settings::values.players[i].connected;
players_controller[i]->setCurrentIndex(
connected ? static_cast<u8>(Settings::values.players[i].type) + 1 : 0);
}
LoadPlayerControllerIndices();
ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
ui->handheld_connected->setChecked(
@@ -191,10 +209,18 @@ void ConfigureInput::loadConfiguration() {
ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled);
ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
updateUIEnabled();
UpdateUIEnabled();
}
void ConfigureInput::restoreDefaults() {
void ConfigureInput::LoadPlayerControllerIndices() {
for (std::size_t i = 0; i < players_controller.size(); ++i) {
const auto connected = Settings::values.players[i].connected;
players_controller[i]->setCurrentIndex(
connected ? static_cast<u8>(Settings::values.players[i].type) + 1 : 0);
}
}
void ConfigureInput::RestoreDefaults() {
players_controller[0]->setCurrentIndex(2);
for (std::size_t i = 1; i < players_controller.size(); ++i) {
@@ -207,5 +233,5 @@ void ConfigureInput::restoreDefaults() {
ui->keyboard_enabled->setCheckState(Qt::Unchecked);
ui->debug_enabled->setCheckState(Qt::Unchecked);
ui->touchscreen_enabled->setCheckState(Qt::Checked);
updateUIEnabled();
UpdateUIEnabled();
}

View File

@@ -30,15 +30,21 @@ public:
~ConfigureInput() override;
/// Save all button configurations to settings file
void applyConfiguration();
void ApplyConfiguration();
private:
void updateUIEnabled();
void changeEvent(QEvent* event) override;
void RetranslateUI();
void RetranslateControllerComboBoxes();
void UpdateUIEnabled();
/// Load configuration settings.
void loadConfiguration();
void LoadConfiguration();
void LoadPlayerControllerIndices();
/// Restore all buttons to their default values.
void restoreDefaults();
void RestoreDefaults();
std::unique_ptr<Ui::ConfigureInput> ui;

View File

@@ -17,7 +17,7 @@
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="gridGroupBox">
<widget class="QGroupBox" name="gridGroupBox_1">
<property name="title">
<string>Players</string>
</property>
@@ -260,7 +260,7 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="gridGroupBox">
<widget class="QGroupBox" name="gridGroupBox_2">
<property name="title">
<string>Handheld</string>
</property>
@@ -332,7 +332,7 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="gridGroupBox">
<widget class="QGroupBox" name="gridGroupBox_3">
<property name="title">
<string>Other</string>
</property>

View File

@@ -245,7 +245,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
button->setContextMenuPolicy(Qt::CustomContextMenu);
connect(button, &QPushButton::released, [=] {
handleClick(
HandleClick(
button_map[button_id],
[=](const Common::ParamPackage& params) { buttons_param[button_id] = params; },
InputCommon::Polling::DeviceType::Button);
@@ -274,7 +274,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
analog_button->setContextMenuPolicy(Qt::CustomContextMenu);
connect(analog_button, &QPushButton::released, [=]() {
handleClick(analog_map_buttons[analog_id][sub_button_id],
HandleClick(analog_map_buttons[analog_id][sub_button_id],
[=](const Common::ParamPackage& params) {
SetAnalogButton(params, analogs_param[analog_id],
analog_sub_buttons[sub_button_id]);
@@ -304,7 +304,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
QMessageBox::information(this, tr("Information"),
tr("After pressing OK, first move your joystick horizontally, "
"and then vertically."));
handleClick(
HandleClick(
analog_map_stick[analog_id],
[=](const Common::ParamPackage& params) { analogs_param[analog_id] = params; },
InputCommon::Polling::DeviceType::Analog);
@@ -312,17 +312,17 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
}
connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); });
connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); });
connect(ui->buttonRestoreDefaults, &QPushButton::released, [this] { RestoreDefaults(); });
timeout_timer->setSingleShot(true);
connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); });
connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
connect(poll_timer.get(), &QTimer::timeout, [this]() {
connect(poll_timer.get(), &QTimer::timeout, [this] {
Common::ParamPackage params;
for (auto& poller : device_pollers) {
params = poller->GetNextInput();
if (params.Has("engine")) {
setPollingResult(params, false);
SetPollingResult(params, false);
return;
}
}
@@ -340,8 +340,8 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
[this, i] { OnControllerButtonClick(static_cast<int>(i)); });
}
this->loadConfiguration();
this->resize(0, 0);
LoadConfiguration();
resize(0, 0);
// TODO(wwylele): enable this when we actually emulate it
ui->buttonHome->setEnabled(false);
@@ -349,7 +349,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
ConfigureInputPlayer::~ConfigureInputPlayer() = default;
void ConfigureInputPlayer::applyConfiguration() {
void ConfigureInputPlayer::ApplyConfiguration() {
auto& buttons =
debug ? Settings::values.debug_pad_buttons : Settings::values.players[player_index].buttons;
auto& analogs =
@@ -373,6 +373,19 @@ void ConfigureInputPlayer::applyConfiguration() {
Settings::values.players[player_index].button_color_right = colors[3];
}
void ConfigureInputPlayer::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QDialog::changeEvent(event);
}
void ConfigureInputPlayer::RetranslateUI() {
ui->retranslateUi(this);
UpdateButtonLabels();
}
void ConfigureInputPlayer::OnControllerButtonClick(int i) {
const QColor new_bg_color = QColorDialog::getColor(controller_colors[i]);
if (!new_bg_color.isValid())
@@ -382,7 +395,7 @@ void ConfigureInputPlayer::OnControllerButtonClick(int i) {
QStringLiteral("QPushButton { background-color: %1 }").arg(controller_colors[i].name()));
}
void ConfigureInputPlayer::loadConfiguration() {
void ConfigureInputPlayer::LoadConfiguration() {
if (debug) {
std::transform(Settings::values.debug_pad_buttons.begin(),
Settings::values.debug_pad_buttons.end(), buttons_param.begin(),
@@ -399,7 +412,7 @@ void ConfigureInputPlayer::loadConfiguration() {
[](const std::string& str) { return Common::ParamPackage(str); });
}
updateButtonLabels();
UpdateButtonLabels();
if (debug)
return;
@@ -421,7 +434,7 @@ void ConfigureInputPlayer::loadConfiguration() {
}
}
void ConfigureInputPlayer::restoreDefaults() {
void ConfigureInputPlayer::RestoreDefaults() {
for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
buttons_param[button_id] = Common::ParamPackage{
InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
@@ -434,7 +447,7 @@ void ConfigureInputPlayer::restoreDefaults() {
SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
}
}
updateButtonLabels();
UpdateButtonLabels();
}
void ConfigureInputPlayer::ClearAll() {
@@ -458,10 +471,10 @@ void ConfigureInputPlayer::ClearAll() {
}
}
updateButtonLabels();
UpdateButtonLabels();
}
void ConfigureInputPlayer::updateButtonLabels() {
void ConfigureInputPlayer::UpdateButtonLabels() {
for (int button = 0; button < Settings::NativeButton::NumButtons; button++) {
button_map[button]->setText(ButtonToText(buttons_param[button]));
}
@@ -481,7 +494,7 @@ void ConfigureInputPlayer::updateButtonLabels() {
}
}
void ConfigureInputPlayer::handleClick(
void ConfigureInputPlayer::HandleClick(
QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
InputCommon::Polling::DeviceType type) {
button->setText(tr("[press key]"));
@@ -509,7 +522,7 @@ void ConfigureInputPlayer::handleClick(
poll_timer->start(200); // Check for new inputs every 200ms
}
void ConfigureInputPlayer::setPollingResult(const Common::ParamPackage& params, bool abort) {
void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) {
releaseKeyboard();
releaseMouse();
timeout_timer->stop();
@@ -522,22 +535,23 @@ void ConfigureInputPlayer::setPollingResult(const Common::ParamPackage& params,
(*input_setter)(params);
}
updateButtonLabels();
UpdateButtonLabels();
input_setter = std::nullopt;
}
void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
if (!input_setter || !event)
if (!input_setter || !event) {
return;
}
if (event->key() != Qt::Key_Escape) {
if (want_keyboard_keys) {
setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
false);
} else {
// Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
return;
}
}
setPollingResult({}, true);
SetPollingResult({}, true);
}

View File

@@ -38,28 +38,31 @@ public:
~ConfigureInputPlayer() override;
/// Save all button configurations to settings file
void applyConfiguration();
void ApplyConfiguration();
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
void OnControllerButtonClick(int i);
/// Load configuration settings.
void loadConfiguration();
void LoadConfiguration();
/// Restore all buttons to their default values.
void restoreDefaults();
void RestoreDefaults();
/// Clear all input configuration
void ClearAll();
/// Update UI to reflect current configuration.
void updateButtonLabels();
void UpdateButtonLabels();
/// Called when the button was pressed.
void handleClick(QPushButton* button,
void HandleClick(QPushButton* button,
std::function<void(const Common::ParamPackage&)> new_input_setter,
InputCommon::Polling::DeviceType type);
/// Finish polling and configure input using the input_setter
void setPollingResult(const Common::ParamPackage& params, bool abort);
void SetPollingResult(const Common::ParamPackage& params, bool abort);
/// Handle key press events.
void keyPressEvent(QKeyEvent* event) override;

View File

@@ -15,12 +15,12 @@ namespace {
template <typename Dialog, typename... Args>
void CallConfigureDialog(ConfigureInputSimple* caller, Args&&... args) {
caller->applyConfiguration();
caller->ApplyConfiguration();
Dialog dialog(caller, std::forward<Args>(args)...);
const auto res = dialog.exec();
if (res == QDialog::Accepted) {
dialog.applyConfiguration();
dialog.ApplyConfiguration();
}
}
@@ -103,27 +103,41 @@ ConfigureInputSimple::ConfigureInputSimple(QWidget* parent)
&ConfigureInputSimple::OnSelectProfile);
connect(ui->profile_configure, &QPushButton::pressed, this, &ConfigureInputSimple::OnConfigure);
this->loadConfiguration();
LoadConfiguration();
}
ConfigureInputSimple::~ConfigureInputSimple() = default;
void ConfigureInputSimple::applyConfiguration() {
void ConfigureInputSimple::ApplyConfiguration() {
auto index = ui->profile_combobox->currentIndex();
// Make the stored index for "Custom" very large so that if new profiles are added it
// doesn't change.
if (index >= static_cast<int>(INPUT_PROFILES.size() - 1))
if (index >= static_cast<int>(INPUT_PROFILES.size() - 1)) {
index = std::numeric_limits<int>::max();
}
UISettings::values.profile_index = index;
}
void ConfigureInputSimple::loadConfiguration() {
void ConfigureInputSimple::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QWidget::changeEvent(event);
}
void ConfigureInputSimple::RetranslateUI() {
ui->retranslateUi(this);
}
void ConfigureInputSimple::LoadConfiguration() {
const auto index = UISettings::values.profile_index;
if (index >= static_cast<int>(INPUT_PROFILES.size()) || index < 0)
if (index >= static_cast<int>(INPUT_PROFILES.size()) || index < 0) {
ui->profile_combobox->setCurrentIndex(static_cast<int>(INPUT_PROFILES.size() - 1));
else
} else {
ui->profile_combobox->setCurrentIndex(index);
}
}
void ConfigureInputSimple::OnSelectProfile(int index) {

View File

@@ -27,11 +27,14 @@ public:
~ConfigureInputSimple() override;
/// Save all button configurations to settings file
void applyConfiguration();
void ApplyConfiguration();
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
/// Load configuration settings.
void loadConfiguration();
void LoadConfiguration();
void OnSelectProfile(int index);
void OnConfigure();

View File

@@ -84,7 +84,7 @@ ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent)
button->setContextMenuPolicy(Qt::CustomContextMenu);
connect(button, &QPushButton::released, [=] {
handleClick(
HandleClick(
button_map[button_id],
[=](const Common::ParamPackage& params) { buttons_param[button_id] = params; },
InputCommon::Polling::DeviceType::Button);
@@ -105,48 +105,60 @@ ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent)
}
connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); });
connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); });
connect(ui->buttonRestoreDefaults, &QPushButton::released, [this] { RestoreDefaults(); });
timeout_timer->setSingleShot(true);
connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); });
connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
connect(poll_timer.get(), &QTimer::timeout, [this]() {
connect(poll_timer.get(), &QTimer::timeout, [this] {
Common::ParamPackage params;
for (auto& poller : device_pollers) {
params = poller->GetNextInput();
if (params.Has("engine")) {
setPollingResult(params, false);
SetPollingResult(params, false);
return;
}
}
});
loadConfiguration();
LoadConfiguration();
resize(0, 0);
}
ConfigureMouseAdvanced::~ConfigureMouseAdvanced() = default;
void ConfigureMouseAdvanced::applyConfiguration() {
void ConfigureMouseAdvanced::ApplyConfiguration() {
std::transform(buttons_param.begin(), buttons_param.end(),
Settings::values.mouse_buttons.begin(),
[](const Common::ParamPackage& param) { return param.Serialize(); });
}
void ConfigureMouseAdvanced::loadConfiguration() {
void ConfigureMouseAdvanced::LoadConfiguration() {
std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(),
buttons_param.begin(),
[](const std::string& str) { return Common::ParamPackage(str); });
updateButtonLabels();
UpdateButtonLabels();
}
void ConfigureMouseAdvanced::restoreDefaults() {
void ConfigureMouseAdvanced::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QDialog::changeEvent(event);
}
void ConfigureMouseAdvanced::RetranslateUI() {
ui->retranslateUi(this);
}
void ConfigureMouseAdvanced::RestoreDefaults() {
for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) {
buttons_param[button_id] = Common::ParamPackage{
InputCommon::GenerateKeyboardParam(Config::default_mouse_buttons[button_id])};
}
updateButtonLabels();
UpdateButtonLabels();
}
void ConfigureMouseAdvanced::ClearAll() {
@@ -157,16 +169,16 @@ void ConfigureMouseAdvanced::ClearAll() {
}
}
updateButtonLabels();
UpdateButtonLabels();
}
void ConfigureMouseAdvanced::updateButtonLabels() {
void ConfigureMouseAdvanced::UpdateButtonLabels() {
for (int button = 0; button < Settings::NativeMouseButton::NumMouseButtons; button++) {
button_map[button]->setText(ButtonToText(buttons_param[button]));
}
}
void ConfigureMouseAdvanced::handleClick(
void ConfigureMouseAdvanced::HandleClick(
QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
InputCommon::Polling::DeviceType type) {
button->setText(tr("[press key]"));
@@ -194,7 +206,7 @@ void ConfigureMouseAdvanced::handleClick(
poll_timer->start(200); // Check for new inputs every 200ms
}
void ConfigureMouseAdvanced::setPollingResult(const Common::ParamPackage& params, bool abort) {
void ConfigureMouseAdvanced::SetPollingResult(const Common::ParamPackage& params, bool abort) {
releaseKeyboard();
releaseMouse();
timeout_timer->stop();
@@ -207,22 +219,23 @@ void ConfigureMouseAdvanced::setPollingResult(const Common::ParamPackage& params
(*input_setter)(params);
}
updateButtonLabels();
UpdateButtonLabels();
input_setter = std::nullopt;
}
void ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) {
if (!input_setter || !event)
if (!input_setter || !event) {
return;
}
if (event->key() != Qt::Key_Escape) {
if (want_keyboard_keys) {
setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
false);
} else {
// Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
return;
}
}
setPollingResult({}, true);
SetPollingResult({}, true);
}

View File

@@ -25,26 +25,29 @@ public:
explicit ConfigureMouseAdvanced(QWidget* parent);
~ConfigureMouseAdvanced() override;
void applyConfiguration();
void ApplyConfiguration();
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
/// Load configuration settings.
void loadConfiguration();
void LoadConfiguration();
/// Restore all buttons to their default values.
void restoreDefaults();
void RestoreDefaults();
/// Clear all input configuration
void ClearAll();
/// Update UI to reflect current configuration.
void updateButtonLabels();
void UpdateButtonLabels();
/// Called when the button was pressed.
void handleClick(QPushButton* button,
void HandleClick(QPushButton* button,
std::function<void(const Common::ParamPackage&)> new_input_setter,
InputCommon::Polling::DeviceType type);
/// Finish polling and configure input using the input_setter
void setPollingResult(const Common::ParamPackage& params, bool abort);
void SetPollingResult(const Common::ParamPackage& params, bool abort);
/// Handle key press events.
void keyPressEvent(QKeyEvent* event) override;

View File

@@ -13,6 +13,8 @@
#include <QTimer>
#include <QTreeView>
#include "common/common_paths.h"
#include "common/file_util.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/xts_archive.h"
@@ -65,12 +67,12 @@ ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id)
connect(item_model, &QStandardItemModel::itemChanged,
[] { UISettings::values.is_game_list_reload_pending.exchange(true); });
this->loadConfiguration();
LoadConfiguration();
}
ConfigurePerGameGeneral::~ConfigurePerGameGeneral() = default;
void ConfigurePerGameGeneral::applyConfiguration() {
void ConfigurePerGameGeneral::ApplyConfiguration() {
std::vector<std::string> disabled_addons;
for (const auto& item : list_items) {
@@ -79,15 +81,35 @@ void ConfigurePerGameGeneral::applyConfiguration() {
disabled_addons.push_back(item.front()->text().toStdString());
}
auto current = Settings::values.disabled_addons[title_id];
std::sort(disabled_addons.begin(), disabled_addons.end());
std::sort(current.begin(), current.end());
if (disabled_addons != current) {
FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP +
"game_list" + DIR_SEP + fmt::format("{:016X}.pv.txt", title_id));
}
Settings::values.disabled_addons[title_id] = disabled_addons;
}
void ConfigurePerGameGeneral::loadFromFile(FileSys::VirtualFile file) {
this->file = std::move(file);
this->loadConfiguration();
void ConfigurePerGameGeneral::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QDialog::changeEvent(event);
}
void ConfigurePerGameGeneral::loadConfiguration() {
void ConfigurePerGameGeneral::RetranslateUI() {
ui->retranslateUi(this);
}
void ConfigurePerGameGeneral::LoadFromFile(FileSys::VirtualFile file) {
this->file = std::move(file);
LoadConfiguration();
}
void ConfigurePerGameGeneral::LoadConfiguration() {
if (file == nullptr) {
return;
}

View File

@@ -30,11 +30,16 @@ public:
~ConfigurePerGameGeneral() override;
/// Save all button configurations to settings file
void applyConfiguration();
void ApplyConfiguration();
void loadFromFile(FileSys::VirtualFile file);
void LoadFromFile(FileSys::VirtualFile file);
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
void LoadConfiguration();
std::unique_ptr<Ui::ConfigurePerGameGeneral> ui;
FileSys::VirtualFile file;
u64 title_id;
@@ -45,6 +50,4 @@ private:
QGraphicsScene* scene;
std::vector<QList<QStandardItem*>> list_items;
void loadConfiguration();
};

View File

@@ -33,14 +33,13 @@ constexpr std::array<u8, 107> backup_jpeg{
0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
};
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);
}
QString GetAccountUsername(const Service::Account::ProfileManager& manager,
Service::Account::UUID uuid) {
QString GetAccountUsername(const Service::Account::ProfileManager& manager, Common::UUID uuid) {
Service::Account::ProfileBase profile;
if (!manager.GetProfileBase(uuid, profile)) {
return {};
@@ -51,14 +50,14 @@ QString GetAccountUsername(const Service::Account::ProfileManager& manager,
return QString::fromStdString(text);
}
QString FormatUserEntryText(const QString& username, Service::Account::UUID uuid) {
QString FormatUserEntryText(const QString& username, Common::UUID uuid) {
return ConfigureProfileManager::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()));
}
QPixmap GetIcon(Service::Account::UUID uuid) {
QPixmap GetIcon(Common::UUID uuid) {
QPixmap icon{GetImagePath(uuid)};
if (!icon) {
@@ -81,11 +80,10 @@ ConfigureProfileManager ::ConfigureProfileManager(QWidget* parent)
profile_manager(std::make_unique<Service::Account::ProfileManager>()) {
ui->setupUi(this);
layout = new QVBoxLayout;
tree_view = new QTreeView;
item_model = new QStandardItemModel(tree_view);
item_model->insertColumns(0, 1);
tree_view->setModel(item_model);
tree_view->setAlternatingRowColors(true);
tree_view->setSelectionMode(QHeaderView::SingleSelection);
tree_view->setSelectionBehavior(QHeaderView::SelectRows);
@@ -97,13 +95,11 @@ ConfigureProfileManager ::ConfigureProfileManager(QWidget* parent)
tree_view->setIconSize({64, 64});
tree_view->setContextMenuPolicy(Qt::NoContextMenu);
item_model->insertColumns(0, 1);
item_model->setHeaderData(0, Qt::Horizontal, tr("Users"));
// We must register all custom types with the Qt Automoc system so that we are able to use it
// with signals/slots. In this case, QList falls under the umbrells of custom types.
qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
layout = new QVBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
layout->addWidget(tree_view);
@@ -120,12 +116,26 @@ ConfigureProfileManager ::ConfigureProfileManager(QWidget* parent)
scene = new QGraphicsScene;
ui->current_user_icon->setScene(scene);
this->setConfiguration();
SetConfiguration();
RetranslateUI();
}
ConfigureProfileManager::~ConfigureProfileManager() = default;
void ConfigureProfileManager::setConfiguration() {
void ConfigureProfileManager::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QWidget::changeEvent(event);
}
void ConfigureProfileManager::RetranslateUI() {
ui->retranslateUi(this);
item_model->setHeaderData(0, Qt::Horizontal, tr("Users"));
}
void ConfigureProfileManager::SetConfiguration() {
enabled = !Core::System::GetInstance().IsPoweredOn();
item_model->removeRows(0, item_model->rowCount());
list_items.clear();
@@ -165,9 +175,10 @@ void ConfigureProfileManager::UpdateCurrentUser() {
ui->current_user_username->setText(username);
}
void ConfigureProfileManager::applyConfiguration() {
if (!enabled)
void ConfigureProfileManager::ApplyConfiguration() {
if (!enabled) {
return;
}
Settings::Apply();
}
@@ -190,7 +201,7 @@ void ConfigureProfileManager::AddUser() {
return;
}
const auto uuid = Service::Account::UUID::Generate();
const auto uuid = Common::UUID::Generate();
profile_manager->CreateNewUser(uuid, username.toStdString());
item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)});

View File

@@ -30,10 +30,14 @@ public:
explicit ConfigureProfileManager(QWidget* parent = nullptr);
~ConfigureProfileManager() override;
void applyConfiguration();
void setConfiguration();
void ApplyConfiguration();
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
void SetConfiguration();
void PopulateUserList();
void UpdateCurrentUser();

View File

@@ -23,22 +23,36 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::
connect(ui->rng_seed_checkbox, &QCheckBox::stateChanged, this, [this](bool checked) {
ui->rng_seed_edit->setEnabled(checked);
if (!checked)
if (!checked) {
ui->rng_seed_edit->setText(QStringLiteral("00000000"));
}
});
connect(ui->custom_rtc_checkbox, &QCheckBox::stateChanged, this, [this](bool checked) {
ui->custom_rtc_edit->setEnabled(checked);
if (!checked)
if (!checked) {
ui->custom_rtc_edit->setDateTime(QDateTime::currentDateTime());
}
});
this->setConfiguration();
SetConfiguration();
}
ConfigureSystem::~ConfigureSystem() = default;
void ConfigureSystem::setConfiguration() {
void ConfigureSystem::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QWidget::changeEvent(event);
}
void ConfigureSystem::RetranslateUI() {
ui->retranslateUi(this);
}
void ConfigureSystem::SetConfiguration() {
enabled = !Core::System::GetInstance().IsPoweredOn();
ui->combo_language->setCurrentIndex(Settings::values.language_index);
@@ -61,22 +75,25 @@ void ConfigureSystem::setConfiguration() {
void ConfigureSystem::ReadSystemSettings() {}
void ConfigureSystem::applyConfiguration() {
if (!enabled)
void ConfigureSystem::ApplyConfiguration() {
if (!enabled) {
return;
}
Settings::values.language_index = ui->combo_language->currentIndex();
if (ui->rng_seed_checkbox->isChecked())
if (ui->rng_seed_checkbox->isChecked()) {
Settings::values.rng_seed = ui->rng_seed_edit->text().toULongLong(nullptr, 16);
else
} else {
Settings::values.rng_seed = std::nullopt;
}
if (ui->custom_rtc_checkbox->isChecked())
if (ui->custom_rtc_checkbox->isChecked()) {
Settings::values.custom_rtc =
std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch());
else
} else {
Settings::values.custom_rtc = std::nullopt;
}
Settings::Apply();
}
@@ -89,8 +106,10 @@ void ConfigureSystem::RefreshConsoleID() {
"if you use an outdated config savegame. Continue?");
reply = QMessageBox::critical(this, tr("Warning"), warning_text,
QMessageBox::No | QMessageBox::Yes);
if (reply == QMessageBox::No)
if (reply == QMessageBox::No) {
return;
}
u64 console_id{};
ui->label_console_id->setText(
tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper()));

View File

@@ -20,10 +20,14 @@ public:
explicit ConfigureSystem(QWidget* parent = nullptr);
~ConfigureSystem() override;
void applyConfiguration();
void setConfiguration();
void ApplyConfiguration();
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
void SetConfiguration();
void ReadSystemSettings();
void RefreshConsoleID();

View File

@@ -12,29 +12,41 @@ ConfigureTouchscreenAdvanced::ConfigureTouchscreenAdvanced(QWidget* parent)
ui->setupUi(this);
connect(ui->restore_defaults_button, &QPushButton::pressed, this,
&ConfigureTouchscreenAdvanced::restoreDefaults);
&ConfigureTouchscreenAdvanced::RestoreDefaults);
loadConfiguration();
LoadConfiguration();
resize(0, 0);
}
ConfigureTouchscreenAdvanced::~ConfigureTouchscreenAdvanced() = default;
void ConfigureTouchscreenAdvanced::applyConfiguration() {
void ConfigureTouchscreenAdvanced::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QDialog::changeEvent(event);
}
void ConfigureTouchscreenAdvanced::RetranslateUI() {
ui->retranslateUi(this);
}
void ConfigureTouchscreenAdvanced::ApplyConfiguration() {
Settings::values.touchscreen.finger = ui->finger_box->value();
Settings::values.touchscreen.diameter_x = ui->diameter_x_box->value();
Settings::values.touchscreen.diameter_y = ui->diameter_y_box->value();
Settings::values.touchscreen.rotation_angle = ui->angle_box->value();
}
void ConfigureTouchscreenAdvanced::loadConfiguration() {
void ConfigureTouchscreenAdvanced::LoadConfiguration() {
ui->finger_box->setValue(Settings::values.touchscreen.finger);
ui->diameter_x_box->setValue(Settings::values.touchscreen.diameter_x);
ui->diameter_y_box->setValue(Settings::values.touchscreen.diameter_y);
ui->angle_box->setValue(Settings::values.touchscreen.rotation_angle);
}
void ConfigureTouchscreenAdvanced::restoreDefaults() {
void ConfigureTouchscreenAdvanced::RestoreDefaults() {
ui->finger_box->setValue(0);
ui->diameter_x_box->setValue(15);
ui->diameter_y_box->setValue(15);

View File

@@ -18,13 +18,16 @@ public:
explicit ConfigureTouchscreenAdvanced(QWidget* parent);
~ConfigureTouchscreenAdvanced() override;
void applyConfiguration();
void ApplyConfiguration();
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
/// Load configuration settings.
void loadConfiguration();
void LoadConfiguration();
/// Restore all buttons to their default values.
void restoreDefaults();
void RestoreDefaults();
std::unique_ptr<Ui::ConfigureTouchscreenAdvanced> ui;
};

View File

@@ -22,41 +22,61 @@ ConfigureWeb::ConfigureWeb(QWidget* parent)
#ifndef USE_DISCORD_PRESENCE
ui->discord_group->setVisible(false);
#endif
this->setConfiguration();
SetConfiguration();
RetranslateUI();
}
ConfigureWeb::~ConfigureWeb() = default;
void ConfigureWeb::setConfiguration() {
ui->web_credentials_disclaimer->setWordWrap(true);
ui->telemetry_learn_more->setOpenExternalLinks(true);
void ConfigureWeb::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QWidget::changeEvent(event);
}
void ConfigureWeb::RetranslateUI() {
ui->retranslateUi(this);
ui->telemetry_learn_more->setText(
tr("<a href='https://yuzu-emu.org/help/feature/telemetry/'><span style=\"text-decoration: "
"underline; color:#039be5;\">Learn more</span></a>"));
ui->web_signup_link->setOpenExternalLinks(true);
ui->web_signup_link->setText(
tr("<a href='https://profile.yuzu-emu.org/'><span style=\"text-decoration: underline; "
"color:#039be5;\">Sign up</span></a>"));
ui->web_token_info_link->setOpenExternalLinks(true);
ui->web_token_info_link->setText(
tr("<a href='https://yuzu-emu.org/wiki/yuzu-web-service/'><span style=\"text-decoration: "
"underline; color:#039be5;\">What is my token?</span></a>"));
ui->label_telemetry_id->setText(
tr("Telemetry ID: 0x%1").arg(QString::number(Core::GetTelemetryId(), 16).toUpper()));
}
void ConfigureWeb::SetConfiguration() {
ui->web_credentials_disclaimer->setWordWrap(true);
ui->telemetry_learn_more->setOpenExternalLinks(true);
ui->web_signup_link->setOpenExternalLinks(true);
ui->web_token_info_link->setOpenExternalLinks(true);
ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry);
ui->edit_username->setText(QString::fromStdString(Settings::values.yuzu_username));
ui->edit_token->setText(QString::fromStdString(Settings::values.yuzu_token));
// Connect after setting the values, to avoid calling OnLoginChanged now
connect(ui->edit_token, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged);
connect(ui->edit_username, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged);
ui->label_telemetry_id->setText(
tr("Telemetry ID: 0x%1").arg(QString::number(Core::GetTelemetryId(), 16).toUpper()));
user_verified = true;
ui->toggle_discordrpc->setChecked(UISettings::values.enable_discord_presence);
}
void ConfigureWeb::applyConfiguration() {
void ConfigureWeb::ApplyConfiguration() {
Settings::values.enable_telemetry = ui->toggle_telemetry->isChecked();
UISettings::values.enable_discord_presence = ui->toggle_discordrpc->isChecked();
if (user_verified) {
@@ -120,7 +140,3 @@ void ConfigureWeb::OnLoginVerified() {
"correctly, and that your internet connection is working."));
}
}
void ConfigureWeb::retranslateUi() {
ui->retranslateUi(this);
}

View File

@@ -19,16 +19,18 @@ public:
explicit ConfigureWeb(QWidget* parent = nullptr);
~ConfigureWeb() override;
void applyConfiguration();
void retranslateUi();
void ApplyConfiguration();
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
void RefreshTelemetryID();
void OnLoginChanged();
void VerifyLogin();
void OnLoginVerified();
void setConfiguration();
void SetConfiguration();
bool user_verified = true;
QFutureWatcher<bool> verify_watcher;

View File

@@ -8,7 +8,9 @@
#include <vector>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QSettings>
#include "common/common_paths.h"
#include "common/file_util.h"
@@ -30,13 +32,108 @@
#include "yuzu/ui_settings.h"
namespace {
QString GetGameListCachedObject(const std::string& filename, const std::string& ext,
const std::function<QString()>& generator) {
if (!UISettings::values.cache_game_list || filename == "0000000000000000") {
return generator();
}
const auto path = FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list" +
DIR_SEP + filename + '.' + ext;
FileUtil::CreateFullPath(path);
if (!FileUtil::Exists(path)) {
const auto str = generator();
QFile file{QString::fromStdString(path)};
if (file.open(QFile::WriteOnly)) {
file.write(str.toUtf8());
}
return str;
}
QFile file{QString::fromStdString(path)};
if (file.open(QFile::ReadOnly)) {
return QString::fromUtf8(file.readAll());
}
return generator();
}
std::pair<std::vector<u8>, std::string> GetGameListCachedObject(
const std::string& filename, const std::string& ext,
const std::function<std::pair<std::vector<u8>, std::string>()>& generator) {
if (!UISettings::values.cache_game_list || filename == "0000000000000000") {
return generator();
}
const auto path1 = FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list" +
DIR_SEP + filename + ".jpeg";
const auto path2 = FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list" +
DIR_SEP + filename + ".appname.txt";
FileUtil::CreateFullPath(path1);
if (!FileUtil::Exists(path1) || !FileUtil::Exists(path2)) {
const auto [icon, nacp] = generator();
QFile file1{QString::fromStdString(path1)};
if (!file1.open(QFile::WriteOnly)) {
LOG_ERROR(Frontend, "Failed to open cache file.");
return generator();
}
if (!file1.resize(icon.size())) {
LOG_ERROR(Frontend, "Failed to resize cache file to necessary size.");
return generator();
}
if (file1.write(reinterpret_cast<const char*>(icon.data()), icon.size()) != icon.size()) {
LOG_ERROR(Frontend, "Failed to write data to cache file.");
return generator();
}
QFile file2{QString::fromStdString(path2)};
if (file2.open(QFile::WriteOnly)) {
file2.write(nacp.data(), nacp.size());
}
return std::make_pair(icon, nacp);
}
QFile file1(QString::fromStdString(path1));
QFile file2(QString::fromStdString(path2));
if (!file1.open(QFile::ReadOnly)) {
LOG_ERROR(Frontend, "Failed to open cache file for reading.");
return generator();
}
if (!file2.open(QFile::ReadOnly)) {
LOG_ERROR(Frontend, "Failed to open cache file for reading.");
return generator();
}
std::vector<u8> vec(file1.size());
if (file1.read(reinterpret_cast<char*>(vec.data()), vec.size()) !=
static_cast<s64>(vec.size())) {
return generator();
}
const auto data = file2.readAll();
return std::make_pair(vec, data.toStdString());
}
void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, const FileSys::NCA& nca,
std::vector<u8>& icon, std::string& name) {
auto [nacp, icon_file] = patch_manager.ParseControlNCA(nca);
if (icon_file != nullptr)
icon = icon_file->ReadAllBytes();
if (nacp != nullptr)
name = nacp->GetApplicationName();
std::tie(icon, name) = GetGameListCachedObject(
fmt::format("{:016X}", patch_manager.GetTitleID()), {}, [&patch_manager, &nca] {
const auto [nacp, icon_f] = patch_manager.ParseControlNCA(nca);
return std::make_pair(icon_f->ReadAllBytes(), nacp->GetApplicationName());
});
}
bool HasSupportedFileExtension(const std::string& file_name) {
@@ -114,8 +211,11 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri
};
if (UISettings::values.show_add_ons) {
list.insert(
2, new GameListItem(FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable())));
const auto patch_versions = GetGameListCachedObject(
fmt::format("{:016X}", patch.GetTitleID()), "pv.txt", [&patch, &loader] {
return FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable());
});
list.insert(2, new GameListItem(patch_versions));
}
return list;

View File

@@ -238,15 +238,13 @@ void GMainWindow::ProfileSelectorSelectProfile() {
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
dialog.setWindowModality(Qt::WindowModal);
dialog.exec();
if (!dialog.GetStatus()) {
if (dialog.exec() == QDialog::Rejected) {
emit ProfileSelectorFinishedSelection(std::nullopt);
return;
}
Service::Account::ProfileManager manager;
const auto uuid = manager.GetUser(dialog.GetIndex());
const auto uuid = manager.GetUser(static_cast<std::size_t>(dialog.GetIndex()));
if (!uuid.has_value()) {
emit ProfileSelectorFinishedSelection(std::nullopt);
return;
@@ -261,9 +259,8 @@ void GMainWindow::SoftwareKeyboardGetText(
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
dialog.setWindowModality(Qt::WindowModal);
dialog.exec();
if (!dialog.GetStatus()) {
if (dialog.exec() == QDialog::Rejected) {
emit SoftwareKeyboardFinishedText(std::nullopt);
return;
}
@@ -850,11 +847,6 @@ bool GMainWindow::LoadROM(const QString& filename) {
QMessageBox::critical(this, tr("Error while loading ROM!"),
tr("The ROM format is not supported."));
break;
case Core::System::ResultStatus::ErrorSystemMode:
LOG_CRITICAL(Frontend, "Failed to load ROM!");
QMessageBox::critical(this, tr("Error while loading ROM!"),
tr("Could not determine the system mode."));
break;
case Core::System::ResultStatus::ErrorVideoCore:
QMessageBox::critical(
this, tr("An error occurred initializing the video core."),
@@ -901,11 +893,12 @@ void GMainWindow::SelectAndSetCurrentUser() {
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
dialog.setWindowModality(Qt::WindowModal);
dialog.exec();
if (dialog.GetStatus()) {
Settings::values.current_user = static_cast<s32>(dialog.GetIndex());
if (dialog.exec() == QDialog::Rejected) {
return;
}
Settings::values.current_user = dialog.GetIndex();
}
void GMainWindow::BootGame(const QString& filename) {
@@ -1055,14 +1048,13 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
ASSERT(program_id != 0);
const auto select_profile = [this]() -> s32 {
const auto select_profile = [this] {
QtProfileSelectionDialog dialog(this);
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
dialog.setWindowModality(Qt::WindowModal);
dialog.exec();
if (!dialog.GetStatus()) {
if (dialog.exec() == QDialog::Rejected) {
return -1;
}
@@ -1070,11 +1062,12 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
};
const auto index = select_profile();
if (index == -1)
if (index == -1) {
return;
}
Service::Account::ProfileManager manager;
const auto user_id = manager.GetUser(index);
const auto user_id = manager.GetUser(static_cast<std::size_t>(index));
ASSERT(user_id);
path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser,
FileSys::SaveDataType::SaveData,
@@ -1296,10 +1289,10 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
}
ConfigurePerGameGeneral dialog(this, title_id);
dialog.loadFromFile(v_file);
dialog.LoadFromFile(v_file);
auto result = dialog.exec();
if (result == QDialog::Accepted) {
dialog.applyConfiguration();
dialog.ApplyConfiguration();
const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
if (reload) {
@@ -1396,6 +1389,8 @@ void GMainWindow::OnMenuInstallToNAND() {
tr("The file was successfully installed."));
game_list->PopulateAsync(UISettings::values.game_directory_path,
UISettings::values.game_directory_deepscan);
FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) +
DIR_SEP + "game_list");
};
const auto failed = [this]() {
@@ -1688,26 +1683,31 @@ void GMainWindow::ToggleWindowMode() {
}
void GMainWindow::OnConfigure() {
ConfigureDialog configureDialog(this, hotkey_registry);
auto old_theme = UISettings::values.theme;
const auto old_theme = UISettings::values.theme;
const bool old_discord_presence = UISettings::values.enable_discord_presence;
auto result = configureDialog.exec();
if (result == QDialog::Accepted) {
configureDialog.applyConfiguration();
InitializeHotkeys();
if (UISettings::values.theme != old_theme)
UpdateUITheme();
if (UISettings::values.enable_discord_presence != old_discord_presence)
SetDiscordEnabled(UISettings::values.enable_discord_presence);
const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
if (reload) {
game_list->PopulateAsync(UISettings::values.game_directory_path,
UISettings::values.game_directory_deepscan);
}
config->Save();
ConfigureDialog configure_dialog(this, hotkey_registry);
const auto result = configure_dialog.exec();
if (result != QDialog::Accepted) {
return;
}
configure_dialog.ApplyConfiguration();
InitializeHotkeys();
if (UISettings::values.theme != old_theme) {
UpdateUITheme();
}
if (UISettings::values.enable_discord_presence != old_discord_presence) {
SetDiscordEnabled(UISettings::values.enable_discord_presence);
}
const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
if (reload) {
game_list->PopulateAsync(UISettings::values.game_directory_path,
UISettings::values.game_directory_deepscan);
}
config->Save();
}
void GMainWindow::OnLoadAmiibo() {

View File

@@ -104,7 +104,7 @@ signals:
void ErrorDisplayFinished();
void ProfileSelectorFinishedSelection(std::optional<Service::Account::UUID> uuid);
void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid);
void SoftwareKeyboardFinishedText(std::optional<std::u16string> text);
void SoftwareKeyboardFinishedCheckDialog();

View File

@@ -79,6 +79,7 @@ struct Values {
uint8_t row_1_text_id;
uint8_t row_2_text_id;
std::atomic_bool is_game_list_reload_pending{false};
bool cache_game_list;
};
extern Values values;

Some files were not shown because too many files have changed in this diff Show More