Compare commits
121 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9fa890f14 | ||
|
|
d68795c665 | ||
|
|
32775125b7 | ||
|
|
96535c13a5 | ||
|
|
ea680bea60 | ||
|
|
56cf5b7b17 | ||
|
|
02e6602baa | ||
|
|
4ee087fb3c | ||
|
|
19b2571aec | ||
|
|
6209fe0c27 | ||
|
|
8b433beff3 | ||
|
|
7cfb29de23 | ||
|
|
3cf7246e37 | ||
|
|
fed6ab14c3 | ||
|
|
e696ed1f4d | ||
|
|
a81645400f | ||
|
|
48fcb43585 | ||
|
|
5454494adb | ||
|
|
de16c1e453 | ||
|
|
ae53b84efd | ||
|
|
5b95de0c9c | ||
|
|
731b4bd691 | ||
|
|
ba03bfa430 | ||
|
|
5ce6b8fea7 | ||
|
|
0682a908c0 | ||
|
|
c7b6c9de9c | ||
|
|
76d515327b | ||
|
|
c70529c1ec | ||
|
|
7901de2b75 | ||
|
|
a819116154 | ||
|
|
03f274d8c1 | ||
|
|
0072275d25 | ||
|
|
6273a7aeee | ||
|
|
0149b4245c | ||
|
|
6552d75be1 | ||
|
|
224fcabe46 | ||
|
|
585e6fd426 | ||
|
|
3c7ba00d73 | ||
|
|
d21b2164e9 | ||
|
|
14cede1a0c | ||
|
|
405dd03dae | ||
|
|
241646f423 | ||
|
|
5b8f70ea2e | ||
|
|
0b701751da | ||
|
|
238bc4a077 | ||
|
|
e156834297 | ||
|
|
786995a81e | ||
|
|
e8899e7027 | ||
|
|
e658118aa9 | ||
|
|
d104478f1b | ||
|
|
4476fd29d6 | ||
|
|
e634ce847f | ||
|
|
84364c9d4f | ||
|
|
5c9c33e8ad | ||
|
|
edd5b6f12f | ||
|
|
b725d1fdf7 | ||
|
|
287ee1cd63 | ||
|
|
87eca5b209 | ||
|
|
75640c9c71 | ||
|
|
8cb2e7d881 | ||
|
|
f7319b0d3c | ||
|
|
97605e36f7 | ||
|
|
98060c6f7b | ||
|
|
95d3965f31 | ||
|
|
3844b5c0c5 | ||
|
|
8a537a2021 | ||
|
|
c6c74248fe | ||
|
|
0478308094 | ||
|
|
53b3c3ab7f | ||
|
|
cd47af8af0 | ||
|
|
9761936e02 | ||
|
|
1af13e0802 | ||
|
|
c681690358 | ||
|
|
7384b33c4f | ||
|
|
c95ded3a4a | ||
|
|
e6676afa18 | ||
|
|
ebf1b58a22 | ||
|
|
e1ea8cc721 | ||
|
|
b4f63db04e | ||
|
|
3619b31fc0 | ||
|
|
fcde356f15 | ||
|
|
958fa15a4c | ||
|
|
f9db75fe40 | ||
|
|
3bd503d59c | ||
|
|
09f4150241 | ||
|
|
d2b2b05b6a | ||
|
|
723bd89883 | ||
|
|
70f189d7af | ||
|
|
a80467db57 | ||
|
|
d2a630c41f | ||
|
|
9b12623743 | ||
|
|
c7387e6504 | ||
|
|
f1219e3a87 | ||
|
|
6001af2b89 | ||
|
|
4ed9ef15c4 | ||
|
|
9bc18eada8 | ||
|
|
454cf1dc09 | ||
|
|
81a39181b0 | ||
|
|
004277477a | ||
|
|
b8e885c6e5 | ||
|
|
ab552e4a25 | ||
|
|
9ea8eb6b2e | ||
|
|
ab362aa7e5 | ||
|
|
9a0fb7d9fb | ||
|
|
3088e36237 | ||
|
|
e2bf581e3a | ||
|
|
ba2cdcdc5a | ||
|
|
ab7472345b | ||
|
|
2d2ef05d8c | ||
|
|
8f183a47dd | ||
|
|
97d425c304 | ||
|
|
e20db909ee | ||
|
|
bdaa76c0db | ||
|
|
1c0226815d | ||
|
|
48eb3742b9 | ||
|
|
9078bb9854 | ||
|
|
5ee19add1b | ||
|
|
2e8177f0c9 | ||
|
|
df264d2ccb | ||
|
|
f2f679bf3f | ||
|
|
b04e39107f |
@@ -57,3 +57,4 @@ done
|
||||
|
||||
pip3 install pefile
|
||||
python3 .travis/linux-mingw/scan_dll.py package/*.exe "package/"
|
||||
python3 .travis/linux-mingw/scan_dll.py package/imageformats/*.dll "package/"
|
||||
|
||||
@@ -377,7 +377,7 @@ if (CLANG_FORMAT)
|
||||
set(CCOMMENT "Running clang format against all the .h and .cpp files in src/")
|
||||
if (WIN32)
|
||||
add_custom_target(clang-format
|
||||
COMMAND powershell.exe -Command "Get-ChildItem ${SRCS}/* -Include *.cpp,*.h -Recurse | Foreach {${CLANG_FORMAT} -i $_.fullname}"
|
||||
COMMAND powershell.exe -Command "${CLANG_FORMAT} -i @(Get-ChildItem -Recurse ${SRCS}/* -Include \'*.h\', \'*.cpp\')"
|
||||
COMMENT ${CCOMMENT})
|
||||
elseif(MINGW)
|
||||
add_custom_target(clang-format
|
||||
|
||||
23
appveyor.yml
23
appveyor.yml
@@ -125,17 +125,6 @@ after_build:
|
||||
Copy-Item -path "$CMAKE_SOURCE_DIR/license.txt" -destination $RELEASE_DIST
|
||||
Copy-Item -path "$CMAKE_SOURCE_DIR/README.md" -destination $RELEASE_DIST
|
||||
|
||||
# copy all the dll dependencies to the release folder
|
||||
. "./.appveyor/UtilityFunctions.ps1"
|
||||
$DLLSearchPath = "C:\msys64\mingw64\bin;$env:PATH"
|
||||
$MingwDLLs = RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu.exe"
|
||||
$MingwDLLs += RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu_cmd.exe"
|
||||
Write-Host "Detected the following dependencies:"
|
||||
Write-Host $MingwDLLs
|
||||
foreach ($file in $MingwDLLs) {
|
||||
Copy-Item -path "$file" -force -destination "$RELEASE_DIST"
|
||||
}
|
||||
|
||||
# copy the qt windows plugin dll to platforms
|
||||
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/platforms/qwindows.dll" -force -destination "$RELEASE_DIST/platforms"
|
||||
|
||||
@@ -145,6 +134,18 @@ after_build:
|
||||
# copy the qt jpeg imageformat dll to platforms
|
||||
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/imageformats/qjpeg.dll" -force -destination "$RELEASE_DIST/imageformats"
|
||||
|
||||
# copy all the dll dependencies to the release folder
|
||||
. "./.appveyor/UtilityFunctions.ps1"
|
||||
$DLLSearchPath = "C:\msys64\mingw64\bin;$env:PATH"
|
||||
$MingwDLLs = RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu.exe"
|
||||
$MingwDLLs += RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu_cmd.exe"
|
||||
$MingwDLLs += RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\imageformats\qjpeg.dll"
|
||||
Write-Host "Detected the following dependencies:"
|
||||
Write-Host $MingwDLLs
|
||||
foreach ($file in $MingwDLLs) {
|
||||
Copy-Item -path "$file" -force -destination "$RELEASE_DIST"
|
||||
}
|
||||
|
||||
7z a -tzip $MINGW_BUILD_ZIP $RELEASE_DIST\*
|
||||
7z a $MINGW_SEVENZIP $RELEASE_DIST
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ struct AuxInfo {
|
||||
std::array<u8, 24> output_mix_buffers;
|
||||
u32_le mix_buffer_count;
|
||||
u32_le sample_rate; // Stored in the aux buffer currently
|
||||
u32_le sampe_count;
|
||||
u32_le sample_count;
|
||||
u64_le send_buffer_info;
|
||||
u64_le send_buffer_base;
|
||||
|
||||
|
||||
@@ -117,21 +117,21 @@ private:
|
||||
// We don't delete it because we want BitField to be trivially copyable.
|
||||
constexpr BitField& operator=(const BitField&) = default;
|
||||
|
||||
// StorageType is T for non-enum types and the underlying type of T if
|
||||
// UnderlyingType is T for non-enum types and the underlying type of T if
|
||||
// T is an enumeration. Note that T is wrapped within an enable_if in the
|
||||
// former case to workaround compile errors which arise when using
|
||||
// std::underlying_type<T>::type directly.
|
||||
using StorageType = typename std::conditional_t<std::is_enum<T>::value, std::underlying_type<T>,
|
||||
std::enable_if<true, T>>::type;
|
||||
using UnderlyingType = typename std::conditional_t<std::is_enum_v<T>, std::underlying_type<T>,
|
||||
std::enable_if<true, T>>::type;
|
||||
|
||||
// Unsigned version of StorageType
|
||||
using StorageTypeU = std::make_unsigned_t<StorageType>;
|
||||
// We store the value as the unsigned type to avoid undefined behaviour on value shifting
|
||||
using StorageType = std::make_unsigned_t<UnderlyingType>;
|
||||
|
||||
public:
|
||||
/// Constants to allow limited introspection of fields if needed
|
||||
static constexpr std::size_t position = Position;
|
||||
static constexpr std::size_t bits = Bits;
|
||||
static constexpr StorageType mask = (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position;
|
||||
static constexpr StorageType mask = (((StorageType)~0) >> (8 * sizeof(T) - bits)) << position;
|
||||
|
||||
/**
|
||||
* Formats a value by masking and shifting it according to the field parameters. A value
|
||||
@@ -148,11 +148,12 @@ public:
|
||||
* union in a constexpr context.
|
||||
*/
|
||||
static constexpr FORCE_INLINE T ExtractValue(const StorageType& storage) {
|
||||
if (std::numeric_limits<T>::is_signed) {
|
||||
if constexpr (std::numeric_limits<UnderlyingType>::is_signed) {
|
||||
std::size_t shift = 8 * sizeof(T) - bits;
|
||||
return (T)((storage << (shift - position)) >> shift);
|
||||
return static_cast<T>(static_cast<UnderlyingType>(storage << (shift - position)) >>
|
||||
shift);
|
||||
} else {
|
||||
return (T)((storage & mask) >> position);
|
||||
return static_cast<T>((storage & mask) >> position);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,10 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <codecvt>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
#include "common/common_paths.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
@@ -33,24 +32,6 @@ std::string ToUpper(std::string str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
// For Debugging. Read out an u8 array.
|
||||
std::string ArrayToString(const u8* data, std::size_t size, int line_len, bool spaces) {
|
||||
std::ostringstream oss;
|
||||
oss << std::setfill('0') << std::hex;
|
||||
|
||||
for (int line = 0; size; ++data, --size) {
|
||||
oss << std::setw(2) << (int)*data;
|
||||
|
||||
if (line_len == ++line) {
|
||||
oss << '\n';
|
||||
line = 0;
|
||||
} else if (spaces)
|
||||
oss << ' ';
|
||||
}
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string StringFromBuffer(const std::vector<u8>& data) {
|
||||
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
||||
}
|
||||
@@ -75,40 +56,6 @@ std::string StripQuotes(const std::string& s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
bool TryParse(const std::string& str, u32* const output) {
|
||||
char* endptr = nullptr;
|
||||
|
||||
// Reset errno to a value other than ERANGE
|
||||
errno = 0;
|
||||
|
||||
unsigned long value = strtoul(str.c_str(), &endptr, 0);
|
||||
|
||||
if (!endptr || *endptr)
|
||||
return false;
|
||||
|
||||
if (errno == ERANGE)
|
||||
return false;
|
||||
|
||||
#if ULONG_MAX > UINT_MAX
|
||||
if (value >= 0x100000000ull && value <= 0xFFFFFFFF00000000ull)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
*output = static_cast<u32>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryParse(const std::string& str, bool* const output) {
|
||||
if ("1" == str || "true" == ToLower(str))
|
||||
*output = true;
|
||||
else if ("0" == str || "false" == ToLower(str))
|
||||
*output = false;
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string StringFromBool(bool value) {
|
||||
return value ? "True" : "False";
|
||||
}
|
||||
@@ -267,6 +214,15 @@ std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t
|
||||
return std::string(buffer, len);
|
||||
}
|
||||
|
||||
std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
|
||||
std::size_t max_len) {
|
||||
std::size_t len = 0;
|
||||
while (len < max_len && buffer[len] != '\0')
|
||||
++len;
|
||||
|
||||
return std::u16string(buffer.begin(), buffer.begin() + len);
|
||||
}
|
||||
|
||||
const char* TrimSourcePath(const char* path, const char* root) {
|
||||
const char* p = path;
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
@@ -19,44 +17,13 @@ std::string ToLower(std::string str);
|
||||
/// Make a string uppercase
|
||||
std::string ToUpper(std::string str);
|
||||
|
||||
std::string ArrayToString(const u8* data, std::size_t size, int line_len = 20, bool spaces = true);
|
||||
|
||||
std::string StringFromBuffer(const std::vector<u8>& data);
|
||||
|
||||
std::string StripSpaces(const std::string& s);
|
||||
std::string StripQuotes(const std::string& s);
|
||||
|
||||
// Thousand separator. Turns 12345678 into 12,345,678
|
||||
template <typename I>
|
||||
std::string ThousandSeparate(I value, int spaces = 0) {
|
||||
std::ostringstream oss;
|
||||
|
||||
// std::locale("") seems to be broken on many platforms
|
||||
#if defined _WIN32 || (defined __linux__ && !defined __clang__)
|
||||
oss.imbue(std::locale(""));
|
||||
#endif
|
||||
oss << std::setw(spaces) << value;
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string StringFromBool(bool value);
|
||||
|
||||
bool TryParse(const std::string& str, bool* output);
|
||||
bool TryParse(const std::string& str, u32* output);
|
||||
|
||||
template <typename N>
|
||||
static bool TryParse(const std::string& str, N* const output) {
|
||||
std::istringstream iss(str);
|
||||
|
||||
N tmp = 0;
|
||||
if (iss >> tmp) {
|
||||
*output = tmp;
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string TabsToSpaces(int tab_size, std::string in);
|
||||
|
||||
void SplitString(const std::string& str, char delim, std::vector<std::string>& output);
|
||||
@@ -99,6 +66,14 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) {
|
||||
*/
|
||||
std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len);
|
||||
|
||||
/**
|
||||
* Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
|
||||
* null-terminated, then the string ends at the greatest multiple of two less then or equal to
|
||||
* max_len_bytes.
|
||||
*/
|
||||
std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
|
||||
std::size_t max_len);
|
||||
|
||||
/**
|
||||
* Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
|
||||
* intended to be used to strip a system-specific build directory from the `__FILE__` macro,
|
||||
|
||||
@@ -77,6 +77,8 @@ add_library(core STATIC
|
||||
file_sys/vfs_vector.h
|
||||
file_sys/xts_archive.cpp
|
||||
file_sys/xts_archive.h
|
||||
frontend/applets/software_keyboard.cpp
|
||||
frontend/applets/software_keyboard.h
|
||||
frontend/emu_window.cpp
|
||||
frontend/emu_window.h
|
||||
frontend/framebuffer_layout.cpp
|
||||
@@ -150,6 +152,10 @@ add_library(core STATIC
|
||||
hle/service/am/applet_ae.h
|
||||
hle/service/am/applet_oe.cpp
|
||||
hle/service/am/applet_oe.h
|
||||
hle/service/am/applets/applets.cpp
|
||||
hle/service/am/applets/applets.h
|
||||
hle/service/am/applets/software_keyboard.cpp
|
||||
hle/service/am/applets/software_keyboard.h
|
||||
hle/service/am/idle.cpp
|
||||
hle/service/am/idle.h
|
||||
hle/service/am/omm.cpp
|
||||
|
||||
@@ -23,12 +23,14 @@
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/service/am/applets/software_keyboard.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/settings.h"
|
||||
#include "core/telemetry_session.h"
|
||||
#include "frontend/applets/software_keyboard.h"
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
@@ -136,6 +138,10 @@ struct System::Impl {
|
||||
if (virtual_filesystem == nullptr)
|
||||
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
|
||||
|
||||
/// Create default implementations of applets if one is not provided.
|
||||
if (software_keyboard == nullptr)
|
||||
software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
|
||||
|
||||
auto main_process = Kernel::Process::Create(kernel, "main");
|
||||
kernel.MakeCurrentProcess(main_process.get());
|
||||
|
||||
@@ -289,6 +295,9 @@ struct System::Impl {
|
||||
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
|
||||
std::size_t active_core{}; ///< Active core, only used in single thread mode
|
||||
|
||||
/// Frontend applets
|
||||
std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
|
||||
|
||||
/// Service manager
|
||||
std::shared_ptr<Service::SM::ServiceManager> service_manager;
|
||||
|
||||
@@ -488,6 +497,14 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
|
||||
return impl->virtual_filesystem;
|
||||
}
|
||||
|
||||
void System::SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet) {
|
||||
impl->software_keyboard = std::move(applet);
|
||||
}
|
||||
|
||||
const Core::Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const {
|
||||
return *impl->software_keyboard;
|
||||
}
|
||||
|
||||
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
|
||||
return impl->Init(emu_window);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
class SoftwareKeyboardApplet;
|
||||
} // namespace Core::Frontend
|
||||
|
||||
namespace FileSys {
|
||||
@@ -236,6 +237,10 @@ public:
|
||||
|
||||
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
|
||||
|
||||
void SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet);
|
||||
|
||||
const Core::Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const;
|
||||
|
||||
private:
|
||||
System();
|
||||
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_)
|
||||
BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir dump_root_)
|
||||
: nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
|
||||
dump_root(std::move(dump_root_)),
|
||||
sysnand_cache(std::make_unique<RegisteredCache>(
|
||||
GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
|
||||
usrnand_cache(std::make_unique<RegisteredCache>(
|
||||
@@ -32,4 +33,10 @@ VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
|
||||
return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id));
|
||||
}
|
||||
|
||||
VirtualDir BISFactory::GetModificationDumpRoot(u64 title_id) const {
|
||||
if (title_id == 0)
|
||||
return nullptr;
|
||||
return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id));
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -17,17 +17,19 @@ class RegisteredCache;
|
||||
/// registered caches.
|
||||
class BISFactory {
|
||||
public:
|
||||
explicit BISFactory(VirtualDir nand_root, VirtualDir load_root);
|
||||
explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root);
|
||||
~BISFactory();
|
||||
|
||||
RegisteredCache* GetSystemNANDContents() const;
|
||||
RegisteredCache* GetUserNANDContents() const;
|
||||
|
||||
VirtualDir GetModificationLoadRoot(u64 title_id) const;
|
||||
VirtualDir GetModificationDumpRoot(u64 title_id) const;
|
||||
|
||||
private:
|
||||
VirtualDir nand_root;
|
||||
VirtualDir load_root;
|
||||
VirtualDir dump_root;
|
||||
|
||||
std::unique_ptr<RegisteredCache> sysnand_cache;
|
||||
std::unique_ptr<RegisteredCache> usrnand_cache;
|
||||
|
||||
@@ -176,7 +176,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
|
||||
for (const VirtualFile& file : partitions[static_cast<std::size_t>(part)]->GetFiles()) {
|
||||
if (file->GetExtension() != "nca")
|
||||
continue;
|
||||
auto nca = std::make_shared<NCA>(file);
|
||||
auto nca = std::make_shared<NCA>(file, nullptr, 0, keys);
|
||||
// TODO(DarkLordZach): Add proper Rev1+ Support
|
||||
if (nca->IsUpdate())
|
||||
continue;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace Loader {
|
||||
@@ -107,5 +108,7 @@ private:
|
||||
std::shared_ptr<NSP> secure_partition;
|
||||
std::shared_ptr<NCA> program;
|
||||
std::vector<std::shared_ptr<NCA>> ncas;
|
||||
|
||||
Core::Crypto::KeyManager keys;
|
||||
};
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -101,8 +101,9 @@ static bool IsValidNCA(const NCAHeader& header) {
|
||||
return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
|
||||
}
|
||||
|
||||
NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
|
||||
: file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) {
|
||||
NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset,
|
||||
Core::Crypto::KeyManager keys_)
|
||||
: file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)), keys(std::move(keys_)) {
|
||||
if (file == nullptr) {
|
||||
status = Loader::ResultStatus::ErrorNullFile;
|
||||
return;
|
||||
|
||||
@@ -79,7 +79,8 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
|
||||
class NCA : public ReadOnlyVfsDirectory {
|
||||
public:
|
||||
explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr,
|
||||
u64 bktr_base_ivfc_offset = 0);
|
||||
u64 bktr_base_ivfc_offset = 0,
|
||||
Core::Crypto::KeyManager keys = Core::Crypto::KeyManager());
|
||||
~NCA() override;
|
||||
|
||||
Loader::ResultStatus GetStatus() const;
|
||||
|
||||
@@ -66,4 +66,10 @@ std::string NACP::GetVersionString() const {
|
||||
return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(),
|
||||
raw->version_string.size());
|
||||
}
|
||||
|
||||
std::vector<u8> NACP::GetRawBytes() const {
|
||||
std::vector<u8> out(sizeof(RawNACP));
|
||||
std::memcpy(out.data(), raw.get(), sizeof(RawNACP));
|
||||
return out;
|
||||
}
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -81,6 +81,7 @@ public:
|
||||
u64 GetTitleId() const;
|
||||
u64 GetDLCBaseTitleId() const;
|
||||
std::string GetVersionString() const;
|
||||
std::vector<u8> GetRawBytes() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<RawNACP> raw;
|
||||
|
||||
@@ -8,25 +8,10 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
namespace ErrCodes {
|
||||
enum {
|
||||
NotFound = 1,
|
||||
TitleNotFound = 1002,
|
||||
SdCardNotFound = 2001,
|
||||
RomFSNotFound = 2520,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrorModule::FS, ErrCodes::NotFound);
|
||||
|
||||
// TODO(bunnei): Replace these with correct errors for Switch OS
|
||||
constexpr ResultCode ERROR_INVALID_PATH(-1);
|
||||
constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(-1);
|
||||
constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(-1);
|
||||
constexpr ResultCode ERROR_FILE_NOT_FOUND(-1);
|
||||
constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(-1);
|
||||
constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(-1);
|
||||
constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(-1);
|
||||
constexpr ResultCode ERROR_DIRECTORY_NOT_EMPTY(-1);
|
||||
constexpr ResultCode ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1};
|
||||
constexpr ResultCode ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002};
|
||||
constexpr ResultCode ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001};
|
||||
constexpr ResultCode ERROR_INVALID_OFFSET{ErrorModule::FS, 6061};
|
||||
constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::FS, 6062};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "core/file_sys/vfs_vector.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
@@ -119,6 +120,18 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
|
||||
const auto build_id_raw = Common::HexArrayToString(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) {
|
||||
LOG_INFO(Loader, "Dumping NSO for build_id={}, title_id={:016X}", build_id, title_id);
|
||||
const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
|
||||
if (dump_dir != nullptr) {
|
||||
const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
|
||||
const auto file = nso_dir->CreateFile(fmt::format("{}.nso", build_id));
|
||||
|
||||
file->Resize(nso.size());
|
||||
file->WriteBytes(nso);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO(Loader, "Patching NSO for build_id={}", build_id);
|
||||
|
||||
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
|
||||
|
||||
@@ -106,9 +106,12 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
|
||||
|
||||
VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
|
||||
std::string_view path) const {
|
||||
if (dir->GetFileRelative(path) != nullptr)
|
||||
return dir->GetFileRelative(path);
|
||||
if (dir->GetDirectoryRelative(path) != nullptr) {
|
||||
const auto file = dir->GetFileRelative(path);
|
||||
if (file != nullptr)
|
||||
return file;
|
||||
|
||||
const auto nca_dir = dir->GetDirectoryRelative(path);
|
||||
if (nca_dir != nullptr) {
|
||||
const auto nca_dir = dir->GetDirectoryRelative(path);
|
||||
VirtualFile file = nullptr;
|
||||
|
||||
@@ -225,7 +228,7 @@ void RegisteredCache::ProcessFiles(const std::vector<NcaID>& ids) {
|
||||
|
||||
if (file == nullptr)
|
||||
continue;
|
||||
const auto nca = std::make_shared<NCA>(parser(file, id));
|
||||
const auto nca = std::make_shared<NCA>(parser(file, id), nullptr, 0, keys);
|
||||
if (nca->GetStatus() != Loader::ResultStatus::Success ||
|
||||
nca->GetType() != NCAContentType::Meta) {
|
||||
continue;
|
||||
@@ -315,7 +318,7 @@ std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType t
|
||||
const auto raw = GetEntryRaw(title_id, type);
|
||||
if (raw == nullptr)
|
||||
return nullptr;
|
||||
return std::make_unique<NCA>(raw);
|
||||
return std::make_unique<NCA>(raw, nullptr, 0, keys);
|
||||
}
|
||||
|
||||
std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <vector>
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
@@ -133,6 +134,8 @@ private:
|
||||
|
||||
VirtualDir dir;
|
||||
RegisteredCacheParsingFunction parser;
|
||||
Core::Crypto::KeyManager keys;
|
||||
|
||||
// maps tid -> NcaID of meta
|
||||
boost::container::flat_map<u64, NcaID> meta_id;
|
||||
// maps tid -> meta
|
||||
|
||||
@@ -83,6 +83,24 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr
|
||||
return MakeResult<VirtualDir>(std::move(out));
|
||||
}
|
||||
|
||||
VirtualDir SaveDataFactory::GetSaveDataSpaceDirectory(SaveDataSpaceId space) const {
|
||||
return dir->GetDirectoryRelative(GetSaveDataSpaceIdPath(space));
|
||||
}
|
||||
|
||||
std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
|
||||
switch (space) {
|
||||
case SaveDataSpaceId::NandSystem:
|
||||
return "/system/";
|
||||
case SaveDataSpaceId::NandUser:
|
||||
return "/user/";
|
||||
case SaveDataSpaceId::TemporaryStorage:
|
||||
return "/temp/";
|
||||
default:
|
||||
ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
|
||||
return "/unrecognized/"; ///< To prevent corruption when ignoring asserts.
|
||||
}
|
||||
}
|
||||
|
||||
std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
|
||||
u128 user_id, u64 save_id) {
|
||||
// According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
|
||||
@@ -90,21 +108,7 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
|
||||
if (type == SaveDataType::SaveData && title_id == 0)
|
||||
title_id = Core::CurrentProcess()->GetTitleID();
|
||||
|
||||
std::string out;
|
||||
|
||||
switch (space) {
|
||||
case SaveDataSpaceId::NandSystem:
|
||||
out = "/system/";
|
||||
break;
|
||||
case SaveDataSpaceId::NandUser:
|
||||
out = "/user/";
|
||||
break;
|
||||
case SaveDataSpaceId::TemporaryStorage:
|
||||
out = "/temp/";
|
||||
break;
|
||||
default:
|
||||
ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
|
||||
}
|
||||
std::string out = GetSaveDataSpaceIdPath(space);
|
||||
|
||||
switch (type) {
|
||||
case SaveDataType::SystemSaveData:
|
||||
|
||||
@@ -52,6 +52,9 @@ public:
|
||||
|
||||
ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta);
|
||||
|
||||
VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
|
||||
|
||||
static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space);
|
||||
static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
|
||||
u128 user_id, u64 save_id);
|
||||
|
||||
|
||||
@@ -252,7 +252,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto next_nca = std::make_shared<NCA>(next_file);
|
||||
auto next_nca = std::make_shared<NCA>(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 ||
|
||||
|
||||
@@ -70,6 +70,8 @@ private:
|
||||
std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas;
|
||||
std::vector<VirtualFile> ticket_files;
|
||||
|
||||
Core::Crypto::KeyManager keys;
|
||||
|
||||
VirtualFile romfs;
|
||||
VirtualDir exefs;
|
||||
};
|
||||
|
||||
29
src/core/frontend/applets/software_keyboard.cpp
Normal file
29
src/core/frontend/applets/software_keyboard.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
SoftwareKeyboardApplet::~SoftwareKeyboardApplet() = default;
|
||||
|
||||
void DefaultSoftwareKeyboardApplet::RequestText(
|
||||
std::function<void(std::optional<std::u16string>)> out,
|
||||
SoftwareKeyboardParameters parameters) const {
|
||||
if (parameters.initial_text.empty())
|
||||
out(u"yuzu");
|
||||
|
||||
out(parameters.initial_text);
|
||||
}
|
||||
|
||||
void DefaultSoftwareKeyboardApplet::SendTextCheckDialog(
|
||||
std::u16string error_message, std::function<void()> finished_check) const {
|
||||
LOG_WARNING(Service_AM,
|
||||
"(STUBBED) called - Default fallback software keyboard does not support text "
|
||||
"check! (error_message={})",
|
||||
Common::UTF16ToUTF8(error_message));
|
||||
finished_check();
|
||||
}
|
||||
} // namespace Core::Frontend
|
||||
54
src/core/frontend/applets/software_keyboard.h
Normal file
54
src/core/frontend/applets/software_keyboard.h
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
struct SoftwareKeyboardParameters {
|
||||
std::u16string submit_text;
|
||||
std::u16string header_text;
|
||||
std::u16string sub_text;
|
||||
std::u16string guide_text;
|
||||
std::u16string initial_text;
|
||||
std::size_t max_length;
|
||||
bool password;
|
||||
bool cursor_at_beginning;
|
||||
|
||||
union {
|
||||
u8 value;
|
||||
|
||||
BitField<1, 1, u8> disable_space;
|
||||
BitField<2, 1, u8> disable_address;
|
||||
BitField<3, 1, u8> disable_percent;
|
||||
BitField<4, 1, u8> disable_slash;
|
||||
BitField<6, 1, u8> disable_number;
|
||||
BitField<7, 1, u8> disable_download_code;
|
||||
};
|
||||
};
|
||||
|
||||
class SoftwareKeyboardApplet {
|
||||
public:
|
||||
virtual ~SoftwareKeyboardApplet();
|
||||
|
||||
virtual void RequestText(std::function<void(std::optional<std::u16string>)> out,
|
||||
SoftwareKeyboardParameters parameters) const = 0;
|
||||
virtual void SendTextCheckDialog(std::u16string error_message,
|
||||
std::function<void()> finished_check) const = 0;
|
||||
};
|
||||
|
||||
class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet {
|
||||
public:
|
||||
void RequestText(std::function<void(std::optional<std::u16string>)> out,
|
||||
SoftwareKeyboardParameters parameters) const override;
|
||||
void SendTextCheckDialog(std::u16string error_message,
|
||||
std::function<void()> finished_check) const override;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
||||
@@ -71,6 +71,10 @@ constexpr u32 PSTATE_REGISTER = 33;
|
||||
constexpr u32 UC_ARM64_REG_Q0 = 34;
|
||||
constexpr u32 FPCR_REGISTER = 66;
|
||||
|
||||
// TODO/WiP - Used while working on support for FPU
|
||||
constexpr u32 TODO_DUMMY_REG_997 = 997;
|
||||
constexpr u32 TODO_DUMMY_REG_998 = 998;
|
||||
|
||||
// For sample XML files see the GDB source /gdb/features
|
||||
// GDB also wants the l character at the start
|
||||
// This XML defines what the registers are for this specific ARM device
|
||||
@@ -256,36 +260,6 @@ static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr)
|
||||
}
|
||||
}
|
||||
|
||||
static u128 FpuRead(std::size_t id, Kernel::Thread* thread = nullptr) {
|
||||
if (!thread) {
|
||||
return u128{0};
|
||||
}
|
||||
|
||||
auto& thread_context = thread->GetContext();
|
||||
|
||||
if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
|
||||
return thread_context.vector_registers[id - UC_ARM64_REG_Q0];
|
||||
} else if (id == FPCR_REGISTER) {
|
||||
return u128{thread_context.fpcr, 0};
|
||||
} else {
|
||||
return u128{0};
|
||||
}
|
||||
}
|
||||
|
||||
static void FpuWrite(std::size_t id, u128 val, Kernel::Thread* thread = nullptr) {
|
||||
if (!thread) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& thread_context = thread->GetContext();
|
||||
|
||||
if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
|
||||
thread_context.vector_registers[id - UC_ARM64_REG_Q0] = val;
|
||||
} else if (id == FPCR_REGISTER) {
|
||||
thread_context.fpcr = val[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns hex string character into the equivalent byte.
|
||||
*
|
||||
@@ -435,27 +409,6 @@ static u64 GdbHexToLong(const u8* src) {
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a gdb-formatted hex string into a u128.
|
||||
*
|
||||
* @param src Pointer to hex string.
|
||||
*/
|
||||
static u128 GdbHexToU128(const u8* src) {
|
||||
u128 output;
|
||||
|
||||
for (int i = 0; i < 16; i += 2) {
|
||||
output[0] = (output[0] << 4) | HexCharToValue(src[15 - i - 1]);
|
||||
output[0] = (output[0] << 4) | HexCharToValue(src[15 - i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; i += 2) {
|
||||
output[1] = (output[1] << 4) | HexCharToValue(src[16 + 15 - i - 1]);
|
||||
output[1] = (output[1] << 4) | HexCharToValue(src[16 + 15 - i]);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// Read a byte from the gdb client.
|
||||
static u8 ReadByte() {
|
||||
u8 c;
|
||||
@@ -646,7 +599,8 @@ static void HandleQuery() {
|
||||
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
|
||||
const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
|
||||
for (const auto& thread : threads) {
|
||||
val += fmt::format("{:x},", thread->GetThreadID());
|
||||
val += fmt::format("{:x}", thread->GetThreadID());
|
||||
val += ",";
|
||||
}
|
||||
}
|
||||
val.pop_back();
|
||||
@@ -837,15 +791,11 @@ static void ReadRegister() {
|
||||
} else if (id == PSTATE_REGISTER) {
|
||||
IntToGdbHex(reply, static_cast<u32>(RegRead(id, current_thread)));
|
||||
} else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
|
||||
u128 r = FpuRead(id, current_thread);
|
||||
LongToGdbHex(reply, r[0]);
|
||||
LongToGdbHex(reply + 16, r[1]);
|
||||
LongToGdbHex(reply, RegRead(id, current_thread));
|
||||
} else if (id == FPCR_REGISTER) {
|
||||
u128 r = FpuRead(id, current_thread);
|
||||
IntToGdbHex(reply, static_cast<u32>(r[0]));
|
||||
} else if (id == FPCR_REGISTER + 1) {
|
||||
u128 r = FpuRead(id, current_thread);
|
||||
IntToGdbHex(reply, static_cast<u32>(r[0] >> 32));
|
||||
LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_998, current_thread));
|
||||
} else {
|
||||
LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_997, current_thread));
|
||||
}
|
||||
|
||||
SendReply(reinterpret_cast<char*>(reply));
|
||||
@@ -872,18 +822,13 @@ static void ReadRegisters() {
|
||||
|
||||
bufptr += 8;
|
||||
|
||||
u128 r;
|
||||
|
||||
for (u32 reg = UC_ARM64_REG_Q0; reg < FPCR_REGISTER; reg++) {
|
||||
r = FpuRead(reg, current_thread);
|
||||
LongToGdbHex(bufptr + reg * 32, r[0]);
|
||||
LongToGdbHex(bufptr + reg * 32 + 16, r[1]);
|
||||
for (u32 reg = UC_ARM64_REG_Q0; reg <= UC_ARM64_REG_Q0 + 31; reg++) {
|
||||
LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread));
|
||||
}
|
||||
|
||||
bufptr += 32 * 32;
|
||||
|
||||
r = FpuRead(FPCR_REGISTER, current_thread);
|
||||
IntToGdbHex(bufptr, static_cast<u32>(r[0]));
|
||||
LongToGdbHex(bufptr, RegRead(TODO_DUMMY_REG_998, current_thread));
|
||||
|
||||
bufptr += 8;
|
||||
|
||||
@@ -908,12 +853,14 @@ static void WriteRegister() {
|
||||
} else if (id == PSTATE_REGISTER) {
|
||||
RegWrite(id, GdbHexToInt(buffer_ptr), current_thread);
|
||||
} else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
|
||||
FpuWrite(id, GdbHexToU128(buffer_ptr), current_thread);
|
||||
RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
|
||||
} else if (id == FPCR_REGISTER) {
|
||||
} else if (id == FPCR_REGISTER + 1) {
|
||||
RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr), current_thread);
|
||||
} else {
|
||||
RegWrite(TODO_DUMMY_REG_997, GdbHexToLong(buffer_ptr), current_thread);
|
||||
}
|
||||
|
||||
// Update ARM context, skipping scheduler - no running threads at this point
|
||||
// Update Unicorn context skipping scheduler, no running threads at this point
|
||||
Core::System::GetInstance()
|
||||
.ArmInterface(current_core)
|
||||
.LoadContext(current_thread->GetContext());
|
||||
@@ -938,13 +885,13 @@ static void WriteRegisters() {
|
||||
} else if (reg >= UC_ARM64_REG_Q0 && reg < FPCR_REGISTER) {
|
||||
RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);
|
||||
} else if (reg == FPCR_REGISTER) {
|
||||
RegWrite(FPCR_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread);
|
||||
} else if (reg == FPCR_REGISTER + 1) {
|
||||
RegWrite(FPCR_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread);
|
||||
RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr + i * 16), current_thread);
|
||||
} else {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
}
|
||||
|
||||
// Update ARM context, skipping scheduler - no running threads at this point
|
||||
// Update Unicorn context skipping scheduler, no running threads at this point
|
||||
Core::System::GetInstance()
|
||||
.ArmInterface(current_core)
|
||||
.LoadContext(current_thread->GetContext());
|
||||
@@ -970,6 +917,12 @@ static void ReadMemory() {
|
||||
SendReply("E01");
|
||||
}
|
||||
|
||||
const auto& vm_manager = Core::CurrentProcess()->VMManager();
|
||||
if (addr < vm_manager.GetCodeRegionBaseAddress() ||
|
||||
addr >= vm_manager.GetMapRegionEndAddress()) {
|
||||
return SendReply("E00");
|
||||
}
|
||||
|
||||
if (!Memory::IsValidVirtualAddress(addr)) {
|
||||
return SendReply("E00");
|
||||
}
|
||||
@@ -1014,7 +967,7 @@ void Break(bool is_memory_break) {
|
||||
static void Step() {
|
||||
if (command_length > 1) {
|
||||
RegWrite(PC_REGISTER, GdbHexToLong(command_buffer + 1), current_thread);
|
||||
// Update ARM context, skipping scheduler - no running threads at this point
|
||||
// Update Unicorn context skipping scheduler, no running threads at this point
|
||||
Core::System::GetInstance()
|
||||
.ArmInterface(current_core)
|
||||
.LoadContext(current_thread->GetContext());
|
||||
@@ -1057,7 +1010,7 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
|
||||
breakpoint.addr = addr;
|
||||
breakpoint.len = len;
|
||||
Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
|
||||
static constexpr std::array<u8, 4> btrap{0x00, 0x7d, 0x20, 0xd4};
|
||||
static constexpr std::array<u8, 4> btrap{{0x00, 0x7d, 0x20, 0xd4}};
|
||||
Memory::WriteBlock(addr, btrap.data(), btrap.size());
|
||||
Core::System::GetInstance().InvalidateCpuInstructionCaches();
|
||||
p.insert({addr, breakpoint});
|
||||
@@ -1368,15 +1321,13 @@ void SetCpuStepFlag(bool is_step) {
|
||||
}
|
||||
|
||||
void SendTrap(Kernel::Thread* thread, int trap) {
|
||||
if (!send_trap) {
|
||||
return;
|
||||
if (send_trap) {
|
||||
if (!halt_loop || current_thread == thread) {
|
||||
current_thread = thread;
|
||||
SendSignal(thread, trap);
|
||||
}
|
||||
halt_loop = true;
|
||||
send_trap = false;
|
||||
}
|
||||
|
||||
if (!halt_loop || current_thread == thread) {
|
||||
current_thread = thread;
|
||||
SendSignal(thread, trap);
|
||||
}
|
||||
halt_loop = true;
|
||||
send_trap = false;
|
||||
}
|
||||
}; // namespace GDBStub
|
||||
|
||||
@@ -8,58 +8,28 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
namespace ErrCodes {
|
||||
enum {
|
||||
// Confirmed Switch OS error codes
|
||||
MaxConnectionsReached = 7,
|
||||
InvalidSize = 101,
|
||||
InvalidAddress = 102,
|
||||
HandleTableFull = 105,
|
||||
InvalidMemoryState = 106,
|
||||
InvalidMemoryPermissions = 108,
|
||||
InvalidMemoryRange = 110,
|
||||
InvalidThreadPriority = 112,
|
||||
InvalidProcessorId = 113,
|
||||
InvalidHandle = 114,
|
||||
InvalidPointer = 115,
|
||||
InvalidCombination = 116,
|
||||
Timeout = 117,
|
||||
SynchronizationCanceled = 118,
|
||||
TooLarge = 119,
|
||||
InvalidEnumValue = 120,
|
||||
NoSuchEntry = 121,
|
||||
AlreadyRegistered = 122,
|
||||
SessionClosed = 123,
|
||||
InvalidState = 125,
|
||||
ResourceLimitExceeded = 132,
|
||||
};
|
||||
}
|
||||
// Confirmed Switch kernel error codes
|
||||
|
||||
// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always
|
||||
// double check that the code matches before re-using the constant.
|
||||
|
||||
constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull);
|
||||
constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(ErrorModule::Kernel, ErrCodes::SessionClosed);
|
||||
constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge);
|
||||
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel,
|
||||
ErrCodes::MaxConnectionsReached);
|
||||
constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue);
|
||||
constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel,
|
||||
ErrCodes::InvalidCombination);
|
||||
constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress);
|
||||
constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState);
|
||||
constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel,
|
||||
ErrCodes::InvalidMemoryPermissions);
|
||||
constexpr ResultCode ERR_INVALID_MEMORY_RANGE(ErrorModule::Kernel, ErrCodes::InvalidMemoryRange);
|
||||
constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle);
|
||||
constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
|
||||
constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize);
|
||||
constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::AlreadyRegistered);
|
||||
constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState);
|
||||
constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel,
|
||||
ErrCodes::InvalidThreadPriority);
|
||||
constexpr ResultCode ERR_INVALID_POINTER(ErrorModule::Kernel, ErrCodes::InvalidPointer);
|
||||
constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry);
|
||||
constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout);
|
||||
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
|
||||
constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
|
||||
constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
|
||||
constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
|
||||
constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106};
|
||||
constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108};
|
||||
constexpr ResultCode ERR_INVALID_MEMORY_RANGE{ErrorModule::Kernel, 110};
|
||||
constexpr ResultCode ERR_INVALID_PROCESSOR_ID{ErrorModule::Kernel, 113};
|
||||
constexpr ResultCode ERR_INVALID_THREAD_PRIORITY{ErrorModule::Kernel, 112};
|
||||
constexpr ResultCode ERR_INVALID_HANDLE{ErrorModule::Kernel, 114};
|
||||
constexpr ResultCode ERR_INVALID_POINTER{ErrorModule::Kernel, 115};
|
||||
constexpr ResultCode ERR_INVALID_COMBINATION{ErrorModule::Kernel, 116};
|
||||
constexpr ResultCode RESULT_TIMEOUT{ErrorModule::Kernel, 117};
|
||||
constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118};
|
||||
constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119};
|
||||
constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120};
|
||||
constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121};
|
||||
constexpr ResultCode ERR_ALREADY_REGISTERED{ErrorModule::Kernel, 122};
|
||||
constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123};
|
||||
constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125};
|
||||
constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
@@ -17,6 +15,7 @@
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -35,6 +34,11 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
|
||||
process->process_id = kernel.CreateNewProcessID();
|
||||
process->svc_access_mask.set();
|
||||
|
||||
std::mt19937 rng(Settings::values.rng_seed.value_or(0));
|
||||
std::uniform_int_distribution<u64> distribution;
|
||||
std::generate(process->random_entropy.begin(), process->random_entropy.end(),
|
||||
[&] { return distribution(rng); });
|
||||
|
||||
kernel.AppendNewProcess(process);
|
||||
return process;
|
||||
}
|
||||
@@ -241,83 +245,15 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
|
||||
}
|
||||
|
||||
ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
|
||||
if (target < vm_manager.GetHeapRegionBaseAddress() ||
|
||||
target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (heap_memory == nullptr) {
|
||||
// Initialize heap
|
||||
heap_memory = std::make_shared<std::vector<u8>>();
|
||||
heap_start = heap_end = target;
|
||||
} else {
|
||||
vm_manager.UnmapRange(heap_start, heap_end - heap_start);
|
||||
}
|
||||
|
||||
// If necessary, expand backing vector to cover new heap extents.
|
||||
if (target < heap_start) {
|
||||
heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
|
||||
heap_start = target;
|
||||
vm_manager.RefreshMemoryBlockMappings(heap_memory.get());
|
||||
}
|
||||
if (target + size > heap_end) {
|
||||
heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
|
||||
heap_end = target + size;
|
||||
vm_manager.RefreshMemoryBlockMappings(heap_memory.get());
|
||||
}
|
||||
ASSERT(heap_end - heap_start == heap_memory->size());
|
||||
|
||||
CASCADE_RESULT(auto vma, vm_manager.MapMemoryBlock(target, heap_memory, target - heap_start,
|
||||
size, MemoryState::Heap));
|
||||
vm_manager.Reprotect(vma, perms);
|
||||
|
||||
heap_used = size;
|
||||
|
||||
return MakeResult<VAddr>(heap_end - size);
|
||||
return vm_manager.HeapAllocate(target, size, perms);
|
||||
}
|
||||
|
||||
ResultCode Process::HeapFree(VAddr target, u32 size) {
|
||||
if (target < vm_manager.GetHeapRegionBaseAddress() ||
|
||||
target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode result = vm_manager.UnmapRange(target, size);
|
||||
if (result.IsError())
|
||||
return result;
|
||||
|
||||
heap_used -= size;
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return vm_manager.HeapFree(target, size);
|
||||
}
|
||||
|
||||
ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
|
||||
auto vma = vm_manager.FindVMA(src_addr);
|
||||
|
||||
ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address");
|
||||
ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address");
|
||||
|
||||
// The returned VMA might be a bigger one encompassing the desired address.
|
||||
auto vma_offset = src_addr - vma->first;
|
||||
ASSERT_MSG(vma_offset + size <= vma->second.size,
|
||||
"Shared memory exceeds bounds of mapped block");
|
||||
|
||||
const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
|
||||
std::size_t backing_block_offset = vma->second.offset + vma_offset;
|
||||
|
||||
CASCADE_RESULT(auto new_vma,
|
||||
vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size,
|
||||
MemoryState::Mapped));
|
||||
// Protect mirror with permissions from old region
|
||||
vm_manager.Reprotect(new_vma, vma->second.permissions);
|
||||
// Remove permissions from old region
|
||||
vm_manager.Reprotect(vma, VMAPermission::None);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return vm_manager.MirrorMemory(dst_addr, src_addr, size);
|
||||
}
|
||||
|
||||
ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <bitset>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
@@ -119,6 +120,8 @@ struct CodeSet final {
|
||||
|
||||
class Process final : public Object {
|
||||
public:
|
||||
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
|
||||
|
||||
static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name);
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
@@ -212,6 +215,11 @@ public:
|
||||
total_process_running_time_ticks += ticks;
|
||||
}
|
||||
|
||||
/// Gets 8 bytes of random data for svcGetInfo RandomEntropy
|
||||
u64 GetRandomEntropy(std::size_t index) const {
|
||||
return random_entropy.at(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads process-specifics configuration info with metadata provided
|
||||
* by an executable.
|
||||
@@ -292,17 +300,6 @@ private:
|
||||
u32 allowed_thread_priority_mask = 0xFFFFFFFF;
|
||||
u32 is_virtual_address_memory_enabled = 0;
|
||||
|
||||
// Memory used to back the allocations in the regular heap. A single vector is used to cover
|
||||
// the entire virtual address space extents that bound the allocations, including any holes.
|
||||
// This makes deallocation and reallocation of holes fast and keeps process memory contiguous
|
||||
// in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
|
||||
std::shared_ptr<std::vector<u8>> heap_memory;
|
||||
|
||||
// The left/right bounds of the address space covered by heap_memory.
|
||||
VAddr heap_start = 0;
|
||||
VAddr heap_end = 0;
|
||||
u64 heap_used = 0;
|
||||
|
||||
/// The Thread Local Storage area is allocated as processes create threads,
|
||||
/// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
|
||||
/// holds the TLS for a specific thread. This vector contains which parts are in use for each
|
||||
@@ -321,6 +318,9 @@ private:
|
||||
/// Per-process handle table for storing created object handles in.
|
||||
HandleTable handle_table;
|
||||
|
||||
/// Random values for svcGetInfo RandomEntropy
|
||||
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
|
||||
|
||||
std::string name;
|
||||
};
|
||||
|
||||
|
||||
@@ -123,6 +123,48 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
|
||||
LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot);
|
||||
|
||||
if (!Common::Is4KBAligned(addr)) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (size == 0 || !Common::Is4KBAligned(size)) {
|
||||
return ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
if (!IsValidAddressRange(addr, size)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
const auto permission = static_cast<MemoryPermission>(prot);
|
||||
if (permission != MemoryPermission::None && permission != MemoryPermission::Read &&
|
||||
permission != MemoryPermission::ReadWrite) {
|
||||
return ERR_INVALID_MEMORY_PERMISSIONS;
|
||||
}
|
||||
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
auto& vm_manager = current_process->VMManager();
|
||||
|
||||
if (!IsInsideAddressSpace(vm_manager, addr, size)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
const VMManager::VMAHandle iter = vm_manager.FindVMA(addr);
|
||||
if (iter == vm_manager.vma_map.end()) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
LOG_WARNING(Kernel_SVC, "Uniformity check on protected memory is not implemented.");
|
||||
// TODO: Performs a uniformity check to make sure only protected memory is changed (it doesn't
|
||||
// make sense to allow changing permissions on kernel memory itself, etc).
|
||||
|
||||
const auto converted_permissions = SharedMemory::ConvertPermissions(permission);
|
||||
|
||||
return vm_manager.ReprotectRange(addr, size, converted_permissions);
|
||||
}
|
||||
|
||||
static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) {
|
||||
LOG_WARNING(Kernel_SVC,
|
||||
"(STUBBED) called, addr=0x{:X}, size=0x{:X}, state0=0x{:X}, state1=0x{:X}", addr,
|
||||
@@ -172,7 +214,7 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
|
||||
// Read 1 char beyond the max allowed port name to detect names that are too long.
|
||||
std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1);
|
||||
if (port_name.size() > PortNameMaxLength) {
|
||||
return ERR_PORT_NAME_TOO_LONG;
|
||||
return ERR_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
|
||||
@@ -268,8 +310,9 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
|
||||
|
||||
static constexpr u64 MaxHandles = 0x40;
|
||||
|
||||
if (handle_count > MaxHandles)
|
||||
return ResultCode(ErrorModule::Kernel, ErrCodes::TooLarge);
|
||||
if (handle_count > MaxHandles) {
|
||||
return ERR_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
auto* const thread = GetCurrentThread();
|
||||
|
||||
@@ -334,8 +377,7 @@ static ResultCode CancelSynchronization(Handle thread_handle) {
|
||||
}
|
||||
|
||||
ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny);
|
||||
thread->SetWaitSynchronizationResult(
|
||||
ResultCode(ErrorModule::Kernel, ErrCodes::SynchronizationCanceled));
|
||||
thread->SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED);
|
||||
thread->ResumeFromWait();
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
@@ -559,7 +601,16 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
*result = 0;
|
||||
break;
|
||||
case GetInfoType::RandomEntropy:
|
||||
*result = Settings::values.rng_seed.value_or(0);
|
||||
if (handle != 0) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (info_sub_id >= Process::RANDOM_ENTROPY_SIZE) {
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
*result = current_process->GetRandomEntropy(info_sub_id);
|
||||
return RESULT_SUCCESS;
|
||||
break;
|
||||
case GetInfoType::ASLRRegionBaseAddr:
|
||||
*result = vm_manager.GetASLRRegionBaseAddress();
|
||||
@@ -592,7 +643,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
case GetInfoType::ThreadTickCount: {
|
||||
constexpr u64 num_cpus = 4;
|
||||
if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
|
||||
return ERR_INVALID_COMBINATION_KERNEL;
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
const auto thread =
|
||||
@@ -1143,9 +1194,39 @@ static ResultCode ResetSignal(Handle handle) {
|
||||
|
||||
/// Creates a TransferMemory object
|
||||
static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) {
|
||||
LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size,
|
||||
permissions);
|
||||
*handle = 0;
|
||||
LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size,
|
||||
permissions);
|
||||
|
||||
if (!Common::Is4KBAligned(addr)) {
|
||||
LOG_ERROR(Kernel_SVC, "Address ({:016X}) is not page aligned!", addr);
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (!Common::Is4KBAligned(size) || size == 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Size ({:016X}) is not page aligned or equal to zero!", size);
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (!IsValidAddressRange(addr, size)) {
|
||||
LOG_ERROR(Kernel_SVC, "Address and size cause overflow! (address={:016X}, size={:016X})",
|
||||
addr, size);
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
const auto perms = static_cast<MemoryPermission>(permissions);
|
||||
if (perms != MemoryPermission::None && perms != MemoryPermission::Read &&
|
||||
perms != MemoryPermission::ReadWrite) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid memory permissions for transfer memory! (perms={:08X})",
|
||||
permissions);
|
||||
return ERR_INVALID_MEMORY_PERMISSIONS;
|
||||
}
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
const auto shared_mem_handle = SharedMemory::Create(
|
||||
kernel, handle_table.Get<Process>(CurrentProcess), size, perms, perms, addr);
|
||||
|
||||
CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle));
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1185,7 +1266,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
|
||||
}
|
||||
|
||||
if (mask == 0) {
|
||||
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination);
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
/// This value is used to only change the affinity mask without changing the current ideal core.
|
||||
@@ -1194,12 +1275,12 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
|
||||
if (core == OnlyChangeMask) {
|
||||
core = thread->GetIdealCore();
|
||||
} else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) {
|
||||
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
|
||||
// Error out if the input core isn't enabled in the input mask.
|
||||
if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) {
|
||||
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination);
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
thread->ChangeCore(core, mask);
|
||||
@@ -1288,7 +1369,7 @@ struct FunctionDef {
|
||||
static const FunctionDef SVC_Table[] = {
|
||||
{0x00, nullptr, "Unknown"},
|
||||
{0x01, SvcWrap<SetHeapSize>, "SetHeapSize"},
|
||||
{0x02, nullptr, "SetMemoryPermission"},
|
||||
{0x02, SvcWrap<SetMemoryPermission>, "SetMemoryPermission"},
|
||||
{0x03, SvcWrap<SetMemoryAttribute>, "SetMemoryAttribute"},
|
||||
{0x04, SvcWrap<MapMemory>, "MapMemory"},
|
||||
{0x05, SvcWrap<UnmapMemory>, "UnmapMemory"},
|
||||
|
||||
@@ -121,6 +121,11 @@ void SvcWrap() {
|
||||
FuncReturn(func(Param(0), Param(1), Param(2)).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u64, u64, u32)>
|
||||
void SvcWrap() {
|
||||
FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2))).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u32, u64, u64, u32)>
|
||||
void SvcWrap() {
|
||||
FuncReturn(
|
||||
|
||||
@@ -142,36 +142,7 @@ void Thread::ResumeFromWait() {
|
||||
|
||||
status = ThreadStatus::Ready;
|
||||
|
||||
std::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask);
|
||||
if (!new_processor_id) {
|
||||
new_processor_id = processor_id;
|
||||
}
|
||||
if (ideal_core != -1 &&
|
||||
Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
|
||||
new_processor_id = ideal_core;
|
||||
}
|
||||
|
||||
ASSERT(*new_processor_id < 4);
|
||||
|
||||
// Add thread to new core's scheduler
|
||||
auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
|
||||
|
||||
if (*new_processor_id != processor_id) {
|
||||
// Remove thread from previous core's scheduler
|
||||
scheduler->RemoveThread(this);
|
||||
next_scheduler->AddThread(this, current_priority);
|
||||
}
|
||||
|
||||
processor_id = *new_processor_id;
|
||||
|
||||
// If the thread was ready, unschedule from the previous core and schedule on the new core
|
||||
scheduler->UnscheduleThread(this, current_priority);
|
||||
next_scheduler->ScheduleThread(this, current_priority);
|
||||
|
||||
// Change thread's scheduler
|
||||
scheduler = next_scheduler;
|
||||
|
||||
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
|
||||
ChangeScheduler();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,42 +335,45 @@ void Thread::UpdatePriority() {
|
||||
void Thread::ChangeCore(u32 core, u64 mask) {
|
||||
ideal_core = core;
|
||||
affinity_mask = mask;
|
||||
ChangeScheduler();
|
||||
}
|
||||
|
||||
void Thread::ChangeScheduler() {
|
||||
if (status != ThreadStatus::Ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
std::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
|
||||
|
||||
if (!new_processor_id) {
|
||||
new_processor_id = processor_id;
|
||||
}
|
||||
if (ideal_core != -1 &&
|
||||
Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
|
||||
if (ideal_core != -1 && system.Scheduler(ideal_core).GetCurrentThread() == nullptr) {
|
||||
new_processor_id = ideal_core;
|
||||
}
|
||||
|
||||
ASSERT(*new_processor_id < 4);
|
||||
|
||||
// Add thread to new core's scheduler
|
||||
auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
|
||||
auto& next_scheduler = system.Scheduler(*new_processor_id);
|
||||
|
||||
if (*new_processor_id != processor_id) {
|
||||
// Remove thread from previous core's scheduler
|
||||
scheduler->RemoveThread(this);
|
||||
next_scheduler->AddThread(this, current_priority);
|
||||
next_scheduler.AddThread(this, current_priority);
|
||||
}
|
||||
|
||||
processor_id = *new_processor_id;
|
||||
|
||||
// If the thread was ready, unschedule from the previous core and schedule on the new core
|
||||
scheduler->UnscheduleThread(this, current_priority);
|
||||
next_scheduler->ScheduleThread(this, current_priority);
|
||||
next_scheduler.ScheduleThread(this, current_priority);
|
||||
|
||||
// Change thread's scheduler
|
||||
scheduler = next_scheduler;
|
||||
scheduler = &next_scheduler;
|
||||
|
||||
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
|
||||
system.CpuCore(processor_id).PrepareReschedule();
|
||||
}
|
||||
|
||||
bool Thread::AllWaitObjectsReady() {
|
||||
|
||||
@@ -374,6 +374,8 @@ private:
|
||||
explicit Thread(KernelCore& kernel);
|
||||
~Thread() override;
|
||||
|
||||
void ChangeScheduler();
|
||||
|
||||
Core::ARM_Interface::ThreadContext context{};
|
||||
|
||||
u32 thread_id = 0;
|
||||
|
||||
@@ -243,6 +243,85 @@ ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_p
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
|
||||
if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() ||
|
||||
target + size < target) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (heap_memory == nullptr) {
|
||||
// Initialize heap
|
||||
heap_memory = std::make_shared<std::vector<u8>>();
|
||||
heap_start = heap_end = target;
|
||||
} else {
|
||||
UnmapRange(heap_start, heap_end - heap_start);
|
||||
}
|
||||
|
||||
// If necessary, expand backing vector to cover new heap extents.
|
||||
if (target < heap_start) {
|
||||
heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
|
||||
heap_start = target;
|
||||
RefreshMemoryBlockMappings(heap_memory.get());
|
||||
}
|
||||
if (target + size > heap_end) {
|
||||
heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
|
||||
heap_end = target + size;
|
||||
RefreshMemoryBlockMappings(heap_memory.get());
|
||||
}
|
||||
ASSERT(heap_end - heap_start == heap_memory->size());
|
||||
|
||||
CASCADE_RESULT(auto vma, MapMemoryBlock(target, heap_memory, target - heap_start, size,
|
||||
MemoryState::Heap));
|
||||
Reprotect(vma, perms);
|
||||
|
||||
heap_used = size;
|
||||
|
||||
return MakeResult<VAddr>(heap_end - size);
|
||||
}
|
||||
|
||||
ResultCode VMManager::HeapFree(VAddr target, u64 size) {
|
||||
if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() ||
|
||||
target + size < target) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
const ResultCode result = UnmapRange(target, size);
|
||||
if (result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
heap_used -= size;
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
|
||||
const auto vma = FindVMA(src_addr);
|
||||
|
||||
ASSERT_MSG(vma != vma_map.end(), "Invalid memory address");
|
||||
ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address");
|
||||
|
||||
// The returned VMA might be a bigger one encompassing the desired address.
|
||||
const auto vma_offset = src_addr - vma->first;
|
||||
ASSERT_MSG(vma_offset + size <= vma->second.size,
|
||||
"Shared memory exceeds bounds of mapped block");
|
||||
|
||||
const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
|
||||
const std::size_t backing_block_offset = vma->second.offset + vma_offset;
|
||||
|
||||
CASCADE_RESULT(auto new_vma, MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size,
|
||||
MemoryState::Mapped));
|
||||
// Protect mirror with permissions from old region
|
||||
Reprotect(new_vma, vma->second.permissions);
|
||||
// Remove permissions from old region
|
||||
Reprotect(vma, VMAPermission::None);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) {
|
||||
// If this ever proves to have a noticeable performance impact, allow users of the function to
|
||||
// specify a specific range of addresses to limit the scan to.
|
||||
@@ -495,8 +574,7 @@ u64 VMManager::GetTotalMemoryUsage() const {
|
||||
}
|
||||
|
||||
u64 VMManager::GetTotalHeapUsage() const {
|
||||
LOG_WARNING(Kernel, "(STUBBED) called");
|
||||
return 0x0;
|
||||
return heap_used;
|
||||
}
|
||||
|
||||
VAddr VMManager::GetAddressSpaceBaseAddress() const {
|
||||
|
||||
@@ -186,6 +186,11 @@ public:
|
||||
/// Changes the permissions of a range of addresses, splitting VMAs as necessary.
|
||||
ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms);
|
||||
|
||||
ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
|
||||
ResultCode HeapFree(VAddr target, u64 size);
|
||||
|
||||
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
|
||||
|
||||
/**
|
||||
* Scans all VMAs and updates the page table range of any that use the given vector as backing
|
||||
* memory. This should be called after any operation that causes reallocation of the vector.
|
||||
@@ -343,5 +348,15 @@ private:
|
||||
|
||||
VAddr tls_io_region_base = 0;
|
||||
VAddr tls_io_region_end = 0;
|
||||
|
||||
// Memory used to back the allocations in the regular heap. A single vector is used to cover
|
||||
// the entire virtual address space extents that bound the allocations, including any holes.
|
||||
// This makes deallocation and reallocation of holes fast and keeps process memory contiguous
|
||||
// in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
|
||||
std::shared_ptr<std::vector<u8>> heap_memory;
|
||||
// The left/right bounds of the address space covered by heap_memory.
|
||||
VAddr heap_start = 0;
|
||||
VAddr heap_end = 0;
|
||||
u64 heap_used = 0;
|
||||
};
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
enum class ErrorDescription : u32 {
|
||||
Success = 0,
|
||||
RemoteProcessDead = 301,
|
||||
InvalidOffset = 6061,
|
||||
InvalidLength = 6062,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -252,8 +252,10 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex
|
||||
rb.PushRaw<u128>(INVALID_UUID);
|
||||
return;
|
||||
}
|
||||
auto user_list = profile_manager->GetAllUsers();
|
||||
if (user_list.empty()) {
|
||||
|
||||
const auto user_list = profile_manager->GetAllUsers();
|
||||
if (std::all_of(user_list.begin(), user_list.end(),
|
||||
[](const auto& user) { return user.uuid == INVALID_UUID; })) {
|
||||
rb.Push(ResultCode(-1)); // TODO(ogniK): Find the correct error code
|
||||
rb.PushRaw<u128>(INVALID_UUID);
|
||||
return;
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include <random>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/file_util.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/settings.h"
|
||||
@@ -39,6 +42,19 @@ UUID UUID::Generate() {
|
||||
return UUID{distribution(gen), distribution(gen)};
|
||||
}
|
||||
|
||||
std::string UUID::Format() const {
|
||||
return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
|
||||
}
|
||||
|
||||
std::string UUID::FormatSwitch() const {
|
||||
std::array<u8, 16> s{};
|
||||
std::memcpy(s.data(), uuid.data(), sizeof(u128));
|
||||
return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
|
||||
":02x}{:02x}{:02x}{:02x}{:02x}",
|
||||
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
|
||||
s[12], s[13], s[14], s[15]);
|
||||
}
|
||||
|
||||
ProfileManager::ProfileManager() {
|
||||
ParseUserSaveFile();
|
||||
|
||||
@@ -325,11 +341,12 @@ void ProfileManager::ParseUserSaveFile() {
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < MAX_USERS; ++i) {
|
||||
const auto& user = data.users[i];
|
||||
for (const auto& user : data.users) {
|
||||
if (user.uuid == UUID(INVALID_UUID)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (user.uuid != UUID(INVALID_UUID))
|
||||
AddUser({user.uuid, user.username, user.timestamp, {}, false});
|
||||
AddUser({user.uuid, user.username, user.timestamp, {}, false});
|
||||
}
|
||||
|
||||
std::stable_partition(profiles.begin(), profiles.end(),
|
||||
|
||||
@@ -42,18 +42,9 @@ struct UUID {
|
||||
void Invalidate() {
|
||||
uuid = INVALID_UUID;
|
||||
}
|
||||
std::string Format() const {
|
||||
return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
|
||||
}
|
||||
|
||||
std::string FormatSwitch() const {
|
||||
std::array<u8, 16> s{};
|
||||
std::memcpy(s.data(), uuid.data(), sizeof(u128));
|
||||
return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
|
||||
":02x}{:02x}{:02x}{:02x}{:02x}",
|
||||
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
|
||||
s[12], s[13], s[14], s[15]);
|
||||
}
|
||||
std::string Format() const;
|
||||
std::string FormatSwitch() const;
|
||||
};
|
||||
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
|
||||
|
||||
|
||||
@@ -6,10 +6,14 @@
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include <stack>
|
||||
#include "applets/applets.h"
|
||||
#include "applets/software_keyboard.h"
|
||||
#include "audio_core/audio_renderer.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/shared_memory.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applet_ae.h"
|
||||
@@ -28,6 +32,13 @@
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2};
|
||||
constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
|
||||
|
||||
enum class AppletId : u32 {
|
||||
SoftwareKeyboard = 0x11,
|
||||
};
|
||||
|
||||
constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
|
||||
|
||||
struct LaunchParameters {
|
||||
@@ -203,8 +214,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
|
||||
ISelfController::~ISelfController() = default;
|
||||
|
||||
void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
|
||||
// Takes 3 input u8s with each field located immediately after the previous u8, these are
|
||||
// bool flags. No output.
|
||||
// Takes 3 input u8s with each field located immediately after the previous
|
||||
// u8, these are bool flags. No output.
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
@@ -258,8 +269,8 @@ void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestCont
|
||||
}
|
||||
|
||||
void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) {
|
||||
// Takes 3 input u8s with each field located immediately after the previous u8, these are
|
||||
// bool flags. No output.
|
||||
// Takes 3 input u8s with each field located immediately after the previous
|
||||
// u8, these are bool flags. No output.
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
bool enabled = rp.Pop<bool>();
|
||||
@@ -302,8 +313,8 @@ void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& c
|
||||
}
|
||||
|
||||
void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) {
|
||||
// TODO(Subv): Find out how AM determines the display to use, for now just create the layer
|
||||
// in the Default display.
|
||||
// TODO(Subv): Find out how AM determines the display to use, for now just
|
||||
// create the layer in the Default display.
|
||||
u64 display_id = nvflinger->OpenDisplay("Default");
|
||||
u64 layer_id = nvflinger->CreateLayer(display_id);
|
||||
|
||||
@@ -481,6 +492,24 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext&
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
IStorage::IStorage(std::vector<u8> buffer)
|
||||
: ServiceFramework("IStorage"), buffer(std::move(buffer)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IStorage::Open, "Open"},
|
||||
{1, nullptr, "OpenTransferStorage"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IStorage::~IStorage() = default;
|
||||
|
||||
const std::vector<u8>& IStorage::GetData() const {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
|
||||
const bool use_docked_mode{Settings::values.use_docked_mode};
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
@@ -500,100 +529,15 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
|
||||
public:
|
||||
explicit IStorageAccessor(std::vector<u8> buffer)
|
||||
: ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IStorageAccessor::GetSize, "GetSize"},
|
||||
{10, &IStorageAccessor::Write, "Write"},
|
||||
{11, &IStorageAccessor::Read, "Read"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<u8> buffer;
|
||||
|
||||
void GetSize(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(static_cast<u64>(buffer.size()));
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void Write(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const u64 offset{rp.Pop<u64>()};
|
||||
const std::vector<u8> data{ctx.ReadBuffer()};
|
||||
|
||||
ASSERT(offset + data.size() <= buffer.size());
|
||||
|
||||
std::memcpy(&buffer[offset], data.data(), data.size());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, offset={}", offset);
|
||||
}
|
||||
|
||||
void Read(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const u64 offset{rp.Pop<u64>()};
|
||||
const std::size_t size{ctx.GetWriteBufferSize()};
|
||||
|
||||
ASSERT(offset + size <= buffer.size());
|
||||
|
||||
ctx.WriteBuffer(buffer.data() + offset, size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, offset={}", offset);
|
||||
}
|
||||
};
|
||||
|
||||
class IStorage final : public ServiceFramework<IStorage> {
|
||||
public:
|
||||
explicit IStorage(std::vector<u8> buffer)
|
||||
: ServiceFramework("IStorage"), buffer(std::move(buffer)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IStorage::Open, "Open"},
|
||||
{1, nullptr, "OpenTransferStorage"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<u8> buffer;
|
||||
|
||||
void Open(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<AM::IStorageAccessor>(buffer);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
};
|
||||
|
||||
class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
|
||||
public:
|
||||
explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") {
|
||||
explicit ILibraryAppletAccessor(std::shared_ptr<Applets::Applet> applet)
|
||||
: ServiceFramework("ILibraryAppletAccessor"), applet(std::move(applet)),
|
||||
broker(std::make_shared<Applets::AppletDataBroker>()) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
|
||||
{1, nullptr, "IsCompleted"},
|
||||
{1, &ILibraryAppletAccessor::IsCompleted, "IsCompleted"},
|
||||
{10, &ILibraryAppletAccessor::Start, "Start"},
|
||||
{20, nullptr, "RequestExit"},
|
||||
{25, nullptr, "Terminate"},
|
||||
@@ -602,10 +546,10 @@ public:
|
||||
{100, &ILibraryAppletAccessor::PushInData, "PushInData"},
|
||||
{101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
|
||||
{102, nullptr, "PushExtraStorage"},
|
||||
{103, nullptr, "PushInteractiveInData"},
|
||||
{104, nullptr, "PopInteractiveOutData"},
|
||||
{105, nullptr, "GetPopOutDataEvent"},
|
||||
{106, nullptr, "GetPopInteractiveOutDataEvent"},
|
||||
{103, &ILibraryAppletAccessor::PushInteractiveInData, "PushInteractiveInData"},
|
||||
{104, &ILibraryAppletAccessor::PopInteractiveOutData, "PopInteractiveOutData"},
|
||||
{105, &ILibraryAppletAccessor::GetPopOutDataEvent, "GetPopOutDataEvent"},
|
||||
{106, &ILibraryAppletAccessor::GetPopInteractiveOutDataEvent, "GetPopInteractiveOutDataEvent"},
|
||||
{110, nullptr, "NeedsToExitProcess"},
|
||||
{120, nullptr, "GetLibraryAppletInfo"},
|
||||
{150, nullptr, "RequestForAppletToGetForeground"},
|
||||
@@ -614,40 +558,50 @@ public:
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||
"ILibraryAppletAccessor:StateChangedEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) {
|
||||
state_changed_event->Signal();
|
||||
const auto event = broker->GetStateChangedEvent();
|
||||
event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(state_changed_event);
|
||||
rb.PushCopyObjects(event);
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void IsCompleted(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(applet->TransactionComplete());
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void GetResult(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(applet->GetStatus());
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void Start(Kernel::HLERequestContext& ctx) {
|
||||
ASSERT(applet != nullptr);
|
||||
|
||||
applet->Initialize(broker);
|
||||
applet->Execute();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void PushInData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
storage_stack.push(rp.PopIpcInterface<AM::IStorage>());
|
||||
broker->PushNormalDataFromGame(*rp.PopIpcInterface<IStorage>());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -657,25 +611,146 @@ private:
|
||||
|
||||
void PopOutData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<AM::IStorage>(std::move(storage_stack.top()));
|
||||
|
||||
storage_stack.pop();
|
||||
const auto storage = broker->PopNormalDataToGame();
|
||||
if (storage == nullptr) {
|
||||
rb.Push(ERR_NO_DATA_IN_CHANNEL);
|
||||
return;
|
||||
}
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IStorage>(std::move(*storage));
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
std::stack<std::shared_ptr<AM::IStorage>> storage_stack;
|
||||
Kernel::SharedPtr<Kernel::Event> state_changed_event;
|
||||
void PushInteractiveInData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
broker->PushInteractiveDataFromGame(*rp.PopIpcInterface<IStorage>());
|
||||
|
||||
ASSERT(applet->IsInitialized());
|
||||
applet->ExecuteInteractive();
|
||||
applet->Execute();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void PopInteractiveOutData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
const auto storage = broker->PopInteractiveDataToGame();
|
||||
if (storage == nullptr) {
|
||||
rb.Push(ERR_NO_DATA_IN_CHANNEL);
|
||||
return;
|
||||
}
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IStorage>(std::move(*storage));
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(broker->GetNormalDataEvent());
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void GetPopInteractiveOutDataEvent(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(broker->GetInteractiveDataEvent());
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
std::shared_ptr<Applets::Applet> applet;
|
||||
std::shared_ptr<Applets::AppletDataBroker> broker;
|
||||
};
|
||||
|
||||
void IStorage::Open(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IStorageAccessor>(*this);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
IStorageAccessor::IStorageAccessor(IStorage& storage)
|
||||
: ServiceFramework("IStorageAccessor"), backing(storage) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IStorageAccessor::GetSize, "GetSize"},
|
||||
{10, &IStorageAccessor::Write, "Write"},
|
||||
{11, &IStorageAccessor::Read, "Read"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IStorageAccessor::~IStorageAccessor() = default;
|
||||
|
||||
void IStorageAccessor::GetSize(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(static_cast<u64>(backing.buffer.size()));
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const u64 offset{rp.Pop<u64>()};
|
||||
const std::vector<u8> data{ctx.ReadBuffer()};
|
||||
|
||||
if (data.size() > backing.buffer.size() - offset) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
|
||||
}
|
||||
|
||||
std::memcpy(backing.buffer.data() + offset, data.data(), data.size());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, offset={}", offset);
|
||||
}
|
||||
|
||||
void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const u64 offset{rp.Pop<u64>()};
|
||||
const std::size_t size{ctx.GetWriteBufferSize()};
|
||||
|
||||
if (size > backing.buffer.size() - offset) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(backing.buffer.data() + offset, size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, offset={}", offset);
|
||||
}
|
||||
|
||||
ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"},
|
||||
{1, nullptr, "TerminateAllLibraryApplets"},
|
||||
{2, nullptr, "AreAnyLibraryAppletsLeft"},
|
||||
{10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"},
|
||||
{11, nullptr, "CreateTransferMemoryStorage"},
|
||||
{11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"},
|
||||
{12, nullptr, "CreateHandleStorage"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
@@ -683,11 +758,36 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple
|
||||
|
||||
ILibraryAppletCreator::~ILibraryAppletCreator() = default;
|
||||
|
||||
static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) {
|
||||
switch (id) {
|
||||
case AppletId::SoftwareKeyboard:
|
||||
return std::make_shared<Applets::SoftwareKeyboard>();
|
||||
default:
|
||||
UNREACHABLE_MSG("Unimplemented AppletId [{:08X}]!", static_cast<u32>(id));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_id = rp.PopRaw<AppletId>();
|
||||
const auto applet_mode = rp.PopRaw<u32>();
|
||||
|
||||
LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}",
|
||||
static_cast<u32>(applet_id), applet_mode);
|
||||
|
||||
const auto applet = GetAppletFromId(applet_id);
|
||||
|
||||
if (applet == nullptr) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<AM::ILibraryAppletAccessor>();
|
||||
rb.PushIpcInterface<AM::ILibraryAppletAccessor>(applet);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
@@ -704,6 +804,31 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called, size={}", size);
|
||||
}
|
||||
|
||||
void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
rp.SetCurrentOffset(3);
|
||||
const auto handle{rp.Pop<Kernel::Handle>()};
|
||||
|
||||
const auto shared_mem =
|
||||
Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::SharedMemory>(
|
||||
handle);
|
||||
|
||||
if (shared_mem == nullptr) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto mem_begin = shared_mem->backing_block->begin() + shared_mem->backing_block_offset;
|
||||
const auto mem_end = mem_begin + shared_mem->size;
|
||||
std::vector<u8> memory{mem_begin, mem_end};
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface(std::make_shared<IStorage>(std::move(memory)));
|
||||
}
|
||||
|
||||
IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
@@ -733,7 +858,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
|
||||
{70, nullptr, "RequestToShutdown"},
|
||||
{71, nullptr, "RequestToReboot"},
|
||||
{80, nullptr, "ExitAndRequestToShowThanksMessage"},
|
||||
{90, nullptr, "EnableApplicationCrashReport"},
|
||||
{90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"},
|
||||
{100, nullptr, "InitializeApplicationCopyrightFrameBuffer"},
|
||||
{101, nullptr, "SetApplicationCopyrightImage"},
|
||||
{102, nullptr, "SetApplicationCopyrightVisibility"},
|
||||
@@ -752,6 +877,12 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
|
||||
|
||||
IApplicationFunctions::~IApplicationFunctions() = default;
|
||||
|
||||
void IApplicationFunctions::EnableApplicationCrashReport(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(
|
||||
Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -821,7 +952,8 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
|
||||
// Takes an input u32 Result, no output.
|
||||
// For example, in some cases official apps use this with error 0x2A2 then uses svcBreak.
|
||||
// For example, in some cases official apps use this with error 0x2A2 then
|
||||
// uses svcBreak.
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 result = rp.Pop<u32>();
|
||||
@@ -884,8 +1016,8 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
|
||||
auto message_queue = std::make_shared<AppletMessageQueue>();
|
||||
message_queue->PushMessage(
|
||||
AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on game boot
|
||||
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);
|
||||
|
||||
@@ -155,6 +155,34 @@ private:
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
class IStorage final : public ServiceFramework<IStorage> {
|
||||
public:
|
||||
explicit IStorage(std::vector<u8> buffer);
|
||||
~IStorage() override;
|
||||
|
||||
const std::vector<u8>& GetData() const;
|
||||
|
||||
private:
|
||||
void Open(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::vector<u8> buffer;
|
||||
|
||||
friend class IStorageAccessor;
|
||||
};
|
||||
|
||||
class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
|
||||
public:
|
||||
explicit IStorageAccessor(IStorage& backing);
|
||||
~IStorageAccessor() override;
|
||||
|
||||
private:
|
||||
void GetSize(Kernel::HLERequestContext& ctx);
|
||||
void Write(Kernel::HLERequestContext& ctx);
|
||||
void Read(Kernel::HLERequestContext& ctx);
|
||||
|
||||
IStorage& backing;
|
||||
};
|
||||
|
||||
class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
|
||||
public:
|
||||
ILibraryAppletCreator();
|
||||
@@ -163,6 +191,7 @@ public:
|
||||
private:
|
||||
void CreateLibraryApplet(Kernel::HLERequestContext& ctx);
|
||||
void CreateStorage(Kernel::HLERequestContext& ctx);
|
||||
void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
|
||||
@@ -185,6 +214,7 @@ private:
|
||||
void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
|
||||
void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
|
||||
void EndBlockingHomeButton(Kernel::HLERequestContext& ctx);
|
||||
void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
|
||||
|
||||
115
src/core/hle/service/am/applets/applets.cpp
Normal file
115
src/core/hle/service/am/applets/applets.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/server_port.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
AppletDataBroker::AppletDataBroker() {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||
"ILibraryAppletAccessor:StateChangedEvent");
|
||||
pop_out_data_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||
"ILibraryAppletAccessor:PopDataOutEvent");
|
||||
pop_interactive_out_data_event = Kernel::Event::Create(
|
||||
kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
|
||||
}
|
||||
|
||||
AppletDataBroker::~AppletDataBroker() = default;
|
||||
|
||||
std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() {
|
||||
if (out_channel.empty())
|
||||
return nullptr;
|
||||
|
||||
auto out = std::move(out_channel.front());
|
||||
out_channel.pop();
|
||||
return out;
|
||||
}
|
||||
|
||||
std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() {
|
||||
if (in_channel.empty())
|
||||
return nullptr;
|
||||
|
||||
auto out = std::move(in_channel.front());
|
||||
in_channel.pop();
|
||||
return out;
|
||||
}
|
||||
|
||||
std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() {
|
||||
if (out_interactive_channel.empty())
|
||||
return nullptr;
|
||||
|
||||
auto out = std::move(out_interactive_channel.front());
|
||||
out_interactive_channel.pop();
|
||||
return out;
|
||||
}
|
||||
|
||||
std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() {
|
||||
if (in_interactive_channel.empty())
|
||||
return nullptr;
|
||||
|
||||
auto out = std::move(in_interactive_channel.front());
|
||||
in_interactive_channel.pop();
|
||||
return out;
|
||||
}
|
||||
|
||||
void AppletDataBroker::PushNormalDataFromGame(IStorage storage) {
|
||||
in_channel.push(std::make_unique<IStorage>(storage));
|
||||
}
|
||||
|
||||
void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) {
|
||||
out_channel.push(std::make_unique<IStorage>(storage));
|
||||
pop_out_data_event->Signal();
|
||||
}
|
||||
|
||||
void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) {
|
||||
in_interactive_channel.push(std::make_unique<IStorage>(storage));
|
||||
}
|
||||
|
||||
void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) {
|
||||
out_interactive_channel.push(std::make_unique<IStorage>(storage));
|
||||
pop_interactive_out_data_event->Signal();
|
||||
}
|
||||
|
||||
void AppletDataBroker::SignalStateChanged() const {
|
||||
state_changed_event->Signal();
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetNormalDataEvent() const {
|
||||
return pop_out_data_event;
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetInteractiveDataEvent() const {
|
||||
return pop_interactive_out_data_event;
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetStateChangedEvent() const {
|
||||
return state_changed_event;
|
||||
}
|
||||
|
||||
Applet::Applet() = default;
|
||||
|
||||
Applet::~Applet() = default;
|
||||
|
||||
void Applet::Initialize(std::shared_ptr<AppletDataBroker> broker_) {
|
||||
broker = std::move(broker_);
|
||||
|
||||
const auto common = broker->PopNormalDataToApplet();
|
||||
ASSERT(common != nullptr);
|
||||
|
||||
const auto common_data = common->GetData();
|
||||
|
||||
ASSERT(common_data.size() >= sizeof(CommonArguments));
|
||||
std::memcpy(&common_args, common_data.data(), sizeof(CommonArguments));
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
94
src/core/hle/service/am/applets/applets.h
Normal file
94
src/core/hle/service/am/applets/applets.h
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
|
||||
union ResultCode;
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
class IStorage;
|
||||
|
||||
namespace Applets {
|
||||
|
||||
class AppletDataBroker final {
|
||||
public:
|
||||
AppletDataBroker();
|
||||
~AppletDataBroker();
|
||||
|
||||
std::unique_ptr<IStorage> PopNormalDataToGame();
|
||||
std::unique_ptr<IStorage> PopNormalDataToApplet();
|
||||
|
||||
std::unique_ptr<IStorage> PopInteractiveDataToGame();
|
||||
std::unique_ptr<IStorage> PopInteractiveDataToApplet();
|
||||
|
||||
void PushNormalDataFromGame(IStorage storage);
|
||||
void PushNormalDataFromApplet(IStorage storage);
|
||||
|
||||
void PushInteractiveDataFromGame(IStorage storage);
|
||||
void PushInteractiveDataFromApplet(IStorage storage);
|
||||
|
||||
void SignalStateChanged() const;
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> GetNormalDataEvent() const;
|
||||
Kernel::SharedPtr<Kernel::Event> GetInteractiveDataEvent() const;
|
||||
Kernel::SharedPtr<Kernel::Event> GetStateChangedEvent() const;
|
||||
|
||||
private:
|
||||
// Queues are named from applet's perspective
|
||||
std::queue<std::unique_ptr<IStorage>>
|
||||
in_channel; // PopNormalDataToApplet and PushNormalDataFromGame
|
||||
std::queue<std::unique_ptr<IStorage>>
|
||||
out_channel; // PopNormalDataToGame and PushNormalDataFromApplet
|
||||
std::queue<std::unique_ptr<IStorage>>
|
||||
in_interactive_channel; // PopInteractiveDataToApplet and PushInteractiveDataFromGame
|
||||
std::queue<std::unique_ptr<IStorage>>
|
||||
out_interactive_channel; // PopInteractiveDataToGame and PushInteractiveDataFromApplet
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> state_changed_event;
|
||||
Kernel::SharedPtr<Kernel::Event> pop_out_data_event; // Signaled on PushNormalDataFromApplet
|
||||
Kernel::SharedPtr<Kernel::Event>
|
||||
pop_interactive_out_data_event; // Signaled on PushInteractiveDataFromApplet
|
||||
};
|
||||
|
||||
class Applet {
|
||||
public:
|
||||
Applet();
|
||||
virtual ~Applet();
|
||||
|
||||
virtual void Initialize(std::shared_ptr<AppletDataBroker> broker);
|
||||
|
||||
virtual bool TransactionComplete() const = 0;
|
||||
virtual ResultCode GetStatus() const = 0;
|
||||
virtual void ExecuteInteractive() = 0;
|
||||
virtual void Execute() = 0;
|
||||
|
||||
bool IsInitialized() const {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
protected:
|
||||
struct CommonArguments {
|
||||
u32_le arguments_version;
|
||||
u32_le size;
|
||||
u32_le library_version;
|
||||
u32_le theme_color;
|
||||
u8 play_startup_sound;
|
||||
u64_le system_tick;
|
||||
};
|
||||
static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size.");
|
||||
|
||||
CommonArguments common_args;
|
||||
std::shared_ptr<AppletDataBroker> broker;
|
||||
bool initialized = false;
|
||||
};
|
||||
|
||||
} // namespace Applets
|
||||
} // namespace Service::AM
|
||||
161
src/core/hle/service/am/applets/software_keyboard.cpp
Normal file
161
src/core/hle/service/am/applets/software_keyboard.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include "common/assert.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/software_keyboard.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8;
|
||||
constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4;
|
||||
constexpr std::size_t DEFAULT_MAX_LENGTH = 500;
|
||||
constexpr bool INTERACTIVE_STATUS_OK = false;
|
||||
|
||||
static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters(
|
||||
KeyboardConfig config, std::u16string initial_text) {
|
||||
Core::Frontend::SoftwareKeyboardParameters params{};
|
||||
|
||||
params.submit_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
|
||||
config.submit_text.data(), config.submit_text.size());
|
||||
params.header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
|
||||
config.header_text.data(), config.header_text.size());
|
||||
params.sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.sub_text.data(),
|
||||
config.sub_text.size());
|
||||
params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(),
|
||||
config.guide_text.size());
|
||||
params.initial_text = initial_text;
|
||||
params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit;
|
||||
params.password = static_cast<bool>(config.is_password);
|
||||
params.cursor_at_beginning = static_cast<bool>(config.initial_cursor_position);
|
||||
params.value = static_cast<u8>(config.keyset_disable_bitmask);
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
SoftwareKeyboard::SoftwareKeyboard() = default;
|
||||
|
||||
SoftwareKeyboard::~SoftwareKeyboard() = default;
|
||||
|
||||
void SoftwareKeyboard::Initialize(std::shared_ptr<AppletDataBroker> broker_) {
|
||||
complete = false;
|
||||
initial_text.clear();
|
||||
final_data.clear();
|
||||
|
||||
Applet::Initialize(std::move(broker_));
|
||||
|
||||
const auto keyboard_config_storage = broker->PopNormalDataToApplet();
|
||||
ASSERT(keyboard_config_storage != nullptr);
|
||||
const auto& keyboard_config = keyboard_config_storage->GetData();
|
||||
|
||||
ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig));
|
||||
std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig));
|
||||
|
||||
const auto work_buffer_storage = broker->PopNormalDataToApplet();
|
||||
ASSERT(work_buffer_storage != nullptr);
|
||||
const auto& work_buffer = work_buffer_storage->GetData();
|
||||
|
||||
if (config.initial_string_size == 0)
|
||||
return;
|
||||
|
||||
std::vector<char16_t> string(config.initial_string_size);
|
||||
std::memcpy(string.data(), work_buffer.data() + config.initial_string_offset,
|
||||
string.size() * 2);
|
||||
initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size());
|
||||
}
|
||||
|
||||
bool SoftwareKeyboard::TransactionComplete() const {
|
||||
return complete;
|
||||
}
|
||||
|
||||
ResultCode SoftwareKeyboard::GetStatus() const {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void SoftwareKeyboard::ExecuteInteractive() {
|
||||
if (complete)
|
||||
return;
|
||||
|
||||
const auto storage = broker->PopInteractiveDataToApplet();
|
||||
ASSERT(storage != nullptr);
|
||||
const auto data = storage->GetData();
|
||||
const auto status = static_cast<bool>(data[0]);
|
||||
|
||||
if (status == INTERACTIVE_STATUS_OK) {
|
||||
complete = true;
|
||||
} else {
|
||||
const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()};
|
||||
|
||||
std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string;
|
||||
std::memcpy(string.data(), data.data() + 4, string.size() * 2);
|
||||
frontend.SendTextCheckDialog(
|
||||
Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()),
|
||||
[this] { broker->SignalStateChanged(); });
|
||||
}
|
||||
}
|
||||
|
||||
void SoftwareKeyboard::Execute() {
|
||||
if (complete) {
|
||||
broker->PushNormalDataFromApplet(IStorage{final_data});
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()};
|
||||
|
||||
const auto parameters = ConvertToFrontendParameters(config, initial_text);
|
||||
|
||||
frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(text); },
|
||||
parameters);
|
||||
}
|
||||
|
||||
void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
|
||||
std::vector<u8> output_main(SWKBD_OUTPUT_BUFFER_SIZE);
|
||||
|
||||
if (text.has_value()) {
|
||||
std::vector<u8> output_sub(SWKBD_OUTPUT_BUFFER_SIZE);
|
||||
|
||||
if (config.utf_8) {
|
||||
const u64 size = text->size() + 8;
|
||||
const auto new_text = Common::UTF16ToUTF8(*text);
|
||||
|
||||
std::memcpy(output_sub.data(), &size, sizeof(u64));
|
||||
std::memcpy(output_sub.data() + 8, new_text.data(),
|
||||
std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 8));
|
||||
|
||||
output_main[0] = INTERACTIVE_STATUS_OK;
|
||||
std::memcpy(output_main.data() + 4, new_text.data(),
|
||||
std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4));
|
||||
} else {
|
||||
const u64 size = text->size() * 2 + 8;
|
||||
std::memcpy(output_sub.data(), &size, sizeof(u64));
|
||||
std::memcpy(output_sub.data() + 8, text->data(),
|
||||
std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8));
|
||||
|
||||
output_main[0] = INTERACTIVE_STATUS_OK;
|
||||
std::memcpy(output_main.data() + 4, text->data(),
|
||||
std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4));
|
||||
}
|
||||
|
||||
complete = !config.text_check;
|
||||
final_data = output_main;
|
||||
|
||||
if (complete) {
|
||||
broker->PushNormalDataFromApplet(IStorage{output_main});
|
||||
} else {
|
||||
broker->PushInteractiveDataFromApplet(IStorage{output_sub});
|
||||
}
|
||||
|
||||
broker->SignalStateChanged();
|
||||
} else {
|
||||
output_main[0] = 1;
|
||||
complete = true;
|
||||
broker->PushNormalDataFromApplet(IStorage{output_main});
|
||||
broker->SignalStateChanged();
|
||||
}
|
||||
}
|
||||
} // namespace Service::AM::Applets
|
||||
69
src/core/hle/service/am/applets/software_keyboard.h
Normal file
69
src/core/hle/service/am/applets/software_keyboard.h
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright 2018 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 "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
enum class KeysetDisable : u32 {
|
||||
Space = 0x02,
|
||||
Address = 0x04,
|
||||
Percent = 0x08,
|
||||
Slashes = 0x10,
|
||||
Numbers = 0x40,
|
||||
DownloadCode = 0x80,
|
||||
};
|
||||
|
||||
struct KeyboardConfig {
|
||||
INSERT_PADDING_BYTES(4);
|
||||
std::array<char16_t, 9> submit_text;
|
||||
u16_le left_symbol_key;
|
||||
u16_le right_symbol_key;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
KeysetDisable keyset_disable_bitmask;
|
||||
u32_le initial_cursor_position;
|
||||
std::array<char16_t, 65> header_text;
|
||||
std::array<char16_t, 129> sub_text;
|
||||
std::array<char16_t, 257> guide_text;
|
||||
u32_le length_limit;
|
||||
INSERT_PADDING_BYTES(4);
|
||||
u32_le is_password;
|
||||
INSERT_PADDING_BYTES(5);
|
||||
bool utf_8;
|
||||
bool draw_background;
|
||||
u32_le initial_string_offset;
|
||||
u32_le initial_string_size;
|
||||
u32_le user_dictionary_offset;
|
||||
u32_le user_dictionary_size;
|
||||
bool text_check;
|
||||
u64_le text_check_callback;
|
||||
};
|
||||
static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect size.");
|
||||
|
||||
class SoftwareKeyboard final : public Applet {
|
||||
public:
|
||||
SoftwareKeyboard();
|
||||
~SoftwareKeyboard() override;
|
||||
|
||||
void Initialize(std::shared_ptr<AppletDataBroker> broker) override;
|
||||
|
||||
bool TransactionComplete() const override;
|
||||
ResultCode GetStatus() const override;
|
||||
void ExecuteInteractive() override;
|
||||
void Execute() override;
|
||||
|
||||
void WriteText(std::optional<std::u16string> text);
|
||||
|
||||
private:
|
||||
KeyboardConfig config;
|
||||
std::u16string initial_text;
|
||||
bool complete = false;
|
||||
std::vector<u8> final_data;
|
||||
};
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
@@ -28,13 +28,13 @@ public:
|
||||
{1, &IAudioRenderer::GetSampleCount, "GetSampleCount"},
|
||||
{2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"},
|
||||
{3, &IAudioRenderer::GetState, "GetState"},
|
||||
{4, &IAudioRenderer::RequestUpdate, "RequestUpdate"},
|
||||
{4, &IAudioRenderer::RequestUpdateImpl, "RequestUpdate"},
|
||||
{5, &IAudioRenderer::Start, "Start"},
|
||||
{6, &IAudioRenderer::Stop, "Stop"},
|
||||
{7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
|
||||
{8, nullptr, "SetRenderingTimeLimit"},
|
||||
{9, nullptr, "GetRenderingTimeLimit"},
|
||||
{10, nullptr, "RequestUpdateAuto"},
|
||||
{8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"},
|
||||
{9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
|
||||
{10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"},
|
||||
{11, nullptr, "ExecuteAudioRendererRendering"},
|
||||
};
|
||||
// clang-format on
|
||||
@@ -79,7 +79,7 @@ private:
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
}
|
||||
|
||||
void RequestUpdate(Kernel::HLERequestContext& ctx) {
|
||||
void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
|
||||
ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -110,8 +110,29 @@ private:
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
rendering_time_limit_percent = rp.Pop<u32>();
|
||||
ASSERT(rendering_time_limit_percent >= 0 && rendering_time_limit_percent <= 100);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}",
|
||||
rendering_time_limit_percent);
|
||||
}
|
||||
|
||||
void GetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(rendering_time_limit_percent);
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> system_event;
|
||||
std::unique_ptr<AudioCore::AudioRenderer> renderer;
|
||||
u32 rendering_time_limit_percent = 100;
|
||||
};
|
||||
|
||||
class IAudioDevice final : public ServiceFramework<IAudioDevice> {
|
||||
|
||||
@@ -77,8 +77,8 @@ private:
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(consumed);
|
||||
rb.Push<u64>(performance);
|
||||
rb.Push<u32>(sample_count);
|
||||
rb.Push<u64>(performance);
|
||||
ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,49 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/btdrv/btdrv.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::BtDrv {
|
||||
|
||||
class Bt final : public ServiceFramework<Bt> {
|
||||
public:
|
||||
explicit Bt() : ServiceFramework{"bt"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown0"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
{6, nullptr, "Unknown6"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
{9, &Bt::RegisterEvent, "RegisterEvent"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void RegisterEvent(Kernel::HLERequestContext& ctx) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
register_event =
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "BT:RegisterEvent");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(register_event);
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
}
|
||||
Kernel::SharedPtr<Kernel::Event> register_event;
|
||||
};
|
||||
|
||||
class BtDrv final : public ServiceFramework<BtDrv> {
|
||||
public:
|
||||
explicit BtDrv() : ServiceFramework{"btdrv"} {
|
||||
@@ -67,6 +104,7 @@ public:
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm) {
|
||||
std::make_shared<BtDrv>()->InstallAsService(sm);
|
||||
std::make_shared<Bt>()->InstallAsService(sm);
|
||||
}
|
||||
|
||||
} // namespace Service::BtDrv
|
||||
|
||||
@@ -6,13 +6,118 @@
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/btm/btm.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::BTM {
|
||||
|
||||
class IBtmUserCore final : public ServiceFramework<IBtmUserCore> {
|
||||
public:
|
||||
explicit IBtmUserCore() : ServiceFramework{"IBtmUserCore"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IBtmUserCore::GetScanEvent, "GetScanEvent"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
{6, nullptr, "Unknown6"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
{9, nullptr, "Unknown9"},
|
||||
{10, nullptr, "Unknown10"},
|
||||
{17, &IBtmUserCore::GetConnectionEvent, "GetConnectionEvent"},
|
||||
{18, nullptr, "Unknown18"},
|
||||
{19, nullptr, "Unknown19"},
|
||||
{20, nullptr, "Unknown20"},
|
||||
{21, nullptr, "Unknown21"},
|
||||
{22, nullptr, "Unknown22"},
|
||||
{23, nullptr, "Unknown23"},
|
||||
{24, nullptr, "Unknown24"},
|
||||
{25, nullptr, "Unknown25"},
|
||||
{26, &IBtmUserCore::GetDiscoveryEvent, "AcquireBleServiceDiscoveryEventImpl"},
|
||||
{27, nullptr, "Unknown27"},
|
||||
{28, nullptr, "Unknown28"},
|
||||
{29, nullptr, "Unknown29"},
|
||||
{30, nullptr, "Unknown30"},
|
||||
{31, nullptr, "Unknown31"},
|
||||
{32, nullptr, "Unknown32"},
|
||||
{33, &IBtmUserCore::GetConfigEvent, "GetConfigEvent"},
|
||||
{34, nullptr, "Unknown34"},
|
||||
{35, nullptr, "Unknown35"},
|
||||
{36, nullptr, "Unknown36"},
|
||||
{37, nullptr, "Unknown37"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void GetScanEvent(Kernel::HLERequestContext& ctx) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
scan_event =
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ScanEvent");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(scan_event);
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
}
|
||||
void GetConnectionEvent(Kernel::HLERequestContext& ctx) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
connection_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||
"IBtmUserCore:ConnectionEvent");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(connection_event);
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
}
|
||||
void GetDiscoveryEvent(Kernel::HLERequestContext& ctx) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
service_discovery =
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:Discovery");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(service_discovery);
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
}
|
||||
void GetConfigEvent(Kernel::HLERequestContext& ctx) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
config_event =
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ConfigEvent");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(config_event);
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
}
|
||||
Kernel::SharedPtr<Kernel::Event> scan_event;
|
||||
Kernel::SharedPtr<Kernel::Event> connection_event;
|
||||
Kernel::SharedPtr<Kernel::Event> service_discovery;
|
||||
Kernel::SharedPtr<Kernel::Event> config_event;
|
||||
};
|
||||
|
||||
class BTM_USR final : public ServiceFramework<BTM_USR> {
|
||||
public:
|
||||
explicit BTM_USR() : ServiceFramework{"btm:u"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &BTM_USR::GetCoreImpl, "GetCoreImpl"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void GetCoreImpl(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IBtmUserCore>();
|
||||
LOG_DEBUG(Service_BTM, "called");
|
||||
}
|
||||
};
|
||||
|
||||
class BTM final : public ServiceFramework<BTM> {
|
||||
public:
|
||||
explicit BTM() : ServiceFramework{"btm"} {
|
||||
@@ -116,6 +221,7 @@ void InstallInterfaces(SM::ServiceManager& sm) {
|
||||
std::make_shared<BTM>()->InstallAsService(sm);
|
||||
std::make_shared<BTM_DBG>()->InstallAsService(sm);
|
||||
std::make_shared<BTM_SYS>()->InstallAsService(sm);
|
||||
std::make_shared<BTM_USR>()->InstallAsService(sm);
|
||||
}
|
||||
|
||||
} // namespace Service::BTM
|
||||
|
||||
@@ -303,25 +303,42 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
|
||||
static_cast<u8>(space), save_struct.DebugInfo());
|
||||
|
||||
if (save_data_factory == nullptr) {
|
||||
return ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound);
|
||||
return FileSys::ERROR_ENTITY_NOT_FOUND;
|
||||
}
|
||||
|
||||
return save_data_factory->Open(space, save_struct);
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) {
|
||||
LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space));
|
||||
|
||||
if (save_data_factory == nullptr) {
|
||||
return FileSys::ERROR_ENTITY_NOT_FOUND;
|
||||
}
|
||||
|
||||
return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space));
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualDir> OpenSDMC() {
|
||||
LOG_TRACE(Service_FS, "Opening SDMC");
|
||||
|
||||
if (sdmc_factory == nullptr) {
|
||||
return ResultCode(ErrorModule::FS, FileSys::ErrCodes::SdCardNotFound);
|
||||
return FileSys::ERROR_SD_CARD_NOT_FOUND;
|
||||
}
|
||||
|
||||
return sdmc_factory->Open();
|
||||
}
|
||||
|
||||
std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() {
|
||||
return std::make_unique<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{
|
||||
GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
|
||||
std::shared_ptr<FileSys::RegisteredCacheUnion> registered_cache_union;
|
||||
|
||||
std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() {
|
||||
if (registered_cache_union == nullptr) {
|
||||
registered_cache_union =
|
||||
std::make_shared<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{
|
||||
GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
|
||||
}
|
||||
|
||||
return registered_cache_union;
|
||||
}
|
||||
|
||||
FileSys::RegisteredCache* GetSystemNANDContents() {
|
||||
@@ -360,6 +377,15 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
|
||||
return bis_factory->GetModificationLoadRoot(title_id);
|
||||
}
|
||||
|
||||
FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) {
|
||||
LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id);
|
||||
|
||||
if (bis_factory == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return bis_factory->GetModificationDumpRoot(title_id);
|
||||
}
|
||||
|
||||
void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
|
||||
if (overwrite) {
|
||||
bis_factory = nullptr;
|
||||
@@ -373,13 +399,21 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
|
||||
FileSys::Mode::ReadWrite);
|
||||
auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
|
||||
FileSys::Mode::ReadWrite);
|
||||
auto dump_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
|
||||
FileSys::Mode::ReadWrite);
|
||||
|
||||
if (bis_factory == nullptr)
|
||||
bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory);
|
||||
if (save_data_factory == nullptr)
|
||||
if (bis_factory == nullptr) {
|
||||
bis_factory =
|
||||
std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory);
|
||||
}
|
||||
|
||||
if (save_data_factory == nullptr) {
|
||||
save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
|
||||
if (sdmc_factory == nullptr)
|
||||
}
|
||||
|
||||
if (sdmc_factory == nullptr) {
|
||||
sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
|
||||
}
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) {
|
||||
|
||||
@@ -45,15 +45,17 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora
|
||||
FileSys::ContentRecordType type);
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
|
||||
FileSys::SaveDataDescriptor save_struct);
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
|
||||
ResultVal<FileSys::VirtualDir> OpenSDMC();
|
||||
|
||||
std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
|
||||
std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
|
||||
|
||||
FileSys::RegisteredCache* GetSystemNANDContents();
|
||||
FileSys::RegisteredCache* GetUserNANDContents();
|
||||
FileSys::RegisteredCache* GetSDMCContents();
|
||||
|
||||
FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
|
||||
FileSys::VirtualDir GetModificationDumpRoot(u64 title_id);
|
||||
|
||||
// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
|
||||
// above is called.
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/file_sys/directory.h"
|
||||
@@ -62,12 +63,12 @@ private:
|
||||
// Error checking
|
||||
if (length < 0) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
|
||||
rb.Push(FileSys::ERROR_INVALID_SIZE);
|
||||
return;
|
||||
}
|
||||
if (offset < 0) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset));
|
||||
rb.Push(FileSys::ERROR_INVALID_OFFSET);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -107,12 +108,12 @@ private:
|
||||
// Error checking
|
||||
if (length < 0) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
|
||||
rb.Push(FileSys::ERROR_INVALID_SIZE);
|
||||
return;
|
||||
}
|
||||
if (offset < 0) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset));
|
||||
rb.Push(FileSys::ERROR_INVALID_OFFSET);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -138,12 +139,12 @@ private:
|
||||
// Error checking
|
||||
if (length < 0) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
|
||||
rb.Push(FileSys::ERROR_INVALID_SIZE);
|
||||
return;
|
||||
}
|
||||
if (offset < 0) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset));
|
||||
rb.Push(FileSys::ERROR_INVALID_OFFSET);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -451,7 +452,147 @@ private:
|
||||
VfsDirectoryServiceWrapper backend;
|
||||
};
|
||||
|
||||
class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
|
||||
public:
|
||||
explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space)
|
||||
: ServiceFramework("ISaveDataInfoReader") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
FindAllSaves(space);
|
||||
}
|
||||
|
||||
void ReadSaveDataInfo(Kernel::HLERequestContext& ctx) {
|
||||
// Calculate how many entries we can fit in the output buffer
|
||||
const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(SaveDataInfo);
|
||||
|
||||
// Cap at total number of entries.
|
||||
const u64 actual_entries = std::min(count_entries, info.size() - next_entry_index);
|
||||
|
||||
// Determine data start and end
|
||||
const auto* begin = reinterpret_cast<u8*>(info.data() + next_entry_index);
|
||||
const auto* end = reinterpret_cast<u8*>(info.data() + next_entry_index + actual_entries);
|
||||
const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
|
||||
|
||||
next_entry_index += actual_entries;
|
||||
|
||||
// Write the data to memory
|
||||
ctx.WriteBuffer(begin, range_size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(static_cast<u32>(actual_entries));
|
||||
}
|
||||
|
||||
private:
|
||||
static u64 stoull_be(std::string_view str) {
|
||||
if (str.size() != 16)
|
||||
return 0;
|
||||
|
||||
const auto bytes = Common::HexStringToArray<0x8>(str);
|
||||
u64 out{};
|
||||
std::memcpy(&out, bytes.data(), sizeof(u64));
|
||||
|
||||
return Common::swap64(out);
|
||||
}
|
||||
|
||||
void FindAllSaves(FileSys::SaveDataSpaceId space) {
|
||||
const auto save_root = OpenSaveDataSpace(space);
|
||||
ASSERT(save_root.Succeeded());
|
||||
|
||||
for (const auto& type : (*save_root)->GetSubdirectories()) {
|
||||
if (type->GetName() == "save") {
|
||||
for (const auto& save_id : type->GetSubdirectories()) {
|
||||
for (const auto& user_id : save_id->GetSubdirectories()) {
|
||||
const auto save_id_numeric = stoull_be(save_id->GetName());
|
||||
auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName());
|
||||
std::reverse(user_id_numeric.begin(), user_id_numeric.end());
|
||||
|
||||
if (save_id_numeric != 0) {
|
||||
// System Save Data
|
||||
info.emplace_back(SaveDataInfo{
|
||||
0,
|
||||
space,
|
||||
FileSys::SaveDataType::SystemSaveData,
|
||||
{},
|
||||
user_id_numeric,
|
||||
save_id_numeric,
|
||||
0,
|
||||
user_id->GetSize(),
|
||||
{},
|
||||
});
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& title_id : user_id->GetSubdirectories()) {
|
||||
const auto device =
|
||||
std::all_of(user_id_numeric.begin(), user_id_numeric.end(),
|
||||
[](u8 val) { return val == 0; });
|
||||
info.emplace_back(SaveDataInfo{
|
||||
0,
|
||||
space,
|
||||
device ? FileSys::SaveDataType::DeviceSaveData
|
||||
: FileSys::SaveDataType::SaveData,
|
||||
{},
|
||||
user_id_numeric,
|
||||
save_id_numeric,
|
||||
stoull_be(title_id->GetName()),
|
||||
title_id->GetSize(),
|
||||
{},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) {
|
||||
// Temporary Storage
|
||||
for (const auto& user_id : type->GetSubdirectories()) {
|
||||
for (const auto& title_id : user_id->GetSubdirectories()) {
|
||||
if (!title_id->GetFiles().empty() ||
|
||||
!title_id->GetSubdirectories().empty()) {
|
||||
auto user_id_numeric =
|
||||
Common::HexStringToArray<0x10>(user_id->GetName());
|
||||
std::reverse(user_id_numeric.begin(), user_id_numeric.end());
|
||||
|
||||
info.emplace_back(SaveDataInfo{
|
||||
0,
|
||||
space,
|
||||
FileSys::SaveDataType::TemporaryStorage,
|
||||
{},
|
||||
user_id_numeric,
|
||||
stoull_be(type->GetName()),
|
||||
stoull_be(title_id->GetName()),
|
||||
title_id->GetSize(),
|
||||
{},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SaveDataInfo {
|
||||
u64_le save_id_unknown;
|
||||
FileSys::SaveDataSpaceId space;
|
||||
FileSys::SaveDataType type;
|
||||
INSERT_PADDING_BYTES(0x6);
|
||||
std::array<u8, 0x10> user_id;
|
||||
u64_le save_id;
|
||||
u64_le title_id;
|
||||
u64_le save_image_size;
|
||||
INSERT_PADDING_BYTES(0x28);
|
||||
};
|
||||
static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size.");
|
||||
|
||||
std::vector<SaveDataInfo> info;
|
||||
u64 next_entry_index = 0;
|
||||
};
|
||||
|
||||
FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "MountContent"},
|
||||
{1, &FSP_SRV::Initialize, "Initialize"},
|
||||
@@ -485,7 +626,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
{58, nullptr, "ReadSaveDataFileSystemExtraData"},
|
||||
{59, nullptr, "WriteSaveDataFileSystemExtraData"},
|
||||
{60, nullptr, "OpenSaveDataInfoReader"},
|
||||
{61, nullptr, "OpenSaveDataInfoReaderBySaveDataSpaceId"},
|
||||
{61, &FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId, "OpenSaveDataInfoReaderBySaveDataSpaceId"},
|
||||
{62, nullptr, "OpenCacheStorageList"},
|
||||
{64, nullptr, "OpenSaveDataInternalStorageFileSystem"},
|
||||
{65, nullptr, "UpdateSaveDataMacForDebug"},
|
||||
@@ -544,6 +685,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
{1009, nullptr, "GetAndClearMemoryReportInfo"},
|
||||
{1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -602,7 +744,7 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
if (dir.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
|
||||
rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound));
|
||||
rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -618,6 +760,15 @@ void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
MountSaveData(ctx);
|
||||
}
|
||||
|
||||
void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto space = rp.PopRaw<FileSys::SaveDataSpaceId>();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space));
|
||||
}
|
||||
|
||||
void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
|
||||
@@ -685,7 +836,7 @@ void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
|
||||
static_cast<u8>(storage_id), title_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound));
|
||||
rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
|
||||
}
|
||||
|
||||
} // namespace Service::FileSystem
|
||||
|
||||
@@ -25,6 +25,7 @@ private:
|
||||
void CreateSaveData(Kernel::HLERequestContext& ctx);
|
||||
void MountSaveData(Kernel::HLERequestContext& ctx);
|
||||
void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx);
|
||||
void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx);
|
||||
void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
|
||||
void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
|
||||
void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -40,6 +40,29 @@ enum class JoystickId : std::size_t {
|
||||
Joystick_Right,
|
||||
};
|
||||
|
||||
static std::size_t NPadIdToIndex(u32 npad_id) {
|
||||
switch (npad_id) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
return npad_id;
|
||||
case 8:
|
||||
case NPAD_HANDHELD:
|
||||
return 8;
|
||||
case 9:
|
||||
case NPAD_UNKNOWN:
|
||||
return 9;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown npad id {}", npad_id);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Controller_NPad::Controller_NPad() = default;
|
||||
Controller_NPad::~Controller_NPad() = default;
|
||||
|
||||
@@ -288,10 +311,11 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
|
||||
switch (controller_type) {
|
||||
case NPadControllerType::Handheld:
|
||||
handheld_entry.connection_status.raw = 0;
|
||||
handheld_entry.connection_status.IsConnected.Assign(1);
|
||||
if (!Settings::values.use_docked_mode) {
|
||||
handheld_entry.connection_status.IsWired.Assign(1);
|
||||
}
|
||||
handheld_entry.connection_status.IsWired.Assign(1);
|
||||
handheld_entry.connection_status.IsLeftJoyConnected.Assign(1);
|
||||
handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
|
||||
handheld_entry.connection_status.IsLeftJoyWired.Assign(1);
|
||||
handheld_entry.connection_status.IsRightJoyWired.Assign(1);
|
||||
handheld_entry.pad_states.raw = pad_state.raw;
|
||||
handheld_entry.l_stick = lstick_entry;
|
||||
handheld_entry.r_stick = rstick_entry;
|
||||
@@ -310,6 +334,7 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
|
||||
dual_entry.pad_states.raw = pad_state.raw;
|
||||
dual_entry.l_stick = lstick_entry;
|
||||
dual_entry.r_stick = rstick_entry;
|
||||
break;
|
||||
case NPadControllerType::JoyLeft:
|
||||
left_entry.connection_status.raw = 0;
|
||||
|
||||
@@ -370,16 +395,30 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
|
||||
supported_npad_id_types.clear();
|
||||
supported_npad_id_types.resize(length / sizeof(u32));
|
||||
std::memcpy(supported_npad_id_types.data(), data, length);
|
||||
bool had_controller_update = false;
|
||||
for (std::size_t i = 0; i < connected_controllers.size(); i++) {
|
||||
auto& controller = connected_controllers[i];
|
||||
if (!controller.is_connected) {
|
||||
continue;
|
||||
}
|
||||
if (!IsControllerSupported(PREFERRED_CONTROLLER)) {
|
||||
controller.type = DecideBestController(PREFERRED_CONTROLLER);
|
||||
InitNewlyAddedControler(i);
|
||||
const auto best_type = DecideBestController(PREFERRED_CONTROLLER);
|
||||
const bool is_handheld = (best_type == NPadControllerType::Handheld ||
|
||||
PREFERRED_CONTROLLER == NPadControllerType::Handheld);
|
||||
if (is_handheld) {
|
||||
controller.type = NPadControllerType::None;
|
||||
controller.is_connected = false;
|
||||
AddNewController(best_type);
|
||||
} else {
|
||||
controller.type = best_type;
|
||||
InitNewlyAddedControler(i);
|
||||
}
|
||||
had_controller_update = true;
|
||||
}
|
||||
}
|
||||
if (had_controller_update) {
|
||||
styleset_changed_event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
|
||||
@@ -457,15 +496,11 @@ void Controller_NPad::AddNewController(NPadControllerType controller) {
|
||||
}
|
||||
|
||||
void Controller_NPad::ConnectNPad(u32 npad_id) {
|
||||
if (npad_id >= connected_controllers.size())
|
||||
return;
|
||||
connected_controllers[npad_id].is_connected = true;
|
||||
connected_controllers[NPadIdToIndex(npad_id)].is_connected = true;
|
||||
}
|
||||
|
||||
void Controller_NPad::DisconnectNPad(u32 npad_id) {
|
||||
if (npad_id >= connected_controllers.size())
|
||||
return;
|
||||
connected_controllers[npad_id].is_connected = false;
|
||||
connected_controllers[NPadIdToIndex(npad_id)].is_connected = false;
|
||||
}
|
||||
|
||||
Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
|
||||
|
||||
@@ -286,10 +286,10 @@ public:
|
||||
{519, nullptr, "GetPalmaOperationResult"},
|
||||
{520, nullptr, "ReadPalmaPlayLog"},
|
||||
{521, nullptr, "ResetPalmaPlayLog"},
|
||||
{522, nullptr, "SetIsPalmaAllConnectable"},
|
||||
{522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"},
|
||||
{523, nullptr, "SetIsPalmaPairedConnectable"},
|
||||
{524, nullptr, "PairPalma"},
|
||||
{525, nullptr, "SetPalmaBoostMode"},
|
||||
{525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"},
|
||||
{1000, nullptr, "SetNpadCommunicationMode"},
|
||||
{1001, nullptr, "GetNpadCommunicationMode"},
|
||||
};
|
||||
@@ -596,6 +596,18 @@ private:
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
||||
}
|
||||
};
|
||||
|
||||
class HidDbg final : public ServiceFramework<HidDbg> {
|
||||
|
||||
@@ -212,7 +212,7 @@ private:
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
auto amiibo = nfp_interface.GetAmiiboBuffer();
|
||||
TagInfo tag_info{};
|
||||
std::memcpy(tag_info.uuid.data(), amiibo.uuid.data(), sizeof(tag_info.uuid.size()));
|
||||
tag_info.uuid = amiibo.uuid;
|
||||
tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size());
|
||||
|
||||
tag_info.protocol = 1; // TODO(ogniK): Figure out actual values
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.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/hle_ipc.h"
|
||||
#include "core/hle/service/ns/ns.h"
|
||||
@@ -118,7 +121,7 @@ public:
|
||||
{305, nullptr, "TerminateSystemApplet"},
|
||||
{306, nullptr, "LaunchOverlayApplet"},
|
||||
{307, nullptr, "TerminateOverlayApplet"},
|
||||
{400, nullptr, "GetApplicationControlData"},
|
||||
{400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"},
|
||||
{401, nullptr, "InvalidateAllApplicationControlCache"},
|
||||
{402, nullptr, "RequestDownloadApplicationControlData"},
|
||||
{403, nullptr, "GetMaxApplicationControlCacheCount"},
|
||||
@@ -243,6 +246,65 @@ public:
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
void GetApplicationControlData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto flag = rp.PopRaw<u64>();
|
||||
LOG_DEBUG(Service_NS, "called with flag={:016X}", flag);
|
||||
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
const auto size = ctx.GetWriteBufferSize();
|
||||
|
||||
const FileSys::PatchManager pm{title_id};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
|
||||
std::vector<u8> out;
|
||||
|
||||
if (control.first != nullptr) {
|
||||
if (size < 0x4000) {
|
||||
LOG_ERROR(Service_NS,
|
||||
"output buffer is too small! (actual={:016X}, expected_min=0x4000)",
|
||||
size);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(DarkLordZach): Find a better error code for this.
|
||||
rb.Push(ResultCode(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
out.resize(0x4000);
|
||||
const auto bytes = control.first->GetRawBytes();
|
||||
std::memcpy(out.data(), bytes.data(), bytes.size());
|
||||
} else {
|
||||
LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.",
|
||||
title_id);
|
||||
out.resize(std::min<u64>(0x4000, size));
|
||||
}
|
||||
|
||||
if (control.second != nullptr) {
|
||||
if (size < 0x4000 + control.second->GetSize()) {
|
||||
LOG_ERROR(Service_NS,
|
||||
"output buffer is too small! (actual={:016X}, expected_min={:016X})",
|
||||
size, 0x4000 + control.second->GetSize());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(DarkLordZach): Find a better error code for this.
|
||||
rb.Push(ResultCode(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
out.resize(0x4000 + control.second->GetSize());
|
||||
control.second->Read(out.data() + 0x4000, control.second->GetSize());
|
||||
} else {
|
||||
LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.",
|
||||
title_id);
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(out);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(static_cast<u32>(out.size()));
|
||||
}
|
||||
};
|
||||
|
||||
class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
|
||||
|
||||
@@ -80,8 +80,8 @@ namespace Service {
|
||||
* Creates a function string for logging, complete with the name (or header code, depending
|
||||
* on what's passed in) the port name, and all the cmd_buff arguments.
|
||||
*/
|
||||
static std::string MakeFunctionString(const char* name, const char* port_name,
|
||||
const u32* cmd_buff) {
|
||||
[[maybe_unused]] static std::string MakeFunctionString(const char* name, const char* port_name,
|
||||
const u32* cmd_buff) {
|
||||
// Number of params == bits 0-5 + bits 6-11
|
||||
int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
|
||||
|
||||
|
||||
@@ -28,8 +28,9 @@ void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
std::size_t size = ctx.GetWriteBufferSize();
|
||||
|
||||
std::uniform_int_distribution<u16> distribution(0, std::numeric_limits<u8>::max());
|
||||
std::vector<u8> data(size);
|
||||
std::generate(data.begin(), data.end(), rng);
|
||||
std::generate(data.begin(), data.end(), [&] { return static_cast<u8>(distribution(rng)); });
|
||||
|
||||
ctx.WriteBuffer(data);
|
||||
|
||||
|
||||
@@ -12,10 +12,12 @@
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/nro.h"
|
||||
#include "core/loader/nso.h"
|
||||
#include "core/memory.h"
|
||||
@@ -208,6 +210,9 @@ ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
|
||||
return ResultStatus::ErrorLoadingNRO;
|
||||
}
|
||||
|
||||
if (romfs != nullptr)
|
||||
Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
|
||||
|
||||
process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
|
||||
|
||||
is_loaded = true;
|
||||
|
||||
@@ -154,7 +154,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(const FileSys::VfsFile& file, VAd
|
||||
program_image.resize(image_size);
|
||||
|
||||
// Apply patches if necessary
|
||||
if (pm && pm->HasNSOPatch(nso_header.build_id)) {
|
||||
if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
|
||||
std::vector<u8> pi_header(program_image.size() + 0x100);
|
||||
std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader));
|
||||
std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size());
|
||||
|
||||
@@ -115,7 +115,7 @@ struct Values {
|
||||
// System
|
||||
bool use_docked_mode;
|
||||
bool enable_nfc;
|
||||
std::optional<u64> rng_seed;
|
||||
std::optional<u32> rng_seed;
|
||||
s32 current_user;
|
||||
s32 language_index;
|
||||
|
||||
@@ -159,6 +159,7 @@ struct Values {
|
||||
bool use_gdbstub;
|
||||
u16 gdbstub_port;
|
||||
std::string program_args;
|
||||
bool dump_nso;
|
||||
|
||||
// WebService
|
||||
bool enable_telemetry;
|
||||
|
||||
@@ -121,10 +121,8 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
|
||||
debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandLoaded, nullptr);
|
||||
}
|
||||
|
||||
u32 old = regs.reg_array[method];
|
||||
regs.reg_array[method] = value;
|
||||
|
||||
if (value != old) {
|
||||
if (regs.reg_array[method] != value) {
|
||||
regs.reg_array[method] = value;
|
||||
if (method >= MAXWELL3D_REG_INDEX(vertex_attrib_format) &&
|
||||
method < MAXWELL3D_REG_INDEX(vertex_attrib_format) + regs.vertex_attrib_format.size()) {
|
||||
dirty_flags.vertex_attrib_format = true;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <boost/icl/interval_map.hpp>
|
||||
#include <boost/range/iterator_range_core.hpp>
|
||||
@@ -88,29 +89,25 @@ public:
|
||||
|
||||
/// Invalidates everything in the cache
|
||||
void InvalidateAll() {
|
||||
while (object_cache.begin() != object_cache.end()) {
|
||||
Unregister(*object_cache.begin()->second.begin());
|
||||
while (interval_cache.begin() != interval_cache.end()) {
|
||||
Unregister(*interval_cache.begin()->second.begin());
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Tries to get an object from the cache with the specified address
|
||||
T TryGet(VAddr addr) const {
|
||||
const ObjectInterval interval{addr};
|
||||
for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) {
|
||||
for (auto& cached_object : pair.second) {
|
||||
if (cached_object->GetAddr() == addr) {
|
||||
return cached_object;
|
||||
}
|
||||
}
|
||||
}
|
||||
const auto iter = map_cache.find(addr);
|
||||
if (iter != map_cache.end())
|
||||
return iter->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// Register an object into the cache
|
||||
void Register(const T& object) {
|
||||
object->SetIsRegistered(true);
|
||||
object_cache.add({GetInterval(object), ObjectSet{object}});
|
||||
interval_cache.add({GetInterval(object), ObjectSet{object}});
|
||||
map_cache.insert({object->GetAddr(), object});
|
||||
rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1);
|
||||
}
|
||||
|
||||
@@ -118,13 +115,13 @@ protected:
|
||||
void Unregister(const T& object) {
|
||||
object->SetIsRegistered(false);
|
||||
rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1);
|
||||
|
||||
// Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit
|
||||
if (Settings::values.use_accurate_gpu_emulation) {
|
||||
FlushObject(object);
|
||||
}
|
||||
|
||||
object_cache.subtract({GetInterval(object), ObjectSet{object}});
|
||||
interval_cache.subtract({GetInterval(object), ObjectSet{object}});
|
||||
map_cache.erase(object->GetAddr());
|
||||
}
|
||||
|
||||
/// Returns a ticks counter used for tracking when cached objects were last modified
|
||||
@@ -141,7 +138,7 @@ private:
|
||||
|
||||
std::vector<T> objects;
|
||||
const ObjectInterval interval{addr, addr + size};
|
||||
for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) {
|
||||
for (auto& pair : boost::make_iterator_range(interval_cache.equal_range(interval))) {
|
||||
for (auto& cached_object : pair.second) {
|
||||
if (!cached_object) {
|
||||
continue;
|
||||
@@ -167,15 +164,17 @@ private:
|
||||
}
|
||||
|
||||
using ObjectSet = std::set<T>;
|
||||
using ObjectCache = boost::icl::interval_map<VAddr, ObjectSet>;
|
||||
using ObjectInterval = typename ObjectCache::interval_type;
|
||||
using ObjectCache = std::unordered_map<VAddr, T>;
|
||||
using IntervalCache = boost::icl::interval_map<VAddr, ObjectSet>;
|
||||
using ObjectInterval = typename IntervalCache::interval_type;
|
||||
|
||||
static auto GetInterval(const T& object) {
|
||||
return ObjectInterval::right_open(object->GetAddr(),
|
||||
object->GetAddr() + object->GetSizeInBytes());
|
||||
}
|
||||
|
||||
ObjectCache object_cache; ///< Cache of objects
|
||||
u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing
|
||||
ObjectCache map_cache;
|
||||
IntervalCache interval_cache; ///< Cache of objects
|
||||
u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing
|
||||
VideoCore::RasterizerInterface& rasterizer;
|
||||
};
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/settings.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
|
||||
@@ -550,8 +550,8 @@ void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params
|
||||
if (params.is_layered) {
|
||||
u64 offset = params.GetMipmapLevelOffset(mip_level);
|
||||
u64 offset_gl = 0;
|
||||
u64 layer_size = params.LayerMemorySize();
|
||||
u64 gl_size = params.LayerSizeGL(mip_level);
|
||||
const u64 layer_size = params.LayerMemorySize();
|
||||
const u64 gl_size = params.LayerSizeGL(mip_level);
|
||||
for (u32 i = 0; i < params.depth; i++) {
|
||||
functions[static_cast<std::size_t>(params.pixel_format)](
|
||||
params.MipWidth(mip_level), params.MipBlockHeight(mip_level),
|
||||
@@ -561,7 +561,7 @@ void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params
|
||||
offset_gl += gl_size;
|
||||
}
|
||||
} else {
|
||||
u64 offset = params.GetMipmapLevelOffset(mip_level);
|
||||
const u64 offset = params.GetMipmapLevelOffset(mip_level);
|
||||
functions[static_cast<std::size_t>(params.pixel_format)](
|
||||
params.MipWidth(mip_level), params.MipBlockHeight(mip_level),
|
||||
params.MipHeight(mip_level), params.MipBlockDepth(mip_level), depth, gl_buffer.data(),
|
||||
@@ -715,18 +715,18 @@ static void FastCopySurface(const Surface& src_surface, const Surface& dst_surfa
|
||||
|
||||
MICROPROFILE_DEFINE(OpenGL_CopySurface, "OpenGL", "CopySurface", MP_RGB(128, 192, 64));
|
||||
static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
|
||||
GLuint copy_pbo_handle, GLenum src_attachment = 0,
|
||||
GLenum dst_attachment = 0, std::size_t cubemap_face = 0) {
|
||||
const GLuint copy_pbo_handle, const GLenum src_attachment = 0,
|
||||
const GLenum dst_attachment = 0, const std::size_t cubemap_face = 0) {
|
||||
MICROPROFILE_SCOPE(OpenGL_CopySurface);
|
||||
ASSERT_MSG(dst_attachment == 0, "Unimplemented");
|
||||
|
||||
const auto& src_params{src_surface->GetSurfaceParams()};
|
||||
const auto& dst_params{dst_surface->GetSurfaceParams()};
|
||||
|
||||
auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type);
|
||||
auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type);
|
||||
const auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type);
|
||||
const auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type);
|
||||
|
||||
std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes);
|
||||
const std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes);
|
||||
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle);
|
||||
glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);
|
||||
@@ -750,13 +750,10 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
|
||||
LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during "
|
||||
"reinterpretation but the texture is tiled.");
|
||||
}
|
||||
std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes;
|
||||
std::vector<u8> data(remaining_size);
|
||||
std::memcpy(data.data(), Memory::GetPointer(dst_params.addr + src_params.size_in_bytes),
|
||||
data.size());
|
||||
const std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes;
|
||||
|
||||
glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size,
|
||||
data.data());
|
||||
Memory::GetPointer(dst_params.addr + src_params.size_in_bytes));
|
||||
}
|
||||
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
|
||||
@@ -266,7 +266,8 @@ void OpenGLState::ApplyViewport() const {
|
||||
const auto& updated = viewports[0];
|
||||
if (updated.x != current.x || updated.y != current.y || updated.width != current.width ||
|
||||
updated.height != current.height) {
|
||||
glViewport(updated.x, updated.y, updated.width, updated.height);
|
||||
glViewport(static_cast<GLint>(updated.x), static_cast<GLint>(updated.y),
|
||||
static_cast<GLsizei>(updated.width), static_cast<GLsizei>(updated.height));
|
||||
}
|
||||
if (updated.depth_range_near != current.depth_range_near ||
|
||||
updated.depth_range_far != current.depth_range_far) {
|
||||
@@ -313,7 +314,7 @@ void OpenGLState::ApplyGlobalBlending() const {
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyTargetBlending(int target, bool force) const {
|
||||
void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const {
|
||||
const Blend& updated = blend[target];
|
||||
const Blend& current = cur_state.blend[target];
|
||||
const bool blend_changed = updated.enabled != current.enabled || force;
|
||||
|
||||
@@ -208,7 +208,7 @@ private:
|
||||
void ApplyPrimitiveRestart() const;
|
||||
void ApplyStencilTest() const;
|
||||
void ApplyViewport() const;
|
||||
void ApplyTargetBlending(int target, bool force) const;
|
||||
void ApplyTargetBlending(std::size_t target, bool force) const;
|
||||
void ApplyGlobalBlending() const;
|
||||
void ApplyBlending() const;
|
||||
void ApplyLogicOp() const;
|
||||
|
||||
@@ -45,7 +45,7 @@ constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>();
|
||||
* Instead of going gob by gob, we map the coordinates inside a block and manage from
|
||||
* those. Block_Width is assumed to be 1.
|
||||
*/
|
||||
void PreciseProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
|
||||
void PreciseProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, const bool unswizzle,
|
||||
const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
|
||||
const u32 y_end, const u32 z_end, const u32 tile_offset,
|
||||
const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
|
||||
@@ -81,7 +81,7 @@ void PreciseProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unsw
|
||||
* Instead of going gob by gob, we map the coordinates inside a block and manage from
|
||||
* those. Block_Width is assumed to be 1.
|
||||
*/
|
||||
void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
|
||||
void FastProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, const bool unswizzle,
|
||||
const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
|
||||
const u32 y_end, const u32 z_end, const u32 tile_offset,
|
||||
const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
|
||||
@@ -90,10 +90,10 @@ void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizz
|
||||
u32 z_address = tile_offset;
|
||||
const u32 x_startb = x_start * bytes_per_pixel;
|
||||
const u32 x_endb = x_end * bytes_per_pixel;
|
||||
const u32 copy_size = 16;
|
||||
const u32 gob_size_x = 64;
|
||||
const u32 gob_size_y = 8;
|
||||
const u32 gob_size_z = 1;
|
||||
constexpr u32 copy_size = 16;
|
||||
constexpr u32 gob_size_x = 64;
|
||||
constexpr u32 gob_size_y = 8;
|
||||
constexpr u32 gob_size_z = 1;
|
||||
const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
|
||||
for (u32 z = z_start; z < z_end; z++) {
|
||||
u32 y_address = z_address;
|
||||
@@ -126,23 +126,23 @@ void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizz
|
||||
* https://envytools.readthedocs.io/en/latest/hw/memory/g80-surface.html#blocklinear-surfaces
|
||||
*/
|
||||
template <bool fast>
|
||||
void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, const u32 width,
|
||||
const u32 height, const u32 depth, const u32 bytes_per_pixel,
|
||||
void SwizzledData(u8* const swizzled_data, u8* const unswizzled_data, const bool unswizzle,
|
||||
const u32 width, const u32 height, const u32 depth, const u32 bytes_per_pixel,
|
||||
const u32 out_bytes_per_pixel, const u32 block_height, const u32 block_depth) {
|
||||
auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); };
|
||||
const u32 stride_x = width * out_bytes_per_pixel;
|
||||
const u32 layer_z = height * stride_x;
|
||||
const u32 gob_x_bytes = 64;
|
||||
constexpr u32 gob_x_bytes = 64;
|
||||
const u32 gob_elements_x = gob_x_bytes / bytes_per_pixel;
|
||||
const u32 gob_elements_y = 8;
|
||||
const u32 gob_elements_z = 1;
|
||||
constexpr u32 gob_elements_y = 8;
|
||||
constexpr u32 gob_elements_z = 1;
|
||||
const u32 block_x_elements = gob_elements_x;
|
||||
const u32 block_y_elements = gob_elements_y * block_height;
|
||||
const u32 block_z_elements = gob_elements_z * block_depth;
|
||||
const u32 blocks_on_x = div_ceil(width, block_x_elements);
|
||||
const u32 blocks_on_y = div_ceil(height, block_y_elements);
|
||||
const u32 blocks_on_z = div_ceil(depth, block_z_elements);
|
||||
const u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z;
|
||||
constexpr u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z;
|
||||
const u32 xy_block_size = gob_size * block_height;
|
||||
const u32 block_size = xy_block_size * block_depth;
|
||||
u32 tile_offset = 0;
|
||||
@@ -171,7 +171,7 @@ void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
|
||||
}
|
||||
|
||||
void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
|
||||
u32 out_bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data,
|
||||
u32 out_bytes_per_pixel, u8* const swizzled_data, u8* const unswizzled_data,
|
||||
bool unswizzle, u32 block_height, u32 block_depth) {
|
||||
if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) {
|
||||
SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
|
||||
@@ -325,9 +325,9 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
|
||||
std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
|
||||
u32 block_height, u32 block_depth) {
|
||||
if (tiled) {
|
||||
const u32 gobs_in_x = 64;
|
||||
const u32 gobs_in_y = 8;
|
||||
const u32 gobs_in_z = 1;
|
||||
constexpr u32 gobs_in_x = 64;
|
||||
constexpr u32 gobs_in_y = 8;
|
||||
constexpr u32 gobs_in_z = 1;
|
||||
const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gobs_in_x);
|
||||
const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height);
|
||||
const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth);
|
||||
|
||||
@@ -7,6 +7,8 @@ add_executable(yuzu
|
||||
Info.plist
|
||||
about_dialog.cpp
|
||||
about_dialog.h
|
||||
applets/software_keyboard.cpp
|
||||
applets/software_keyboard.h
|
||||
bootmanager.cpp
|
||||
bootmanager.h
|
||||
compatibility_list.cpp
|
||||
|
||||
152
src/yuzu/applets/software_keyboard.cpp
Normal file
152
src/yuzu/applets/software_keyboard.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFont>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QVBoxLayout>
|
||||
#include "core/hle/lock.h"
|
||||
#include "yuzu/applets/software_keyboard.h"
|
||||
#include "yuzu/main.h"
|
||||
|
||||
QtSoftwareKeyboardValidator::QtSoftwareKeyboardValidator(
|
||||
Core::Frontend::SoftwareKeyboardParameters parameters)
|
||||
: parameters(std::move(parameters)) {}
|
||||
|
||||
QValidator::State QtSoftwareKeyboardValidator::validate(QString& input, int& pos) const {
|
||||
if (input.size() > parameters.max_length)
|
||||
return Invalid;
|
||||
if (parameters.disable_space && input.contains(' '))
|
||||
return Invalid;
|
||||
if (parameters.disable_address && input.contains('@'))
|
||||
return Invalid;
|
||||
if (parameters.disable_percent && input.contains('%'))
|
||||
return Invalid;
|
||||
if (parameters.disable_slash && (input.contains('/') || input.contains('\\')))
|
||||
return Invalid;
|
||||
if (parameters.disable_number &&
|
||||
std::any_of(input.begin(), input.end(), [](QChar c) { return c.isDigit(); })) {
|
||||
return Invalid;
|
||||
}
|
||||
|
||||
if (parameters.disable_download_code &&
|
||||
std::any_of(input.begin(), input.end(), [](QChar c) { return c == 'O' || c == 'I'; })) {
|
||||
return Invalid;
|
||||
}
|
||||
|
||||
return Acceptable;
|
||||
}
|
||||
|
||||
QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog(
|
||||
QWidget* parent, Core::Frontend::SoftwareKeyboardParameters parameters_)
|
||||
: QDialog(parent), parameters(std::move(parameters_)) {
|
||||
layout = new QVBoxLayout;
|
||||
|
||||
header_label = new QLabel(QString::fromStdU16String(parameters.header_text));
|
||||
header_label->setFont({header_label->font().family(), 11, QFont::Bold});
|
||||
if (header_label->text().isEmpty())
|
||||
header_label->setText(tr("Enter text:"));
|
||||
|
||||
sub_label = new QLabel(QString::fromStdU16String(parameters.sub_text));
|
||||
sub_label->setFont({sub_label->font().family(), sub_label->font().pointSize(),
|
||||
sub_label->font().weight(), true});
|
||||
sub_label->setHidden(parameters.sub_text.empty());
|
||||
|
||||
guide_label = new QLabel(QString::fromStdU16String(parameters.guide_text));
|
||||
guide_label->setHidden(parameters.guide_text.empty());
|
||||
|
||||
length_label = new QLabel(QStringLiteral("0/%1").arg(parameters.max_length));
|
||||
length_label->setAlignment(Qt::AlignRight);
|
||||
length_label->setFont({length_label->font().family(), 8});
|
||||
|
||||
line_edit = new QLineEdit;
|
||||
line_edit->setValidator(new QtSoftwareKeyboardValidator(parameters));
|
||||
line_edit->setMaxLength(static_cast<int>(parameters.max_length));
|
||||
line_edit->setText(QString::fromStdU16String(parameters.initial_text));
|
||||
line_edit->setCursorPosition(
|
||||
parameters.cursor_at_beginning ? 0 : static_cast<int>(parameters.initial_text.size()));
|
||||
line_edit->setEchoMode(parameters.password ? QLineEdit::Password : QLineEdit::Normal);
|
||||
|
||||
connect(line_edit, &QLineEdit::textChanged, this, [this](const QString& text) {
|
||||
length_label->setText(QStringLiteral("%1/%2").arg(text.size()).arg(parameters.max_length));
|
||||
});
|
||||
|
||||
buttons = new QDialogButtonBox;
|
||||
buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole);
|
||||
buttons->addButton(parameters.submit_text.empty()
|
||||
? tr("OK")
|
||||
: QString::fromStdU16String(parameters.submit_text),
|
||||
QDialogButtonBox::AcceptRole);
|
||||
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QtSoftwareKeyboardDialog::Submit);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QtSoftwareKeyboardDialog::Reject);
|
||||
layout->addWidget(header_label);
|
||||
layout->addWidget(sub_label);
|
||||
layout->addWidget(guide_label);
|
||||
layout->addWidget(length_label);
|
||||
layout->addWidget(line_edit);
|
||||
layout->addWidget(buttons);
|
||||
setLayout(layout);
|
||||
setWindowTitle(tr("Software Keyboard"));
|
||||
}
|
||||
|
||||
QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() = default;
|
||||
|
||||
void QtSoftwareKeyboardDialog::Submit() {
|
||||
ok = true;
|
||||
text = line_edit->text().toStdU16String();
|
||||
accept();
|
||||
}
|
||||
|
||||
void QtSoftwareKeyboardDialog::Reject() {
|
||||
ok = false;
|
||||
text.clear();
|
||||
accept();
|
||||
}
|
||||
|
||||
std::u16string QtSoftwareKeyboardDialog::GetText() const {
|
||||
return text;
|
||||
}
|
||||
|
||||
bool QtSoftwareKeyboardDialog::GetStatus() const {
|
||||
return ok;
|
||||
}
|
||||
|
||||
QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& main_window) {
|
||||
connect(this, &QtSoftwareKeyboard::MainWindowGetText, &main_window,
|
||||
&GMainWindow::SoftwareKeyboardGetText, Qt::QueuedConnection);
|
||||
connect(this, &QtSoftwareKeyboard::MainWindowTextCheckDialog, &main_window,
|
||||
&GMainWindow::SoftwareKeyboardInvokeCheckDialog, Qt::BlockingQueuedConnection);
|
||||
connect(&main_window, &GMainWindow::SoftwareKeyboardFinishedText, this,
|
||||
&QtSoftwareKeyboard::MainWindowFinishedText, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
QtSoftwareKeyboard::~QtSoftwareKeyboard() = default;
|
||||
|
||||
void QtSoftwareKeyboard::RequestText(std::function<void(std::optional<std::u16string>)> out,
|
||||
Core::Frontend::SoftwareKeyboardParameters parameters) const {
|
||||
text_output = out;
|
||||
emit MainWindowGetText(parameters);
|
||||
}
|
||||
|
||||
void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message,
|
||||
std::function<void()> finished_check) const {
|
||||
this->finished_check = finished_check;
|
||||
emit MainWindowTextCheckDialog(error_message);
|
||||
}
|
||||
|
||||
void QtSoftwareKeyboard::MainWindowFinishedText(std::optional<std::u16string> text) {
|
||||
// Acquire the HLE mutex
|
||||
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
|
||||
text_output(text);
|
||||
}
|
||||
|
||||
void QtSoftwareKeyboard::MainWindowFinishedCheckDialog() {
|
||||
// Acquire the HLE mutex
|
||||
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
|
||||
finished_check();
|
||||
}
|
||||
80
src/yuzu/applets/software_keyboard.h
Normal file
80
src/yuzu/applets/software_keyboard.h
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QValidator>
|
||||
#include "common/assert.h"
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
|
||||
class GMainWindow;
|
||||
class QDialogButtonBox;
|
||||
class QLabel;
|
||||
class QLineEdit;
|
||||
class QVBoxLayout;
|
||||
class QtSoftwareKeyboard;
|
||||
|
||||
class QtSoftwareKeyboardValidator final : public QValidator {
|
||||
public:
|
||||
explicit QtSoftwareKeyboardValidator(Core::Frontend::SoftwareKeyboardParameters parameters);
|
||||
State validate(QString& input, int& pos) const override;
|
||||
|
||||
private:
|
||||
Core::Frontend::SoftwareKeyboardParameters parameters;
|
||||
};
|
||||
|
||||
class QtSoftwareKeyboardDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QtSoftwareKeyboardDialog(QWidget* parent,
|
||||
Core::Frontend::SoftwareKeyboardParameters parameters);
|
||||
~QtSoftwareKeyboardDialog() override;
|
||||
|
||||
void Submit();
|
||||
void Reject();
|
||||
|
||||
std::u16string GetText() const;
|
||||
bool GetStatus() const;
|
||||
|
||||
private:
|
||||
bool ok = false;
|
||||
std::u16string text;
|
||||
|
||||
QDialogButtonBox* buttons;
|
||||
QLabel* header_label;
|
||||
QLabel* sub_label;
|
||||
QLabel* guide_label;
|
||||
QLabel* length_label;
|
||||
QLineEdit* line_edit;
|
||||
QVBoxLayout* layout;
|
||||
|
||||
Core::Frontend::SoftwareKeyboardParameters parameters;
|
||||
};
|
||||
|
||||
class QtSoftwareKeyboard final : public QObject, public Core::Frontend::SoftwareKeyboardApplet {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtSoftwareKeyboard(GMainWindow& parent);
|
||||
~QtSoftwareKeyboard() override;
|
||||
|
||||
void RequestText(std::function<void(std::optional<std::u16string>)> out,
|
||||
Core::Frontend::SoftwareKeyboardParameters parameters) const override;
|
||||
void SendTextCheckDialog(std::u16string error_message,
|
||||
std::function<void()> finished_check) const override;
|
||||
|
||||
signals:
|
||||
void MainWindowGetText(Core::Frontend::SoftwareKeyboardParameters parameters) const;
|
||||
void MainWindowTextCheckDialog(std::u16string error_message) const;
|
||||
|
||||
public slots:
|
||||
void MainWindowFinishedText(std::optional<std::u16string> text);
|
||||
void MainWindowFinishedCheckDialog();
|
||||
|
||||
private:
|
||||
mutable std::function<void(std::optional<std::u16string>)> text_output;
|
||||
mutable std::function<void()> finished_check;
|
||||
};
|
||||
@@ -153,6 +153,7 @@ void Config::ReadValues() {
|
||||
Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool();
|
||||
Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt();
|
||||
Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString();
|
||||
Settings::values.dump_nso = qt_config->value("dump_nso", false).toBool();
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("WebService");
|
||||
@@ -170,6 +171,7 @@ void Config::ReadValues() {
|
||||
|
||||
qt_config->beginGroup("UIGameList");
|
||||
UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool();
|
||||
UISettings::values.show_add_ons = qt_config->value("show_add_ons", true).toBool();
|
||||
UISettings::values.icon_size = qt_config->value("icon_size", 64).toUInt();
|
||||
UISettings::values.row_1_text_id = qt_config->value("row_1_text_id", 3).toUInt();
|
||||
UISettings::values.row_2_text_id = qt_config->value("row_2_text_id", 2).toUInt();
|
||||
@@ -295,6 +297,7 @@ void Config::SaveValues() {
|
||||
qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub);
|
||||
qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port);
|
||||
qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args));
|
||||
qt_config->setValue("dump_nso", Settings::values.dump_nso);
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("WebService");
|
||||
@@ -310,6 +313,7 @@ void Config::SaveValues() {
|
||||
|
||||
qt_config->beginGroup("UIGameList");
|
||||
qt_config->setValue("show_unknown", UISettings::values.show_unknown);
|
||||
qt_config->setValue("show_add_ons", UISettings::values.show_add_ons);
|
||||
qt_config->setValue("icon_size", UISettings::values.icon_size);
|
||||
qt_config->setValue("row_1_text_id", UISettings::values.row_1_text_id);
|
||||
qt_config->setValue("row_2_text_id", UISettings::values.row_2_text_id);
|
||||
|
||||
@@ -34,6 +34,7 @@ void ConfigureDebug::setConfiguration() {
|
||||
ui->toggle_console->setChecked(UISettings::values.show_console);
|
||||
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
|
||||
ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
|
||||
ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso);
|
||||
}
|
||||
|
||||
void ConfigureDebug::applyConfiguration() {
|
||||
@@ -42,6 +43,7 @@ void ConfigureDebug::applyConfiguration() {
|
||||
UISettings::values.show_console = ui->toggle_console->isChecked();
|
||||
Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
|
||||
Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
|
||||
Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked();
|
||||
Debugger::ToggleConsole();
|
||||
Log::Filter filter;
|
||||
filter.ParseFilterString(Settings::values.log_filter);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
<height>357</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -129,6 +129,25 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_4">
|
||||
<property name="title">
|
||||
<string>Dump</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="dump_decompressed_nso">
|
||||
<property name="whatsThis">
|
||||
<string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Dump Decompressed NSOs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
|
||||
@@ -42,6 +42,7 @@ ConfigureGameList::~ConfigureGameList() = default;
|
||||
|
||||
void ConfigureGameList::applyConfiguration() {
|
||||
UISettings::values.show_unknown = ui->show_unknown->isChecked();
|
||||
UISettings::values.show_add_ons = ui->show_add_ons->isChecked();
|
||||
UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt();
|
||||
UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt();
|
||||
UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt();
|
||||
@@ -50,6 +51,7 @@ void ConfigureGameList::applyConfiguration() {
|
||||
|
||||
void ConfigureGameList::setConfiguration() {
|
||||
ui->show_unknown->setChecked(UISettings::values.show_unknown);
|
||||
ui->show_add_ons->setChecked(UISettings::values.show_add_ons);
|
||||
ui->icon_size_combobox->setCurrentIndex(
|
||||
ui->icon_size_combobox->findData(UISettings::values.icon_size));
|
||||
ui->row_1_text_combobox->setCurrentIndex(
|
||||
|
||||
@@ -1,126 +1,133 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ConfigureGameList</class>
|
||||
<widget class="QWidget" name="ConfigureGeneral">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>300</width>
|
||||
<height>377</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="HorizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="VerticalLayout">
|
||||
<widget class="QWidget" name="ConfigureGameList">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>300</width>
|
||||
<height>377</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="HorizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="VerticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="GeneralGroupBox">
|
||||
<property name="title">
|
||||
<string>General</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="GeneralHorizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="GeneralVerticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="GeneralGroupBox">
|
||||
<property name="title">
|
||||
<string>General</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="GeneralHorizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="GeneralVerticalLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="show_unknown">
|
||||
<property name="text">
|
||||
<string>Show files with type 'Unknown'</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QCheckBox" name="show_unknown">
|
||||
<property name="text">
|
||||
<string>Show files with type 'Unknown'</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="IconSizeGroupBox">
|
||||
<property name="title">
|
||||
<string>Icon Size</string>
|
||||
<widget class="QCheckBox" name="show_add_ons">
|
||||
<property name="text">
|
||||
<string>Show Add-Ons Column</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="IconSizeGroupBox">
|
||||
<property name="title">
|
||||
<string>Icon Size</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="icon_size_qhbox_layout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="icon_size_qvbox_layout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="icon_size_qhbox_layout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="icon_size_label">
|
||||
<property name="text">
|
||||
<string>Icon Size:</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="icon_size_qhbox_layout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="icon_size_qvbox_layout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="icon_size_qhbox_layout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="icon_size_label">
|
||||
<property name="text">
|
||||
<string>Icon Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="icon_size_combobox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="icon_size_combobox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="RowGroupBox">
|
||||
<property name="title">
|
||||
<string>Row Text</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="RowHorizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="RowVerticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="row_1_qhbox_layout">
|
||||
<item>
|
||||
<widget class="QLabel" name="row_1_label">
|
||||
<property name="text">
|
||||
<string>Row 1 Text:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="row_1_text_combobox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="RowGroupBox">
|
||||
<property name="title">
|
||||
<string>Row Text</string>
|
||||
<layout class="QHBoxLayout" name="row_2_qhbox_layout">
|
||||
<item>
|
||||
<widget class="QLabel" name="row_2_label">
|
||||
<property name="text">
|
||||
<string>Row 2 Text:</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="RowHorizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="RowVerticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="row_1_qhbox_layout">
|
||||
<item>
|
||||
<widget class="QLabel" name="row_1_label">
|
||||
<property name="text">
|
||||
<string>Row 1 Text:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="row_1_text_combobox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="row_2_qhbox_layout">
|
||||
<item>
|
||||
<widget class="QLabel" name="row_2_label">
|
||||
<property name="text">
|
||||
<string>Row 2 Text:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="row_2_text_combobox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="row_2_text_combobox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
@@ -47,6 +47,9 @@ void ConfigureGeneral::OnDockedModeChanged(bool last_state, bool new_state) {
|
||||
}
|
||||
|
||||
Core::System& system{Core::System::GetInstance()};
|
||||
if (!system.IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
Service::SM::ServiceManager& sm = system.ServiceManager();
|
||||
|
||||
// Message queue is shared between these services, we just need to signal an operation
|
||||
|
||||
@@ -140,7 +140,7 @@ ConfigureSystem::ConfigureSystem(QWidget* parent)
|
||||
connect(ui->rng_seed_checkbox, &QCheckBox::stateChanged, this, [this](bool checked) {
|
||||
ui->rng_seed_edit->setEnabled(checked);
|
||||
if (!checked)
|
||||
ui->rng_seed_edit->setText(QStringLiteral("0000000000000000"));
|
||||
ui->rng_seed_edit->setText(QStringLiteral("00000000"));
|
||||
});
|
||||
|
||||
scene = new QGraphicsScene;
|
||||
@@ -165,9 +165,8 @@ void ConfigureSystem::setConfiguration() {
|
||||
ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.has_value());
|
||||
ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.has_value());
|
||||
|
||||
const auto rng_seed = QString("%1")
|
||||
.arg(Settings::values.rng_seed.value_or(0), 16, 16, QLatin1Char{'0'})
|
||||
.toUpper();
|
||||
const auto rng_seed =
|
||||
QString("%1").arg(Settings::values.rng_seed.value_or(0), 8, 16, QLatin1Char{'0'}).toUpper();
|
||||
ui->rng_seed_edit->setText(rng_seed);
|
||||
}
|
||||
|
||||
|
||||
@@ -269,10 +269,10 @@
|
||||
</font>
|
||||
</property>
|
||||
<property name="inputMask">
|
||||
<string>HHHHHHHHHHHHHHHH</string>
|
||||
<string notr="true">HHHHHHHH</string>
|
||||
</property>
|
||||
<property name="maxLength">
|
||||
<number>16</number>
|
||||
<number>8</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -215,12 +215,18 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent)
|
||||
tree_view->setUniformRowHeights(true);
|
||||
tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
item_model->insertColumns(0, COLUMN_COUNT);
|
||||
item_model->insertColumns(0, UISettings::values.show_add_ons ? COLUMN_COUNT : COLUMN_COUNT - 1);
|
||||
item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
|
||||
item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility"));
|
||||
item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons"));
|
||||
item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type"));
|
||||
item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size"));
|
||||
|
||||
if (UISettings::values.show_add_ons) {
|
||||
item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons"));
|
||||
item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type"));
|
||||
item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size"));
|
||||
} else {
|
||||
item_model->setHeaderData(COLUMN_FILE_TYPE - 1, Qt::Horizontal, tr("File type"));
|
||||
item_model->setHeaderData(COLUMN_SIZE - 1, Qt::Horizontal, tr("Size"));
|
||||
}
|
||||
|
||||
connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
|
||||
connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
|
||||
@@ -394,6 +400,25 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
|
||||
}
|
||||
|
||||
tree_view->setEnabled(false);
|
||||
|
||||
// Update the columns in case UISettings has changed
|
||||
item_model->removeColumns(0, item_model->columnCount());
|
||||
item_model->insertColumns(0, UISettings::values.show_add_ons ? COLUMN_COUNT : COLUMN_COUNT - 1);
|
||||
item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
|
||||
item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility"));
|
||||
|
||||
if (UISettings::values.show_add_ons) {
|
||||
item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons"));
|
||||
item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type"));
|
||||
item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size"));
|
||||
} else {
|
||||
item_model->setHeaderData(COLUMN_FILE_TYPE - 1, Qt::Horizontal, tr("File type"));
|
||||
item_model->setHeaderData(COLUMN_SIZE - 1, Qt::Horizontal, tr("Size"));
|
||||
item_model->removeColumns(COLUMN_COUNT - 1, 1);
|
||||
}
|
||||
|
||||
LoadInterfaceLayout();
|
||||
|
||||
// Delete any rows that might already exist if we're repopulating
|
||||
item_model->removeRows(0, item_model->rowCount());
|
||||
|
||||
|
||||
@@ -123,17 +123,22 @@ void GameListWorker::AddInstalledTitlesToGameList() {
|
||||
if (it != compatibility_list.end())
|
||||
compatibility = it->second.first;
|
||||
|
||||
emit EntryReady({
|
||||
QList<QStandardItem*> list{
|
||||
new GameListItemPath(
|
||||
FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name),
|
||||
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
|
||||
program_id),
|
||||
new GameListItemCompat(compatibility),
|
||||
new GameListItem(FormatPatchNameVersions(patch, *loader)),
|
||||
new GameListItem(
|
||||
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
|
||||
new GameListItemSize(file->GetSize()),
|
||||
});
|
||||
};
|
||||
|
||||
if (UISettings::values.show_add_ons) {
|
||||
list.insert(2, new GameListItem(FormatPatchNameVersions(patch, *loader)));
|
||||
}
|
||||
|
||||
emit EntryReady(list);
|
||||
}
|
||||
|
||||
const auto control_data = cache->ListEntriesFilter(FileSys::TitleType::Application,
|
||||
@@ -216,18 +221,23 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
|
||||
if (it != compatibility_list.end())
|
||||
compatibility = it->second.first;
|
||||
|
||||
emit EntryReady({
|
||||
QList<QStandardItem*> list{
|
||||
new GameListItemPath(
|
||||
FormatGameName(physical_name), icon, QString::fromStdString(name),
|
||||
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
|
||||
program_id),
|
||||
new GameListItemCompat(compatibility),
|
||||
new GameListItem(
|
||||
FormatPatchNameVersions(patch, *loader, loader->IsRomFSUpdatable())),
|
||||
new GameListItem(
|
||||
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
|
||||
new GameListItemSize(FileUtil::GetSize(physical_name)),
|
||||
});
|
||||
};
|
||||
|
||||
if (UISettings::values.show_add_ons) {
|
||||
list.insert(2, new GameListItem(FormatPatchNameVersions(
|
||||
patch, *loader, loader->IsRomFSUpdatable())));
|
||||
}
|
||||
|
||||
emit EntryReady(std::move(list));
|
||||
} else if (is_dir && recursion > 0) {
|
||||
watch_list.append(QString::fromStdString(physical_name));
|
||||
AddFstEntriesToGameList(physical_name, recursion - 1);
|
||||
|
||||
@@ -8,11 +8,13 @@
|
||||
#include <thread>
|
||||
|
||||
// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
|
||||
#include "applets/software_keyboard.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
// These are wrappers to avoid the calls to CreateDirectory and CreateFile becuase of the Windows
|
||||
// These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows
|
||||
// defines.
|
||||
static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper(
|
||||
const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::Mode mode) {
|
||||
@@ -59,6 +61,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||
#include "core/file_sys/romfs.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/file_sys/submission_package.h"
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/filesystem/fsp_ldr.h"
|
||||
@@ -204,6 +207,27 @@ GMainWindow::~GMainWindow() {
|
||||
delete render_window;
|
||||
}
|
||||
|
||||
void GMainWindow::SoftwareKeyboardGetText(
|
||||
const Core::Frontend::SoftwareKeyboardParameters& parameters) {
|
||||
QtSoftwareKeyboardDialog dialog(this, parameters);
|
||||
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
|
||||
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
|
||||
dialog.setWindowModality(Qt::WindowModal);
|
||||
dialog.exec();
|
||||
|
||||
if (!dialog.GetStatus()) {
|
||||
emit SoftwareKeyboardFinishedText(std::nullopt);
|
||||
return;
|
||||
}
|
||||
|
||||
emit SoftwareKeyboardFinishedText(dialog.GetText());
|
||||
}
|
||||
|
||||
void GMainWindow::SoftwareKeyboardInvokeCheckDialog(std::u16string error_message) {
|
||||
QMessageBox::warning(this, tr("Text Check Failed"), QString::fromStdU16String(error_message));
|
||||
emit SoftwareKeyboardFinishedCheckDialog();
|
||||
}
|
||||
|
||||
void GMainWindow::InitializeWidgets() {
|
||||
#ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING
|
||||
ui.action_Report_Compatibility->setVisible(true);
|
||||
@@ -308,6 +332,8 @@ void GMainWindow::InitializeHotkeys() {
|
||||
Qt::ApplicationShortcut);
|
||||
hotkey_registry.RegisterHotkey("Main Window", "Decrease Speed Limit", QKeySequence("-"),
|
||||
Qt::ApplicationShortcut);
|
||||
hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2),
|
||||
Qt::ApplicationShortcut);
|
||||
hotkey_registry.LoadHotkeys();
|
||||
|
||||
connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
|
||||
@@ -361,6 +387,12 @@ void GMainWindow::InitializeHotkeys() {
|
||||
UpdateStatusBar();
|
||||
}
|
||||
});
|
||||
connect(hotkey_registry.GetHotkey("Main Window", "Load Amiibo", this), &QShortcut::activated,
|
||||
this, [&] {
|
||||
if (ui.action_Load_Amiibo->isEnabled()) {
|
||||
OnLoadAmiibo();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void GMainWindow::SetDefaultUIGeometry() {
|
||||
@@ -551,6 +583,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
||||
|
||||
system.SetGPUDebugContext(debug_context);
|
||||
|
||||
system.SetSoftwareKeyboard(std::make_unique<QtSoftwareKeyboard>(*this));
|
||||
|
||||
const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())};
|
||||
|
||||
const auto drd_callout =
|
||||
@@ -1220,8 +1254,13 @@ void GMainWindow::OnMenuRecentFile() {
|
||||
|
||||
void GMainWindow::OnStartGame() {
|
||||
emu_thread->SetRunning(true);
|
||||
|
||||
qRegisterMetaType<Core::Frontend::SoftwareKeyboardParameters>(
|
||||
"Core::Frontend::SoftwareKeyboardParameters");
|
||||
qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus");
|
||||
qRegisterMetaType<std::string>("std::string");
|
||||
qRegisterMetaType<std::optional<std::u16string>>("std::optional<std::u16string>");
|
||||
|
||||
connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError);
|
||||
|
||||
ui.action_Start->setEnabled(false);
|
||||
|
||||
@@ -29,6 +29,10 @@ class ProfilerWidget;
|
||||
class WaitTreeWidget;
|
||||
enum class GameListOpenTarget;
|
||||
|
||||
namespace Core::Frontend {
|
||||
struct SoftwareKeyboardParameters;
|
||||
} // namespace Core::Frontend
|
||||
|
||||
namespace FileSys {
|
||||
class RegisteredCacheUnion;
|
||||
class VfsFilesystem;
|
||||
@@ -95,6 +99,13 @@ signals:
|
||||
// Signal that tells widgets to update icons to use the current theme
|
||||
void UpdateThemedIcons();
|
||||
|
||||
void SoftwareKeyboardFinishedText(std::optional<std::u16string> text);
|
||||
void SoftwareKeyboardFinishedCheckDialog();
|
||||
|
||||
public slots:
|
||||
void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters);
|
||||
void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message);
|
||||
|
||||
private:
|
||||
void InitializeWidgets();
|
||||
void InitializeDebugWidgets();
|
||||
|
||||
@@ -70,6 +70,8 @@
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Load_Amiibo"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Open_yuzu_Folder"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Exit"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Emulation">
|
||||
@@ -110,7 +112,6 @@
|
||||
<string>&Help</string>
|
||||
</property>
|
||||
<addaction name="action_Report_Compatibility"/>
|
||||
<addaction name="action_Open_yuzu_Folder" />
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_About"/>
|
||||
</widget>
|
||||
|
||||
@@ -59,6 +59,7 @@ struct Values {
|
||||
|
||||
// Game List
|
||||
bool show_unknown;
|
||||
bool show_add_ons;
|
||||
uint32_t icon_size;
|
||||
uint8_t row_1_text_id;
|
||||
uint8_t row_2_text_id;
|
||||
|
||||
@@ -148,6 +148,7 @@ void Config::ReadValues() {
|
||||
Settings::values.gdbstub_port =
|
||||
static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
|
||||
Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", "");
|
||||
Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false);
|
||||
|
||||
// Web Service
|
||||
Settings::values.enable_telemetry =
|
||||
|
||||
@@ -206,6 +206,8 @@ log_filter = *:Trace
|
||||
# Port for listening to GDB connections.
|
||||
use_gdbstub=false
|
||||
gdbstub_port=24689
|
||||
# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
|
||||
dump_nso=false
|
||||
|
||||
[WebService]
|
||||
# Whether or not to enable telemetry
|
||||
|
||||
Reference in New Issue
Block a user