Compare commits
283 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0eb0c24269 | ||
|
|
aca40de224 | ||
|
|
c9d886c84e | ||
|
|
8070cb3f6b | ||
|
|
be020f7621 | ||
|
|
9e689a81f8 | ||
|
|
fb9124b6cd | ||
|
|
f732cd5a4b | ||
|
|
36259c01c2 | ||
|
|
7fb7d3c218 | ||
|
|
65c748fbd3 | ||
|
|
63a5f48e7e | ||
|
|
2a9e388290 | ||
|
|
313cc36fec | ||
|
|
48807e9a24 | ||
|
|
2321656dbe | ||
|
|
e721c344ae | ||
|
|
6ec48af222 | ||
|
|
f12eb40834 | ||
|
|
7ad11e3867 | ||
|
|
772c86a260 | ||
|
|
3b9d89839d | ||
|
|
30b176f92b | ||
|
|
3f3a93f13b | ||
|
|
54a02d14fd | ||
|
|
447bdac298 | ||
|
|
cca663792f | ||
|
|
3c7eed16dc | ||
|
|
70b595a63b | ||
|
|
ad50cd7df9 | ||
|
|
e23110bd9f | ||
|
|
abdce723eb | ||
|
|
55481df50f | ||
|
|
beb3d77a79 | ||
|
|
e86af37ecb | ||
|
|
da5a537029 | ||
|
|
58032e0085 | ||
|
|
2521007c09 | ||
|
|
6be79bab37 | ||
|
|
b50557d1f0 | ||
|
|
4705d1b523 | ||
|
|
965608e6d1 | ||
|
|
c72ef5f405 | ||
|
|
9968c0883a | ||
|
|
79163fca80 | ||
|
|
306a24aad7 | ||
|
|
d992909636 | ||
|
|
6e1db6b703 | ||
|
|
8eae66907e | ||
|
|
f6f1a8f26a | ||
|
|
1ca2b504bf | ||
|
|
dd9ace502b | ||
|
|
3f3c3ca5f9 | ||
|
|
cae9708a00 | ||
|
|
f477c5dfdd | ||
|
|
efa7d8d04b | ||
|
|
bb4a1e059c | ||
|
|
dfe4b3f723 | ||
|
|
7d417d501d | ||
|
|
c2146c4eef | ||
|
|
fd6549be73 | ||
|
|
0b03e8a98f | ||
|
|
5829ba1ccc | ||
|
|
4ed2774c26 | ||
|
|
f67039c067 | ||
|
|
d10fc2d727 | ||
|
|
223ca80753 | ||
|
|
5aeabd9a17 | ||
|
|
88bc39374f | ||
|
|
c0abc7124d | ||
|
|
fb234560b0 | ||
|
|
18d24fbdd0 | ||
|
|
36665ce0b2 | ||
|
|
58c8a44e7a | ||
|
|
19dc36ce06 | ||
|
|
192f1f7ebe | ||
|
|
5d005b87a3 | ||
|
|
3c4238657d | ||
|
|
01ff38cca8 | ||
|
|
73dcb13619 | ||
|
|
d018ac2c60 | ||
|
|
54684feffa | ||
|
|
3898c3903e | ||
|
|
675aa5f719 | ||
|
|
b889167b2c | ||
|
|
e447d8aafa | ||
|
|
6ff9008230 | ||
|
|
c96450f6e2 | ||
|
|
9d2d349d7b | ||
|
|
d40a38df8d | ||
|
|
ce21973022 | ||
|
|
df3ee4f444 | ||
|
|
4fab0d392b | ||
|
|
9003e19797 | ||
|
|
5f8d2a2044 | ||
|
|
db2e5e5fa6 | ||
|
|
a468273221 | ||
|
|
7565389700 | ||
|
|
e723441e37 | ||
|
|
34841a41c3 | ||
|
|
0837290992 | ||
|
|
75de730e28 | ||
|
|
10a83653ee | ||
|
|
4504302abc | ||
|
|
4b2ff1e00e | ||
|
|
0b6df52109 | ||
|
|
b8b05a484a | ||
|
|
4d63f97945 | ||
|
|
de982deb25 | ||
|
|
e49ae3bf92 | ||
|
|
221996a194 | ||
|
|
334e6370f9 | ||
|
|
a9f3c54871 | ||
|
|
69e490b303 | ||
|
|
8f8fa82c83 | ||
|
|
e2f7933b3f | ||
|
|
96412848a9 | ||
|
|
100ed88e15 | ||
|
|
82cd87f6c2 | ||
|
|
d1812316e1 | ||
|
|
51ba60b27e | ||
|
|
97c8c9f49a | ||
|
|
6acdae0e4c | ||
|
|
d7587842eb | ||
|
|
198a0395bb | ||
|
|
fed773a86c | ||
|
|
082740d34d | ||
|
|
03d489dcf5 | ||
|
|
9422cf7c10 | ||
|
|
fac3706253 | ||
|
|
7232a1ed16 | ||
|
|
3dd7643214 | ||
|
|
4db28f72f6 | ||
|
|
2d83553ea7 | ||
|
|
cb728797b0 | ||
|
|
a56f687793 | ||
|
|
b01f9c8a70 | ||
|
|
561ce29c98 | ||
|
|
b7de31ac97 | ||
|
|
6f69f06873 | ||
|
|
9f755218a1 | ||
|
|
3809041c24 | ||
|
|
60bf761afb | ||
|
|
228f516bb4 | ||
|
|
9251354152 | ||
|
|
0966665fc2 | ||
|
|
ea1525dab1 | ||
|
|
bdf9faab33 | ||
|
|
e60ed2bb3e | ||
|
|
fcac55d5bf | ||
|
|
175aa343ff | ||
|
|
1bf4154e7d | ||
|
|
9097301d92 | ||
|
|
06c4ce8645 | ||
|
|
007ffbef1c | ||
|
|
58c0d37422 | ||
|
|
07f7ce1da2 | ||
|
|
b8c75a845b | ||
|
|
6c81c8f5b7 | ||
|
|
4e81fc8296 | ||
|
|
d267948a73 | ||
|
|
6162cb922e | ||
|
|
4530511ee4 | ||
|
|
a79831d9d0 | ||
|
|
7731a0e2d1 | ||
|
|
c2ed348bdd | ||
|
|
9098905dd1 | ||
|
|
d65a4af895 | ||
|
|
6bd034eae9 | ||
|
|
2131f71573 | ||
|
|
41b4674458 | ||
|
|
07cc7e0c12 | ||
|
|
1bbc9debfb | ||
|
|
5192521dc3 | ||
|
|
94f2be5473 | ||
|
|
a4a58be2d4 | ||
|
|
345e73f2fe | ||
|
|
28d7c2f5a5 | ||
|
|
b347543e83 | ||
|
|
4e2071b6d9 | ||
|
|
ba677ccb5a | ||
|
|
de0b1cb2b2 | ||
|
|
e0002599ac | ||
|
|
324e470879 | ||
|
|
549fd18ac4 | ||
|
|
16e8625a30 | ||
|
|
2b30000a1e | ||
|
|
03d10ea3b4 | ||
|
|
1af4414861 | ||
|
|
d86f9cd709 | ||
|
|
b711cdce78 | ||
|
|
bc930754cc | ||
|
|
3d471e732d | ||
|
|
3b26206dbd | ||
|
|
6b0695b3cd | ||
|
|
6c410104f4 | ||
|
|
fa59a7b4d8 | ||
|
|
1b4503c571 | ||
|
|
5f3aacdc37 | ||
|
|
2787a0c287 | ||
|
|
4b396f375c | ||
|
|
0cefb7bcb4 | ||
|
|
84139586c9 | ||
|
|
fb94871791 | ||
|
|
bab21e8cb3 | ||
|
|
4111971cbd | ||
|
|
ed82fa3a91 | ||
|
|
c9983ad9a7 | ||
|
|
1b7d619914 | ||
|
|
80a8456af8 | ||
|
|
908a5a00c5 | ||
|
|
26fcdf087d | ||
|
|
c28694d907 | ||
|
|
ca470890a3 | ||
|
|
5cef446f42 | ||
|
|
e78d069a81 | ||
|
|
8893d63612 | ||
|
|
0c64a6f0f2 | ||
|
|
c5b20a108d | ||
|
|
908ca1fc72 | ||
|
|
f375e10411 | ||
|
|
5c665fcc5b | ||
|
|
61d2498f00 | ||
|
|
5799404b78 | ||
|
|
c7b5c245e1 | ||
|
|
6ca20ad7ba | ||
|
|
90792cdb6e | ||
|
|
50e3269f3b | ||
|
|
5fb6781c61 | ||
|
|
335127af69 | ||
|
|
c0e7b91145 | ||
|
|
c140b6ae2c | ||
|
|
0360c40e90 | ||
|
|
a9521c983b | ||
|
|
0f08f2d562 | ||
|
|
18c1d91920 | ||
|
|
969cd6dc1d | ||
|
|
a62088539e | ||
|
|
c7daddb715 | ||
|
|
0af3b4d9f4 | ||
|
|
ee81fb94cd | ||
|
|
7e2bcf04b4 | ||
|
|
f981efdf8d | ||
|
|
a602bcaaf8 | ||
|
|
7bdef6106e | ||
|
|
81d361d9f8 | ||
|
|
e34368249f | ||
|
|
288d027e89 | ||
|
|
825ffd7b1f | ||
|
|
bf35138d1d | ||
|
|
3a26b49c2c | ||
|
|
fc0bf91a96 | ||
|
|
511bf3435d | ||
|
|
f279e792b7 | ||
|
|
5ddc9cede5 | ||
|
|
ae5a46256e | ||
|
|
819006d0d3 | ||
|
|
c508a8d82a | ||
|
|
f79823fda7 | ||
|
|
4ec8a3df08 | ||
|
|
5669ff3cbd | ||
|
|
2f2a61887a | ||
|
|
fe8e6618f2 | ||
|
|
9db119f8a2 | ||
|
|
c417b4fe28 | ||
|
|
ce9f7ac4f2 | ||
|
|
a76bd49268 | ||
|
|
c3548967b1 | ||
|
|
b550a01f74 | ||
|
|
0f37096820 | ||
|
|
421c3e831a | ||
|
|
b77fde7c5c | ||
|
|
cdf52b9374 | ||
|
|
34e9736910 | ||
|
|
fc309bf893 | ||
|
|
24392c8ec8 | ||
|
|
bdc47693f1 | ||
|
|
186db894b9 | ||
|
|
2dde8f5cfe | ||
|
|
6f0ee45b5c | ||
|
|
658d2ee97d | ||
|
|
5574be21cc | ||
|
|
2179ad7483 |
@@ -70,6 +70,7 @@ set(HASH_FILES
|
||||
"${VIDEO_CORE}/shader/decode/half_set.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/half_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/hfma2.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/image.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/integer_set.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/integer_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/memory.cpp"
|
||||
|
||||
@@ -88,6 +88,7 @@ add_subdirectory(tests)
|
||||
|
||||
if (ENABLE_SDL2)
|
||||
add_subdirectory(yuzu_cmd)
|
||||
add_subdirectory(yuzu_tester)
|
||||
endif()
|
||||
|
||||
if (ENABLE_QT)
|
||||
|
||||
@@ -51,6 +51,10 @@ void Stream::Stop() {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void Stream::SetVolume(float volume) {
|
||||
game_volume = volume;
|
||||
}
|
||||
|
||||
Stream::State Stream::GetState() const {
|
||||
return state;
|
||||
}
|
||||
@@ -62,8 +66,8 @@ s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
|
||||
return Core::Timing::usToCycles(us);
|
||||
}
|
||||
|
||||
static void VolumeAdjustSamples(std::vector<s16>& samples) {
|
||||
const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)};
|
||||
static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) {
|
||||
const float volume{std::clamp(Settings::values.volume - (1.0f - game_volume), 0.0f, 1.0f)};
|
||||
|
||||
if (volume == 1.0f) {
|
||||
return;
|
||||
@@ -97,11 +101,11 @@ void Stream::PlayNextBuffer() {
|
||||
active_buffer = queued_buffers.front();
|
||||
queued_buffers.pop();
|
||||
|
||||
VolumeAdjustSamples(active_buffer->GetSamples());
|
||||
VolumeAdjustSamples(active_buffer->GetSamples(), game_volume);
|
||||
|
||||
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
|
||||
|
||||
core_timing.ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
|
||||
core_timing.ScheduleEvent(GetBufferReleaseCycles(*active_buffer), release_event, {});
|
||||
}
|
||||
|
||||
void Stream::ReleaseActiveBuffer() {
|
||||
|
||||
@@ -61,6 +61,12 @@ public:
|
||||
/// Returns a vector of recently released buffers specified by tag
|
||||
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(std::size_t max_count);
|
||||
|
||||
void SetVolume(float volume);
|
||||
|
||||
float GetVolume() const {
|
||||
return game_volume;
|
||||
}
|
||||
|
||||
/// Returns true if the stream is currently playing
|
||||
bool IsPlaying() const {
|
||||
return state == State::Playing;
|
||||
@@ -94,6 +100,7 @@ private:
|
||||
|
||||
u32 sample_rate; ///< Sample rate of the stream
|
||||
Format format; ///< Format of the stream
|
||||
float game_volume = 1.0f; ///< The volume the game currently has set
|
||||
ReleaseCallback release_callback; ///< Buffer release callback for the stream
|
||||
State state{State::Stopped}; ///< Playback state of the stream
|
||||
Core::Timing::EventType* release_event{}; ///< Core timing release event for the stream
|
||||
|
||||
@@ -44,6 +44,7 @@ add_custom_command(OUTPUT scm_rev.cpp
|
||||
"${VIDEO_CORE}/shader/decode/half_set.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/half_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/hfma2.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/image.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/integer_set.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/integer_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/memory.cpp"
|
||||
@@ -74,6 +75,7 @@ add_library(common STATIC
|
||||
assert.h
|
||||
detached_tasks.cpp
|
||||
detached_tasks.h
|
||||
binary_find.h
|
||||
bit_field.h
|
||||
bit_util.h
|
||||
cityhash.cpp
|
||||
|
||||
@@ -19,6 +19,12 @@ constexpr T AlignDown(T value, std::size_t size) {
|
||||
return static_cast<T>(value - value % size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T AlignBits(T value, std::size_t align) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
return static_cast<T>((value + ((1ULL << align) - 1)) >> align << align);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool Is4KBAligned(T value) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
|
||||
21
src/common/binary_find.h
Normal file
21
src/common/binary_find.h
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <class ForwardIt, class T, class Compare = std::less<>>
|
||||
ForwardIt BinaryFind(ForwardIt first, ForwardIt last, const T& value, Compare comp = {}) {
|
||||
// Note: BOTH type T and the type after ForwardIt is dereferenced
|
||||
// must be implicitly convertible to BOTH Type1 and Type2, used in Compare.
|
||||
// This is stricter than lower_bound requirement (see above)
|
||||
|
||||
first = std::lower_bound(first, last, value, comp);
|
||||
return first != last && !comp(value, *first) ? first : last;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -97,4 +97,48 @@ inline u32 CountTrailingZeroes64(u64 value) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
inline u32 MostSignificantBit32(const u32 value) {
|
||||
unsigned long result;
|
||||
_BitScanReverse(&result, value);
|
||||
return static_cast<u32>(result);
|
||||
}
|
||||
|
||||
inline u32 MostSignificantBit64(const u64 value) {
|
||||
unsigned long result;
|
||||
_BitScanReverse64(&result, value);
|
||||
return static_cast<u32>(result);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
inline u32 MostSignificantBit32(const u32 value) {
|
||||
return 31U - static_cast<u32>(__builtin_clz(value));
|
||||
}
|
||||
|
||||
inline u32 MostSignificantBit64(const u64 value) {
|
||||
return 63U - static_cast<u32>(__builtin_clzll(value));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
inline u32 Log2Floor32(const u32 value) {
|
||||
return MostSignificantBit32(value);
|
||||
}
|
||||
|
||||
inline u32 Log2Ceil32(const u32 value) {
|
||||
const u32 log2_f = Log2Floor32(value);
|
||||
return log2_f + ((value ^ (1U << log2_f)) != 0U);
|
||||
}
|
||||
|
||||
inline u32 Log2Floor64(const u64 value) {
|
||||
return MostSignificantBit64(value);
|
||||
}
|
||||
|
||||
inline u32 Log2Ceil64(const u64 value) {
|
||||
const u64 log2_f = static_cast<u64>(Log2Floor64(value));
|
||||
return static_cast<u32>(log2_f + ((value ^ (1ULL << log2_f)) != 0ULL));
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
#if !defined(ARCHITECTURE_x86_64)
|
||||
|
||||
@@ -30,13 +30,6 @@ std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string HexVectorToString(const std::vector<u8>& vector, bool upper) {
|
||||
std::string out;
|
||||
for (u8 c : vector)
|
||||
out += fmt::format(upper ? "{:02X}" : "{:02x}", c);
|
||||
return out;
|
||||
}
|
||||
|
||||
std::array<u8, 16> operator""_array16(const char* str, std::size_t len) {
|
||||
if (len != 32) {
|
||||
LOG_ERROR(Common,
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <fmt/format.h>
|
||||
#include "common/common_types.h"
|
||||
@@ -30,13 +31,20 @@ std::array<u8, Size> HexStringToArray(std::string_view str) {
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string HexVectorToString(const std::vector<u8>& vector, bool upper = true);
|
||||
template <typename ContiguousContainer>
|
||||
std::string HexToString(const ContiguousContainer& data, bool upper = true) {
|
||||
static_assert(std::is_same_v<typename ContiguousContainer::value_type, u8>,
|
||||
"Underlying type within the contiguous container must be u8.");
|
||||
|
||||
constexpr std::size_t pad_width = 2;
|
||||
|
||||
template <std::size_t Size>
|
||||
std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) {
|
||||
std::string out;
|
||||
for (u8 c : array)
|
||||
out.reserve(std::size(data) * pad_width);
|
||||
|
||||
for (const u8 c : data) {
|
||||
out += fmt::format(upper ? "{:02X}" : "{:02x}", c);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,8 @@ add_library(core STATIC
|
||||
file_sys/fsmitm_romfsbuild.h
|
||||
file_sys/ips_layer.cpp
|
||||
file_sys/ips_layer.h
|
||||
file_sys/kernel_executable.cpp
|
||||
file_sys/kernel_executable.h
|
||||
file_sys/mode.h
|
||||
file_sys/nca_metadata.cpp
|
||||
file_sys/nca_metadata.h
|
||||
@@ -173,6 +175,7 @@ add_library(core STATIC
|
||||
hle/service/acc/acc_u0.h
|
||||
hle/service/acc/acc_u1.cpp
|
||||
hle/service/acc/acc_u1.h
|
||||
hle/service/acc/errors.h
|
||||
hle/service/acc/profile_manager.cpp
|
||||
hle/service/acc/profile_manager.h
|
||||
hle/service/am/am.cpp
|
||||
@@ -207,8 +210,6 @@ add_library(core STATIC
|
||||
hle/service/apm/apm.h
|
||||
hle/service/apm/interface.cpp
|
||||
hle/service/apm/interface.h
|
||||
hle/service/arp/arp.cpp
|
||||
hle/service/arp/arp.h
|
||||
hle/service/audio/audctl.cpp
|
||||
hle/service/audio/audctl.h
|
||||
hle/service/audio/auddbg.cpp
|
||||
@@ -270,10 +271,20 @@ add_library(core STATIC
|
||||
hle/service/filesystem/fsp_srv.h
|
||||
hle/service/fgm/fgm.cpp
|
||||
hle/service/fgm/fgm.h
|
||||
hle/service/friend/errors.h
|
||||
hle/service/friend/friend.cpp
|
||||
hle/service/friend/friend.h
|
||||
hle/service/friend/interface.cpp
|
||||
hle/service/friend/interface.h
|
||||
hle/service/glue/arp.cpp
|
||||
hle/service/glue/arp.h
|
||||
hle/service/glue/bgtc.cpp
|
||||
hle/service/glue/bgtc.h
|
||||
hle/service/glue/errors.h
|
||||
hle/service/glue/glue.cpp
|
||||
hle/service/glue/glue.h
|
||||
hle/service/glue/manager.cpp
|
||||
hle/service/glue/manager.h
|
||||
hle/service/grc/grc.cpp
|
||||
hle/service/grc/grc.h
|
||||
hle/service/hid/hid.cpp
|
||||
@@ -420,6 +431,8 @@ add_library(core STATIC
|
||||
hle/service/time/interface.h
|
||||
hle/service/time/time.cpp
|
||||
hle/service/time/time.h
|
||||
hle/service/time/time_sharedmemory.cpp
|
||||
hle/service/time/time_sharedmemory.h
|
||||
hle/service/usb/usb.cpp
|
||||
hle/service/usb/usb.h
|
||||
hle/service/vi/display/vi_display.cpp
|
||||
@@ -440,6 +453,8 @@ add_library(core STATIC
|
||||
loader/deconstructed_rom_directory.h
|
||||
loader/elf.cpp
|
||||
loader/elf.h
|
||||
loader/kip.cpp
|
||||
loader/kip.h
|
||||
loader/loader.cpp
|
||||
loader/loader.h
|
||||
loader/nax.cpp
|
||||
@@ -459,19 +474,20 @@ add_library(core STATIC
|
||||
memory_setup.h
|
||||
perf_stats.cpp
|
||||
perf_stats.h
|
||||
reporter.cpp
|
||||
reporter.h
|
||||
settings.cpp
|
||||
settings.h
|
||||
telemetry_session.cpp
|
||||
telemetry_session.h
|
||||
tracer/citrace.h
|
||||
tracer/recorder.cpp
|
||||
tracer/recorder.h
|
||||
tools/freezer.cpp
|
||||
tools/freezer.h
|
||||
)
|
||||
|
||||
create_target_directory_groups(core)
|
||||
|
||||
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
|
||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt mbedtls opus unicorn open_source_archives)
|
||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt json-headers mbedtls opus unicorn open_source_archives)
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
|
||||
target_link_libraries(core PRIVATE web_service)
|
||||
|
||||
@@ -2,26 +2,213 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/core.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Core {
|
||||
void ARM_Interface::LogBacktrace() const {
|
||||
VAddr fp = GetReg(29);
|
||||
VAddr lr = GetReg(30);
|
||||
const VAddr sp = GetReg(13);
|
||||
const VAddr pc = GetPC();
|
||||
|
||||
LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
|
||||
namespace {
|
||||
|
||||
constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
|
||||
constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
|
||||
constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
|
||||
constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
|
||||
|
||||
enum class ELFSymbolType : u8 {
|
||||
None = 0,
|
||||
Object = 1,
|
||||
Function = 2,
|
||||
Section = 3,
|
||||
File = 4,
|
||||
Common = 5,
|
||||
TLS = 6,
|
||||
};
|
||||
|
||||
enum class ELFSymbolBinding : u8 {
|
||||
Local = 0,
|
||||
Global = 1,
|
||||
Weak = 2,
|
||||
};
|
||||
|
||||
enum class ELFSymbolVisibility : u8 {
|
||||
Default = 0,
|
||||
Internal = 1,
|
||||
Hidden = 2,
|
||||
Protected = 3,
|
||||
};
|
||||
|
||||
struct ELFSymbol {
|
||||
u32 name_index;
|
||||
union {
|
||||
u8 info;
|
||||
|
||||
BitField<0, 4, ELFSymbolType> type;
|
||||
BitField<4, 4, ELFSymbolBinding> binding;
|
||||
};
|
||||
ELFSymbolVisibility visibility;
|
||||
u16 sh_index;
|
||||
u64 value;
|
||||
u64 size;
|
||||
};
|
||||
static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size.");
|
||||
|
||||
using Symbols = std::vector<std::pair<ELFSymbol, std::string>>;
|
||||
|
||||
Symbols GetSymbols(VAddr text_offset) {
|
||||
const auto mod_offset = text_offset + Memory::Read32(text_offset + 4);
|
||||
|
||||
if (mod_offset < text_offset || (mod_offset & 0b11) != 0 ||
|
||||
Memory::Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto dynamic_offset = Memory::Read32(mod_offset + 0x4) + mod_offset;
|
||||
|
||||
VAddr string_table_offset{};
|
||||
VAddr symbol_table_offset{};
|
||||
u64 symbol_entry_size{};
|
||||
|
||||
VAddr dynamic_index = dynamic_offset;
|
||||
while (true) {
|
||||
LOG_ERROR(Core_ARM, "{:016X}", lr);
|
||||
const auto tag = Memory::Read64(dynamic_index);
|
||||
const auto value = Memory::Read64(dynamic_index + 0x8);
|
||||
dynamic_index += 0x10;
|
||||
|
||||
if (tag == ELF_DYNAMIC_TAG_NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (tag == ELF_DYNAMIC_TAG_STRTAB) {
|
||||
string_table_offset = value;
|
||||
} else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
|
||||
symbol_table_offset = value;
|
||||
} else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
|
||||
symbol_entry_size = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto string_table_address = text_offset + string_table_offset;
|
||||
const auto symbol_table_address = text_offset + symbol_table_offset;
|
||||
|
||||
Symbols out;
|
||||
|
||||
VAddr symbol_index = symbol_table_address;
|
||||
while (symbol_index < string_table_address) {
|
||||
ELFSymbol symbol{};
|
||||
Memory::ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol));
|
||||
|
||||
VAddr string_offset = string_table_address + symbol.name_index;
|
||||
std::string name;
|
||||
for (u8 c = Memory::Read8(string_offset); c != 0; c = Memory::Read8(++string_offset)) {
|
||||
name += static_cast<char>(c);
|
||||
}
|
||||
|
||||
symbol_index += symbol_entry_size;
|
||||
out.push_back({symbol, name});
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr func_address) {
|
||||
const auto iter =
|
||||
std::find_if(symbols.begin(), symbols.end(), [func_address](const auto& pair) {
|
||||
const auto& [symbol, name] = pair;
|
||||
const auto end_address = symbol.value + symbol.size;
|
||||
return func_address >= symbol.value && func_address < end_address;
|
||||
});
|
||||
|
||||
if (iter == symbols.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
constexpr u64 SEGMENT_BASE = 0x7100000000ull;
|
||||
|
||||
std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
|
||||
std::vector<BacktraceEntry> out;
|
||||
|
||||
auto fp = GetReg(29);
|
||||
auto lr = GetReg(30);
|
||||
|
||||
while (true) {
|
||||
out.push_back({"", 0, lr, 0});
|
||||
if (!fp) {
|
||||
break;
|
||||
}
|
||||
lr = Memory::Read64(fp + 8) - 4;
|
||||
fp = Memory::Read64(fp);
|
||||
}
|
||||
|
||||
std::map<VAddr, std::string> modules;
|
||||
auto& loader{System::GetInstance().GetAppLoader()};
|
||||
if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::map<std::string, Symbols> symbols;
|
||||
for (const auto& module : modules) {
|
||||
symbols.insert_or_assign(module.second, GetSymbols(module.first));
|
||||
}
|
||||
|
||||
for (auto& entry : out) {
|
||||
VAddr base = 0;
|
||||
for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) {
|
||||
const auto& module{*iter};
|
||||
if (entry.original_address >= module.first) {
|
||||
entry.module = module.second;
|
||||
base = module.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
entry.offset = entry.original_address - base;
|
||||
entry.address = SEGMENT_BASE + entry.offset;
|
||||
|
||||
if (entry.module.empty())
|
||||
entry.module = "unknown";
|
||||
|
||||
const auto symbol_set = symbols.find(entry.module);
|
||||
if (symbol_set != symbols.end()) {
|
||||
const auto symbol = GetSymbolName(symbol_set->second, entry.offset);
|
||||
if (symbol.has_value()) {
|
||||
// TODO(DarkLordZach): Add demangling of symbol names.
|
||||
entry.name = *symbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void ARM_Interface::LogBacktrace() const {
|
||||
const VAddr sp = GetReg(13);
|
||||
const VAddr pc = GetPC();
|
||||
LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
|
||||
LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address",
|
||||
"Offset", "Symbol");
|
||||
LOG_ERROR(Core_ARM, "");
|
||||
|
||||
const auto backtrace = GetBacktrace();
|
||||
for (const auto& entry : backtrace) {
|
||||
LOG_ERROR(Core_ARM, "{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address,
|
||||
entry.original_address, entry.offset, entry.name);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
@@ -152,6 +153,16 @@ public:
|
||||
/// Prepare core for thread reschedule (if needed to correctly handle state)
|
||||
virtual void PrepareReschedule() = 0;
|
||||
|
||||
struct BacktraceEntry {
|
||||
std::string module;
|
||||
u64 address;
|
||||
u64 original_address;
|
||||
u64 offset;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
std::vector<BacktraceEntry> GetBacktrace() const;
|
||||
|
||||
/// fp (= r29) points to the last frame record.
|
||||
/// Note that this is the frame record for the *previous* frame, not the current one.
|
||||
/// Note we need to subtract 4 from our last read to get the proper address
|
||||
|
||||
@@ -257,9 +257,6 @@ void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) {
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::PrepareReschedule() {
|
||||
if (jit == nullptr)
|
||||
return;
|
||||
|
||||
jit->HaltExecution();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,19 +25,47 @@
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
#include "core/hle/service/glue/manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "core/reporter.h"
|
||||
#include "core/settings.h"
|
||||
#include "core/telemetry_session.h"
|
||||
#include "core/tools/freezer.h"
|
||||
#include "file_sys/cheat_engine.h"
|
||||
#include "file_sys/patch_manager.h"
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
namespace {
|
||||
|
||||
FileSys::StorageId GetStorageIdForFrontendSlot(
|
||||
std::optional<FileSys::ContentProviderUnionSlot> slot) {
|
||||
if (!slot.has_value()) {
|
||||
return FileSys::StorageId::None;
|
||||
}
|
||||
|
||||
switch (*slot) {
|
||||
case FileSys::ContentProviderUnionSlot::UserNAND:
|
||||
return FileSys::StorageId::NandUser;
|
||||
case FileSys::ContentProviderUnionSlot::SysNAND:
|
||||
return FileSys::StorageId::NandSystem;
|
||||
case FileSys::ContentProviderUnionSlot::SDMC:
|
||||
return FileSys::StorageId::SdCard;
|
||||
case FileSys::ContentProviderUnionSlot::FrontendManual:
|
||||
return FileSys::StorageId::Host;
|
||||
default:
|
||||
return FileSys::StorageId::None;
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
/*static*/ System System::s_instance;
|
||||
|
||||
FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
@@ -74,7 +102,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
return vfs->OpenFile(path, FileSys::Mode::Read);
|
||||
}
|
||||
struct System::Impl {
|
||||
explicit Impl(System& system) : kernel{system}, cpu_core_manager{system} {}
|
||||
explicit Impl(System& system) : kernel{system}, cpu_core_manager{system}, reporter{system} {}
|
||||
|
||||
Cpu& CurrentCpuCore() {
|
||||
return cpu_core_manager.GetCurrentCore();
|
||||
@@ -109,6 +137,9 @@ struct System::Impl {
|
||||
/// Create default implementations of applets if one is not provided.
|
||||
applet_manager.SetDefaultAppletsIfMissing();
|
||||
|
||||
/// Reset all glue registrations
|
||||
arp_manager.ResetAll();
|
||||
|
||||
telemetry_session = std::make_unique<Core::TelemetrySession>();
|
||||
service_manager = std::make_shared<Service::SM::ServiceManager>();
|
||||
|
||||
@@ -150,7 +181,8 @@ struct System::Impl {
|
||||
}
|
||||
|
||||
telemetry_session->AddInitialInfo(*app_loader);
|
||||
auto main_process = Kernel::Process::Create(system, "main");
|
||||
auto main_process =
|
||||
Kernel::Process::Create(system, "main", Kernel::Process::ProcessType::Userland);
|
||||
const auto [load_result, load_parameters] = app_loader->Load(*main_process);
|
||||
if (load_result != Loader::ResultStatus::Success) {
|
||||
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
|
||||
@@ -159,6 +191,7 @@ struct System::Impl {
|
||||
return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
|
||||
static_cast<u32>(load_result));
|
||||
}
|
||||
AddGlueRegistrationForProcess(*app_loader, *main_process);
|
||||
kernel.MakeCurrentProcess(main_process.get());
|
||||
|
||||
// Main process has been loaded and been made current.
|
||||
@@ -217,6 +250,31 @@ struct System::Impl {
|
||||
return app_loader->ReadTitle(out);
|
||||
}
|
||||
|
||||
void AddGlueRegistrationForProcess(Loader::AppLoader& loader, Kernel::Process& process) {
|
||||
std::vector<u8> nacp_data;
|
||||
FileSys::NACP nacp;
|
||||
if (loader.ReadControlData(nacp) == Loader::ResultStatus::Success) {
|
||||
nacp_data = nacp.GetRawBytes();
|
||||
} else {
|
||||
nacp_data.resize(sizeof(FileSys::RawNACP));
|
||||
}
|
||||
|
||||
Service::Glue::ApplicationLaunchProperty launch{};
|
||||
launch.title_id = process.GetTitleID();
|
||||
|
||||
FileSys::PatchManager pm{launch.title_id};
|
||||
launch.version = pm.GetGameVersion().value_or(0);
|
||||
|
||||
// TODO(DarkLordZach): When FSController/Game Card Support is added, if
|
||||
// current_process_game_card use correct StorageId
|
||||
launch.base_game_storage_id = GetStorageIdForFrontendSlot(content_provider->GetSlotForEntry(
|
||||
launch.title_id, FileSys::ContentRecordType::Program));
|
||||
launch.update_storage_id = GetStorageIdForFrontendSlot(content_provider->GetSlotForEntry(
|
||||
FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program));
|
||||
|
||||
arp_manager.Register(launch.title_id, launch, std::move(nacp_data));
|
||||
}
|
||||
|
||||
void SetStatus(ResultStatus new_status, const char* details = nullptr) {
|
||||
status = new_status;
|
||||
if (details) {
|
||||
@@ -243,16 +301,22 @@ struct System::Impl {
|
||||
bool is_powered_on = false;
|
||||
|
||||
std::unique_ptr<FileSys::CheatEngine> cheat_engine;
|
||||
std::unique_ptr<Tools::Freezer> memory_freezer;
|
||||
|
||||
/// Frontend applets
|
||||
Service::AM::Applets::AppletManager applet_manager;
|
||||
|
||||
/// Glue services
|
||||
Service::Glue::ARPManager arp_manager;
|
||||
|
||||
/// Service manager
|
||||
std::shared_ptr<Service::SM::ServiceManager> service_manager;
|
||||
|
||||
/// Telemetry session for this emulation session
|
||||
std::unique_ptr<Core::TelemetrySession> telemetry_session;
|
||||
|
||||
Reporter reporter;
|
||||
|
||||
ResultStatus status = ResultStatus::Success;
|
||||
std::string status_details = "";
|
||||
|
||||
@@ -492,6 +556,18 @@ void System::ClearContentProvider(FileSys::ContentProviderUnionSlot slot) {
|
||||
impl->content_provider->ClearSlot(slot);
|
||||
}
|
||||
|
||||
const Reporter& System::GetReporter() const {
|
||||
return impl->reporter;
|
||||
}
|
||||
|
||||
Service::Glue::ARPManager& System::GetARPManager() {
|
||||
return impl->arp_manager;
|
||||
}
|
||||
|
||||
const Service::Glue::ARPManager& System::GetARPManager() const {
|
||||
return impl->arp_manager;
|
||||
}
|
||||
|
||||
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
|
||||
return impl->Init(*this, emu_window);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <map>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
@@ -42,6 +43,10 @@ struct AppletFrontendSet;
|
||||
class AppletManager;
|
||||
} // namespace AM::Applets
|
||||
|
||||
namespace Glue {
|
||||
class ARPManager;
|
||||
}
|
||||
|
||||
namespace SM {
|
||||
class ServiceManager;
|
||||
} // namespace SM
|
||||
@@ -68,6 +73,7 @@ class Cpu;
|
||||
class ExclusiveMonitor;
|
||||
class FrameLimiter;
|
||||
class PerfStats;
|
||||
class Reporter;
|
||||
class TelemetrySession;
|
||||
|
||||
struct PerfStatsResults;
|
||||
@@ -284,6 +290,12 @@ public:
|
||||
|
||||
void ClearContentProvider(FileSys::ContentProviderUnionSlot slot);
|
||||
|
||||
const Reporter& GetReporter() const;
|
||||
|
||||
Service::Glue::ARPManager& GetARPManager();
|
||||
|
||||
const Service::Glue::ARPManager& GetARPManager() const;
|
||||
|
||||
private:
|
||||
System();
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ bool CpuBarrier::Rendezvous() {
|
||||
Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier,
|
||||
std::size_t core_index)
|
||||
: cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} {
|
||||
if (Settings::values.use_cpu_jit) {
|
||||
if (Settings::values.cpu_jit_enabled) {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index);
|
||||
#else
|
||||
@@ -70,7 +70,7 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba
|
||||
Cpu::~Cpu() = default;
|
||||
|
||||
std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) {
|
||||
if (Settings::values.use_cpu_jit) {
|
||||
if (Settings::values.cpu_jit_enabled) {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
return std::make_unique<DynarmicExclusiveMonitor>(num_cores);
|
||||
#else
|
||||
|
||||
@@ -56,12 +56,12 @@ void CoreTiming::Initialize() {
|
||||
}
|
||||
|
||||
void CoreTiming::Shutdown() {
|
||||
MoveEvents();
|
||||
ClearPendingEvents();
|
||||
UnregisterAllEvents();
|
||||
}
|
||||
|
||||
EventType* CoreTiming::RegisterEvent(const std::string& name, TimedCallback callback) {
|
||||
std::lock_guard guard{inner_mutex};
|
||||
// check for existing type with same name.
|
||||
// we want event type names to remain unique so that we can use them for serialization.
|
||||
ASSERT_MSG(event_types.find(name) == event_types.end(),
|
||||
@@ -82,6 +82,7 @@ void CoreTiming::UnregisterAllEvents() {
|
||||
|
||||
void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
|
||||
ASSERT(event_type != nullptr);
|
||||
std::lock_guard guard{inner_mutex};
|
||||
const s64 timeout = GetTicks() + cycles_into_future;
|
||||
|
||||
// If this event needs to be scheduled before the next advance(), force one early
|
||||
@@ -93,12 +94,8 @@ void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_ty
|
||||
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||
}
|
||||
|
||||
void CoreTiming::ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type,
|
||||
u64 userdata) {
|
||||
ts_queue.Push(Event{global_timer + cycles_into_future, 0, userdata, event_type});
|
||||
}
|
||||
|
||||
void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) {
|
||||
std::lock_guard guard{inner_mutex};
|
||||
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
|
||||
return e.type == event_type && e.userdata == userdata;
|
||||
});
|
||||
@@ -110,10 +107,6 @@ void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) {
|
||||
}
|
||||
}
|
||||
|
||||
void CoreTiming::UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata) {
|
||||
unschedule_queue.Push(std::make_pair(event_type, userdata));
|
||||
}
|
||||
|
||||
u64 CoreTiming::GetTicks() const {
|
||||
u64 ticks = static_cast<u64>(global_timer);
|
||||
if (!is_global_timer_sane) {
|
||||
@@ -135,6 +128,7 @@ void CoreTiming::ClearPendingEvents() {
|
||||
}
|
||||
|
||||
void CoreTiming::RemoveEvent(const EventType* event_type) {
|
||||
std::lock_guard guard{inner_mutex};
|
||||
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(),
|
||||
[&](const Event& e) { return e.type == event_type; });
|
||||
|
||||
@@ -145,11 +139,6 @@ void CoreTiming::RemoveEvent(const EventType* event_type) {
|
||||
}
|
||||
}
|
||||
|
||||
void CoreTiming::RemoveNormalAndThreadsafeEvent(const EventType* event_type) {
|
||||
MoveEvents();
|
||||
RemoveEvent(event_type);
|
||||
}
|
||||
|
||||
void CoreTiming::ForceExceptionCheck(s64 cycles) {
|
||||
cycles = std::max<s64>(0, cycles);
|
||||
if (downcount <= cycles) {
|
||||
@@ -162,19 +151,8 @@ void CoreTiming::ForceExceptionCheck(s64 cycles) {
|
||||
downcount = static_cast<int>(cycles);
|
||||
}
|
||||
|
||||
void CoreTiming::MoveEvents() {
|
||||
for (Event ev; ts_queue.Pop(ev);) {
|
||||
ev.fifo_order = event_fifo_id++;
|
||||
event_queue.emplace_back(std::move(ev));
|
||||
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||
}
|
||||
}
|
||||
|
||||
void CoreTiming::Advance() {
|
||||
MoveEvents();
|
||||
for (std::pair<const EventType*, u64> ev; unschedule_queue.Pop(ev);) {
|
||||
UnscheduleEvent(ev.first, ev.second);
|
||||
}
|
||||
std::unique_lock<std::mutex> guard(inner_mutex);
|
||||
|
||||
const int cycles_executed = slice_length - downcount;
|
||||
global_timer += cycles_executed;
|
||||
@@ -186,7 +164,9 @@ void CoreTiming::Advance() {
|
||||
Event evt = std::move(event_queue.front());
|
||||
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||
event_queue.pop_back();
|
||||
inner_mutex.unlock();
|
||||
evt.type->callback(evt.userdata, global_timer - evt.time);
|
||||
inner_mutex.lock();
|
||||
}
|
||||
|
||||
is_global_timer_sane = false;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
@@ -67,7 +68,7 @@ public:
|
||||
///
|
||||
EventType* RegisterEvent(const std::string& name, TimedCallback callback);
|
||||
|
||||
/// Unregisters all registered events thus far.
|
||||
/// Unregisters all registered events thus far. Note: not thread unsafe
|
||||
void UnregisterAllEvents();
|
||||
|
||||
/// After the first Advance, the slice lengths and the downcount will be reduced whenever an
|
||||
@@ -76,20 +77,10 @@ public:
|
||||
/// Scheduling from a callback will not update the downcount until the Advance() completes.
|
||||
void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0);
|
||||
|
||||
/// This is to be called when outside of hle threads, such as the graphics thread, wants to
|
||||
/// schedule things to be executed on the main thread.
|
||||
///
|
||||
/// @note This doesn't change slice_length and thus events scheduled by this might be
|
||||
/// called with a delay of up to MAX_SLICE_LENGTH
|
||||
void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type,
|
||||
u64 userdata = 0);
|
||||
|
||||
void UnscheduleEvent(const EventType* event_type, u64 userdata);
|
||||
void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata);
|
||||
|
||||
/// We only permit one event of each type in the queue at a time.
|
||||
void RemoveEvent(const EventType* event_type);
|
||||
void RemoveNormalAndThreadsafeEvent(const EventType* event_type);
|
||||
|
||||
void ForceExceptionCheck(s64 cycles);
|
||||
|
||||
@@ -120,7 +111,6 @@ private:
|
||||
|
||||
/// Clear all pending events. This should ONLY be done on exit.
|
||||
void ClearPendingEvents();
|
||||
void MoveEvents();
|
||||
|
||||
s64 global_timer = 0;
|
||||
s64 idled_cycles = 0;
|
||||
@@ -143,14 +133,9 @@ private:
|
||||
// remain stable regardless of rehashes/resizing.
|
||||
std::unordered_map<std::string, EventType> event_types;
|
||||
|
||||
// The queue for storing the events from other threads threadsafe until they will be added
|
||||
// to the event_queue by the emu thread
|
||||
Common::MPSCQueue<Event> ts_queue;
|
||||
|
||||
// The queue for unscheduling the events from other threads threadsafe
|
||||
Common::MPSCQueue<std::pair<const EventType*, u64>> unschedule_queue;
|
||||
|
||||
EventType* ev_lost = nullptr;
|
||||
|
||||
std::mutex inner_mutex;
|
||||
};
|
||||
|
||||
} // namespace Core::Timing
|
||||
|
||||
@@ -572,7 +572,7 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
|
||||
<< "# If you are experiencing issues involving keys, it may help to delete this file\n";
|
||||
}
|
||||
|
||||
file << fmt::format("\n{} = {}", keyname, Common::HexArrayToString(key));
|
||||
file << fmt::format("\n{} = {}", keyname, Common::HexToString(key));
|
||||
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, filename, category == KeyCategory::Title);
|
||||
}
|
||||
|
||||
@@ -583,7 +583,7 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
|
||||
Key128 rights_id;
|
||||
std::memcpy(rights_id.data(), &field2, sizeof(u64));
|
||||
std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64));
|
||||
WriteKeyToFile(KeyCategory::Title, Common::HexArrayToString(rights_id), key);
|
||||
WriteKeyToFile(KeyCategory::Title, Common::HexToString(rights_id), key);
|
||||
}
|
||||
|
||||
auto category = KeyCategory::Standard;
|
||||
|
||||
@@ -22,8 +22,10 @@
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/crypto/partition_data_manager.h"
|
||||
#include "core/crypto/xts_encryption_layer.h"
|
||||
#include "core/file_sys/kernel_executable.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
#include "core/file_sys/vfs_vector.h"
|
||||
|
||||
using namespace Common;
|
||||
|
||||
@@ -45,36 +47,6 @@ struct Package2Header {
|
||||
};
|
||||
static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size.");
|
||||
|
||||
struct INIHeader {
|
||||
u32_le magic;
|
||||
u32_le size;
|
||||
u32_le process_count;
|
||||
INSERT_PADDING_BYTES(4);
|
||||
};
|
||||
static_assert(sizeof(INIHeader) == 0x10, "INIHeader has incorrect size.");
|
||||
|
||||
struct SectionHeader {
|
||||
u32_le offset;
|
||||
u32_le size_decompressed;
|
||||
u32_le size_compressed;
|
||||
u32_le attribute;
|
||||
};
|
||||
static_assert(sizeof(SectionHeader) == 0x10, "SectionHeader has incorrect size.");
|
||||
|
||||
struct KIPHeader {
|
||||
u32_le magic;
|
||||
std::array<char, 12> name;
|
||||
u64_le title_id;
|
||||
u32_le category;
|
||||
u8 priority;
|
||||
u8 core;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u8 flags;
|
||||
std::array<SectionHeader, 6> sections;
|
||||
std::array<u32, 0x20> capabilities;
|
||||
};
|
||||
static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size.");
|
||||
|
||||
const std::array<SHA256Hash, 0x10> source_hashes{
|
||||
"B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source
|
||||
"7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source
|
||||
@@ -170,65 +142,6 @@ const std::array<SHA256Hash, 0x20> master_key_hashes{
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F
|
||||
};
|
||||
|
||||
static std::vector<u8> DecompressBLZ(const std::vector<u8>& in) {
|
||||
const auto data_size = in.size() - 0xC;
|
||||
|
||||
u32 compressed_size{};
|
||||
u32 init_index{};
|
||||
u32 additional_size{};
|
||||
std::memcpy(&compressed_size, in.data() + data_size, sizeof(u32));
|
||||
std::memcpy(&init_index, in.data() + data_size + 0x4, sizeof(u32));
|
||||
std::memcpy(&additional_size, in.data() + data_size + 0x8, sizeof(u32));
|
||||
|
||||
std::vector<u8> out(in.size() + additional_size);
|
||||
|
||||
if (compressed_size == in.size())
|
||||
std::memcpy(out.data(), in.data() + in.size() - compressed_size, compressed_size);
|
||||
else
|
||||
std::memcpy(out.data(), in.data(), compressed_size);
|
||||
|
||||
auto index = in.size() - init_index;
|
||||
auto out_index = out.size();
|
||||
|
||||
while (out_index > 0) {
|
||||
--index;
|
||||
auto control = in[index];
|
||||
for (size_t i = 0; i < 8; ++i) {
|
||||
if ((control & 0x80) > 0) {
|
||||
ASSERT(index >= 2);
|
||||
index -= 2;
|
||||
u64 segment_offset = in[index] | in[index + 1] << 8;
|
||||
u64 segment_size = ((segment_offset >> 12) & 0xF) + 3;
|
||||
segment_offset &= 0xFFF;
|
||||
segment_offset += 3;
|
||||
|
||||
if (out_index < segment_size)
|
||||
segment_size = out_index;
|
||||
|
||||
ASSERT(out_index >= segment_size);
|
||||
|
||||
out_index -= segment_size;
|
||||
|
||||
for (size_t j = 0; j < segment_size; ++j) {
|
||||
ASSERT(out_index + j + segment_offset < out.size());
|
||||
out[out_index + j] = out[out_index + j + segment_offset];
|
||||
}
|
||||
} else {
|
||||
ASSERT(out_index >= 1);
|
||||
--out_index;
|
||||
--index;
|
||||
out[out_index] = in[index];
|
||||
}
|
||||
|
||||
control <<= 1;
|
||||
if (out_index == 0)
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static u8 CalculateMaxKeyblobSourceHash() {
|
||||
for (s8 i = 0x1F; i >= 0; --i) {
|
||||
if (keyblob_source_hashes[i] != SHA256Hash{})
|
||||
@@ -478,37 +391,22 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa
|
||||
cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()});
|
||||
cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt);
|
||||
|
||||
INIHeader ini;
|
||||
std::memcpy(&ini, c.data(), sizeof(INIHeader));
|
||||
if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1'))
|
||||
const auto ini_file = std::make_shared<FileSys::VectorVfsFile>(c);
|
||||
const FileSys::INI ini{ini_file};
|
||||
if (ini.GetStatus() != Loader::ResultStatus::Success)
|
||||
return;
|
||||
|
||||
u64 offset = sizeof(INIHeader);
|
||||
for (size_t i = 0; i < ini.process_count; ++i) {
|
||||
KIPHeader kip;
|
||||
std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader));
|
||||
if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1'))
|
||||
for (const auto& kip : ini.GetKIPs()) {
|
||||
if (kip.GetStatus() != Loader::ResultStatus::Success)
|
||||
return;
|
||||
|
||||
const auto name =
|
||||
Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size());
|
||||
|
||||
if (name != "FS" && name != "spl") {
|
||||
offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
|
||||
kip.sections[1].size_compressed + kip.sections[2].size_compressed;
|
||||
if (kip.GetName() != "FS" && kip.GetName() != "spl") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const u64 initial_offset = sizeof(KIPHeader) + offset;
|
||||
const auto text_begin = c.cbegin() + initial_offset;
|
||||
const auto text_end = text_begin + kip.sections[0].size_compressed;
|
||||
const std::vector<u8> text = DecompressBLZ({text_begin, text_end});
|
||||
|
||||
const auto rodata_end = text_end + kip.sections[1].size_compressed;
|
||||
const std::vector<u8> rodata = DecompressBLZ({text_end, rodata_end});
|
||||
|
||||
const auto data_end = rodata_end + kip.sections[2].size_compressed;
|
||||
const std::vector<u8> data = DecompressBLZ({rodata_end, data_end});
|
||||
const auto& text = kip.GetTextSection();
|
||||
const auto& rodata = kip.GetRODataSection();
|
||||
const auto& data = kip.GetDataSection();
|
||||
|
||||
std::vector<u8> out;
|
||||
out.reserve(text.size() + rodata.size() + data.size());
|
||||
@@ -516,12 +414,9 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa
|
||||
out.insert(out.end(), rodata.begin(), rodata.end());
|
||||
out.insert(out.end(), data.begin(), data.end());
|
||||
|
||||
offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
|
||||
kip.sections[1].size_compressed + kip.sections[2].size_compressed;
|
||||
|
||||
if (name == "FS")
|
||||
if (kip.GetName() == "FS")
|
||||
package2_fs[static_cast<size_t>(type)] = std::move(out);
|
||||
else if (name == "spl")
|
||||
else if (kip.GetName() == "spl")
|
||||
package2_spl[static_cast<size_t>(type)] = std::move(out);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,16 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure", "logo"};
|
||||
constexpr std::array partition_names{
|
||||
"update",
|
||||
"normal",
|
||||
"secure",
|
||||
"logo",
|
||||
};
|
||||
|
||||
XCI::XCI(VirtualFile file_)
|
||||
: file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA},
|
||||
partitions(0x4) {
|
||||
partitions(partition_names.size()) {
|
||||
if (file->ReadObject(&header) != sizeof(GamecardHeader)) {
|
||||
status = Loader::ResultStatus::ErrorBadXCIHeader;
|
||||
return;
|
||||
@@ -43,23 +48,24 @@ XCI::XCI(VirtualFile file_)
|
||||
|
||||
for (XCIPartition partition :
|
||||
{XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) {
|
||||
auto raw = main_hfs.GetFile(partition_names[static_cast<std::size_t>(partition)]);
|
||||
if (raw != nullptr)
|
||||
partitions[static_cast<std::size_t>(partition)] =
|
||||
std::make_shared<PartitionFilesystem>(raw);
|
||||
const auto partition_idx = static_cast<std::size_t>(partition);
|
||||
auto raw = main_hfs.GetFile(partition_names[partition_idx]);
|
||||
|
||||
if (raw != nullptr) {
|
||||
partitions[partition_idx] = std::make_shared<PartitionFilesystem>(std::move(raw));
|
||||
}
|
||||
}
|
||||
|
||||
secure_partition = std::make_shared<NSP>(
|
||||
main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]));
|
||||
|
||||
const auto secure_ncas = secure_partition->GetNCAsCollapsed();
|
||||
std::copy(secure_ncas.begin(), secure_ncas.end(), std::back_inserter(ncas));
|
||||
|
||||
ncas = secure_partition->GetNCAsCollapsed();
|
||||
program =
|
||||
secure_partition->GetNCA(secure_partition->GetProgramTitleID(), ContentRecordType::Program);
|
||||
program_nca_status = secure_partition->GetProgramStatus(secure_partition->GetProgramTitleID());
|
||||
if (program_nca_status == Loader::ResultStatus::ErrorNSPMissingProgramNCA)
|
||||
if (program_nca_status == Loader::ResultStatus::ErrorNSPMissingProgramNCA) {
|
||||
program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA;
|
||||
}
|
||||
|
||||
auto result = AddNCAFromPartition(XCIPartition::Update);
|
||||
if (result != Loader::ResultStatus::Success) {
|
||||
@@ -147,8 +153,9 @@ std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const {
|
||||
|
||||
VirtualFile XCI::GetNCAFileByType(NCAContentType type) const {
|
||||
auto nca = GetNCAByType(type);
|
||||
if (nca != nullptr)
|
||||
if (nca != nullptr) {
|
||||
return nca->GetBaseFile();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -169,17 +176,22 @@ VirtualDir XCI::GetParentDirectory() const {
|
||||
}
|
||||
|
||||
Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
|
||||
if (partitions[static_cast<std::size_t>(part)] == nullptr) {
|
||||
const auto partition_index = static_cast<std::size_t>(part);
|
||||
const auto& partition = partitions[partition_index];
|
||||
|
||||
if (partition == nullptr) {
|
||||
return Loader::ResultStatus::ErrorXCIMissingPartition;
|
||||
}
|
||||
|
||||
for (const VirtualFile& file : partitions[static_cast<std::size_t>(part)]->GetFiles()) {
|
||||
if (file->GetExtension() != "nca")
|
||||
for (const VirtualFile& file : partition->GetFiles()) {
|
||||
if (file->GetExtension() != "nca") {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto nca = std::make_shared<NCA>(file, nullptr, 0, keys);
|
||||
// TODO(DarkLordZach): Add proper Rev1+ Support
|
||||
if (nca->IsUpdate())
|
||||
if (nca->IsUpdate()) {
|
||||
continue;
|
||||
}
|
||||
if (nca->GetType() == NCAContentType::Program) {
|
||||
program_nca_status = nca->GetStatus();
|
||||
}
|
||||
@@ -188,7 +200,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
|
||||
} else {
|
||||
const u16 error_id = static_cast<u16>(nca->GetStatus());
|
||||
LOG_CRITICAL(Loader, "Could not load NCA {}/{}, failed with error code {:04X} ({})",
|
||||
partition_names[static_cast<std::size_t>(part)], nca->GetName(), error_id,
|
||||
partition_names[partition_index], nca->GetName(), error_id,
|
||||
nca->GetStatus());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,13 +452,13 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s
|
||||
|
||||
switch (s_header.raw.header.crypto_type) {
|
||||
case NCASectionCryptoType::NONE:
|
||||
LOG_DEBUG(Crypto, "called with mode=NONE");
|
||||
LOG_TRACE(Crypto, "called with mode=NONE");
|
||||
return in;
|
||||
case NCASectionCryptoType::CTR:
|
||||
// During normal BKTR decryption, this entire function is skipped. This is for the metadata,
|
||||
// which uses the same CTR as usual.
|
||||
case NCASectionCryptoType::BKTR:
|
||||
LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
|
||||
LOG_TRACE(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
|
||||
{
|
||||
std::optional<Core::Crypto::Key128> key = {};
|
||||
if (has_rights_id) {
|
||||
|
||||
@@ -87,6 +87,10 @@ u64 NACP::GetDefaultJournalSaveSize() const {
|
||||
return raw.user_account_save_data_journal_size;
|
||||
}
|
||||
|
||||
bool NACP::GetUserAccountSwitchLock() const {
|
||||
return raw.user_account_switch_lock != 0;
|
||||
}
|
||||
|
||||
u32 NACP::GetSupportedLanguages() const {
|
||||
return raw.supported_languages;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,8 @@ struct RawNACP {
|
||||
std::array<LanguageEntry, 16> language_entries;
|
||||
std::array<u8, 0x25> isbn;
|
||||
u8 startup_user_account;
|
||||
INSERT_PADDING_BYTES(2);
|
||||
u8 user_account_switch_lock;
|
||||
u8 addon_content_registration_type;
|
||||
u32_le application_attribute;
|
||||
u32_le supported_languages;
|
||||
u32_le parental_control;
|
||||
@@ -111,6 +112,7 @@ public:
|
||||
u64 GetDefaultJournalSaveSize() const;
|
||||
u32 GetSupportedLanguages() const;
|
||||
std::vector<u8> GetRawBytes() const;
|
||||
bool GetUserAccountSwitchLock() const;
|
||||
|
||||
private:
|
||||
RawNACP raw{};
|
||||
|
||||
@@ -287,7 +287,6 @@ void IPSwitchCompiler::Parse() {
|
||||
} else {
|
||||
// hex replacement
|
||||
const auto value = patch_line.substr(9);
|
||||
replace.reserve(value.size() / 2);
|
||||
replace = Common::HexStringToVector(value, is_little_endian);
|
||||
}
|
||||
|
||||
@@ -295,7 +294,7 @@ void IPSwitchCompiler::Parse() {
|
||||
LOG_INFO(Loader,
|
||||
"[IPSwitchCompiler ('{}')] - Patching value at offset 0x{:08X} "
|
||||
"with byte string '{}'",
|
||||
patch_text->GetName(), offset, Common::HexVectorToString(replace));
|
||||
patch_text->GetName(), offset, Common::HexToString(replace));
|
||||
}
|
||||
|
||||
patch.records.insert_or_assign(offset, std::move(replace));
|
||||
|
||||
228
src/core/file_sys/kernel_executable.cpp
Normal file
228
src/core/file_sys/kernel_executable.cpp
Normal file
@@ -0,0 +1,228 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/string_util.h"
|
||||
#include "core/file_sys/kernel_executable.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
constexpr u32 INI_MAX_KIPS = 0x50;
|
||||
|
||||
namespace {
|
||||
bool DecompressBLZ(std::vector<u8>& data) {
|
||||
if (data.size() < 0xC)
|
||||
return {};
|
||||
|
||||
const auto data_size = data.size() - 0xC;
|
||||
|
||||
u32 compressed_size{};
|
||||
u32 init_index{};
|
||||
u32 additional_size{};
|
||||
std::memcpy(&compressed_size, data.data() + data_size, sizeof(u32));
|
||||
std::memcpy(&init_index, data.data() + data_size + 0x4, sizeof(u32));
|
||||
std::memcpy(&additional_size, data.data() + data_size + 0x8, sizeof(u32));
|
||||
|
||||
const auto start_offset = data.size() - compressed_size;
|
||||
data.resize(compressed_size + additional_size + start_offset);
|
||||
|
||||
std::size_t index = compressed_size - init_index;
|
||||
std::size_t out_index = compressed_size + additional_size;
|
||||
|
||||
while (out_index > 0) {
|
||||
--index;
|
||||
auto control = data[index + start_offset];
|
||||
for (size_t i = 0; i < 8; ++i) {
|
||||
if (((control << i) & 0x80) > 0) {
|
||||
if (index < 2) {
|
||||
return false;
|
||||
}
|
||||
index -= 2;
|
||||
std::size_t segment_offset =
|
||||
data[index + start_offset] | data[index + start_offset + 1] << 8;
|
||||
std::size_t segment_size = ((segment_offset >> 12) & 0xF) + 3;
|
||||
segment_offset &= 0xFFF;
|
||||
segment_offset += 3;
|
||||
|
||||
if (out_index < segment_size)
|
||||
segment_size = out_index;
|
||||
|
||||
if (out_index < segment_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
out_index -= segment_size;
|
||||
|
||||
for (size_t j = 0; j < segment_size; ++j) {
|
||||
if (out_index + j + segment_offset + start_offset >= data.size()) {
|
||||
return false;
|
||||
}
|
||||
data[out_index + j + start_offset] =
|
||||
data[out_index + j + segment_offset + start_offset];
|
||||
}
|
||||
} else {
|
||||
if (out_index < 1) {
|
||||
return false;
|
||||
}
|
||||
--out_index;
|
||||
--index;
|
||||
data[out_index + start_offset] = data[index + start_offset];
|
||||
}
|
||||
|
||||
if (out_index == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
KIP::KIP(const VirtualFile& file) : status(Loader::ResultStatus::Success) {
|
||||
if (file == nullptr) {
|
||||
status = Loader::ResultStatus::ErrorNullFile;
|
||||
return;
|
||||
}
|
||||
|
||||
if (file->GetSize() < sizeof(KIPHeader) || file->ReadObject(&header) != sizeof(KIPHeader)) {
|
||||
status = Loader::ResultStatus::ErrorBadKIPHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
if (header.magic != Common::MakeMagic('K', 'I', 'P', '1')) {
|
||||
status = Loader::ResultStatus::ErrorBadKIPHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
u64 offset = sizeof(KIPHeader);
|
||||
for (std::size_t i = 0; i < header.sections.size(); ++i) {
|
||||
auto compressed = file->ReadBytes(header.sections[i].compressed_size, offset);
|
||||
offset += header.sections[i].compressed_size;
|
||||
|
||||
if (header.sections[i].compressed_size == 0 && header.sections[i].decompressed_size != 0) {
|
||||
decompressed_sections[i] = std::vector<u8>(header.sections[i].decompressed_size);
|
||||
} else if (header.sections[i].compressed_size == header.sections[i].decompressed_size) {
|
||||
decompressed_sections[i] = std::move(compressed);
|
||||
} else {
|
||||
decompressed_sections[i] = compressed;
|
||||
if (!DecompressBLZ(decompressed_sections[i])) {
|
||||
status = Loader::ResultStatus::ErrorBLZDecompressionFailed;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader::ResultStatus KIP::GetStatus() const {
|
||||
return status;
|
||||
}
|
||||
|
||||
std::string KIP::GetName() const {
|
||||
return Common::StringFromFixedZeroTerminatedBuffer(header.name.data(), header.name.size());
|
||||
}
|
||||
|
||||
u64 KIP::GetTitleID() const {
|
||||
return header.title_id;
|
||||
}
|
||||
|
||||
std::vector<u8> KIP::GetSectionDecompressed(u8 index) const {
|
||||
return decompressed_sections[index];
|
||||
}
|
||||
|
||||
bool KIP::Is64Bit() const {
|
||||
return (header.flags & 0x8) != 0;
|
||||
}
|
||||
|
||||
bool KIP::Is39BitAddressSpace() const {
|
||||
return (header.flags & 0x10) != 0;
|
||||
}
|
||||
|
||||
bool KIP::IsService() const {
|
||||
return (header.flags & 0x20) != 0;
|
||||
}
|
||||
|
||||
std::vector<u32> KIP::GetKernelCapabilities() const {
|
||||
return std::vector<u32>(header.capabilities.begin(), header.capabilities.end());
|
||||
}
|
||||
|
||||
s32 KIP::GetMainThreadPriority() const {
|
||||
return header.main_thread_priority;
|
||||
}
|
||||
|
||||
u32 KIP::GetMainThreadStackSize() const {
|
||||
return header.sections[1].attribute;
|
||||
}
|
||||
|
||||
u32 KIP::GetMainThreadCpuCore() const {
|
||||
return header.default_core;
|
||||
}
|
||||
|
||||
const std::vector<u8>& KIP::GetTextSection() const {
|
||||
return decompressed_sections[0];
|
||||
}
|
||||
|
||||
const std::vector<u8>& KIP::GetRODataSection() const {
|
||||
return decompressed_sections[1];
|
||||
}
|
||||
|
||||
const std::vector<u8>& KIP::GetDataSection() const {
|
||||
return decompressed_sections[2];
|
||||
}
|
||||
|
||||
u32 KIP::GetTextOffset() const {
|
||||
return header.sections[0].offset;
|
||||
}
|
||||
|
||||
u32 KIP::GetRODataOffset() const {
|
||||
return header.sections[1].offset;
|
||||
}
|
||||
|
||||
u32 KIP::GetDataOffset() const {
|
||||
return header.sections[2].offset;
|
||||
}
|
||||
|
||||
u32 KIP::GetBSSSize() const {
|
||||
return header.sections[3].decompressed_size;
|
||||
}
|
||||
|
||||
u32 KIP::GetBSSOffset() const {
|
||||
return header.sections[3].offset;
|
||||
}
|
||||
|
||||
INI::INI(const VirtualFile& file) : status(Loader::ResultStatus::Success) {
|
||||
if (file->GetSize() < sizeof(INIHeader) || file->ReadObject(&header) != sizeof(INIHeader)) {
|
||||
status = Loader::ResultStatus::ErrorBadINIHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
if (header.magic != Common::MakeMagic('I', 'N', 'I', '1')) {
|
||||
status = Loader::ResultStatus::ErrorBadINIHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
if (header.kip_count > INI_MAX_KIPS) {
|
||||
status = Loader::ResultStatus::ErrorINITooManyKIPs;
|
||||
return;
|
||||
}
|
||||
|
||||
u64 offset = sizeof(INIHeader);
|
||||
for (std::size_t i = 0; i < header.kip_count; ++i) {
|
||||
const auto kip_file =
|
||||
std::make_shared<OffsetVfsFile>(file, file->GetSize() - offset, offset);
|
||||
KIP kip(kip_file);
|
||||
if (kip.GetStatus() == Loader::ResultStatus::Success) {
|
||||
kips.push_back(std::move(kip));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader::ResultStatus INI::GetStatus() const {
|
||||
return status;
|
||||
}
|
||||
|
||||
const std::vector<KIP>& INI::GetKIPs() const {
|
||||
return kips;
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
99
src/core/file_sys/kernel_executable.h
Normal file
99
src/core/file_sys/kernel_executable.h
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
struct KIPSectionHeader {
|
||||
u32_le offset;
|
||||
u32_le decompressed_size;
|
||||
u32_le compressed_size;
|
||||
u32_le attribute;
|
||||
};
|
||||
static_assert(sizeof(KIPSectionHeader) == 0x10, "KIPSectionHeader has incorrect size.");
|
||||
|
||||
struct KIPHeader {
|
||||
u32_le magic;
|
||||
std::array<char, 0xC> name;
|
||||
u64_le title_id;
|
||||
u32_le process_category;
|
||||
u8 main_thread_priority;
|
||||
u8 default_core;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u8 flags;
|
||||
std::array<KIPSectionHeader, 6> sections;
|
||||
std::array<u32, 0x20> capabilities;
|
||||
};
|
||||
static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size.");
|
||||
|
||||
struct INIHeader {
|
||||
u32_le magic;
|
||||
u32_le size;
|
||||
u32_le kip_count;
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
};
|
||||
static_assert(sizeof(INIHeader) == 0x10, "INIHeader has incorrect size.");
|
||||
|
||||
// Kernel Internal Process
|
||||
class KIP {
|
||||
public:
|
||||
explicit KIP(const VirtualFile& file);
|
||||
|
||||
Loader::ResultStatus GetStatus() const;
|
||||
|
||||
std::string GetName() const;
|
||||
u64 GetTitleID() const;
|
||||
std::vector<u8> GetSectionDecompressed(u8 index) const;
|
||||
|
||||
// Executable Flags
|
||||
bool Is64Bit() const;
|
||||
bool Is39BitAddressSpace() const;
|
||||
bool IsService() const;
|
||||
|
||||
std::vector<u32> GetKernelCapabilities() const;
|
||||
|
||||
s32 GetMainThreadPriority() const;
|
||||
u32 GetMainThreadStackSize() const;
|
||||
u32 GetMainThreadCpuCore() const;
|
||||
|
||||
const std::vector<u8>& GetTextSection() const;
|
||||
const std::vector<u8>& GetRODataSection() const;
|
||||
const std::vector<u8>& GetDataSection() const;
|
||||
|
||||
u32 GetTextOffset() const;
|
||||
u32 GetRODataOffset() const;
|
||||
u32 GetDataOffset() const;
|
||||
|
||||
u32 GetBSSSize() const;
|
||||
u32 GetBSSOffset() const;
|
||||
|
||||
private:
|
||||
Loader::ResultStatus status;
|
||||
|
||||
KIPHeader header{};
|
||||
std::array<std::vector<u8>, 6> decompressed_sections;
|
||||
};
|
||||
|
||||
class INI {
|
||||
public:
|
||||
explicit INI(const VirtualFile& file);
|
||||
|
||||
Loader::ResultStatus GetStatus() const;
|
||||
|
||||
const std::vector<KIP>& GetKIPs() const;
|
||||
|
||||
private:
|
||||
Loader::ResultStatus status;
|
||||
|
||||
INIHeader header{};
|
||||
std::vector<KIP> kips;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "common/common_funcs.h"
|
||||
@@ -34,9 +35,9 @@ enum class ContentRecordType : u8 {
|
||||
Program = 1,
|
||||
Data = 2,
|
||||
Control = 3,
|
||||
Manual = 4,
|
||||
Legal = 5,
|
||||
Patch = 6,
|
||||
HtmlDocument = 4,
|
||||
LegalInformation = 5,
|
||||
DeltaFragment = 6,
|
||||
};
|
||||
|
||||
struct ContentRecord {
|
||||
@@ -69,11 +70,15 @@ struct CNMTHeader {
|
||||
u64_le title_id;
|
||||
u32_le title_version;
|
||||
TitleType type;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u8 reserved;
|
||||
u16_le table_offset;
|
||||
u16_le number_content_entries;
|
||||
u16_le number_meta_entries;
|
||||
INSERT_PADDING_BYTES(12);
|
||||
u8 attributes;
|
||||
std::array<u8, 2> reserved2;
|
||||
u8 is_committed;
|
||||
u32_le required_download_system_version;
|
||||
std::array<u8, 4> reserved3;
|
||||
};
|
||||
static_assert(sizeof(CNMTHeader) == 0x20, "CNMTHeader has incorrect size.");
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
|
||||
if (!compiler.IsValid())
|
||||
continue;
|
||||
|
||||
auto this_build_id = Common::HexArrayToString(compiler.GetBuildID());
|
||||
auto this_build_id = Common::HexToString(compiler.GetBuildID());
|
||||
this_build_id =
|
||||
this_build_id.substr(0, this_build_id.find_last_not_of('0') + 1);
|
||||
|
||||
@@ -168,7 +168,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
|
||||
return nso;
|
||||
}
|
||||
|
||||
const auto build_id_raw = Common::HexArrayToString(header.build_id);
|
||||
const auto build_id_raw = Common::HexToString(header.build_id);
|
||||
const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
|
||||
|
||||
if (Settings::values.dump_nso) {
|
||||
@@ -219,7 +219,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
|
||||
}
|
||||
|
||||
bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
|
||||
const auto build_id_raw = Common::HexArrayToString(build_id_);
|
||||
const auto build_id_raw = Common::HexToString(build_id_);
|
||||
const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
|
||||
|
||||
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id);
|
||||
@@ -235,7 +235,7 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
|
||||
static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& system, u64 title_id,
|
||||
const std::array<u8, 0x20>& build_id_,
|
||||
const VirtualDir& base_path, bool upper) {
|
||||
const auto build_id_raw = Common::HexArrayToString(build_id_, upper);
|
||||
const auto build_id_raw = Common::HexToString(build_id_, upper);
|
||||
const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
|
||||
const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
|
||||
|
||||
@@ -493,6 +493,16 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
|
||||
return out;
|
||||
}
|
||||
|
||||
std::optional<u32> PatchManager::GetGameVersion() const {
|
||||
const auto& installed = Core::System::GetInstance().GetContentProvider();
|
||||
const auto update_tid = GetUpdateTitleID(title_id);
|
||||
if (installed.HasEntry(update_tid, ContentRecordType::Program)) {
|
||||
return installed.GetEntryVersion(update_tid);
|
||||
}
|
||||
|
||||
return installed.GetEntryVersion(title_id);
|
||||
}
|
||||
|
||||
std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
|
||||
const auto& installed = Core::System::GetInstance().GetContentProvider();
|
||||
|
||||
|
||||
@@ -66,8 +66,13 @@ public:
|
||||
std::map<std::string, std::string, std::less<>> GetPatchVersionNames(
|
||||
VirtualFile update_raw = nullptr) const;
|
||||
|
||||
// Given title_id of the program, attempts to get the control data of the update and parse it,
|
||||
// falling back to the base control data.
|
||||
// If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails,
|
||||
// it will fallback to the Meta-type NCA of the base game. If that fails, the result will be
|
||||
// std::nullopt
|
||||
std::optional<u32> GetGameVersion() const;
|
||||
|
||||
// Given title_id of the program, attempts to get the control data of the update and parse
|
||||
// it, falling back to the base control data.
|
||||
std::pair<std::unique_ptr<NACP>, VirtualFile> GetControlMetadata() const;
|
||||
|
||||
// Version of GetControlMetadata that takes an arbitrary NCA
|
||||
|
||||
@@ -51,6 +51,21 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
|
||||
return Loader::ResultStatus::Success;
|
||||
}
|
||||
|
||||
void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space,
|
||||
u8 main_thread_prio, u8 main_thread_core,
|
||||
u32 main_thread_stack_size, u64 title_id,
|
||||
u64 filesystem_permissions,
|
||||
KernelCapabilityDescriptors capabilities) {
|
||||
npdm_header.has_64_bit_instructions.Assign(is_64_bit);
|
||||
npdm_header.address_space_type.Assign(address_space);
|
||||
npdm_header.main_thread_priority = main_thread_prio;
|
||||
npdm_header.main_thread_cpu = main_thread_core;
|
||||
npdm_header.main_stack_size = main_thread_stack_size;
|
||||
aci_header.title_id = title_id;
|
||||
aci_file_access.permissions = filesystem_permissions;
|
||||
aci_kernel_capabilities = std ::move(capabilities);
|
||||
}
|
||||
|
||||
bool ProgramMetadata::Is64BitProgram() const {
|
||||
return npdm_header.has_64_bit_instructions;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,11 @@ public:
|
||||
|
||||
Loader::ResultStatus Load(VirtualFile file);
|
||||
|
||||
// Load from parameters instead of NPDM file, used for KIP
|
||||
void LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space, u8 main_thread_prio,
|
||||
u8 main_thread_core, u32 main_thread_stack_size, u64 title_id,
|
||||
u64 filesystem_permissions, KernelCapabilityDescriptors capabilities);
|
||||
|
||||
bool Is64BitProgram() const;
|
||||
ProgramAddressSpaceType GetAddressSpaceType() const;
|
||||
u8 GetMainThreadPriority() const;
|
||||
|
||||
@@ -53,13 +53,14 @@ static bool FollowsNcaIdFormat(std::string_view name) {
|
||||
|
||||
static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper,
|
||||
bool within_two_digit) {
|
||||
if (!within_two_digit)
|
||||
return fmt::format("/{}.nca", Common::HexArrayToString(nca_id, second_hex_upper));
|
||||
if (!within_two_digit) {
|
||||
return fmt::format("/{}.nca", Common::HexToString(nca_id, second_hex_upper));
|
||||
}
|
||||
|
||||
Core::Crypto::SHA256Hash hash{};
|
||||
mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
|
||||
return fmt::format("/000000{:02X}/{}.nca", hash[0],
|
||||
Common::HexArrayToString(nca_id, second_hex_upper));
|
||||
Common::HexToString(nca_id, second_hex_upper));
|
||||
}
|
||||
|
||||
static std::string GetCNMTName(TitleType type, u64 title_id) {
|
||||
@@ -98,7 +99,7 @@ ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
|
||||
return ContentRecordType::Data;
|
||||
case NCAContentType::Manual:
|
||||
// TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal.
|
||||
return ContentRecordType::Manual;
|
||||
return ContentRecordType::HtmlDocument;
|
||||
default:
|
||||
UNREACHABLE_MSG("Invalid NCAContentType={:02X}", static_cast<u8>(type));
|
||||
}
|
||||
@@ -376,10 +377,11 @@ std::vector<ContentProviderEntry> RegisteredCache::ListEntriesFilter(
|
||||
}
|
||||
|
||||
static std::shared_ptr<NCA> GetNCAFromNSPForID(const NSP& nsp, const NcaID& id) {
|
||||
const auto file = nsp.GetFile(fmt::format("{}.nca", Common::HexArrayToString(id, false)));
|
||||
if (file == nullptr)
|
||||
auto file = nsp.GetFile(fmt::format("{}.nca", Common::HexToString(id, false)));
|
||||
if (file == nullptr) {
|
||||
return nullptr;
|
||||
return std::make_shared<NCA>(file);
|
||||
}
|
||||
return std::make_shared<NCA>(std::move(file));
|
||||
}
|
||||
|
||||
InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_exists,
|
||||
@@ -395,8 +397,8 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
|
||||
});
|
||||
|
||||
if (meta_iter == ncas.end()) {
|
||||
LOG_ERROR(Loader, "The XCI you are attempting to install does not have a metadata NCA and "
|
||||
"is therefore malformed. Double check your encryption keys.");
|
||||
LOG_ERROR(Loader, "The file you are attempting to install does not have a metadata NCA and "
|
||||
"is therefore malformed. Check your encryption keys.");
|
||||
return InstallResult::ErrorMetaFailed;
|
||||
}
|
||||
|
||||
@@ -413,6 +415,9 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
|
||||
const auto cnmt_file = section0->GetFiles()[0];
|
||||
const CNMT cnmt(cnmt_file);
|
||||
for (const auto& record : cnmt.GetContentRecords()) {
|
||||
// Ignore DeltaFragments, they are not useful to us
|
||||
if (record.type == ContentRecordType::DeltaFragment)
|
||||
continue;
|
||||
const auto nca = GetNCAFromNSPForID(nsp, record.nca_id);
|
||||
if (nca == nullptr)
|
||||
return InstallResult::ErrorCopyFailed;
|
||||
@@ -643,6 +648,20 @@ ContentProviderUnion::ListEntriesFilterOrigin(std::optional<ContentProviderUnion
|
||||
return out;
|
||||
}
|
||||
|
||||
std::optional<ContentProviderUnionSlot> ContentProviderUnion::GetSlotForEntry(
|
||||
u64 title_id, ContentRecordType type) const {
|
||||
const auto iter =
|
||||
std::find_if(providers.begin(), providers.end(), [title_id, type](const auto& provider) {
|
||||
return provider.second != nullptr && provider.second->HasEntry(title_id, type);
|
||||
});
|
||||
|
||||
if (iter == providers.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return iter->first;
|
||||
}
|
||||
|
||||
ManualContentProvider::~ManualContentProvider() = default;
|
||||
|
||||
void ManualContentProvider::AddEntry(TitleType title_type, ContentRecordType content_type,
|
||||
|
||||
@@ -199,6 +199,9 @@ public:
|
||||
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
|
||||
std::optional<u64> title_id = {}) const;
|
||||
|
||||
std::optional<ContentProviderUnionSlot> GetSlotForEntry(u64 title_id,
|
||||
ContentRecordType type) const;
|
||||
|
||||
private:
|
||||
std::map<ContentProviderUnionSlot, ContentProvider*> providers;
|
||||
};
|
||||
|
||||
@@ -235,27 +235,33 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
|
||||
const auto section0 = nca->GetSubdirectories()[0];
|
||||
|
||||
for (const auto& inner_file : section0->GetFiles()) {
|
||||
if (inner_file->GetExtension() != "cnmt")
|
||||
if (inner_file->GetExtension() != "cnmt") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const CNMT cnmt(inner_file);
|
||||
auto& ncas_title = ncas[cnmt.GetTitleID()];
|
||||
|
||||
ncas_title[{cnmt.GetType(), ContentRecordType::Meta}] = nca;
|
||||
for (const auto& rec : cnmt.GetContentRecords()) {
|
||||
const auto id_string = Common::HexArrayToString(rec.nca_id, false);
|
||||
const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
|
||||
const auto id_string = Common::HexToString(rec.nca_id, false);
|
||||
auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
|
||||
|
||||
if (next_file == nullptr) {
|
||||
LOG_WARNING(Service_FS,
|
||||
"NCA with ID {}.nca is listed in content metadata, but cannot "
|
||||
"be found in PFS. NSP appears to be corrupted.",
|
||||
id_string);
|
||||
if (rec.type != ContentRecordType::DeltaFragment) {
|
||||
LOG_WARNING(Service_FS,
|
||||
"NCA with ID {}.nca is listed in content metadata, but cannot "
|
||||
"be found in PFS. NSP appears to be corrupted.",
|
||||
id_string);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
auto next_nca = std::make_shared<NCA>(next_file, nullptr, 0, keys);
|
||||
if (next_nca->GetType() == NCAContentType::Program)
|
||||
auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0, keys);
|
||||
if (next_nca->GetType() == NCAContentType::Program) {
|
||||
program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
|
||||
}
|
||||
if (next_nca->GetStatus() == Loader::ResultStatus::Success ||
|
||||
(next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
|
||||
(cnmt.GetTitleID() & 0x800) != 0)) {
|
||||
|
||||
@@ -66,7 +66,7 @@ NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id)
|
||||
Core::Crypto::SHA256Hash hash{};
|
||||
mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
|
||||
status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0],
|
||||
Common::HexArrayToString(nca_id, false)));
|
||||
Common::HexToString(nca_id, false)));
|
||||
}
|
||||
|
||||
NAX::~NAX() = default;
|
||||
|
||||
@@ -7,9 +7,38 @@
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
ParentalControlsApplet::~ParentalControlsApplet() = default;
|
||||
|
||||
DefaultParentalControlsApplet::~DefaultParentalControlsApplet() = default;
|
||||
|
||||
void DefaultParentalControlsApplet::VerifyPIN(std::function<void(bool)> finished,
|
||||
bool suspend_future_verification_temporarily) {
|
||||
LOG_INFO(Service_AM,
|
||||
"Application requested frontend to verify PIN (normal), "
|
||||
"suspend_future_verification_temporarily={}, verifying as correct.",
|
||||
suspend_future_verification_temporarily);
|
||||
finished(true);
|
||||
}
|
||||
|
||||
void DefaultParentalControlsApplet::VerifyPINForSettings(std::function<void(bool)> finished) {
|
||||
LOG_INFO(Service_AM,
|
||||
"Application requested frontend to verify PIN (settings), verifying as correct.");
|
||||
finished(true);
|
||||
}
|
||||
|
||||
void DefaultParentalControlsApplet::RegisterPIN(std::function<void()> finished) {
|
||||
LOG_INFO(Service_AM, "Application requested frontend to register new PIN");
|
||||
finished();
|
||||
}
|
||||
|
||||
void DefaultParentalControlsApplet::ChangePIN(std::function<void()> finished) {
|
||||
LOG_INFO(Service_AM, "Application requested frontend to change PIN to new value");
|
||||
finished();
|
||||
}
|
||||
|
||||
PhotoViewerApplet::~PhotoViewerApplet() = default;
|
||||
|
||||
DefaultPhotoViewerApplet::~DefaultPhotoViewerApplet() {}
|
||||
DefaultPhotoViewerApplet::~DefaultPhotoViewerApplet() = default;
|
||||
|
||||
void DefaultPhotoViewerApplet::ShowPhotosForApplication(u64 title_id,
|
||||
std::function<void()> finished) const {
|
||||
@@ -24,4 +53,72 @@ void DefaultPhotoViewerApplet::ShowAllPhotos(std::function<void()> finished) con
|
||||
finished();
|
||||
}
|
||||
|
||||
ECommerceApplet::~ECommerceApplet() = default;
|
||||
|
||||
DefaultECommerceApplet::~DefaultECommerceApplet() = default;
|
||||
|
||||
void DefaultECommerceApplet::ShowApplicationInformation(
|
||||
std::function<void()> finished, u64 title_id, std::optional<u128> user_id,
|
||||
std::optional<bool> full_display, std::optional<std::string> extra_parameter) {
|
||||
const auto value = user_id.value_or(u128{});
|
||||
LOG_INFO(Service_AM,
|
||||
"Application requested frontend show application information for EShop, "
|
||||
"title_id={:016X}, user_id={:016X}{:016X}, full_display={}, extra_parameter={}",
|
||||
title_id, value[1], value[0],
|
||||
full_display.has_value() ? fmt::format("{}", *full_display) : "null",
|
||||
extra_parameter.value_or("null"));
|
||||
finished();
|
||||
}
|
||||
|
||||
void DefaultECommerceApplet::ShowAddOnContentList(std::function<void()> finished, u64 title_id,
|
||||
std::optional<u128> user_id,
|
||||
std::optional<bool> full_display) {
|
||||
const auto value = user_id.value_or(u128{});
|
||||
LOG_INFO(Service_AM,
|
||||
"Application requested frontend show add on content list for EShop, "
|
||||
"title_id={:016X}, user_id={:016X}{:016X}, full_display={}",
|
||||
title_id, value[1], value[0],
|
||||
full_display.has_value() ? fmt::format("{}", *full_display) : "null");
|
||||
finished();
|
||||
}
|
||||
|
||||
void DefaultECommerceApplet::ShowSubscriptionList(std::function<void()> finished, u64 title_id,
|
||||
std::optional<u128> user_id) {
|
||||
const auto value = user_id.value_or(u128{});
|
||||
LOG_INFO(Service_AM,
|
||||
"Application requested frontend show subscription list for EShop, title_id={:016X}, "
|
||||
"user_id={:016X}{:016X}",
|
||||
title_id, value[1], value[0]);
|
||||
finished();
|
||||
}
|
||||
|
||||
void DefaultECommerceApplet::ShowConsumableItemList(std::function<void()> finished, u64 title_id,
|
||||
std::optional<u128> user_id) {
|
||||
const auto value = user_id.value_or(u128{});
|
||||
LOG_INFO(
|
||||
Service_AM,
|
||||
"Application requested frontend show consumable item list for EShop, title_id={:016X}, "
|
||||
"user_id={:016X}{:016X}",
|
||||
title_id, value[1], value[0]);
|
||||
finished();
|
||||
}
|
||||
|
||||
void DefaultECommerceApplet::ShowShopHome(std::function<void()> finished, u128 user_id,
|
||||
bool full_display) {
|
||||
LOG_INFO(Service_AM,
|
||||
"Application requested frontend show home menu for EShop, user_id={:016X}{:016X}, "
|
||||
"full_display={}",
|
||||
user_id[1], user_id[0], full_display);
|
||||
finished();
|
||||
}
|
||||
|
||||
void DefaultECommerceApplet::ShowSettings(std::function<void()> finished, u128 user_id,
|
||||
bool full_display) {
|
||||
LOG_INFO(Service_AM,
|
||||
"Application requested frontend show settings menu for EShop, user_id={:016X}{:016X}, "
|
||||
"full_display={}",
|
||||
user_id[1], user_id[0], full_display);
|
||||
finished();
|
||||
}
|
||||
|
||||
} // namespace Core::Frontend
|
||||
|
||||
@@ -5,10 +5,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
class ParentalControlsApplet {
|
||||
public:
|
||||
virtual ~ParentalControlsApplet();
|
||||
|
||||
// Prompts the user to enter a PIN and calls the callback with whether or not it matches the
|
||||
// correct PIN. If the bool is passed, and the PIN was recently entered correctly, the frontend
|
||||
// should not prompt and simply return true.
|
||||
virtual void VerifyPIN(std::function<void(bool)> finished,
|
||||
bool suspend_future_verification_temporarily) = 0;
|
||||
|
||||
// Prompts the user to enter a PIN and calls the callback for correctness. Frontends can
|
||||
// optionally alert the user that this is to change parental controls settings.
|
||||
virtual void VerifyPINForSettings(std::function<void(bool)> finished) = 0;
|
||||
|
||||
// Prompts the user to create a new PIN for pctl and stores it with the service.
|
||||
virtual void RegisterPIN(std::function<void()> finished) = 0;
|
||||
|
||||
// Prompts the user to verify the current PIN and then store a new one into pctl.
|
||||
virtual void ChangePIN(std::function<void()> finished) = 0;
|
||||
};
|
||||
|
||||
class DefaultParentalControlsApplet final : public ParentalControlsApplet {
|
||||
public:
|
||||
~DefaultParentalControlsApplet() override;
|
||||
|
||||
void VerifyPIN(std::function<void(bool)> finished,
|
||||
bool suspend_future_verification_temporarily) override;
|
||||
void VerifyPINForSettings(std::function<void(bool)> finished) override;
|
||||
void RegisterPIN(std::function<void()> finished) override;
|
||||
void ChangePIN(std::function<void()> finished) override;
|
||||
};
|
||||
|
||||
class PhotoViewerApplet {
|
||||
public:
|
||||
virtual ~PhotoViewerApplet();
|
||||
@@ -25,4 +58,55 @@ public:
|
||||
void ShowAllPhotos(std::function<void()> finished) const override;
|
||||
};
|
||||
|
||||
class ECommerceApplet {
|
||||
public:
|
||||
virtual ~ECommerceApplet();
|
||||
|
||||
// Shows a page with application icons, description, name, and price.
|
||||
virtual void ShowApplicationInformation(std::function<void()> finished, u64 title_id,
|
||||
std::optional<u128> user_id = {},
|
||||
std::optional<bool> full_display = {},
|
||||
std::optional<std::string> extra_parameter = {}) = 0;
|
||||
|
||||
// Shows a page with all of the add on content available for a game, with name, description, and
|
||||
// price.
|
||||
virtual void ShowAddOnContentList(std::function<void()> finished, u64 title_id,
|
||||
std::optional<u128> user_id = {},
|
||||
std::optional<bool> full_display = {}) = 0;
|
||||
|
||||
// Shows a page with all of the subscriptions (recurring payments) for a game, with name,
|
||||
// description, price, and renewal period.
|
||||
virtual void ShowSubscriptionList(std::function<void()> finished, u64 title_id,
|
||||
std::optional<u128> user_id = {}) = 0;
|
||||
|
||||
// Shows a page with a list of any additional game related purchasable items (DLC,
|
||||
// subscriptions, etc) for a particular game, with name, description, type, and price.
|
||||
virtual void ShowConsumableItemList(std::function<void()> finished, u64 title_id,
|
||||
std::optional<u128> user_id = {}) = 0;
|
||||
|
||||
// Shows the home page of the shop.
|
||||
virtual void ShowShopHome(std::function<void()> finished, u128 user_id, bool full_display) = 0;
|
||||
|
||||
// Shows the user settings page of the shop.
|
||||
virtual void ShowSettings(std::function<void()> finished, u128 user_id, bool full_display) = 0;
|
||||
};
|
||||
|
||||
class DefaultECommerceApplet : public ECommerceApplet {
|
||||
public:
|
||||
~DefaultECommerceApplet() override;
|
||||
|
||||
void ShowApplicationInformation(std::function<void()> finished, u64 title_id,
|
||||
std::optional<u128> user_id, std::optional<bool> full_display,
|
||||
std::optional<std::string> extra_parameter) override;
|
||||
void ShowAddOnContentList(std::function<void()> finished, u64 title_id,
|
||||
std::optional<u128> user_id,
|
||||
std::optional<bool> full_display) override;
|
||||
void ShowSubscriptionList(std::function<void()> finished, u64 title_id,
|
||||
std::optional<u128> user_id) override;
|
||||
void ShowConsumableItemList(std::function<void()> finished, u64 title_id,
|
||||
std::optional<u128> user_id) override;
|
||||
void ShowShopHome(std::function<void()> finished, u128 user_id, bool full_display) override;
|
||||
void ShowSettings(std::function<void()> finished, u128 user_id, bool full_display) override;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
||||
|
||||
@@ -11,9 +11,9 @@ WebBrowserApplet::~WebBrowserApplet() = default;
|
||||
|
||||
DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default;
|
||||
|
||||
void DefaultWebBrowserApplet::OpenPage(std::string_view filename,
|
||||
std::function<void()> unpack_romfs_callback,
|
||||
std::function<void()> finished_callback) {
|
||||
void DefaultWebBrowserApplet::OpenPageLocal(std::string_view filename,
|
||||
std::function<void()> unpack_romfs_callback,
|
||||
std::function<void()> finished_callback) {
|
||||
LOG_INFO(Service_AM,
|
||||
"(STUBBED) called - No suitable web browser implementation found to open website page "
|
||||
"at '{}'!",
|
||||
|
||||
@@ -13,16 +13,16 @@ class WebBrowserApplet {
|
||||
public:
|
||||
virtual ~WebBrowserApplet();
|
||||
|
||||
virtual void OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback,
|
||||
std::function<void()> finished_callback) = 0;
|
||||
virtual void OpenPageLocal(std::string_view url, std::function<void()> unpack_romfs_callback,
|
||||
std::function<void()> finished_callback) = 0;
|
||||
};
|
||||
|
||||
class DefaultWebBrowserApplet final : public WebBrowserApplet {
|
||||
public:
|
||||
~DefaultWebBrowserApplet() override;
|
||||
|
||||
void OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback,
|
||||
std::function<void()> finished_callback) override;
|
||||
void OpenPageLocal(std::string_view url, std::function<void()> unpack_romfs_callback,
|
||||
std::function<void()> finished_callback) override;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
||||
|
||||
@@ -99,7 +99,8 @@ struct KernelCore::Impl {
|
||||
|
||||
void Shutdown() {
|
||||
next_object_id = 0;
|
||||
next_process_id = Process::ProcessIDMin;
|
||||
next_kernel_process_id = Process::InitialKIPIDMin;
|
||||
next_user_process_id = Process::ProcessIDMin;
|
||||
next_thread_id = 1;
|
||||
|
||||
process_list.clear();
|
||||
@@ -132,7 +133,8 @@ struct KernelCore::Impl {
|
||||
}
|
||||
|
||||
std::atomic<u32> next_object_id{0};
|
||||
std::atomic<u64> next_process_id{Process::ProcessIDMin};
|
||||
std::atomic<u64> next_kernel_process_id{Process::InitialKIPIDMin};
|
||||
std::atomic<u64> next_user_process_id{Process::ProcessIDMin};
|
||||
std::atomic<u64> next_thread_id{1};
|
||||
|
||||
// Lists all processes that exist in the current session.
|
||||
@@ -226,8 +228,12 @@ u64 KernelCore::CreateNewThreadID() {
|
||||
return impl->next_thread_id++;
|
||||
}
|
||||
|
||||
u64 KernelCore::CreateNewProcessID() {
|
||||
return impl->next_process_id++;
|
||||
u64 KernelCore::CreateNewKernelProcessID() {
|
||||
return impl->next_kernel_process_id++;
|
||||
}
|
||||
|
||||
u64 KernelCore::CreateNewUserProcessID() {
|
||||
return impl->next_user_process_id++;
|
||||
}
|
||||
|
||||
Core::Timing::EventType* KernelCore::ThreadWakeupCallbackEventType() const {
|
||||
|
||||
@@ -96,7 +96,10 @@ private:
|
||||
u32 CreateNewObjectID();
|
||||
|
||||
/// Creates a new process ID, incrementing the internal process ID counter;
|
||||
u64 CreateNewProcessID();
|
||||
u64 CreateNewKernelProcessID();
|
||||
|
||||
/// Creates a new process ID, incrementing the internal process ID counter;
|
||||
u64 CreateNewUserProcessID();
|
||||
|
||||
/// Creates a new thread ID, incrementing the internal thread ID counter.
|
||||
u64 CreateNewThreadID();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include "common/alignment.h"
|
||||
@@ -48,7 +49,58 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) {
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
SharedPtr<Process> Process::Create(Core::System& system, std::string name) {
|
||||
// Represents a page used for thread-local storage.
|
||||
//
|
||||
// Each TLS page contains slots that may be used by processes and threads.
|
||||
// Every process and thread is created with a slot in some arbitrary page
|
||||
// (whichever page happens to have an available slot).
|
||||
class TLSPage {
|
||||
public:
|
||||
static constexpr std::size_t num_slot_entries = Memory::PAGE_SIZE / Memory::TLS_ENTRY_SIZE;
|
||||
|
||||
explicit TLSPage(VAddr address) : base_address{address} {}
|
||||
|
||||
bool HasAvailableSlots() const {
|
||||
return !is_slot_used.all();
|
||||
}
|
||||
|
||||
VAddr GetBaseAddress() const {
|
||||
return base_address;
|
||||
}
|
||||
|
||||
std::optional<VAddr> ReserveSlot() {
|
||||
for (std::size_t i = 0; i < is_slot_used.size(); i++) {
|
||||
if (is_slot_used[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
is_slot_used[i] = true;
|
||||
return base_address + (i * Memory::TLS_ENTRY_SIZE);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void ReleaseSlot(VAddr address) {
|
||||
// Ensure that all given addresses are consistent with how TLS pages
|
||||
// are intended to be used when releasing slots.
|
||||
ASSERT(IsWithinPage(address));
|
||||
ASSERT((address % Memory::TLS_ENTRY_SIZE) == 0);
|
||||
|
||||
const std::size_t index = (address - base_address) / Memory::TLS_ENTRY_SIZE;
|
||||
is_slot_used[index] = false;
|
||||
}
|
||||
|
||||
private:
|
||||
bool IsWithinPage(VAddr address) const {
|
||||
return base_address <= address && address < base_address + Memory::PAGE_SIZE;
|
||||
}
|
||||
|
||||
VAddr base_address;
|
||||
std::bitset<num_slot_entries> is_slot_used;
|
||||
};
|
||||
|
||||
SharedPtr<Process> Process::Create(Core::System& system, std::string name, ProcessType type) {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
SharedPtr<Process> process(new Process(system));
|
||||
@@ -56,7 +108,8 @@ SharedPtr<Process> Process::Create(Core::System& system, std::string name) {
|
||||
process->resource_limit = kernel.GetSystemResourceLimit();
|
||||
process->status = ProcessStatus::Created;
|
||||
process->program_id = 0;
|
||||
process->process_id = kernel.CreateNewProcessID();
|
||||
process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID()
|
||||
: kernel.CreateNewUserProcessID();
|
||||
process->capabilities.InitializeForMetadatalessProcess();
|
||||
|
||||
std::mt19937 rng(Settings::values.rng_seed.value_or(0));
|
||||
@@ -179,61 +232,55 @@ void Process::PrepareForTermination() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a free location for the TLS section of a thread.
|
||||
* @param tls_slots The TLS page array of the thread's owner process.
|
||||
* Returns a tuple of (page, slot, alloc_needed) where:
|
||||
* page: The index of the first allocated TLS page that has free slots.
|
||||
* slot: The index of the first free slot in the indicated page.
|
||||
* alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full).
|
||||
* Attempts to find a TLS page that contains a free slot for
|
||||
* use by a thread.
|
||||
*
|
||||
* @returns If a page with an available slot is found, then an iterator
|
||||
* pointing to the page is returned. Otherwise the end iterator
|
||||
* is returned instead.
|
||||
*/
|
||||
static std::tuple<std::size_t, std::size_t, bool> FindFreeThreadLocalSlot(
|
||||
const std::vector<std::bitset<8>>& tls_slots) {
|
||||
// Iterate over all the allocated pages, and try to find one where not all slots are used.
|
||||
for (std::size_t page = 0; page < tls_slots.size(); ++page) {
|
||||
const auto& page_tls_slots = tls_slots[page];
|
||||
if (!page_tls_slots.all()) {
|
||||
// We found a page with at least one free slot, find which slot it is
|
||||
for (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) {
|
||||
if (!page_tls_slots.test(slot)) {
|
||||
return std::make_tuple(page, slot, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_tuple(0, 0, true);
|
||||
static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) {
|
||||
return std::find_if(tls_pages.begin(), tls_pages.end(),
|
||||
[](const auto& page) { return page.HasAvailableSlots(); });
|
||||
}
|
||||
|
||||
VAddr Process::MarkNextAvailableTLSSlotAsUsed(Thread& thread) {
|
||||
auto [available_page, available_slot, needs_allocation] = FindFreeThreadLocalSlot(tls_slots);
|
||||
const VAddr tls_begin = vm_manager.GetTLSIORegionBaseAddress();
|
||||
VAddr Process::CreateTLSRegion() {
|
||||
auto tls_page_iter = FindTLSPageWithAvailableSlots(tls_pages);
|
||||
|
||||
if (needs_allocation) {
|
||||
tls_slots.emplace_back(0); // The page is completely available at the start
|
||||
available_page = tls_slots.size() - 1;
|
||||
available_slot = 0; // Use the first slot in the new page
|
||||
if (tls_page_iter == tls_pages.cend()) {
|
||||
const auto region_address =
|
||||
vm_manager.FindFreeRegion(vm_manager.GetTLSIORegionBaseAddress(),
|
||||
vm_manager.GetTLSIORegionEndAddress(), Memory::PAGE_SIZE);
|
||||
ASSERT(region_address.Succeeded());
|
||||
|
||||
// Allocate some memory from the end of the linear heap for this region.
|
||||
auto& tls_memory = thread.GetTLSMemory();
|
||||
tls_memory->insert(tls_memory->end(), Memory::PAGE_SIZE, 0);
|
||||
const auto map_result = vm_manager.MapMemoryBlock(
|
||||
*region_address, std::make_shared<std::vector<u8>>(Memory::PAGE_SIZE), 0,
|
||||
Memory::PAGE_SIZE, MemoryState::ThreadLocal);
|
||||
ASSERT(map_result.Succeeded());
|
||||
|
||||
vm_manager.RefreshMemoryBlockMappings(tls_memory.get());
|
||||
tls_pages.emplace_back(*region_address);
|
||||
|
||||
vm_manager.MapMemoryBlock(tls_begin + available_page * Memory::PAGE_SIZE, tls_memory, 0,
|
||||
Memory::PAGE_SIZE, MemoryState::ThreadLocal);
|
||||
const auto reserve_result = tls_pages.back().ReserveSlot();
|
||||
ASSERT(reserve_result.has_value());
|
||||
|
||||
return *reserve_result;
|
||||
}
|
||||
|
||||
tls_slots[available_page].set(available_slot);
|
||||
|
||||
return tls_begin + available_page * Memory::PAGE_SIZE + available_slot * Memory::TLS_ENTRY_SIZE;
|
||||
return *tls_page_iter->ReserveSlot();
|
||||
}
|
||||
|
||||
void Process::FreeTLSSlot(VAddr tls_address) {
|
||||
const VAddr tls_base = tls_address - vm_manager.GetTLSIORegionBaseAddress();
|
||||
const VAddr tls_page = tls_base / Memory::PAGE_SIZE;
|
||||
const VAddr tls_slot = (tls_base % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
|
||||
void Process::FreeTLSRegion(VAddr tls_address) {
|
||||
const VAddr aligned_address = Common::AlignDown(tls_address, Memory::PAGE_SIZE);
|
||||
auto iter =
|
||||
std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) {
|
||||
return page.GetBaseAddress() == aligned_address;
|
||||
});
|
||||
|
||||
tls_slots[tls_page].reset(tls_slot);
|
||||
// Something has gone very wrong if we're freeing a region
|
||||
// with no actual page available.
|
||||
ASSERT(iter != tls_pages.cend());
|
||||
|
||||
iter->ReleaseSlot(tls_address);
|
||||
}
|
||||
|
||||
void Process::LoadModule(CodeSet module_, VAddr base_addr) {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <cstddef>
|
||||
#include <list>
|
||||
#include <string>
|
||||
@@ -32,6 +31,7 @@ namespace Kernel {
|
||||
class KernelCore;
|
||||
class ResourceLimit;
|
||||
class Thread;
|
||||
class TLSPage;
|
||||
|
||||
struct CodeSet;
|
||||
|
||||
@@ -73,9 +73,15 @@ public:
|
||||
ProcessIDMax = 0xFFFFFFFFFFFFFFFF,
|
||||
};
|
||||
|
||||
// Used to determine how process IDs are assigned.
|
||||
enum class ProcessType {
|
||||
KernelInternal,
|
||||
Userland,
|
||||
};
|
||||
|
||||
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
|
||||
|
||||
static SharedPtr<Process> Create(Core::System& system, std::string name);
|
||||
static SharedPtr<Process> Create(Core::System& system, std::string name, ProcessType type);
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "Process";
|
||||
@@ -254,10 +260,10 @@ public:
|
||||
// Thread-local storage management
|
||||
|
||||
// Marks the next available region as used and returns the address of the slot.
|
||||
VAddr MarkNextAvailableTLSSlotAsUsed(Thread& thread);
|
||||
[[nodiscard]] VAddr CreateTLSRegion();
|
||||
|
||||
// Frees a used TLS slot identified by the given address
|
||||
void FreeTLSSlot(VAddr tls_address);
|
||||
void FreeTLSRegion(VAddr tls_address);
|
||||
|
||||
private:
|
||||
explicit Process(Core::System& system);
|
||||
@@ -284,7 +290,7 @@ private:
|
||||
u64 code_memory_size = 0;
|
||||
|
||||
/// Current status of the process
|
||||
ProcessStatus status;
|
||||
ProcessStatus status{};
|
||||
|
||||
/// The ID of this process
|
||||
u64 process_id = 0;
|
||||
@@ -304,7 +310,7 @@ private:
|
||||
/// holds the TLS for a specific thread. This vector contains which parts are in use for each
|
||||
/// page as a bitmask.
|
||||
/// This vector will grow as more pages are allocated for new threads.
|
||||
std::vector<std::bitset<8>> tls_slots;
|
||||
std::vector<TLSPage> tls_pages;
|
||||
|
||||
/// Contains the parsed process capability descriptors.
|
||||
ProcessCapabilities capabilities;
|
||||
@@ -333,7 +339,7 @@ private:
|
||||
Mutex mutex;
|
||||
|
||||
/// Random values for svcGetInfo RandomEntropy
|
||||
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
|
||||
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{};
|
||||
|
||||
/// List of threads that are running with this process as their owner.
|
||||
std::list<const Thread*> thread_list;
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/reporter.h"
|
||||
|
||||
namespace Kernel {
|
||||
namespace {
|
||||
@@ -97,9 +98,9 @@ ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_add
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
if (!vm_manager.IsWithinNewMapRegion(dst_addr, size)) {
|
||||
if (!vm_manager.IsWithinStackRegion(dst_addr, size)) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Destination is not within the new map region, addr=0x{:016X}, size=0x{:016X}",
|
||||
"Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}",
|
||||
dst_addr, size);
|
||||
return ERR_INVALID_MEMORY_RANGE;
|
||||
}
|
||||
@@ -594,6 +595,7 @@ struct BreakReason {
|
||||
static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
|
||||
BreakReason break_reason{reason};
|
||||
bool has_dumped_buffer{};
|
||||
std::vector<u8> debug_buffer;
|
||||
|
||||
const auto handle_debug_buffer = [&](VAddr addr, u64 sz) {
|
||||
if (sz == 0 || addr == 0 || has_dumped_buffer) {
|
||||
@@ -605,7 +607,7 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
|
||||
LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", Memory::Read32(addr));
|
||||
} else {
|
||||
// We don't know what's in here so we'll hexdump it
|
||||
std::vector<u8> debug_buffer(sz);
|
||||
debug_buffer.resize(sz);
|
||||
Memory::ReadBlock(addr, debug_buffer.data(), sz);
|
||||
std::string hexdump;
|
||||
for (std::size_t i = 0; i < debug_buffer.size(); i++) {
|
||||
@@ -664,6 +666,10 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
|
||||
break;
|
||||
}
|
||||
|
||||
system.GetReporter().SaveSvcBreakReport(
|
||||
static_cast<u32>(break_reason.break_type.Value()), break_reason.signal_debugger, info1,
|
||||
info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt);
|
||||
|
||||
if (!break_reason.signal_debugger) {
|
||||
LOG_CRITICAL(
|
||||
Debug_Emulated,
|
||||
@@ -720,8 +726,8 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
|
||||
// 2.0.0+
|
||||
ASLRRegionBaseAddr = 12,
|
||||
ASLRRegionSize = 13,
|
||||
NewMapRegionBaseAddr = 14,
|
||||
NewMapRegionSize = 15,
|
||||
StackRegionBaseAddr = 14,
|
||||
StackRegionSize = 15,
|
||||
// 3.0.0+
|
||||
IsVirtualAddressMemoryEnabled = 16,
|
||||
PersonalMmHeapUsage = 17,
|
||||
@@ -746,8 +752,8 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
|
||||
case GetInfoType::HeapRegionSize:
|
||||
case GetInfoType::ASLRRegionBaseAddr:
|
||||
case GetInfoType::ASLRRegionSize:
|
||||
case GetInfoType::NewMapRegionBaseAddr:
|
||||
case GetInfoType::NewMapRegionSize:
|
||||
case GetInfoType::StackRegionBaseAddr:
|
||||
case GetInfoType::StackRegionSize:
|
||||
case GetInfoType::TotalPhysicalMemoryAvailable:
|
||||
case GetInfoType::TotalPhysicalMemoryUsed:
|
||||
case GetInfoType::IsVirtualAddressMemoryEnabled:
|
||||
@@ -800,12 +806,12 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
|
||||
*result = process->VMManager().GetASLRRegionSize();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::NewMapRegionBaseAddr:
|
||||
*result = process->VMManager().GetNewMapRegionBaseAddress();
|
||||
case GetInfoType::StackRegionBaseAddr:
|
||||
*result = process->VMManager().GetStackRegionBaseAddress();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::NewMapRegionSize:
|
||||
*result = process->VMManager().GetNewMapRegionSize();
|
||||
case GetInfoType::StackRegionSize:
|
||||
*result = process->VMManager().GetStackRegionSize();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::TotalPhysicalMemoryAvailable:
|
||||
|
||||
@@ -65,7 +65,7 @@ void Thread::Stop() {
|
||||
owner_process->UnregisterThread(this);
|
||||
|
||||
// Mark the TLS slot in the thread's page as free.
|
||||
owner_process->FreeTLSSlot(tls_address);
|
||||
owner_process->FreeTLSRegion(tls_address);
|
||||
}
|
||||
|
||||
void Thread::WakeAfterDelay(s64 nanoseconds) {
|
||||
@@ -76,13 +76,13 @@ 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::System::GetInstance().CoreTiming().ScheduleEvent(
|
||||
cycles, kernel.ThreadWakeupCallbackEventType(), callback_handle);
|
||||
}
|
||||
|
||||
void Thread::CancelWakeupTimer() {
|
||||
Core::System::GetInstance().CoreTiming().UnscheduleEventThreadsafe(
|
||||
kernel.ThreadWakeupCallbackEventType(), callback_handle);
|
||||
Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(),
|
||||
callback_handle);
|
||||
}
|
||||
|
||||
static std::optional<s32> GetNextProcessorId(u64 mask) {
|
||||
@@ -205,9 +205,9 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
|
||||
thread->name = std::move(name);
|
||||
thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
|
||||
thread->owner_process = &owner_process;
|
||||
thread->tls_address = thread->owner_process->CreateTLSRegion();
|
||||
thread->scheduler = &system.Scheduler(processor_id);
|
||||
thread->scheduler->AddThread(thread);
|
||||
thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
|
||||
|
||||
thread->owner_process->RegisterThread(thread.get());
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -78,9 +77,6 @@ enum class ThreadActivity : u32 {
|
||||
|
||||
class Thread final : public WaitObject {
|
||||
public:
|
||||
using TLSMemory = std::vector<u8>;
|
||||
using TLSMemoryPtr = std::shared_ptr<TLSMemory>;
|
||||
|
||||
using MutexWaitingThreads = std::vector<SharedPtr<Thread>>;
|
||||
|
||||
using ThreadContext = Core::ARM_Interface::ThreadContext;
|
||||
@@ -169,14 +165,6 @@ public:
|
||||
return thread_id;
|
||||
}
|
||||
|
||||
TLSMemoryPtr& GetTLSMemory() {
|
||||
return tls_memory;
|
||||
}
|
||||
|
||||
const TLSMemoryPtr& GetTLSMemory() const {
|
||||
return tls_memory;
|
||||
}
|
||||
|
||||
/// Resumes a thread from waiting
|
||||
void ResumeFromWait();
|
||||
|
||||
@@ -463,11 +451,9 @@ private:
|
||||
u32 ideal_core{0xFFFFFFFF};
|
||||
u64 affinity_mask{0x1};
|
||||
|
||||
TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>();
|
||||
ThreadActivity activity = ThreadActivity::Normal;
|
||||
|
||||
std::string name;
|
||||
|
||||
ThreadActivity activity = ThreadActivity::Normal;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -68,9 +68,7 @@ VMManager::VMManager(Core::System& system) : system{system} {
|
||||
Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
|
||||
}
|
||||
|
||||
VMManager::~VMManager() {
|
||||
Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
|
||||
}
|
||||
VMManager::~VMManager() = default;
|
||||
|
||||
void VMManager::Reset(FileSys::ProgramAddressSpaceType type) {
|
||||
Clear();
|
||||
@@ -154,22 +152,33 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
|
||||
}
|
||||
|
||||
ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const {
|
||||
// Find the first Free VMA.
|
||||
const VAddr base = GetASLRRegionBaseAddress();
|
||||
const VMAHandle vma_handle = std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) {
|
||||
if (vma.second.type != VMAType::Free)
|
||||
return false;
|
||||
return FindFreeRegion(GetASLRRegionBaseAddress(), GetASLRRegionEndAddress(), size);
|
||||
}
|
||||
|
||||
const VAddr vma_end = vma.second.base + vma.second.size;
|
||||
return vma_end > base && vma_end >= base + size;
|
||||
});
|
||||
ResultVal<VAddr> VMManager::FindFreeRegion(VAddr begin, VAddr end, u64 size) const {
|
||||
ASSERT(begin < end);
|
||||
ASSERT(size <= end - begin);
|
||||
|
||||
if (vma_handle == vma_map.end()) {
|
||||
const VMAHandle vma_handle =
|
||||
std::find_if(vma_map.begin(), vma_map.end(), [begin, end, size](const auto& vma) {
|
||||
if (vma.second.type != VMAType::Free) {
|
||||
return false;
|
||||
}
|
||||
const VAddr vma_base = vma.second.base;
|
||||
const VAddr vma_end = vma_base + vma.second.size;
|
||||
const VAddr assumed_base = (begin < vma_base) ? vma_base : begin;
|
||||
const VAddr used_range = assumed_base + size;
|
||||
|
||||
return vma_base <= assumed_base && assumed_base < used_range && used_range < end &&
|
||||
used_range <= vma_end;
|
||||
});
|
||||
|
||||
if (vma_handle == vma_map.cend()) {
|
||||
// TODO(Subv): Find the correct error code here.
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
const VAddr target = std::max(base, vma_handle->second.base);
|
||||
const VAddr target = std::max(begin, vma_handle->second.base);
|
||||
return MakeResult<VAddr>(target);
|
||||
}
|
||||
|
||||
@@ -616,9 +625,11 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
|
||||
void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType type) {
|
||||
u64 map_region_size = 0;
|
||||
u64 heap_region_size = 0;
|
||||
u64 new_map_region_size = 0;
|
||||
u64 stack_region_size = 0;
|
||||
u64 tls_io_region_size = 0;
|
||||
|
||||
u64 stack_and_tls_io_end = 0;
|
||||
|
||||
switch (type) {
|
||||
case FileSys::ProgramAddressSpaceType::Is32Bit:
|
||||
case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
|
||||
@@ -634,6 +645,7 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty
|
||||
map_region_size = 0;
|
||||
heap_region_size = 0x80000000;
|
||||
}
|
||||
stack_and_tls_io_end = 0x40000000;
|
||||
break;
|
||||
case FileSys::ProgramAddressSpaceType::Is36Bit:
|
||||
address_space_width = 36;
|
||||
@@ -643,6 +655,7 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty
|
||||
aslr_region_end = aslr_region_base + 0xFF8000000;
|
||||
map_region_size = 0x180000000;
|
||||
heap_region_size = 0x180000000;
|
||||
stack_and_tls_io_end = 0x80000000;
|
||||
break;
|
||||
case FileSys::ProgramAddressSpaceType::Is39Bit:
|
||||
address_space_width = 39;
|
||||
@@ -652,7 +665,7 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty
|
||||
aslr_region_end = aslr_region_base + 0x7FF8000000;
|
||||
map_region_size = 0x1000000000;
|
||||
heap_region_size = 0x180000000;
|
||||
new_map_region_size = 0x80000000;
|
||||
stack_region_size = 0x80000000;
|
||||
tls_io_region_size = 0x1000000000;
|
||||
break;
|
||||
default:
|
||||
@@ -660,6 +673,8 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty
|
||||
return;
|
||||
}
|
||||
|
||||
const u64 stack_and_tls_io_begin = aslr_region_base;
|
||||
|
||||
address_space_base = 0;
|
||||
address_space_end = 1ULL << address_space_width;
|
||||
|
||||
@@ -670,15 +685,20 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty
|
||||
heap_region_end = heap_region_base + heap_region_size;
|
||||
heap_end = heap_region_base;
|
||||
|
||||
new_map_region_base = heap_region_end;
|
||||
new_map_region_end = new_map_region_base + new_map_region_size;
|
||||
stack_region_base = heap_region_end;
|
||||
stack_region_end = stack_region_base + stack_region_size;
|
||||
|
||||
tls_io_region_base = new_map_region_end;
|
||||
tls_io_region_base = stack_region_end;
|
||||
tls_io_region_end = tls_io_region_base + tls_io_region_size;
|
||||
|
||||
if (new_map_region_size == 0) {
|
||||
new_map_region_base = address_space_base;
|
||||
new_map_region_end = address_space_end;
|
||||
if (stack_region_size == 0) {
|
||||
stack_region_base = stack_and_tls_io_begin;
|
||||
stack_region_end = stack_and_tls_io_end;
|
||||
}
|
||||
|
||||
if (tls_io_region_size == 0) {
|
||||
tls_io_region_base = stack_and_tls_io_begin;
|
||||
tls_io_region_end = stack_and_tls_io_end;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -870,21 +890,21 @@ bool VMManager::IsWithinMapRegion(VAddr address, u64 size) const {
|
||||
return IsInsideAddressRange(address, size, GetMapRegionBaseAddress(), GetMapRegionEndAddress());
|
||||
}
|
||||
|
||||
VAddr VMManager::GetNewMapRegionBaseAddress() const {
|
||||
return new_map_region_base;
|
||||
VAddr VMManager::GetStackRegionBaseAddress() const {
|
||||
return stack_region_base;
|
||||
}
|
||||
|
||||
VAddr VMManager::GetNewMapRegionEndAddress() const {
|
||||
return new_map_region_end;
|
||||
VAddr VMManager::GetStackRegionEndAddress() const {
|
||||
return stack_region_end;
|
||||
}
|
||||
|
||||
u64 VMManager::GetNewMapRegionSize() const {
|
||||
return new_map_region_end - new_map_region_base;
|
||||
u64 VMManager::GetStackRegionSize() const {
|
||||
return stack_region_end - stack_region_base;
|
||||
}
|
||||
|
||||
bool VMManager::IsWithinNewMapRegion(VAddr address, u64 size) const {
|
||||
return IsInsideAddressRange(address, size, GetNewMapRegionBaseAddress(),
|
||||
GetNewMapRegionEndAddress());
|
||||
bool VMManager::IsWithinStackRegion(VAddr address, u64 size) const {
|
||||
return IsInsideAddressRange(address, size, GetStackRegionBaseAddress(),
|
||||
GetStackRegionEndAddress());
|
||||
}
|
||||
|
||||
VAddr VMManager::GetTLSIORegionBaseAddress() const {
|
||||
|
||||
@@ -362,13 +362,38 @@ public:
|
||||
ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state);
|
||||
|
||||
/**
|
||||
* Finds the first free address that can hold a region of the desired size.
|
||||
* Finds the first free memory region of the given size within
|
||||
* the user-addressable ASLR memory region.
|
||||
*
|
||||
* @param size Size of the desired region.
|
||||
* @return The found free address.
|
||||
* @param size The size of the desired region in bytes.
|
||||
*
|
||||
* @returns If successful, the base address of the free region with
|
||||
* the given size.
|
||||
*/
|
||||
ResultVal<VAddr> FindFreeRegion(u64 size) const;
|
||||
|
||||
/**
|
||||
* Finds the first free address range that can hold a region of the desired size
|
||||
*
|
||||
* @param begin The starting address of the range.
|
||||
* This is treated as an inclusive beginning address.
|
||||
*
|
||||
* @param end The ending address of the range.
|
||||
* This is treated as an exclusive ending address.
|
||||
*
|
||||
* @param size The size of the free region to attempt to locate,
|
||||
* in bytes.
|
||||
*
|
||||
* @returns If successful, the base address of the free region with
|
||||
* the given size.
|
||||
*
|
||||
* @returns If unsuccessful, a result containing an error code.
|
||||
*
|
||||
* @pre The starting address must be less than the ending address.
|
||||
* @pre The size must not exceed the address range itself.
|
||||
*/
|
||||
ResultVal<VAddr> FindFreeRegion(VAddr begin, VAddr end, u64 size) const;
|
||||
|
||||
/**
|
||||
* Maps a memory-mapped IO region at a given address.
|
||||
*
|
||||
@@ -571,17 +596,17 @@ public:
|
||||
/// Determines whether or not the specified range is within the map region.
|
||||
bool IsWithinMapRegion(VAddr address, u64 size) const;
|
||||
|
||||
/// Gets the base address of the new map region.
|
||||
VAddr GetNewMapRegionBaseAddress() const;
|
||||
/// Gets the base address of the stack region.
|
||||
VAddr GetStackRegionBaseAddress() const;
|
||||
|
||||
/// Gets the end address of the new map region.
|
||||
VAddr GetNewMapRegionEndAddress() const;
|
||||
/// Gets the end address of the stack region.
|
||||
VAddr GetStackRegionEndAddress() const;
|
||||
|
||||
/// Gets the total size of the new map region in bytes.
|
||||
u64 GetNewMapRegionSize() const;
|
||||
/// Gets the total size of the stack region in bytes.
|
||||
u64 GetStackRegionSize() const;
|
||||
|
||||
/// Determines whether or not the given address range is within the new map region
|
||||
bool IsWithinNewMapRegion(VAddr address, u64 size) const;
|
||||
/// Determines whether or not the given address range is within the stack region
|
||||
bool IsWithinStackRegion(VAddr address, u64 size) const;
|
||||
|
||||
/// Gets the base address of the TLS IO region.
|
||||
VAddr GetTLSIORegionBaseAddress() const;
|
||||
@@ -701,8 +726,8 @@ private:
|
||||
VAddr map_region_base = 0;
|
||||
VAddr map_region_end = 0;
|
||||
|
||||
VAddr new_map_region_base = 0;
|
||||
VAddr new_map_region_end = 0;
|
||||
VAddr stack_region_base = 0;
|
||||
VAddr stack_region_end = 0;
|
||||
|
||||
VAddr tls_io_region_base = 0;
|
||||
VAddr tls_io_region_end = 0;
|
||||
|
||||
@@ -12,13 +12,22 @@
|
||||
#include "common/swap.h"
|
||||
#include "core/constants.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/acc/acc.h"
|
||||
#include "core/hle/service/acc/acc_aa.h"
|
||||
#include "core/hle/service/acc/acc_su.h"
|
||||
#include "core/hle/service/acc/acc_u0.h"
|
||||
#include "core/hle/service/acc/acc_u1.h"
|
||||
#include "core/hle/service/acc/errors.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/glue/arp.h"
|
||||
#include "core/hle/service/glue/manager.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
@@ -90,7 +99,7 @@ private:
|
||||
LOG_WARNING(Service_ACC,
|
||||
"Failed to load user provided image! Falling back to built-in backup...");
|
||||
ctx.WriteBuffer(Core::Constants::ACCOUNT_BACKUP_JPEG);
|
||||
rb.Push<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size());
|
||||
rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size()));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -112,9 +121,9 @@ private:
|
||||
if (!image.IsOpen()) {
|
||||
LOG_WARNING(Service_ACC,
|
||||
"Failed to load user provided image! Falling back to built-in backup...");
|
||||
rb.Push<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size());
|
||||
rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size()));
|
||||
} else {
|
||||
rb.Push<u32>(SanitizeJPEGSize(image.GetSize()));
|
||||
rb.Push(SanitizeJPEGSize(image.GetSize()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,9 +223,71 @@ void Module::Interface::IsUserRegistrationRequestPermitted(Kernel::HLERequestCon
|
||||
}
|
||||
|
||||
void Module::Interface::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto pid = rp.Pop<u64>();
|
||||
|
||||
LOG_DEBUG(Service_ACC, "called, process_id={}", pid);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(InitializeApplicationInfoBase(pid));
|
||||
}
|
||||
|
||||
void Module::Interface::InitializeApplicationInfoRestricted(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto pid = rp.Pop<u64>();
|
||||
|
||||
LOG_WARNING(Service_ACC, "(Partial implementation) called, process_id={}", pid);
|
||||
|
||||
// TODO(ogniK): We require checking if the user actually owns the title and what not. As of
|
||||
// currently, we assume the user owns the title. InitializeApplicationInfoBase SHOULD be called
|
||||
// first then we do extra checks if the game is a digital copy.
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(InitializeApplicationInfoBase(pid));
|
||||
}
|
||||
|
||||
ResultCode Module::Interface::InitializeApplicationInfoBase(u64 process_id) {
|
||||
if (application_info) {
|
||||
LOG_ERROR(Service_ACC, "Application already initialized");
|
||||
return ERR_ACCOUNTINFO_ALREADY_INITIALIZED;
|
||||
}
|
||||
|
||||
const auto& list = system.Kernel().GetProcessList();
|
||||
const auto iter = std::find_if(list.begin(), list.end(), [&process_id](const auto& process) {
|
||||
return process->GetProcessID() == process_id;
|
||||
});
|
||||
|
||||
if (iter == list.end()) {
|
||||
LOG_ERROR(Service_ACC, "Failed to find process ID");
|
||||
application_info.application_type = ApplicationType::Unknown;
|
||||
|
||||
return ERR_ACCOUNTINFO_BAD_APPLICATION;
|
||||
}
|
||||
|
||||
const auto launch_property = system.GetARPManager().GetLaunchProperty((*iter)->GetTitleID());
|
||||
|
||||
if (launch_property.Failed()) {
|
||||
LOG_ERROR(Service_ACC, "Failed to get launch property");
|
||||
return ERR_ACCOUNTINFO_BAD_APPLICATION;
|
||||
}
|
||||
|
||||
switch (launch_property->base_game_storage_id) {
|
||||
case FileSys::StorageId::GameCard:
|
||||
application_info.application_type = ApplicationType::GameCard;
|
||||
break;
|
||||
case FileSys::StorageId::Host:
|
||||
case FileSys::StorageId::NandUser:
|
||||
case FileSys::StorageId::SdCard:
|
||||
application_info.application_type = ApplicationType::Digital;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Service_ACC, "Invalid game storage ID");
|
||||
return ERR_ACCOUNTINFO_BAD_APPLICATION;
|
||||
}
|
||||
|
||||
LOG_WARNING(Service_ACC, "ApplicationInfo init required");
|
||||
// TODO(ogniK): Actual initalization here
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx) {
|
||||
@@ -226,6 +297,31 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo
|
||||
rb.PushIpcInterface<IManagerForApplication>();
|
||||
}
|
||||
|
||||
void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
FileSys::NACP nacp;
|
||||
const auto res = system.GetAppLoader().ReadControlData(nacp);
|
||||
|
||||
bool is_locked = false;
|
||||
|
||||
if (res != Loader::ResultStatus::Success) {
|
||||
FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
|
||||
auto nacp_unique = pm.GetControlMetadata().first;
|
||||
|
||||
if (nacp_unique != nullptr) {
|
||||
is_locked = nacp_unique->GetUserAccountSwitchLock();
|
||||
} else {
|
||||
LOG_ERROR(Service_ACC, "nacp_unique is null!");
|
||||
}
|
||||
} else {
|
||||
is_locked = nacp.GetUserAccountSwitchLock();
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(is_locked);
|
||||
}
|
||||
|
||||
void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
// A u8 is passed into this function which we can safely ignore. It's to determine if we have
|
||||
@@ -251,19 +347,25 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex
|
||||
}
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module,
|
||||
std::shared_ptr<ProfileManager> profile_manager, const char* name)
|
||||
std::shared_ptr<ProfileManager> profile_manager, Core::System& system,
|
||||
const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)),
|
||||
profile_manager(std::move(profile_manager)) {}
|
||||
profile_manager(std::move(profile_manager)), system(system) {}
|
||||
|
||||
Module::Interface::~Interface() = default;
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
void InstallInterfaces(Core::System& system) {
|
||||
auto module = std::make_shared<Module>();
|
||||
auto profile_manager = std::make_shared<ProfileManager>();
|
||||
std::make_shared<ACC_AA>(module, profile_manager)->InstallAsService(service_manager);
|
||||
std::make_shared<ACC_SU>(module, profile_manager)->InstallAsService(service_manager);
|
||||
std::make_shared<ACC_U0>(module, profile_manager)->InstallAsService(service_manager);
|
||||
std::make_shared<ACC_U1>(module, profile_manager)->InstallAsService(service_manager);
|
||||
|
||||
std::make_shared<ACC_AA>(module, profile_manager, system)
|
||||
->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<ACC_SU>(module, profile_manager, system)
|
||||
->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<ACC_U0>(module, profile_manager, system)
|
||||
->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<ACC_U1>(module, profile_manager, system)
|
||||
->InstallAsService(system.ServiceManager());
|
||||
}
|
||||
|
||||
} // namespace Service::Account
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/glue/manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::Account {
|
||||
@@ -15,7 +16,8 @@ public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
explicit Interface(std::shared_ptr<Module> module,
|
||||
std::shared_ptr<ProfileManager> profile_manager, const char* name);
|
||||
std::shared_ptr<ProfileManager> profile_manager, Core::System& system,
|
||||
const char* name);
|
||||
~Interface() override;
|
||||
|
||||
void GetUserCount(Kernel::HLERequestContext& ctx);
|
||||
@@ -25,17 +27,40 @@ public:
|
||||
void GetLastOpenedUser(Kernel::HLERequestContext& ctx);
|
||||
void GetProfile(Kernel::HLERequestContext& ctx);
|
||||
void InitializeApplicationInfo(Kernel::HLERequestContext& ctx);
|
||||
void InitializeApplicationInfoRestricted(Kernel::HLERequestContext& ctx);
|
||||
void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx);
|
||||
void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx);
|
||||
void TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx);
|
||||
void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx);
|
||||
|
||||
private:
|
||||
ResultCode InitializeApplicationInfoBase(u64 process_id);
|
||||
|
||||
enum class ApplicationType : u32_le {
|
||||
GameCard = 0,
|
||||
Digital = 1,
|
||||
Unknown = 3,
|
||||
};
|
||||
|
||||
struct ApplicationInfo {
|
||||
Service::Glue::ApplicationLaunchProperty launch_property;
|
||||
ApplicationType application_type;
|
||||
|
||||
constexpr explicit operator bool() const {
|
||||
return launch_property.title_id != 0x0;
|
||||
}
|
||||
};
|
||||
|
||||
ApplicationInfo application_info{};
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
std::shared_ptr<ProfileManager> profile_manager;
|
||||
Core::System& system;
|
||||
};
|
||||
};
|
||||
|
||||
/// Registers all ACC services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
void InstallInterfaces(Core::System& system);
|
||||
|
||||
} // namespace Service::Account
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
ACC_AA::ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
|
||||
: Module::Interface(std::move(module), std::move(profile_manager), "acc:aa") {
|
||||
ACC_AA::ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
|
||||
Core::System& system)
|
||||
: Module::Interface(std::move(module), std::move(profile_manager), system, "acc:aa") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "EnsureCacheAsync"},
|
||||
{1, nullptr, "LoadCache"},
|
||||
|
||||
@@ -10,8 +10,8 @@ namespace Service::Account {
|
||||
|
||||
class ACC_AA final : public Module::Interface {
|
||||
public:
|
||||
explicit ACC_AA(std::shared_ptr<Module> module,
|
||||
std::shared_ptr<ProfileManager> profile_manager);
|
||||
explicit ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
|
||||
Core::System& system);
|
||||
~ACC_AA() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
|
||||
: Module::Interface(std::move(module), std::move(profile_manager), "acc:su") {
|
||||
ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
|
||||
Core::System& system)
|
||||
: Module::Interface(std::move(module), std::move(profile_manager), system, "acc:su") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ACC_SU::GetUserCount, "GetUserCount"},
|
||||
|
||||
@@ -10,8 +10,8 @@ namespace Service::Account {
|
||||
|
||||
class ACC_SU final : public Module::Interface {
|
||||
public:
|
||||
explicit ACC_SU(std::shared_ptr<Module> module,
|
||||
std::shared_ptr<ProfileManager> profile_manager);
|
||||
explicit ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
|
||||
Core::System& system);
|
||||
~ACC_SU() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
|
||||
: Module::Interface(std::move(module), std::move(profile_manager), "acc:u0") {
|
||||
ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
|
||||
Core::System& system)
|
||||
: Module::Interface(std::move(module), std::move(profile_manager), system, "acc:u0") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ACC_U0::GetUserCount, "GetUserCount"},
|
||||
@@ -30,9 +31,9 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
|
||||
{120, nullptr, "CreateGuestLoginRequest"},
|
||||
{130, nullptr, "LoadOpenContext"},
|
||||
{131, nullptr, "ListOpenContextStoredUsers"},
|
||||
{140, nullptr, "InitializeApplicationInfo"},
|
||||
{140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"},
|
||||
{141, nullptr, "ListQualifiedUsers"},
|
||||
{150, nullptr, "IsUserAccountSwitchLocked"},
|
||||
{150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ namespace Service::Account {
|
||||
|
||||
class ACC_U0 final : public Module::Interface {
|
||||
public:
|
||||
explicit ACC_U0(std::shared_ptr<Module> module,
|
||||
std::shared_ptr<ProfileManager> profile_manager);
|
||||
explicit ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
|
||||
Core::System& system);
|
||||
~ACC_U0() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
|
||||
: Module::Interface(std::move(module), std::move(profile_manager), "acc:u1") {
|
||||
ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
|
||||
Core::System& system)
|
||||
: Module::Interface(std::move(module), std::move(profile_manager), system, "acc:u1") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ACC_U1::GetUserCount, "GetUserCount"},
|
||||
|
||||
@@ -10,8 +10,8 @@ namespace Service::Account {
|
||||
|
||||
class ACC_U1 final : public Module::Interface {
|
||||
public:
|
||||
explicit ACC_U1(std::shared_ptr<Module> module,
|
||||
std::shared_ptr<ProfileManager> profile_manager);
|
||||
explicit ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
|
||||
Core::System& system);
|
||||
~ACC_U1() override;
|
||||
};
|
||||
|
||||
|
||||
14
src/core/hle/service/acc/errors.h
Normal file
14
src/core/hle/service/acc/errors.h
Normal file
@@ -0,0 +1,14 @@
|
||||
// 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::Account {
|
||||
|
||||
constexpr ResultCode ERR_ACCOUNTINFO_BAD_APPLICATION{ErrorModule::Account, 22};
|
||||
constexpr ResultCode ERR_ACCOUNTINFO_ALREADY_INITIALIZED{ErrorModule::Account, 41};
|
||||
|
||||
} // namespace Service::Account
|
||||
@@ -270,8 +270,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
|
||||
{70, nullptr, "ReportMultimediaError"},
|
||||
{71, nullptr, "GetCurrentIlluminanceEx"},
|
||||
{80, nullptr, "SetWirelessPriorityMode"},
|
||||
{90, nullptr, "GetAccumulatedSuspendedTickValue"},
|
||||
{91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"},
|
||||
{90, &ISelfController::GetAccumulatedSuspendedTickValue, "GetAccumulatedSuspendedTickValue"},
|
||||
{91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"},
|
||||
{100, nullptr, "SetAlbumImageTakenNotificationEnabled"},
|
||||
{1000, nullptr, "GetDebugStorageChannel"},
|
||||
};
|
||||
@@ -282,6 +282,15 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
"ISelfController:LaunchableEvent");
|
||||
|
||||
// This event is created by AM on the first time GetAccumulatedSuspendedTickChangedEvent() is
|
||||
// called. Yuzu can just create it unconditionally, since it doesn't need to support multiple
|
||||
// ISelfControllers. The event is signaled on creation, and on transition from suspended -> not
|
||||
// suspended if the event has previously been created by a call to
|
||||
// GetAccumulatedSuspendedTickChangedEvent.
|
||||
accumulated_suspended_tick_changed_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Manual, "ISelfController:AccumulatedSuspendedTickChangedEvent");
|
||||
accumulated_suspended_tick_changed_event.writable->Signal();
|
||||
}
|
||||
|
||||
ISelfController::~ISelfController() = default;
|
||||
@@ -444,6 +453,25 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
|
||||
rb.Push<u32>(idle_time_detection_extension);
|
||||
}
|
||||
|
||||
void ISelfController::GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called.");
|
||||
|
||||
// This command returns the total number of system ticks since ISelfController creation
|
||||
// where the game was suspended. Since Yuzu doesn't implement game suspension, this command
|
||||
// can just always return 0 ticks.
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(0);
|
||||
}
|
||||
|
||||
void ISelfController::GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called.");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(accumulated_suspended_tick_changed_event.readable);
|
||||
}
|
||||
|
||||
AppletMessageQueue::AppletMessageQueue() {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
@@ -871,7 +899,9 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") {
|
||||
ILibraryAppletCreator::ILibraryAppletCreator(u64 current_process_title_id)
|
||||
: ServiceFramework("ILibraryAppletCreator"),
|
||||
current_process_title_id(current_process_title_id) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"},
|
||||
{1, nullptr, "TerminateAllLibraryApplets"},
|
||||
@@ -894,7 +924,7 @@ void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx)
|
||||
static_cast<u32>(applet_id), applet_mode);
|
||||
|
||||
const auto& applet_manager{Core::System::GetInstance().GetAppletManager()};
|
||||
const auto applet = applet_manager.GetApplet(applet_id);
|
||||
const auto applet = applet_manager.GetApplet(applet_id, current_process_title_id);
|
||||
|
||||
if (applet == nullptr) {
|
||||
LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", static_cast<u32>(applet_id));
|
||||
@@ -1218,13 +1248,13 @@ void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system) {
|
||||
auto message_queue = std::make_shared<AppletMessageQueue>();
|
||||
message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on
|
||||
// game boot
|
||||
|
||||
std::make_shared<AppletAE>(nvflinger, message_queue)->InstallAsService(service_manager);
|
||||
std::make_shared<AppletOE>(nvflinger, message_queue)->InstallAsService(service_manager);
|
||||
std::make_shared<AppletAE>(nvflinger, message_queue, system)->InstallAsService(service_manager);
|
||||
std::make_shared<AppletOE>(nvflinger, message_queue, system)->InstallAsService(service_manager);
|
||||
std::make_shared<IdleSys>()->InstallAsService(service_manager);
|
||||
std::make_shared<OMM>()->InstallAsService(service_manager);
|
||||
std::make_shared<SPSM>()->InstallAsService(service_manager);
|
||||
|
||||
@@ -133,9 +133,13 @@ private:
|
||||
void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
|
||||
void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
|
||||
void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
|
||||
void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
|
||||
void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
Kernel::EventPair launchable_event;
|
||||
Kernel::EventPair accumulated_suspended_tick_changed_event;
|
||||
|
||||
u32 idle_time_detection_extension = 0;
|
||||
u64 num_fatal_sections_entered = 0;
|
||||
};
|
||||
@@ -198,13 +202,15 @@ private:
|
||||
|
||||
class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
|
||||
public:
|
||||
ILibraryAppletCreator();
|
||||
ILibraryAppletCreator(u64 current_process_title_id);
|
||||
~ILibraryAppletCreator() override;
|
||||
|
||||
private:
|
||||
void CreateLibraryApplet(Kernel::HLERequestContext& ctx);
|
||||
void CreateStorage(Kernel::HLERequestContext& ctx);
|
||||
void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx);
|
||||
|
||||
u64 current_process_title_id;
|
||||
};
|
||||
|
||||
class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
|
||||
@@ -261,7 +267,7 @@ public:
|
||||
|
||||
/// Registers all AM services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system);
|
||||
|
||||
} // namespace AM
|
||||
} // namespace Service
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applet_ae.h"
|
||||
#include "core/hle/service/nvflinger/nvflinger.h"
|
||||
@@ -13,9 +14,10 @@ namespace Service::AM {
|
||||
class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
|
||||
public:
|
||||
explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue,
|
||||
Core::System& system)
|
||||
: ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)),
|
||||
msg_queue(std::move(msg_queue)) {
|
||||
msg_queue(std::move(msg_queue)), system(system) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
|
||||
@@ -96,7 +98,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ILibraryAppletCreator>();
|
||||
rb.PushIpcInterface<ILibraryAppletCreator>(system.CurrentProcess()->GetTitleID());
|
||||
}
|
||||
|
||||
void GetApplicationFunctions(Kernel::HLERequestContext& ctx) {
|
||||
@@ -109,14 +111,15 @@ private:
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
|
||||
public:
|
||||
explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system)
|
||||
: ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)),
|
||||
msg_queue(std::move(msg_queue)) {
|
||||
msg_queue(std::move(msg_queue)), system(system) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
|
||||
@@ -191,7 +194,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ILibraryAppletCreator>();
|
||||
rb.PushIpcInterface<ILibraryAppletCreator>(system.CurrentProcess()->GetTitleID());
|
||||
}
|
||||
|
||||
void GetHomeMenuFunctions(Kernel::HLERequestContext& ctx) {
|
||||
@@ -219,6 +222,7 @@ private:
|
||||
}
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) {
|
||||
@@ -226,7 +230,7 @@ void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ISystemAppletProxy>(nvflinger, msg_queue);
|
||||
rb.PushIpcInterface<ISystemAppletProxy>(nvflinger, msg_queue, system);
|
||||
}
|
||||
|
||||
void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) {
|
||||
@@ -234,7 +238,7 @@ void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue);
|
||||
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue, system);
|
||||
}
|
||||
|
||||
void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
|
||||
@@ -242,13 +246,13 @@ void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue);
|
||||
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue, system);
|
||||
}
|
||||
|
||||
AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system)
|
||||
: ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)),
|
||||
msg_queue(std::move(msg_queue)) {
|
||||
msg_queue(std::move(msg_queue)), system(system) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace AM {
|
||||
class AppletAE final : public ServiceFramework<AppletAE> {
|
||||
public:
|
||||
explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue);
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system);
|
||||
~AppletAE() override;
|
||||
|
||||
const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
|
||||
@@ -30,6 +30,7 @@ private:
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
} // namespace AM
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applet_oe.h"
|
||||
#include "core/hle/service/nvflinger/nvflinger.h"
|
||||
@@ -13,9 +14,9 @@ namespace Service::AM {
|
||||
class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
|
||||
public:
|
||||
explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system)
|
||||
: ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)),
|
||||
msg_queue(std::move(msg_queue)) {
|
||||
msg_queue(std::move(msg_queue)), system(system) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
|
||||
@@ -87,7 +88,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ILibraryAppletCreator>();
|
||||
rb.PushIpcInterface<ILibraryAppletCreator>(system.CurrentProcess()->GetTitleID());
|
||||
}
|
||||
|
||||
void GetApplicationFunctions(Kernel::HLERequestContext& ctx) {
|
||||
@@ -100,6 +101,7 @@ private:
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
|
||||
@@ -107,13 +109,13 @@ void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue);
|
||||
rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue, system);
|
||||
}
|
||||
|
||||
AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system)
|
||||
: ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)),
|
||||
msg_queue(std::move(msg_queue)) {
|
||||
msg_queue(std::move(msg_queue)), system(system) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace AM {
|
||||
class AppletOE final : public ServiceFramework<AppletOE> {
|
||||
public:
|
||||
explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue);
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system);
|
||||
~AppletOE() override;
|
||||
|
||||
const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
|
||||
@@ -28,6 +28,7 @@ private:
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
} // namespace AM
|
||||
|
||||
@@ -35,12 +35,28 @@ AppletDataBroker::AppletDataBroker() {
|
||||
|
||||
AppletDataBroker::~AppletDataBroker() = default;
|
||||
|
||||
AppletDataBroker::RawChannelData AppletDataBroker::PeekDataToAppletForDebug() const {
|
||||
std::vector<std::vector<u8>> out_normal;
|
||||
|
||||
for (const auto& storage : in_channel) {
|
||||
out_normal.push_back(storage->GetData());
|
||||
}
|
||||
|
||||
std::vector<std::vector<u8>> out_interactive;
|
||||
|
||||
for (const auto& storage : in_interactive_channel) {
|
||||
out_interactive.push_back(storage->GetData());
|
||||
}
|
||||
|
||||
return {std::move(out_normal), std::move(out_interactive)};
|
||||
}
|
||||
|
||||
std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() {
|
||||
if (out_channel.empty())
|
||||
return nullptr;
|
||||
|
||||
auto out = std::move(out_channel.front());
|
||||
out_channel.pop();
|
||||
out_channel.pop_front();
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -49,7 +65,7 @@ std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() {
|
||||
return nullptr;
|
||||
|
||||
auto out = std::move(in_channel.front());
|
||||
in_channel.pop();
|
||||
in_channel.pop_front();
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -58,7 +74,7 @@ std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() {
|
||||
return nullptr;
|
||||
|
||||
auto out = std::move(out_interactive_channel.front());
|
||||
out_interactive_channel.pop();
|
||||
out_interactive_channel.pop_front();
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -67,25 +83,25 @@ std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() {
|
||||
return nullptr;
|
||||
|
||||
auto out = std::move(in_interactive_channel.front());
|
||||
in_interactive_channel.pop();
|
||||
in_interactive_channel.pop_front();
|
||||
return out;
|
||||
}
|
||||
|
||||
void AppletDataBroker::PushNormalDataFromGame(IStorage storage) {
|
||||
in_channel.push(std::make_unique<IStorage>(storage));
|
||||
in_channel.push_back(std::make_unique<IStorage>(storage));
|
||||
}
|
||||
|
||||
void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) {
|
||||
out_channel.push(std::make_unique<IStorage>(storage));
|
||||
out_channel.push_back(std::make_unique<IStorage>(storage));
|
||||
pop_out_data_event.writable->Signal();
|
||||
}
|
||||
|
||||
void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) {
|
||||
in_interactive_channel.push(std::make_unique<IStorage>(storage));
|
||||
in_interactive_channel.push_back(std::make_unique<IStorage>(storage));
|
||||
}
|
||||
|
||||
void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) {
|
||||
out_interactive_channel.push(std::make_unique<IStorage>(storage));
|
||||
out_interactive_channel.push_back(std::make_unique<IStorage>(storage));
|
||||
pop_interactive_out_data_event.writable->Signal();
|
||||
}
|
||||
|
||||
@@ -123,12 +139,14 @@ void Applet::Initialize() {
|
||||
|
||||
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(ParentalControlsApplet parental_controls, ErrorApplet error,
|
||||
PhotoViewer photo_viewer, ProfileSelect profile_select,
|
||||
SoftwareKeyboard software_keyboard, WebBrowser web_browser,
|
||||
ECommerceApplet e_commerce)
|
||||
: parental_controls{std::move(parental_controls)}, 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)},
|
||||
e_commerce{std::move(e_commerce)} {}
|
||||
|
||||
AppletFrontendSet::~AppletFrontendSet() = default;
|
||||
|
||||
@@ -141,6 +159,8 @@ AppletManager::AppletManager() = default;
|
||||
AppletManager::~AppletManager() = default;
|
||||
|
||||
void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
|
||||
if (set.parental_controls != nullptr)
|
||||
frontend.parental_controls = std::move(set.parental_controls);
|
||||
if (set.error != nullptr)
|
||||
frontend.error = std::move(set.error);
|
||||
if (set.photo_viewer != nullptr)
|
||||
@@ -151,17 +171,21 @@ void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
|
||||
frontend.software_keyboard = std::move(set.software_keyboard);
|
||||
if (set.web_browser != nullptr)
|
||||
frontend.web_browser = std::move(set.web_browser);
|
||||
if (set.e_commerce != nullptr)
|
||||
frontend.e_commerce = std::move(set.e_commerce);
|
||||
}
|
||||
|
||||
void AppletManager::SetDefaultAppletFrontendSet() {
|
||||
frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
|
||||
frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>();
|
||||
frontend.profile_select = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
|
||||
frontend.software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
|
||||
frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
|
||||
ClearAll();
|
||||
SetDefaultAppletsIfMissing();
|
||||
}
|
||||
|
||||
void AppletManager::SetDefaultAppletsIfMissing() {
|
||||
if (frontend.parental_controls == nullptr) {
|
||||
frontend.parental_controls =
|
||||
std::make_unique<Core::Frontend::DefaultParentalControlsApplet>();
|
||||
}
|
||||
|
||||
if (frontend.error == nullptr) {
|
||||
frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
|
||||
}
|
||||
@@ -182,14 +206,20 @@ void AppletManager::SetDefaultAppletsIfMissing() {
|
||||
if (frontend.web_browser == nullptr) {
|
||||
frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
|
||||
}
|
||||
|
||||
if (frontend.e_commerce == nullptr) {
|
||||
frontend.e_commerce = std::make_unique<Core::Frontend::DefaultECommerceApplet>();
|
||||
}
|
||||
}
|
||||
|
||||
void AppletManager::ClearAll() {
|
||||
frontend = {};
|
||||
}
|
||||
|
||||
std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const {
|
||||
std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, u64 current_process_title_id) const {
|
||||
switch (id) {
|
||||
case AppletId::Auth:
|
||||
return std::make_shared<Auth>(*frontend.parental_controls);
|
||||
case AppletId::Error:
|
||||
return std::make_shared<Error>(*frontend.error);
|
||||
case AppletId::ProfileSelect:
|
||||
@@ -198,13 +228,16 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const {
|
||||
return std::make_shared<SoftwareKeyboard>(*frontend.software_keyboard);
|
||||
case AppletId::PhotoViewer:
|
||||
return std::make_shared<PhotoViewer>(*frontend.photo_viewer);
|
||||
case AppletId::LibAppletShop:
|
||||
return std::make_shared<WebBrowser>(*frontend.web_browser, current_process_title_id,
|
||||
frontend.e_commerce.get());
|
||||
case AppletId::LibAppletOff:
|
||||
return std::make_shared<WebBrowser>(*frontend.web_browser);
|
||||
return std::make_shared<WebBrowser>(*frontend.web_browser, current_process_title_id);
|
||||
default:
|
||||
UNIMPLEMENTED_MSG(
|
||||
"No backend implementation exists for applet_id={:02X}! Falling back to stub applet.",
|
||||
static_cast<u8>(id));
|
||||
return std::make_shared<StubApplet>();
|
||||
return std::make_shared<StubApplet>(id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
union ResultCode;
|
||||
|
||||
namespace Core::Frontend {
|
||||
class ECommerceApplet;
|
||||
class ErrorApplet;
|
||||
class ParentalControlsApplet;
|
||||
class PhotoViewerApplet;
|
||||
class ProfileSelectApplet;
|
||||
class SoftwareKeyboardApplet;
|
||||
@@ -54,6 +56,14 @@ public:
|
||||
AppletDataBroker();
|
||||
~AppletDataBroker();
|
||||
|
||||
struct RawChannelData {
|
||||
std::vector<std::vector<u8>> normal;
|
||||
std::vector<std::vector<u8>> interactive;
|
||||
};
|
||||
|
||||
// Retrieves but does not pop the data sent to applet.
|
||||
RawChannelData PeekDataToAppletForDebug() const;
|
||||
|
||||
std::unique_ptr<IStorage> PopNormalDataToGame();
|
||||
std::unique_ptr<IStorage> PopNormalDataToApplet();
|
||||
|
||||
@@ -76,16 +86,16 @@ private:
|
||||
// Queues are named from applet's perspective
|
||||
|
||||
// PopNormalDataToApplet and PushNormalDataFromGame
|
||||
std::queue<std::unique_ptr<IStorage>> in_channel;
|
||||
std::deque<std::unique_ptr<IStorage>> in_channel;
|
||||
|
||||
// PopNormalDataToGame and PushNormalDataFromApplet
|
||||
std::queue<std::unique_ptr<IStorage>> out_channel;
|
||||
std::deque<std::unique_ptr<IStorage>> out_channel;
|
||||
|
||||
// PopInteractiveDataToApplet and PushInteractiveDataFromGame
|
||||
std::queue<std::unique_ptr<IStorage>> in_interactive_channel;
|
||||
std::deque<std::unique_ptr<IStorage>> in_interactive_channel;
|
||||
|
||||
// PopInteractiveDataToGame and PushInteractiveDataFromApplet
|
||||
std::queue<std::unique_ptr<IStorage>> out_interactive_channel;
|
||||
std::deque<std::unique_ptr<IStorage>> out_interactive_channel;
|
||||
|
||||
Kernel::EventPair state_changed_event;
|
||||
|
||||
@@ -137,15 +147,19 @@ protected:
|
||||
};
|
||||
|
||||
struct AppletFrontendSet {
|
||||
using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
|
||||
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>;
|
||||
using ECommerceApplet = std::unique_ptr<Core::Frontend::ECommerceApplet>;
|
||||
|
||||
AppletFrontendSet();
|
||||
AppletFrontendSet(ErrorApplet error, PhotoViewer photo_viewer, ProfileSelect profile_select,
|
||||
SoftwareKeyboard software_keyboard, WebBrowser web_browser);
|
||||
AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error,
|
||||
PhotoViewer photo_viewer, ProfileSelect profile_select,
|
||||
SoftwareKeyboard software_keyboard, WebBrowser web_browser,
|
||||
ECommerceApplet e_commerce);
|
||||
~AppletFrontendSet();
|
||||
|
||||
AppletFrontendSet(const AppletFrontendSet&) = delete;
|
||||
@@ -154,11 +168,13 @@ struct AppletFrontendSet {
|
||||
AppletFrontendSet(AppletFrontendSet&&) noexcept;
|
||||
AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept;
|
||||
|
||||
ParentalControlsApplet parental_controls;
|
||||
ErrorApplet error;
|
||||
PhotoViewer photo_viewer;
|
||||
ProfileSelect profile_select;
|
||||
SoftwareKeyboard software_keyboard;
|
||||
WebBrowser web_browser;
|
||||
ECommerceApplet e_commerce;
|
||||
};
|
||||
|
||||
class AppletManager {
|
||||
@@ -171,7 +187,7 @@ public:
|
||||
void SetDefaultAppletsIfMissing();
|
||||
void ClearAll();
|
||||
|
||||
std::shared_ptr<Applet> GetApplet(AppletId id) const;
|
||||
std::shared_ptr<Applet> GetApplet(AppletId id, u64 current_process_title_id) const;
|
||||
|
||||
private:
|
||||
AppletFrontendSet frontend;
|
||||
|
||||
@@ -9,8 +9,10 @@
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/applets/error.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/error.h"
|
||||
#include "core/reporter.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
@@ -143,9 +145,12 @@ void Error::Execute() {
|
||||
}
|
||||
|
||||
const auto callback = [this] { DisplayCompleted(); };
|
||||
const auto title_id = Core::CurrentProcess()->GetTitleID();
|
||||
const auto& reporter{Core::System::GetInstance().GetReporter()};
|
||||
|
||||
switch (mode) {
|
||||
case ErrorAppletMode::ShowError:
|
||||
reporter.SaveErrorReport(title_id, error_code);
|
||||
frontend.ShowError(error_code, callback);
|
||||
break;
|
||||
case ErrorAppletMode::ShowSystemError:
|
||||
@@ -156,14 +161,18 @@ void Error::Execute() {
|
||||
const auto& detail_text =
|
||||
system ? args->system_error.detail_text : args->application_error.detail_text;
|
||||
|
||||
frontend.ShowCustomErrorText(
|
||||
error_code,
|
||||
Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size()),
|
||||
Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size()),
|
||||
callback);
|
||||
const auto main_text_string =
|
||||
Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size());
|
||||
const auto detail_text_string =
|
||||
Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size());
|
||||
|
||||
reporter.SaveErrorReport(title_id, error_code, main_text_string, detail_text_string);
|
||||
frontend.ShowCustomErrorText(error_code, main_text_string, detail_text_string, callback);
|
||||
break;
|
||||
}
|
||||
case ErrorAppletMode::ShowErrorRecord:
|
||||
reporter.SaveErrorReport(title_id, error_code,
|
||||
fmt::format("{:016X}", args->error_record.posix_time));
|
||||
frontend.ShowErrorWithTimestamp(
|
||||
error_code, std::chrono::seconds{args->error_record.posix_time}, callback);
|
||||
break;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/hex_util.h"
|
||||
@@ -13,27 +13,144 @@
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/general_backend.h"
|
||||
#include "core/reporter.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
static void LogCurrentStorage(AppletDataBroker& broker, std::string prefix) {
|
||||
constexpr ResultCode ERROR_INVALID_PIN{ErrorModule::PCTL, 221};
|
||||
|
||||
static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) {
|
||||
std::unique_ptr<IStorage> storage = broker.PopNormalDataToApplet();
|
||||
for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) {
|
||||
const auto data = storage->GetData();
|
||||
LOG_INFO(Service_AM,
|
||||
"called (STUBBED), during {} recieved normal data with size={:08X}, data={}",
|
||||
prefix, data.size(), Common::HexVectorToString(data));
|
||||
"called (STUBBED), during {} received normal data with size={:08X}, data={}",
|
||||
prefix, data.size(), Common::HexToString(data));
|
||||
}
|
||||
|
||||
storage = broker.PopInteractiveDataToApplet();
|
||||
for (; storage != nullptr; storage = broker.PopInteractiveDataToApplet()) {
|
||||
const auto data = storage->GetData();
|
||||
LOG_INFO(Service_AM,
|
||||
"called (STUBBED), during {} recieved interactive data with size={:08X}, data={}",
|
||||
prefix, data.size(), Common::HexVectorToString(data));
|
||||
"called (STUBBED), during {} received interactive data with size={:08X}, data={}",
|
||||
prefix, data.size(), Common::HexToString(data));
|
||||
}
|
||||
}
|
||||
|
||||
Auth::Auth(Core::Frontend::ParentalControlsApplet& frontend) : frontend(frontend) {}
|
||||
|
||||
Auth::~Auth() = default;
|
||||
|
||||
void Auth::Initialize() {
|
||||
Applet::Initialize();
|
||||
complete = false;
|
||||
|
||||
const auto storage = broker.PopNormalDataToApplet();
|
||||
ASSERT(storage != nullptr);
|
||||
const auto data = storage->GetData();
|
||||
ASSERT(data.size() >= 0xC);
|
||||
|
||||
struct Arg {
|
||||
INSERT_PADDING_BYTES(4);
|
||||
AuthAppletType type;
|
||||
u8 arg0;
|
||||
u8 arg1;
|
||||
u8 arg2;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
};
|
||||
static_assert(sizeof(Arg) == 0xC, "Arg (AuthApplet) has incorrect size.");
|
||||
|
||||
Arg arg{};
|
||||
std::memcpy(&arg, data.data(), sizeof(Arg));
|
||||
|
||||
type = arg.type;
|
||||
arg0 = arg.arg0;
|
||||
arg1 = arg.arg1;
|
||||
arg2 = arg.arg2;
|
||||
}
|
||||
|
||||
bool Auth::TransactionComplete() const {
|
||||
return complete;
|
||||
}
|
||||
|
||||
ResultCode Auth::GetStatus() const {
|
||||
return successful ? RESULT_SUCCESS : ERROR_INVALID_PIN;
|
||||
}
|
||||
|
||||
void Auth::ExecuteInteractive() {
|
||||
UNREACHABLE_MSG("Unexpected interactive applet data.");
|
||||
}
|
||||
|
||||
void Auth::Execute() {
|
||||
if (complete) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto unimplemented_log = [this] {
|
||||
UNIMPLEMENTED_MSG("Unimplemented Auth applet type for type={:08X}, arg0={:02X}, "
|
||||
"arg1={:02X}, arg2={:02X}",
|
||||
static_cast<u32>(type), arg0, arg1, arg2);
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case AuthAppletType::ShowParentalAuthentication: {
|
||||
const auto callback = [this](bool successful) { AuthFinished(successful); };
|
||||
|
||||
if (arg0 == 1 && arg1 == 0 && arg2 == 1) {
|
||||
// ShowAuthenticatorForConfiguration
|
||||
frontend.VerifyPINForSettings(callback);
|
||||
} else if (arg1 == 0 && arg2 == 0) {
|
||||
// ShowParentalAuthentication(bool)
|
||||
frontend.VerifyPIN(callback, static_cast<bool>(arg0));
|
||||
} else {
|
||||
unimplemented_log();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AuthAppletType::RegisterParentalPasscode: {
|
||||
const auto callback = [this] { AuthFinished(true); };
|
||||
|
||||
if (arg0 == 0 && arg1 == 0 && arg2 == 0) {
|
||||
// RegisterParentalPasscode
|
||||
frontend.RegisterPIN(callback);
|
||||
} else {
|
||||
unimplemented_log();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AuthAppletType::ChangeParentalPasscode: {
|
||||
const auto callback = [this] { AuthFinished(true); };
|
||||
|
||||
if (arg0 == 0 && arg1 == 0 && arg2 == 0) {
|
||||
// ChangeParentalPasscode
|
||||
frontend.ChangePIN(callback);
|
||||
} else {
|
||||
unimplemented_log();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
unimplemented_log();
|
||||
}
|
||||
}
|
||||
|
||||
void Auth::AuthFinished(bool successful) {
|
||||
this->successful = successful;
|
||||
|
||||
struct Return {
|
||||
ResultCode result_code;
|
||||
};
|
||||
static_assert(sizeof(Return) == 0x4, "Return (AuthApplet) has incorrect size.");
|
||||
|
||||
Return return_{GetStatus()};
|
||||
|
||||
std::vector<u8> out(sizeof(Return));
|
||||
std::memcpy(out.data(), &return_, sizeof(Return));
|
||||
|
||||
broker.PushNormalDataFromApplet(IStorage{out});
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
PhotoViewer::PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend) : frontend(frontend) {}
|
||||
|
||||
PhotoViewer::~PhotoViewer() = default;
|
||||
@@ -83,13 +200,20 @@ void PhotoViewer::ViewFinished() {
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
StubApplet::StubApplet() = default;
|
||||
StubApplet::StubApplet(AppletId id) : id(id) {}
|
||||
|
||||
StubApplet::~StubApplet() = default;
|
||||
|
||||
void StubApplet::Initialize() {
|
||||
LOG_WARNING(Service_AM, "called (STUBBED)");
|
||||
Applet::Initialize();
|
||||
|
||||
const auto data = broker.PeekDataToAppletForDebug();
|
||||
Core::System::GetInstance().GetReporter().SaveUnimplementedAppletReport(
|
||||
static_cast<u32>(id), common_args.arguments_version, common_args.library_version,
|
||||
common_args.theme_color, common_args.play_startup_sound, common_args.system_tick,
|
||||
data.normal, data.interactive);
|
||||
|
||||
LogCurrentStorage(broker, "Initialize");
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,36 @@
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
enum class AuthAppletType : u32 {
|
||||
ShowParentalAuthentication,
|
||||
RegisterParentalPasscode,
|
||||
ChangeParentalPasscode,
|
||||
};
|
||||
|
||||
class Auth final : public Applet {
|
||||
public:
|
||||
explicit Auth(Core::Frontend::ParentalControlsApplet& frontend);
|
||||
~Auth() override;
|
||||
|
||||
void Initialize() override;
|
||||
bool TransactionComplete() const override;
|
||||
ResultCode GetStatus() const override;
|
||||
void ExecuteInteractive() override;
|
||||
void Execute() override;
|
||||
|
||||
void AuthFinished(bool successful = true);
|
||||
|
||||
private:
|
||||
Core::Frontend::ParentalControlsApplet& frontend;
|
||||
bool complete = false;
|
||||
bool successful = false;
|
||||
|
||||
AuthAppletType type = AuthAppletType::ShowParentalAuthentication;
|
||||
u8 arg0 = 0;
|
||||
u8 arg1 = 0;
|
||||
u8 arg2 = 0;
|
||||
};
|
||||
|
||||
enum class PhotoViewerAppletMode : u8 {
|
||||
CurrentApp = 0,
|
||||
AllApps = 1,
|
||||
@@ -34,7 +64,7 @@ private:
|
||||
|
||||
class StubApplet final : public Applet {
|
||||
public:
|
||||
StubApplet();
|
||||
explicit StubApplet(AppletId id);
|
||||
~StubApplet() override;
|
||||
|
||||
void Initialize() override;
|
||||
@@ -43,6 +73,9 @@ public:
|
||||
ResultCode GetStatus() const override;
|
||||
void ExecuteInteractive() override;
|
||||
void Execute() override;
|
||||
|
||||
private:
|
||||
AppletId id;
|
||||
};
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/romfs.h"
|
||||
#include "core/file_sys/system_archive/system_archive.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
#include "core/frontend/applets/general_frontend.h"
|
||||
#include "core/frontend/applets/web_browser.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/am/applets/web_browser.h"
|
||||
@@ -28,74 +30,187 @@
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
// TODO(DarkLordZach): There are other arguments in the WebBuffer structure that are currently not
|
||||
// parsed, for example footer mode and left stick mode. Some of these are not particularly relevant,
|
||||
// but some may be worth an implementation.
|
||||
constexpr u16 WEB_ARGUMENT_URL_TYPE = 0x6;
|
||||
|
||||
struct WebBufferHeader {
|
||||
u16 count;
|
||||
INSERT_PADDING_BYTES(6);
|
||||
enum class WebArgTLVType : u16 {
|
||||
InitialURL = 0x1,
|
||||
ShopArgumentsURL = 0x2, ///< TODO(DarkLordZach): This is not the official name.
|
||||
CallbackURL = 0x3,
|
||||
CallbackableURL = 0x4,
|
||||
ApplicationID = 0x5,
|
||||
DocumentPath = 0x6,
|
||||
DocumentKind = 0x7,
|
||||
SystemDataID = 0x8,
|
||||
ShareStartPage = 0x9,
|
||||
Whitelist = 0xA,
|
||||
News = 0xB,
|
||||
UserID = 0xE,
|
||||
AlbumEntry0 = 0xF,
|
||||
ScreenShotEnabled = 0x10,
|
||||
EcClientCertEnabled = 0x11,
|
||||
Unk12 = 0x12,
|
||||
PlayReportEnabled = 0x13,
|
||||
Unk14 = 0x14,
|
||||
Unk15 = 0x15,
|
||||
BootDisplayKind = 0x17,
|
||||
BackgroundKind = 0x18,
|
||||
FooterEnabled = 0x19,
|
||||
PointerEnabled = 0x1A,
|
||||
LeftStickMode = 0x1B,
|
||||
KeyRepeatFrame1 = 0x1C,
|
||||
KeyRepeatFrame2 = 0x1D,
|
||||
BootAsMediaPlayerInv = 0x1E,
|
||||
DisplayUrlKind = 0x1F,
|
||||
BootAsMediaPlayer = 0x21,
|
||||
ShopJumpEnabled = 0x22,
|
||||
MediaAutoPlayEnabled = 0x23,
|
||||
LobbyParameter = 0x24,
|
||||
ApplicationAlbumEntry = 0x26,
|
||||
JsExtensionEnabled = 0x27,
|
||||
AdditionalCommentText = 0x28,
|
||||
TouchEnabledOnContents = 0x29,
|
||||
UserAgentAdditionalString = 0x2A,
|
||||
AdditionalMediaData0 = 0x2B,
|
||||
MediaPlayerAutoCloseEnabled = 0x2C,
|
||||
PageCacheEnabled = 0x2D,
|
||||
WebAudioEnabled = 0x2E,
|
||||
Unk2F = 0x2F,
|
||||
YouTubeVideoWhitelist = 0x31,
|
||||
FooterFixedKind = 0x32,
|
||||
PageFadeEnabled = 0x33,
|
||||
MediaCreatorApplicationRatingAge = 0x34,
|
||||
BootLoadingIconEnabled = 0x35,
|
||||
PageScrollIndicationEnabled = 0x36,
|
||||
MediaPlayerSpeedControlEnabled = 0x37,
|
||||
AlbumEntry1 = 0x38,
|
||||
AlbumEntry2 = 0x39,
|
||||
AlbumEntry3 = 0x3A,
|
||||
AdditionalMediaData1 = 0x3B,
|
||||
AdditionalMediaData2 = 0x3C,
|
||||
AdditionalMediaData3 = 0x3D,
|
||||
BootFooterButton = 0x3E,
|
||||
OverrideWebAudioVolume = 0x3F,
|
||||
OverrideMediaAudioVolume = 0x40,
|
||||
BootMode = 0x41,
|
||||
WebSessionEnabled = 0x42,
|
||||
};
|
||||
static_assert(sizeof(WebBufferHeader) == 0x8, "WebBufferHeader has incorrect size.");
|
||||
|
||||
struct WebArgumentHeader {
|
||||
u16 type;
|
||||
enum class ShimKind : u32 {
|
||||
Shop = 1,
|
||||
Login = 2,
|
||||
Offline = 3,
|
||||
Share = 4,
|
||||
Web = 5,
|
||||
Wifi = 6,
|
||||
Lobby = 7,
|
||||
};
|
||||
|
||||
enum class ShopWebTarget {
|
||||
ApplicationInfo,
|
||||
AddOnContentList,
|
||||
SubscriptionList,
|
||||
ConsumableItemList,
|
||||
Home,
|
||||
Settings,
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::size_t SHIM_KIND_COUNT = 0x8;
|
||||
|
||||
struct WebArgHeader {
|
||||
u16 count;
|
||||
INSERT_PADDING_BYTES(2);
|
||||
ShimKind kind;
|
||||
};
|
||||
static_assert(sizeof(WebArgHeader) == 0x8, "WebArgHeader has incorrect size.");
|
||||
|
||||
struct WebArgTLV {
|
||||
WebArgTLVType type;
|
||||
u16 size;
|
||||
u32 offset;
|
||||
};
|
||||
static_assert(sizeof(WebArgumentHeader) == 0x8, "WebArgumentHeader has incorrect size.");
|
||||
static_assert(sizeof(WebArgTLV) == 0x8, "WebArgTLV has incorrect size.");
|
||||
|
||||
struct WebArgumentResult {
|
||||
struct WebCommonReturnValue {
|
||||
u32 result_code;
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
std::array<char, 0x1000> last_url;
|
||||
u64 last_url_size;
|
||||
};
|
||||
static_assert(sizeof(WebArgumentResult) == 0x1010, "WebArgumentResult has incorrect size.");
|
||||
static_assert(sizeof(WebCommonReturnValue) == 0x1010, "WebCommonReturnValue has incorrect size.");
|
||||
|
||||
static std::vector<u8> GetArgumentDataForTagType(const std::vector<u8>& data, u16 type) {
|
||||
WebBufferHeader header;
|
||||
ASSERT(sizeof(WebBufferHeader) <= data.size());
|
||||
std::memcpy(&header, data.data(), sizeof(WebBufferHeader));
|
||||
struct WebWifiPageArg {
|
||||
INSERT_PADDING_BYTES(4);
|
||||
std::array<char, 0x100> connection_test_url;
|
||||
std::array<char, 0x400> initial_url;
|
||||
std::array<u8, 0x10> nifm_network_uuid;
|
||||
u32 nifm_requirement;
|
||||
};
|
||||
static_assert(sizeof(WebWifiPageArg) == 0x518, "WebWifiPageArg has incorrect size.");
|
||||
|
||||
u64 offset = sizeof(WebBufferHeader);
|
||||
for (u16 i = 0; i < header.count; ++i) {
|
||||
WebArgumentHeader arg;
|
||||
ASSERT(offset + sizeof(WebArgumentHeader) <= data.size());
|
||||
std::memcpy(&arg, data.data() + offset, sizeof(WebArgumentHeader));
|
||||
offset += sizeof(WebArgumentHeader);
|
||||
struct WebWifiReturnValue {
|
||||
INSERT_PADDING_BYTES(4);
|
||||
u32 result;
|
||||
};
|
||||
static_assert(sizeof(WebWifiReturnValue) == 0x8, "WebWifiReturnValue has incorrect size.");
|
||||
|
||||
if (arg.type == type) {
|
||||
std::vector<u8> out(arg.size);
|
||||
offset += arg.offset;
|
||||
ASSERT(offset + arg.size <= data.size());
|
||||
std::memcpy(out.data(), data.data() + offset, out.size());
|
||||
enum class OfflineWebSource : u32 {
|
||||
OfflineHtmlPage = 0x1,
|
||||
ApplicationLegalInformation = 0x2,
|
||||
SystemDataPage = 0x3,
|
||||
};
|
||||
|
||||
std::map<WebArgTLVType, std::vector<u8>> GetWebArguments(const std::vector<u8>& arg) {
|
||||
if (arg.size() < sizeof(WebArgHeader))
|
||||
return {};
|
||||
|
||||
WebArgHeader header{};
|
||||
std::memcpy(&header, arg.data(), sizeof(WebArgHeader));
|
||||
|
||||
std::map<WebArgTLVType, std::vector<u8>> out;
|
||||
u64 offset = sizeof(WebArgHeader);
|
||||
for (std::size_t i = 0; i < header.count; ++i) {
|
||||
if (arg.size() < (offset + sizeof(WebArgTLV)))
|
||||
return out;
|
||||
}
|
||||
|
||||
offset += arg.offset + arg.size;
|
||||
WebArgTLV tlv{};
|
||||
std::memcpy(&tlv, arg.data() + offset, sizeof(WebArgTLV));
|
||||
offset += sizeof(WebArgTLV);
|
||||
|
||||
offset += tlv.offset;
|
||||
if (arg.size() < (offset + tlv.size))
|
||||
return out;
|
||||
|
||||
std::vector<u8> data(tlv.size);
|
||||
std::memcpy(data.data(), arg.data() + offset, tlv.size);
|
||||
offset += tlv.size;
|
||||
|
||||
out.insert_or_assign(tlv.type, data);
|
||||
}
|
||||
|
||||
return {};
|
||||
return out;
|
||||
}
|
||||
|
||||
static FileSys::VirtualFile GetManualRomFS() {
|
||||
auto& loader{Core::System::GetInstance().GetAppLoader()};
|
||||
|
||||
FileSys::VirtualFile out;
|
||||
if (loader.ReadManualRomFS(out) == Loader::ResultStatus::Success)
|
||||
return out;
|
||||
|
||||
FileSys::VirtualFile GetApplicationRomFS(u64 title_id, FileSys::ContentRecordType type) {
|
||||
const auto& installed{Core::System::GetInstance().GetContentProvider()};
|
||||
const auto res = installed.GetEntry(Core::System::GetInstance().CurrentProcess()->GetTitleID(),
|
||||
FileSys::ContentRecordType::Manual);
|
||||
const auto res = installed.GetEntry(title_id, type);
|
||||
|
||||
if (res != nullptr)
|
||||
if (res != nullptr) {
|
||||
return res->GetRomFS();
|
||||
}
|
||||
|
||||
if (type == FileSys::ContentRecordType::Data) {
|
||||
return FileSys::SystemArchive::SynthesizeSystemArchive(title_id);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
WebBrowser::WebBrowser(Core::Frontend::WebBrowserApplet& frontend) : frontend(frontend) {}
|
||||
} // Anonymous namespace
|
||||
|
||||
WebBrowser::WebBrowser(Core::Frontend::WebBrowserApplet& frontend, u64 current_process_title_id,
|
||||
Core::Frontend::ECommerceApplet* frontend_e_commerce)
|
||||
: frontend(frontend), frontend_e_commerce(frontend_e_commerce),
|
||||
current_process_title_id(current_process_title_id) {}
|
||||
|
||||
WebBrowser::~WebBrowser() = default;
|
||||
|
||||
@@ -111,24 +226,12 @@ void WebBrowser::Initialize() {
|
||||
ASSERT(web_arg_storage != nullptr);
|
||||
const auto& web_arg = web_arg_storage->GetData();
|
||||
|
||||
const auto url_data = GetArgumentDataForTagType(web_arg, WEB_ARGUMENT_URL_TYPE);
|
||||
filename = Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(url_data.data()), url_data.size());
|
||||
ASSERT(web_arg.size() >= 0x8);
|
||||
std::memcpy(&kind, web_arg.data() + 0x4, sizeof(ShimKind));
|
||||
|
||||
temporary_dir = FileUtil::SanitizePath(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) +
|
||||
"web_applet_manual",
|
||||
FileUtil::DirectorySeparator::PlatformDefault);
|
||||
FileUtil::DeleteDirRecursively(temporary_dir);
|
||||
args = GetWebArguments(web_arg);
|
||||
|
||||
manual_romfs = GetManualRomFS();
|
||||
if (manual_romfs == nullptr) {
|
||||
status = ResultCode(-1);
|
||||
LOG_ERROR(Service_AM, "Failed to find manual for current process!");
|
||||
}
|
||||
|
||||
filename =
|
||||
FileUtil::SanitizePath(temporary_dir + DIR_SEP + "html-document" + DIR_SEP + filename,
|
||||
FileUtil::DirectorySeparator::PlatformDefault);
|
||||
InitializeInternal();
|
||||
}
|
||||
|
||||
bool WebBrowser::TransactionComplete() const {
|
||||
@@ -144,24 +247,25 @@ void WebBrowser::ExecuteInteractive() {
|
||||
}
|
||||
|
||||
void WebBrowser::Execute() {
|
||||
if (complete)
|
||||
if (complete) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (status != RESULT_SUCCESS) {
|
||||
complete = true;
|
||||
return;
|
||||
}
|
||||
|
||||
frontend.OpenPage(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
|
||||
ExecuteInternal();
|
||||
}
|
||||
|
||||
void WebBrowser::UnpackRomFS() {
|
||||
if (unpacked)
|
||||
return;
|
||||
|
||||
ASSERT(manual_romfs != nullptr);
|
||||
ASSERT(offline_romfs != nullptr);
|
||||
const auto dir =
|
||||
FileSys::ExtractRomFS(manual_romfs, FileSys::RomFSExtractionType::SingleDiscard);
|
||||
FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard);
|
||||
const auto& vfs{Core::System::GetInstance().GetFilesystem()};
|
||||
const auto temp_dir = vfs->CreateDirectory(temporary_dir, FileSys::Mode::ReadWrite);
|
||||
FileSys::VfsRawCopyD(dir, temp_dir);
|
||||
@@ -172,17 +276,275 @@ void WebBrowser::UnpackRomFS() {
|
||||
void WebBrowser::Finalize() {
|
||||
complete = true;
|
||||
|
||||
WebArgumentResult out{};
|
||||
WebCommonReturnValue out{};
|
||||
out.result_code = 0;
|
||||
out.last_url_size = 0;
|
||||
|
||||
std::vector<u8> data(sizeof(WebArgumentResult));
|
||||
std::memcpy(data.data(), &out, sizeof(WebArgumentResult));
|
||||
std::vector<u8> data(sizeof(WebCommonReturnValue));
|
||||
std::memcpy(data.data(), &out, sizeof(WebCommonReturnValue));
|
||||
|
||||
broker.PushNormalDataFromApplet(IStorage{data});
|
||||
broker.SignalStateChanged();
|
||||
|
||||
if (!temporary_dir.empty() && FileUtil::IsDirectory(temporary_dir)) {
|
||||
FileUtil::DeleteDirRecursively(temporary_dir);
|
||||
}
|
||||
}
|
||||
|
||||
void WebBrowser::InitializeInternal() {
|
||||
using WebAppletInitializer = void (WebBrowser::*)();
|
||||
|
||||
constexpr std::array<WebAppletInitializer, SHIM_KIND_COUNT> functions{
|
||||
nullptr, &WebBrowser::InitializeShop,
|
||||
nullptr, &WebBrowser::InitializeOffline,
|
||||
nullptr, nullptr,
|
||||
nullptr, nullptr,
|
||||
};
|
||||
|
||||
const auto index = static_cast<u32>(kind);
|
||||
|
||||
if (index > functions.size() || functions[index] == nullptr) {
|
||||
LOG_ERROR(Service_AM, "Invalid shim_kind={:08X}", index);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto function = functions[index];
|
||||
(this->*function)();
|
||||
}
|
||||
|
||||
void WebBrowser::ExecuteInternal() {
|
||||
using WebAppletExecutor = void (WebBrowser::*)();
|
||||
|
||||
constexpr std::array<WebAppletExecutor, SHIM_KIND_COUNT> functions{
|
||||
nullptr, &WebBrowser::ExecuteShop,
|
||||
nullptr, &WebBrowser::ExecuteOffline,
|
||||
nullptr, nullptr,
|
||||
nullptr, nullptr,
|
||||
};
|
||||
|
||||
const auto index = static_cast<u32>(kind);
|
||||
|
||||
if (index > functions.size() || functions[index] == nullptr) {
|
||||
LOG_ERROR(Service_AM, "Invalid shim_kind={:08X}", index);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto function = functions[index];
|
||||
(this->*function)();
|
||||
}
|
||||
|
||||
void WebBrowser::InitializeShop() {
|
||||
if (frontend_e_commerce == nullptr) {
|
||||
LOG_ERROR(Service_AM, "Missing ECommerce Applet frontend!");
|
||||
status = ResultCode(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto user_id_data = args.find(WebArgTLVType::UserID);
|
||||
|
||||
user_id = std::nullopt;
|
||||
if (user_id_data != args.end()) {
|
||||
user_id = u128{};
|
||||
std::memcpy(user_id->data(), user_id_data->second.data(), sizeof(u128));
|
||||
}
|
||||
|
||||
const auto url = args.find(WebArgTLVType::ShopArgumentsURL);
|
||||
|
||||
if (url == args.end()) {
|
||||
LOG_ERROR(Service_AM, "Missing EShop Arguments URL for initialization!");
|
||||
status = ResultCode(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> split_query;
|
||||
Common::SplitString(Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(url->second.data()), url->second.size()),
|
||||
'?', split_query);
|
||||
|
||||
// 2 -> Main URL '?' Query Parameters
|
||||
// Less is missing info, More is malformed
|
||||
if (split_query.size() != 2) {
|
||||
LOG_ERROR(Service_AM, "EShop Arguments has more than one question mark, malformed");
|
||||
status = ResultCode(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> queries;
|
||||
Common::SplitString(split_query[1], '&', queries);
|
||||
|
||||
const auto split_single_query =
|
||||
[](const std::string& in) -> std::pair<std::string, std::string> {
|
||||
const auto index = in.find('=');
|
||||
if (index == std::string::npos || index == in.size() - 1) {
|
||||
return {in, ""};
|
||||
}
|
||||
|
||||
return {in.substr(0, index), in.substr(index + 1)};
|
||||
};
|
||||
|
||||
std::transform(queries.begin(), queries.end(),
|
||||
std::inserter(shop_query, std::next(shop_query.begin())), split_single_query);
|
||||
|
||||
const auto scene = shop_query.find("scene");
|
||||
|
||||
if (scene == shop_query.end()) {
|
||||
LOG_ERROR(Service_AM, "No scene parameter was passed via shop query!");
|
||||
status = ResultCode(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
const std::map<std::string, ShopWebTarget, std::less<>> target_map{
|
||||
{"product_detail", ShopWebTarget::ApplicationInfo},
|
||||
{"aocs", ShopWebTarget::AddOnContentList},
|
||||
{"subscriptions", ShopWebTarget::SubscriptionList},
|
||||
{"consumption", ShopWebTarget::ConsumableItemList},
|
||||
{"settings", ShopWebTarget::Settings},
|
||||
{"top", ShopWebTarget::Home},
|
||||
};
|
||||
|
||||
const auto target = target_map.find(scene->second);
|
||||
if (target == target_map.end()) {
|
||||
LOG_ERROR(Service_AM, "Scene for shop query is invalid! (scene={})", scene->second);
|
||||
status = ResultCode(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
shop_web_target = target->second;
|
||||
|
||||
const auto title_id_data = shop_query.find("dst_app_id");
|
||||
if (title_id_data != shop_query.end()) {
|
||||
title_id = std::stoull(title_id_data->second, nullptr, 0x10);
|
||||
}
|
||||
|
||||
const auto mode_data = shop_query.find("mode");
|
||||
if (mode_data != shop_query.end()) {
|
||||
shop_full_display = mode_data->second == "full";
|
||||
}
|
||||
}
|
||||
|
||||
void WebBrowser::InitializeOffline() {
|
||||
if (args.find(WebArgTLVType::DocumentPath) == args.end() ||
|
||||
args.find(WebArgTLVType::DocumentKind) == args.end() ||
|
||||
args.find(WebArgTLVType::ApplicationID) == args.end()) {
|
||||
status = ResultCode(-1);
|
||||
LOG_ERROR(Service_AM, "Missing necessary parameters for initialization!");
|
||||
}
|
||||
|
||||
const auto url_data = args[WebArgTLVType::DocumentPath];
|
||||
filename = Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(url_data.data()), url_data.size());
|
||||
|
||||
OfflineWebSource source;
|
||||
ASSERT(args[WebArgTLVType::DocumentKind].size() >= 4);
|
||||
std::memcpy(&source, args[WebArgTLVType::DocumentKind].data(), sizeof(OfflineWebSource));
|
||||
|
||||
constexpr std::array<const char*, 3> WEB_SOURCE_NAMES{
|
||||
"manual",
|
||||
"legal",
|
||||
"system",
|
||||
};
|
||||
|
||||
temporary_dir =
|
||||
FileUtil::SanitizePath(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + "web_applet_" +
|
||||
WEB_SOURCE_NAMES[static_cast<u32>(source) - 1],
|
||||
FileUtil::DirectorySeparator::PlatformDefault);
|
||||
FileUtil::DeleteDirRecursively(temporary_dir);
|
||||
|
||||
u64 title_id = 0; // 0 corresponds to current process
|
||||
ASSERT(args[WebArgTLVType::ApplicationID].size() >= 0x8);
|
||||
std::memcpy(&title_id, args[WebArgTLVType::ApplicationID].data(), sizeof(u64));
|
||||
FileSys::ContentRecordType type = FileSys::ContentRecordType::Data;
|
||||
|
||||
switch (source) {
|
||||
case OfflineWebSource::OfflineHtmlPage:
|
||||
// While there is an AppID TLV field, in official SW this is always ignored.
|
||||
title_id = 0;
|
||||
type = FileSys::ContentRecordType::HtmlDocument;
|
||||
break;
|
||||
case OfflineWebSource::ApplicationLegalInformation:
|
||||
type = FileSys::ContentRecordType::LegalInformation;
|
||||
break;
|
||||
case OfflineWebSource::SystemDataPage:
|
||||
type = FileSys::ContentRecordType::Data;
|
||||
break;
|
||||
}
|
||||
|
||||
if (title_id == 0) {
|
||||
title_id = current_process_title_id;
|
||||
}
|
||||
|
||||
offline_romfs = GetApplicationRomFS(title_id, type);
|
||||
if (offline_romfs == nullptr) {
|
||||
status = ResultCode(-1);
|
||||
LOG_ERROR(Service_AM, "Failed to find offline data for request!");
|
||||
}
|
||||
|
||||
std::string path_additional_directory;
|
||||
if (source == OfflineWebSource::OfflineHtmlPage) {
|
||||
path_additional_directory = std::string(DIR_SEP).append("html-document");
|
||||
}
|
||||
|
||||
filename =
|
||||
FileUtil::SanitizePath(temporary_dir + path_additional_directory + DIR_SEP + filename,
|
||||
FileUtil::DirectorySeparator::PlatformDefault);
|
||||
}
|
||||
|
||||
void WebBrowser::ExecuteShop() {
|
||||
const auto callback = [this]() { Finalize(); };
|
||||
|
||||
const auto check_optional_parameter = [this](const auto& p) {
|
||||
if (!p.has_value()) {
|
||||
LOG_ERROR(Service_AM, "Missing one or more necessary parameters for execution!");
|
||||
status = ResultCode(-1);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
switch (shop_web_target) {
|
||||
case ShopWebTarget::ApplicationInfo:
|
||||
if (!check_optional_parameter(title_id))
|
||||
return;
|
||||
frontend_e_commerce->ShowApplicationInformation(callback, *title_id, user_id,
|
||||
shop_full_display, shop_extra_parameter);
|
||||
break;
|
||||
case ShopWebTarget::AddOnContentList:
|
||||
if (!check_optional_parameter(title_id))
|
||||
return;
|
||||
frontend_e_commerce->ShowAddOnContentList(callback, *title_id, user_id, shop_full_display);
|
||||
break;
|
||||
case ShopWebTarget::ConsumableItemList:
|
||||
if (!check_optional_parameter(title_id))
|
||||
return;
|
||||
frontend_e_commerce->ShowConsumableItemList(callback, *title_id, user_id);
|
||||
break;
|
||||
case ShopWebTarget::Home:
|
||||
if (!check_optional_parameter(user_id))
|
||||
return;
|
||||
if (!check_optional_parameter(shop_full_display))
|
||||
return;
|
||||
frontend_e_commerce->ShowShopHome(callback, *user_id, *shop_full_display);
|
||||
break;
|
||||
case ShopWebTarget::Settings:
|
||||
if (!check_optional_parameter(user_id))
|
||||
return;
|
||||
if (!check_optional_parameter(shop_full_display))
|
||||
return;
|
||||
frontend_e_commerce->ShowSettings(callback, *user_id, *shop_full_display);
|
||||
break;
|
||||
case ShopWebTarget::SubscriptionList:
|
||||
if (!check_optional_parameter(title_id))
|
||||
return;
|
||||
frontend_e_commerce->ShowSubscriptionList(callback, *title_id, user_id);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void WebBrowser::ExecuteOffline() {
|
||||
frontend.OpenPageLocal(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
|
||||
}
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
|
||||
@@ -4,15 +4,22 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
enum class ShimKind : u32;
|
||||
enum class ShopWebTarget;
|
||||
enum class WebArgTLVType : u16;
|
||||
|
||||
class WebBrowser final : public Applet {
|
||||
public:
|
||||
WebBrowser(Core::Frontend::WebBrowserApplet& frontend);
|
||||
WebBrowser(Core::Frontend::WebBrowserApplet& frontend, u64 current_process_title_id,
|
||||
Core::Frontend::ECommerceApplet* frontend_e_commerce = nullptr);
|
||||
|
||||
~WebBrowser() override;
|
||||
|
||||
void Initialize() override;
|
||||
@@ -32,15 +39,41 @@ public:
|
||||
void Finalize();
|
||||
|
||||
private:
|
||||
void InitializeInternal();
|
||||
void ExecuteInternal();
|
||||
|
||||
// Specific initializers for the types of web applets
|
||||
void InitializeShop();
|
||||
void InitializeOffline();
|
||||
|
||||
// Specific executors for the types of web applets
|
||||
void ExecuteShop();
|
||||
void ExecuteOffline();
|
||||
|
||||
Core::Frontend::WebBrowserApplet& frontend;
|
||||
|
||||
// Extra frontends for specialized functions
|
||||
Core::Frontend::ECommerceApplet* frontend_e_commerce;
|
||||
|
||||
bool complete = false;
|
||||
bool unpacked = false;
|
||||
ResultCode status = RESULT_SUCCESS;
|
||||
|
||||
FileSys::VirtualFile manual_romfs;
|
||||
u64 current_process_title_id;
|
||||
|
||||
ShimKind kind;
|
||||
std::map<WebArgTLVType, std::vector<u8>> args;
|
||||
|
||||
FileSys::VirtualFile offline_romfs;
|
||||
std::string temporary_dir;
|
||||
std::string filename;
|
||||
|
||||
ShopWebTarget shop_web_target;
|
||||
std::map<std::string, std::string, std::less<>> shop_query;
|
||||
std::optional<u64> title_id = 0;
|
||||
std::optional<u128> user_id;
|
||||
std::optional<bool> shop_full_display;
|
||||
std::string shop_extra_parameter;
|
||||
};
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/arp/arp.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::ARP {
|
||||
|
||||
class ARP_R final : public ServiceFramework<ARP_R> {
|
||||
public:
|
||||
explicit ARP_R() : ServiceFramework{"arp:r"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetApplicationLaunchProperty"},
|
||||
{1, nullptr, "GetApplicationLaunchPropertyWithApplicationId"},
|
||||
{2, nullptr, "GetApplicationControlProperty"},
|
||||
{3, nullptr, "GetApplicationControlPropertyWithApplicationId"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
class IRegistrar final : public ServiceFramework<IRegistrar> {
|
||||
public:
|
||||
explicit IRegistrar() : ServiceFramework{"IRegistrar"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Issue"},
|
||||
{1, nullptr, "SetApplicationLaunchProperty"},
|
||||
{2, nullptr, "SetApplicationControlProperty"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
class ARP_W final : public ServiceFramework<ARP_W> {
|
||||
public:
|
||||
explicit ARP_W() : ServiceFramework{"arp:w"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ARP_W::AcquireRegistrar, "AcquireRegistrar"},
|
||||
{1, nullptr, "DeleteProperties"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void AcquireRegistrar(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ARP, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IRegistrar>();
|
||||
}
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm) {
|
||||
std::make_shared<ARP_R>()->InstallAsService(sm);
|
||||
std::make_shared<ARP_W>()->InstallAsService(sm);
|
||||
}
|
||||
|
||||
} // namespace Service::ARP
|
||||
@@ -1,16 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Service::SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
|
||||
namespace Service::ARP {
|
||||
|
||||
/// Registers all ARP services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& sm);
|
||||
|
||||
} // namespace Service::ARP
|
||||
@@ -58,8 +58,8 @@ public:
|
||||
{9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
|
||||
{10, nullptr, "GetAudioOutPlayedSampleCount"},
|
||||
{11, nullptr, "FlushAudioOutBuffers"},
|
||||
{12, nullptr, "SetAudioOutVolume"},
|
||||
{13, nullptr, "GetAudioOutVolume"},
|
||||
{12, &IAudioOut::SetAudioOutVolume, "SetAudioOutVolume"},
|
||||
{13, &IAudioOut::GetAudioOutVolume, "GetAudioOutVolume"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
@@ -183,6 +183,25 @@ private:
|
||||
rb.Push(static_cast<u32>(stream->GetQueueSize()));
|
||||
}
|
||||
|
||||
void SetAudioOutVolume(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const float volume = rp.Pop<float>();
|
||||
LOG_DEBUG(Service_Audio, "called, volume={}", volume);
|
||||
|
||||
stream->SetVolume(volume);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetAudioOutVolume(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(stream->GetVolume());
|
||||
}
|
||||
|
||||
AudioCore::AudioOut& audio_core;
|
||||
AudioCore::StreamPtr stream;
|
||||
std::string device_name;
|
||||
|
||||
@@ -167,13 +167,12 @@ public:
|
||||
{3, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceName"},
|
||||
{4, &IAudioDevice::QueryAudioDeviceSystemEvent, "QueryAudioDeviceSystemEvent"},
|
||||
{5, &IAudioDevice::GetActiveChannelCount, "GetActiveChannelCount"},
|
||||
{6, &IAudioDevice::ListAudioDeviceName,
|
||||
"ListAudioDeviceNameAuto"}, // TODO(ogniK): Confirm if autos are identical to non auto
|
||||
{6, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceNameAuto"},
|
||||
{7, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolumeAuto"},
|
||||
{8, nullptr, "GetAudioDeviceOutputVolumeAuto"},
|
||||
{10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"},
|
||||
{11, nullptr, "QueryAudioDeviceInputEvent"},
|
||||
{12, nullptr, "QueryAudioDeviceOutputEvent"},
|
||||
{12, &IAudioDevice::QueryAudioDeviceOutputEvent, "QueryAudioDeviceOutputEvent"},
|
||||
{13, nullptr, "GetAudioSystemMasterVolumeSetting"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
@@ -181,6 +180,11 @@ public:
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IAudioOutBufferReleasedEvent");
|
||||
|
||||
// Should only be signalled when an audio output device has been changed, example: speaker
|
||||
// to headset
|
||||
audio_output_device_switch_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, "IAudioDevice:AudioOutputDeviceSwitchedEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -237,7 +241,16 @@ private:
|
||||
rb.Push<u32>(1);
|
||||
}
|
||||
|
||||
void QueryAudioDeviceOutputEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(audio_output_device_switch_event.readable);
|
||||
}
|
||||
|
||||
Kernel::EventPair buffer_event;
|
||||
Kernel::EventPair audio_output_device_switch_event;
|
||||
|
||||
}; // namespace Audio
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "core/hle/service/fatal/fatal.h"
|
||||
#include "core/hle/service/fatal/fatal_p.h"
|
||||
#include "core/hle/service/fatal/fatal_u.h"
|
||||
#include "core/reporter.h"
|
||||
|
||||
namespace Service::Fatal {
|
||||
|
||||
@@ -100,27 +101,10 @@ static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
|
||||
|
||||
LOG_ERROR(Service_Fatal, "{}", crash_report);
|
||||
|
||||
const std::string crashreport_dir =
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + "crash_logs";
|
||||
|
||||
if (!FileUtil::CreateFullPath(crashreport_dir)) {
|
||||
LOG_ERROR(
|
||||
Service_Fatal,
|
||||
"Unable to create crash report directory. Possible log directory permissions issue.");
|
||||
return;
|
||||
}
|
||||
|
||||
const std::time_t t = std::time(nullptr);
|
||||
const std::string crashreport_filename =
|
||||
fmt::format("{}/{:016x}-{:%F-%H%M%S}.log", crashreport_dir, title_id, *std::localtime(&t));
|
||||
|
||||
auto file = FileUtil::IOFile(crashreport_filename, "wb");
|
||||
if (file.IsOpen()) {
|
||||
file.WriteString(crash_report);
|
||||
LOG_ERROR(Service_Fatal, "Saving error report to {}", crashreport_filename);
|
||||
} else {
|
||||
LOG_ERROR(Service_Fatal, "Failed to save error report to {}", crashreport_filename);
|
||||
}
|
||||
Core::System::GetInstance().GetReporter().SaveCrashReport(
|
||||
title_id, error_code, info.set_flags, info.program_entry_point, info.sp, info.pc,
|
||||
info.pstate, info.afsr0, info.afsr1, info.esr, info.far, info.registers, info.backtrace,
|
||||
info.backtrace_size, info.ArchAsString(), info.unk10);
|
||||
}
|
||||
|
||||
static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) {
|
||||
|
||||
12
src/core/hle/service/friend/errors.h
Normal file
12
src/core/hle/service/friend/errors.h
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Service::Friend {
|
||||
|
||||
constexpr ResultCode ERR_NO_NOTIFICATIONS{ErrorModule::Account, 15};
|
||||
}
|
||||
@@ -2,8 +2,13 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <queue>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/uuid.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/friend/errors.h"
|
||||
#include "core/hle/service/friend/friend.h"
|
||||
#include "core/hle/service/friend/interface.h"
|
||||
|
||||
@@ -109,6 +114,105 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
class INotificationService final : public ServiceFramework<INotificationService> {
|
||||
public:
|
||||
INotificationService(Common::UUID uuid) : ServiceFramework("INotificationService"), uuid(uuid) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &INotificationService::GetEvent, "GetEvent"},
|
||||
{1, &INotificationService::Clear, "Clear"},
|
||||
{2, &INotificationService::Pop, "Pop"}
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void GetEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
if (!is_event_created) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
notification_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Manual, "INotificationService:NotifyEvent");
|
||||
is_event_created = true;
|
||||
}
|
||||
rb.PushCopyObjects(notification_event.readable);
|
||||
}
|
||||
|
||||
void Clear(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
while (!notifications.empty()) {
|
||||
notifications.pop();
|
||||
}
|
||||
std::memset(&states, 0, sizeof(States));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Pop(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
|
||||
if (notifications.empty()) {
|
||||
LOG_ERROR(Service_ACC, "No notifications in queue!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_NO_NOTIFICATIONS);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto notification = notifications.front();
|
||||
notifications.pop();
|
||||
|
||||
switch (notification.notification_type) {
|
||||
case NotificationTypes::HasUpdatedFriendsList:
|
||||
states.has_updated_friends = false;
|
||||
break;
|
||||
case NotificationTypes::HasReceivedFriendRequest:
|
||||
states.has_received_friend_request = false;
|
||||
break;
|
||||
default:
|
||||
// HOS seems not have an error case for an unknown notification
|
||||
LOG_WARNING(Service_ACC, "Unknown notification {:08X}",
|
||||
static_cast<u32>(notification.notification_type));
|
||||
break;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<SizedNotificationInfo>(notification);
|
||||
}
|
||||
|
||||
enum class NotificationTypes : u32 {
|
||||
HasUpdatedFriendsList = 0x65,
|
||||
HasReceivedFriendRequest = 0x1
|
||||
};
|
||||
|
||||
struct SizedNotificationInfo {
|
||||
NotificationTypes notification_type;
|
||||
INSERT_PADDING_WORDS(
|
||||
1); // TODO(ogniK): This doesn't seem to be used within any IPC returns as of now
|
||||
u64_le account_id;
|
||||
};
|
||||
static_assert(sizeof(SizedNotificationInfo) == 0x10,
|
||||
"SizedNotificationInfo is an incorrect size");
|
||||
|
||||
struct States {
|
||||
bool has_updated_friends;
|
||||
bool has_received_friend_request;
|
||||
};
|
||||
|
||||
Common::UUID uuid;
|
||||
bool is_event_created = false;
|
||||
Kernel::EventPair notification_event;
|
||||
std::queue<SizedNotificationInfo> notifications;
|
||||
States states{};
|
||||
};
|
||||
|
||||
void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -116,6 +220,17 @@ void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
}
|
||||
|
||||
void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto uuid = rp.PopRaw<Common::UUID>();
|
||||
|
||||
LOG_DEBUG(Service_ACC, "called, uuid={}", uuid.Format());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<INotificationService>(uuid);
|
||||
}
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)) {}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ public:
|
||||
~Interface() override;
|
||||
|
||||
void CreateFriendService(Kernel::HLERequestContext& ctx);
|
||||
void CreateNotificationService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
|
||||
@@ -10,7 +10,7 @@ Friend::Friend(std::shared_ptr<Module> module, const char* name)
|
||||
: Interface(std::move(module), name) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &Friend::CreateFriendService, "CreateFriendService"},
|
||||
{1, nullptr, "CreateNotificationService"},
|
||||
{1, &Friend::CreateNotificationService, "CreateNotificationService"},
|
||||
{2, nullptr, "CreateDaemonSuspendSessionService"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
297
src/core/hle/service/glue/arp.cpp
Normal file
297
src/core/hle/service/glue/arp.cpp
Normal file
@@ -0,0 +1,297 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/glue/arp.h"
|
||||
#include "core/hle/service/glue/errors.h"
|
||||
#include "core/hle/service/glue/manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::Glue {
|
||||
|
||||
namespace {
|
||||
std::optional<u64> GetTitleIDForProcessID(const Core::System& system, u64 process_id) {
|
||||
const auto& list = system.Kernel().GetProcessList();
|
||||
const auto iter = std::find_if(list.begin(), list.end(), [&process_id](const auto& process) {
|
||||
return process->GetProcessID() == process_id;
|
||||
});
|
||||
|
||||
if (iter == list.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return (*iter)->GetTitleID();
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
ARP_R::ARP_R(const Core::System& system, const ARPManager& manager)
|
||||
: ServiceFramework{"arp:r"}, system(system), manager(manager) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ARP_R::GetApplicationLaunchProperty, "GetApplicationLaunchProperty"},
|
||||
{1, &ARP_R::GetApplicationLaunchPropertyWithApplicationId, "GetApplicationLaunchPropertyWithApplicationId"},
|
||||
{2, &ARP_R::GetApplicationControlProperty, "GetApplicationControlProperty"},
|
||||
{3, &ARP_R::GetApplicationControlPropertyWithApplicationId, "GetApplicationControlPropertyWithApplicationId"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
ARP_R::~ARP_R() = default;
|
||||
|
||||
void ARP_R::GetApplicationLaunchProperty(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto process_id = rp.PopRaw<u64>();
|
||||
|
||||
LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id);
|
||||
|
||||
const auto title_id = GetTitleIDForProcessID(system, process_id);
|
||||
if (!title_id.has_value()) {
|
||||
LOG_ERROR(Service_ARP, "Failed to get title ID for process ID!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_NOT_REGISTERED);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto res = manager.GetLaunchProperty(*title_id);
|
||||
|
||||
if (res.Failed()) {
|
||||
LOG_ERROR(Service_ARP, "Failed to get launch property!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(*res);
|
||||
}
|
||||
|
||||
void ARP_R::GetApplicationLaunchPropertyWithApplicationId(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
LOG_DEBUG(Service_ARP, "called, title_id={:016X}", title_id);
|
||||
|
||||
const auto res = manager.GetLaunchProperty(title_id);
|
||||
|
||||
if (res.Failed()) {
|
||||
LOG_ERROR(Service_ARP, "Failed to get launch property!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(*res);
|
||||
}
|
||||
|
||||
void ARP_R::GetApplicationControlProperty(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto process_id = rp.PopRaw<u64>();
|
||||
|
||||
LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id);
|
||||
|
||||
const auto title_id = GetTitleIDForProcessID(system, process_id);
|
||||
if (!title_id.has_value()) {
|
||||
LOG_ERROR(Service_ARP, "Failed to get title ID for process ID!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_NOT_REGISTERED);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto res = manager.GetControlProperty(*title_id);
|
||||
|
||||
if (res.Failed()) {
|
||||
LOG_ERROR(Service_ARP, "Failed to get control property!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(*res);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ARP_R::GetApplicationControlPropertyWithApplicationId(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
LOG_DEBUG(Service_ARP, "called, title_id={:016X}", title_id);
|
||||
|
||||
const auto res = manager.GetControlProperty(title_id);
|
||||
|
||||
if (res.Failed()) {
|
||||
LOG_ERROR(Service_ARP, "Failed to get control property!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(*res);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
class IRegistrar final : public ServiceFramework<IRegistrar> {
|
||||
friend class ARP_W;
|
||||
|
||||
public:
|
||||
explicit IRegistrar(
|
||||
std::function<ResultCode(u64, ApplicationLaunchProperty, std::vector<u8>)> issuer)
|
||||
: ServiceFramework{"IRegistrar"}, issue_process_id(std::move(issuer)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IRegistrar::Issue, "Issue"},
|
||||
{1, &IRegistrar::SetApplicationLaunchProperty, "SetApplicationLaunchProperty"},
|
||||
{2, &IRegistrar::SetApplicationControlProperty, "SetApplicationControlProperty"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void Issue(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto process_id = rp.PopRaw<u64>();
|
||||
|
||||
LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id);
|
||||
|
||||
if (process_id == 0) {
|
||||
LOG_ERROR(Service_ARP, "Must have non-zero process ID!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_INVALID_PROCESS_ID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (issued) {
|
||||
LOG_ERROR(Service_ARP,
|
||||
"Attempted to issue registrar, but registrar is already issued!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_INVALID_ACCESS);
|
||||
return;
|
||||
}
|
||||
|
||||
issue_process_id(process_id, launch, std::move(control));
|
||||
issued = true;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void SetApplicationLaunchProperty(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ARP, "called");
|
||||
|
||||
if (issued) {
|
||||
LOG_ERROR(
|
||||
Service_ARP,
|
||||
"Attempted to set application launch property, but registrar is already issued!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_INVALID_ACCESS);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
launch = rp.PopRaw<ApplicationLaunchProperty>();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void SetApplicationControlProperty(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ARP, "called");
|
||||
|
||||
if (issued) {
|
||||
LOG_ERROR(
|
||||
Service_ARP,
|
||||
"Attempted to set application control property, but registrar is already issued!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_INVALID_ACCESS);
|
||||
return;
|
||||
}
|
||||
|
||||
control = ctx.ReadBuffer();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
std::function<ResultCode(u64, ApplicationLaunchProperty, std::vector<u8>)> issue_process_id;
|
||||
bool issued = false;
|
||||
ApplicationLaunchProperty launch;
|
||||
std::vector<u8> control;
|
||||
};
|
||||
|
||||
ARP_W::ARP_W(const Core::System& system, ARPManager& manager)
|
||||
: ServiceFramework{"arp:w"}, system(system), manager(manager) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ARP_W::AcquireRegistrar, "AcquireRegistrar"},
|
||||
{1, &ARP_W::DeleteProperties, "DeleteProperties"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
ARP_W::~ARP_W() = default;
|
||||
|
||||
void ARP_W::AcquireRegistrar(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ARP, "called");
|
||||
|
||||
registrar = std::make_shared<IRegistrar>(
|
||||
[this](u64 process_id, ApplicationLaunchProperty launch, std::vector<u8> control) {
|
||||
const auto res = GetTitleIDForProcessID(system, process_id);
|
||||
if (!res.has_value()) {
|
||||
return ERR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
return manager.Register(*res, launch, std::move(control));
|
||||
});
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface(registrar);
|
||||
}
|
||||
|
||||
void ARP_W::DeleteProperties(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto process_id = rp.PopRaw<u64>();
|
||||
|
||||
LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id);
|
||||
|
||||
if (process_id == 0) {
|
||||
LOG_ERROR(Service_ARP, "Must have non-zero process ID!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_INVALID_PROCESS_ID);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto title_id = GetTitleIDForProcessID(system, process_id);
|
||||
|
||||
if (!title_id.has_value()) {
|
||||
LOG_ERROR(Service_ARP, "No title ID for process ID!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_NOT_REGISTERED);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(manager.Unregister(*title_id));
|
||||
}
|
||||
|
||||
} // namespace Service::Glue
|
||||
43
src/core/hle/service/glue/arp.h
Normal file
43
src/core/hle/service/glue/arp.h
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::Glue {
|
||||
|
||||
class ARPManager;
|
||||
class IRegistrar;
|
||||
|
||||
class ARP_R final : public ServiceFramework<ARP_R> {
|
||||
public:
|
||||
explicit ARP_R(const Core::System& system, const ARPManager& manager);
|
||||
~ARP_R() override;
|
||||
|
||||
private:
|
||||
void GetApplicationLaunchProperty(Kernel::HLERequestContext& ctx);
|
||||
void GetApplicationLaunchPropertyWithApplicationId(Kernel::HLERequestContext& ctx);
|
||||
void GetApplicationControlProperty(Kernel::HLERequestContext& ctx);
|
||||
void GetApplicationControlPropertyWithApplicationId(Kernel::HLERequestContext& ctx);
|
||||
|
||||
const Core::System& system;
|
||||
const ARPManager& manager;
|
||||
};
|
||||
|
||||
class ARP_W final : public ServiceFramework<ARP_W> {
|
||||
public:
|
||||
explicit ARP_W(const Core::System& system, ARPManager& manager);
|
||||
~ARP_W() override;
|
||||
|
||||
private:
|
||||
void AcquireRegistrar(Kernel::HLERequestContext& ctx);
|
||||
void DeleteProperties(Kernel::HLERequestContext& ctx);
|
||||
|
||||
const Core::System& system;
|
||||
ARPManager& manager;
|
||||
std::shared_ptr<IRegistrar> registrar;
|
||||
};
|
||||
|
||||
} // namespace Service::Glue
|
||||
50
src/core/hle/service/glue/bgtc.cpp
Normal file
50
src/core/hle/service/glue/bgtc.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/glue/bgtc.h"
|
||||
|
||||
namespace Service::Glue {
|
||||
|
||||
BGTC_T::BGTC_T() : ServiceFramework{"bgtc:t"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, nullptr, "NotifyTaskStarting"},
|
||||
{2, nullptr, "NotifyTaskFinished"},
|
||||
{3, nullptr, "GetTriggerEvent"},
|
||||
{4, nullptr, "IsInHalfAwake"},
|
||||
{5, nullptr, "NotifyClientName"},
|
||||
{6, nullptr, "IsInFullAwake"},
|
||||
{11, nullptr, "ScheduleTask"},
|
||||
{12, nullptr, "GetScheduledTaskInterval"},
|
||||
{13, nullptr, "UnscheduleTask"},
|
||||
{14, nullptr, "GetScheduleEvent"},
|
||||
{15, nullptr, "SchedulePeriodicTask"},
|
||||
{101, nullptr, "GetOperationMode"},
|
||||
{102, nullptr, "WillDisconnectNetworkWhenEnteringSleep"},
|
||||
{103, nullptr, "WillStayHalfAwakeInsteadSleep"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
BGTC_T::~BGTC_T() = default;
|
||||
|
||||
BGTC_SC::BGTC_SC() : ServiceFramework{"bgtc:sc"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, nullptr, "GetState"},
|
||||
{2, nullptr, "GetStateChangedEvent"},
|
||||
{3, nullptr, "NotifyEnteringHalfAwake"},
|
||||
{4, nullptr, "NotifyLeavingHalfAwake"},
|
||||
{5, nullptr, "SetIsUsingSleepUnsupportedDevices"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
BGTC_SC::~BGTC_SC() = default;
|
||||
|
||||
} // namespace Service::Glue
|
||||
23
src/core/hle/service/glue/bgtc.h
Normal file
23
src/core/hle/service/glue/bgtc.h
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::Glue {
|
||||
|
||||
class BGTC_T final : public ServiceFramework<BGTC_T> {
|
||||
public:
|
||||
BGTC_T();
|
||||
~BGTC_T() override;
|
||||
};
|
||||
|
||||
class BGTC_SC final : public ServiceFramework<BGTC_SC> {
|
||||
public:
|
||||
BGTC_SC();
|
||||
~BGTC_SC() override;
|
||||
};
|
||||
|
||||
} // namespace Service::Glue
|
||||
16
src/core/hle/service/glue/errors.h
Normal file
16
src/core/hle/service/glue/errors.h
Normal file
@@ -0,0 +1,16 @@
|
||||
// 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::Glue {
|
||||
|
||||
constexpr ResultCode ERR_INVALID_RESOURCE{ErrorModule::ARP, 0x1E};
|
||||
constexpr ResultCode ERR_INVALID_PROCESS_ID{ErrorModule::ARP, 0x1F};
|
||||
constexpr ResultCode ERR_INVALID_ACCESS{ErrorModule::ARP, 0x2A};
|
||||
constexpr ResultCode ERR_NOT_REGISTERED{ErrorModule::ARP, 0x66};
|
||||
|
||||
} // namespace Service::Glue
|
||||
25
src/core/hle/service/glue/glue.cpp
Normal file
25
src/core/hle/service/glue/glue.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/glue/arp.h"
|
||||
#include "core/hle/service/glue/bgtc.h"
|
||||
#include "core/hle/service/glue/glue.h"
|
||||
|
||||
namespace Service::Glue {
|
||||
|
||||
void InstallInterfaces(Core::System& system) {
|
||||
// ARP
|
||||
std::make_shared<ARP_R>(system, system.GetARPManager())
|
||||
->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<ARP_W>(system, system.GetARPManager())
|
||||
->InstallAsService(system.ServiceManager());
|
||||
|
||||
// BackGround Task Controller
|
||||
std::make_shared<BGTC_T>()->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<BGTC_SC>()->InstallAsService(system.ServiceManager());
|
||||
}
|
||||
|
||||
} // namespace Service::Glue
|
||||
16
src/core/hle/service/glue/glue.h
Normal file
16
src/core/hle/service/glue/glue.h
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
} // namespace Core
|
||||
|
||||
namespace Service::Glue {
|
||||
|
||||
/// Registers all Glue services with the specified service manager.
|
||||
void InstallInterfaces(Core::System& system);
|
||||
|
||||
} // namespace Service::Glue
|
||||
78
src/core/hle/service/glue/manager.cpp
Normal file
78
src/core/hle/service/glue/manager.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/glue/errors.h"
|
||||
#include "core/hle/service/glue/manager.h"
|
||||
|
||||
namespace Service::Glue {
|
||||
|
||||
struct ARPManager::MapEntry {
|
||||
ApplicationLaunchProperty launch;
|
||||
std::vector<u8> control;
|
||||
};
|
||||
|
||||
ARPManager::ARPManager() = default;
|
||||
|
||||
ARPManager::~ARPManager() = default;
|
||||
|
||||
ResultVal<ApplicationLaunchProperty> ARPManager::GetLaunchProperty(u64 title_id) const {
|
||||
if (title_id == 0) {
|
||||
return ERR_INVALID_PROCESS_ID;
|
||||
}
|
||||
|
||||
const auto iter = entries.find(title_id);
|
||||
if (iter == entries.end()) {
|
||||
return ERR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
return MakeResult<ApplicationLaunchProperty>(iter->second.launch);
|
||||
}
|
||||
|
||||
ResultVal<std::vector<u8>> ARPManager::GetControlProperty(u64 title_id) const {
|
||||
if (title_id == 0) {
|
||||
return ERR_INVALID_PROCESS_ID;
|
||||
}
|
||||
|
||||
const auto iter = entries.find(title_id);
|
||||
if (iter == entries.end()) {
|
||||
return ERR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
return MakeResult<std::vector<u8>>(iter->second.control);
|
||||
}
|
||||
|
||||
ResultCode ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch,
|
||||
std::vector<u8> control) {
|
||||
if (title_id == 0) {
|
||||
return ERR_INVALID_PROCESS_ID;
|
||||
}
|
||||
|
||||
const auto iter = entries.find(title_id);
|
||||
if (iter != entries.end()) {
|
||||
return ERR_INVALID_ACCESS;
|
||||
}
|
||||
|
||||
entries.insert_or_assign(title_id, MapEntry{launch, std::move(control)});
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ARPManager::Unregister(u64 title_id) {
|
||||
if (title_id == 0) {
|
||||
return ERR_INVALID_PROCESS_ID;
|
||||
}
|
||||
|
||||
const auto iter = entries.find(title_id);
|
||||
if (iter == entries.end()) {
|
||||
return ERR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
entries.erase(iter);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void ARPManager::ResetAll() {
|
||||
entries.clear();
|
||||
}
|
||||
|
||||
} // namespace Service::Glue
|
||||
63
src/core/hle/service/glue/manager.h
Normal file
63
src/core/hle/service/glue/manager.h
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Service::Glue {
|
||||
|
||||
struct ApplicationLaunchProperty {
|
||||
u64 title_id;
|
||||
u32 version;
|
||||
FileSys::StorageId base_game_storage_id;
|
||||
FileSys::StorageId update_storage_id;
|
||||
u8 program_index;
|
||||
u8 reserved;
|
||||
};
|
||||
static_assert(sizeof(ApplicationLaunchProperty) == 0x10,
|
||||
"ApplicationLaunchProperty has incorrect size.");
|
||||
|
||||
// A class to manage state related to the arp:w and arp:r services, specifically the registration
|
||||
// and unregistration of launch and control properties.
|
||||
class ARPManager {
|
||||
public:
|
||||
ARPManager();
|
||||
~ARPManager();
|
||||
|
||||
// Returns the ApplicationLaunchProperty corresponding to the provided title ID if it was
|
||||
// previously registered, otherwise ERR_NOT_REGISTERED if it was never registered or
|
||||
// ERR_INVALID_PROCESS_ID if the title ID is 0.
|
||||
ResultVal<ApplicationLaunchProperty> GetLaunchProperty(u64 title_id) const;
|
||||
|
||||
// Returns a vector of the raw bytes of NACP data (necessarily 0x4000 in size) corresponding to
|
||||
// the provided title ID if it was previously registered, otherwise ERR_NOT_REGISTERED if it was
|
||||
// never registered or ERR_INVALID_PROCESS_ID if the title ID is 0.
|
||||
ResultVal<std::vector<u8>> GetControlProperty(u64 title_id) const;
|
||||
|
||||
// Adds a new entry to the internal database with the provided parameters, returning
|
||||
// ERR_INVALID_ACCESS if attempting to re-register a title ID without an intermediate Unregister
|
||||
// step, and ERR_INVALID_PROCESS_ID if the title ID is 0.
|
||||
ResultCode Register(u64 title_id, ApplicationLaunchProperty launch, std::vector<u8> control);
|
||||
|
||||
// Removes the registration for the provided title ID from the database, returning
|
||||
// ERR_NOT_REGISTERED if it doesn't exist in the database and ERR_INVALID_PROCESS_ID if the
|
||||
// title ID is 0.
|
||||
ResultCode Unregister(u64 title_id);
|
||||
|
||||
// Removes all entries from the database, always succeeds. Should only be used when resetting
|
||||
// system state.
|
||||
void ResetAll();
|
||||
|
||||
private:
|
||||
struct MapEntry;
|
||||
std::map<u64, MapEntry> entries;
|
||||
};
|
||||
|
||||
} // namespace Service::Glue
|
||||
@@ -310,7 +310,7 @@ public:
|
||||
if (!IsValidNROHash(hash)) {
|
||||
LOG_ERROR(Service_LDR,
|
||||
"NRO hash is not present in any currently loaded NRRs (hash={})!",
|
||||
Common::HexArrayToString(hash));
|
||||
Common::HexToString(hash));
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_MISSING_NRR_HASH);
|
||||
return;
|
||||
|
||||
@@ -2,10 +2,18 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <json.hpp>
|
||||
#include "common/file_util.h"
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/prepo/prepo.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/reporter.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::PlayReport {
|
||||
|
||||
@@ -40,8 +48,21 @@ public:
|
||||
|
||||
private:
|
||||
void SaveReportWithUserOld(Kernel::HLERequestContext& ctx) {
|
||||
// TODO(ogniK): Do we want to add play report?
|
||||
LOG_WARNING(Service_PREPO, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto user_id = rp.PopRaw<u128>();
|
||||
const auto process_id = rp.PopRaw<u64>();
|
||||
|
||||
const auto data1 = ctx.ReadBuffer(0);
|
||||
const auto data2 = ctx.ReadBuffer(1);
|
||||
|
||||
LOG_DEBUG(
|
||||
Service_PREPO,
|
||||
"called, user_id={:016X}{:016X}, unk1={:016X}, data1_size={:016X}, data2_size={:016X}",
|
||||
user_id[1], user_id[0], process_id, data1.size(), data2.size());
|
||||
|
||||
const auto& reporter{Core::System::GetInstance().GetReporter()};
|
||||
reporter.SavePlayReport(Core::CurrentProcess()->GetTitleID(), process_id, {data1, data2},
|
||||
user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/aoc/aoc_u.h"
|
||||
#include "core/hle/service/apm/apm.h"
|
||||
#include "core/hle/service/arp/arp.h"
|
||||
#include "core/hle/service/audio/audio.h"
|
||||
#include "core/hle/service/bcat/module.h"
|
||||
#include "core/hle/service/bpc/bpc.h"
|
||||
@@ -33,6 +32,7 @@
|
||||
#include "core/hle/service/fgm/fgm.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/friend/friend.h"
|
||||
#include "core/hle/service/glue/glue.h"
|
||||
#include "core/hle/service/grc/grc.h"
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
#include "core/hle/service/lbl/lbl.h"
|
||||
@@ -68,6 +68,7 @@
|
||||
#include "core/hle/service/usb/usb.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
#include "core/hle/service/wlan/wlan.h"
|
||||
#include "core/reporter.h"
|
||||
|
||||
namespace Service {
|
||||
|
||||
@@ -148,6 +149,8 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext
|
||||
}
|
||||
buf.push_back('}');
|
||||
|
||||
Core::System::GetInstance().GetReporter().SaveUnimplementedFunctionReport(
|
||||
ctx, ctx.GetCommand(), function_name, service_name);
|
||||
UNIMPLEMENTED_MSG("Unknown / unimplemented {}", fmt::to_string(buf));
|
||||
}
|
||||
|
||||
@@ -200,11 +203,10 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
|
||||
|
||||
SM::ServiceManager::InstallInterfaces(sm);
|
||||
|
||||
Account::InstallInterfaces(*sm);
|
||||
AM::InstallInterfaces(*sm, nv_flinger);
|
||||
Account::InstallInterfaces(system);
|
||||
AM::InstallInterfaces(*sm, nv_flinger, system);
|
||||
AOC::InstallInterfaces(*sm);
|
||||
APM::InstallInterfaces(*sm);
|
||||
ARP::InstallInterfaces(*sm);
|
||||
Audio::InstallInterfaces(*sm);
|
||||
BCAT::InstallInterfaces(*sm);
|
||||
BPC::InstallInterfaces(*sm);
|
||||
@@ -218,6 +220,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
|
||||
FGM::InstallInterfaces(*sm);
|
||||
FileSystem::InstallInterfaces(*sm, vfs);
|
||||
Friend::InstallInterfaces(*sm);
|
||||
Glue::InstallInterfaces(system);
|
||||
GRC::InstallInterfaces(*sm);
|
||||
HID::InstallInterfaces(*sm);
|
||||
LBL::InstallInterfaces(*sm);
|
||||
@@ -246,7 +249,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
|
||||
Sockets::InstallInterfaces(*sm);
|
||||
SPL::InstallInterfaces(*sm);
|
||||
SSL::InstallInterfaces(*sm);
|
||||
Time::InstallInterfaces(*sm);
|
||||
Time::InstallInterfaces(system);
|
||||
USB::InstallInterfaces(*sm);
|
||||
VI::InstallInterfaces(*sm, nv_flinger);
|
||||
WLAN::InstallInterfaces(*sm);
|
||||
|
||||
@@ -95,6 +95,14 @@ void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) {
|
||||
PushResponseLanguageCode(ctx, post4_0_0_max_entries);
|
||||
}
|
||||
|
||||
void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(static_cast<u32>(Settings::values.quest_flag));
|
||||
}
|
||||
|
||||
void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index);
|
||||
|
||||
@@ -114,7 +122,7 @@ SET::SET() : ServiceFramework("set") {
|
||||
{5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"},
|
||||
{6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"},
|
||||
{7, nullptr, "GetKeyCodeMap"},
|
||||
{8, nullptr, "GetQuestFlag"},
|
||||
{8, &SET::GetQuestFlag, "GetQuestFlag"},
|
||||
{9, nullptr, "GetKeyCodeMap2"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -42,6 +42,7 @@ private:
|
||||
void GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx);
|
||||
void GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx);
|
||||
void GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx);
|
||||
void GetQuestFlag(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
} // namespace Service::Set
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
|
||||
namespace Service::Time {
|
||||
|
||||
Time::Time(std::shared_ptr<Module> time, const char* name)
|
||||
: Module::Interface(std::move(time), name) {
|
||||
Time::Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory,
|
||||
const char* name)
|
||||
: Module::Interface(std::move(time), std::move(shared_memory), name) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"},
|
||||
@@ -16,12 +17,12 @@ Time::Time(std::shared_ptr<Module> time, const char* name)
|
||||
{3, &Time::GetTimeZoneService, "GetTimeZoneService"},
|
||||
{4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"},
|
||||
{5, nullptr, "GetEphemeralNetworkSystemClock"},
|
||||
{20, nullptr, "GetSharedMemoryNativeHandle"},
|
||||
{20, &Time::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
|
||||
{30, nullptr, "GetStandardNetworkClockOperationEventReadableHandle"},
|
||||
{31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"},
|
||||
{50, nullptr, "SetStandardSteadyClockInternalOffset"},
|
||||
{100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
|
||||
{101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
|
||||
{100, &Time::IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
|
||||
{101, &Time::SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
|
||||
{102, nullptr, "GetStandardUserSystemClockInitialYear"},
|
||||
{200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"},
|
||||
{201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"},
|
||||
|
||||
@@ -8,9 +8,12 @@
|
||||
|
||||
namespace Service::Time {
|
||||
|
||||
class SharedMemory;
|
||||
|
||||
class Time final : public Module::Interface {
|
||||
public:
|
||||
explicit Time(std::shared_ptr<Module> time, const char* name);
|
||||
explicit Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory,
|
||||
const char* name);
|
||||
~Time() override;
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user