summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/crypto/key_manager.cpp1
-rw-r--r--src/core/file_sys/ips_layer.cpp2
-rw-r--r--src/core/file_sys/vfs.cpp8
-rw-r--r--src/core/hle/ipc.h5
-rw-r--r--src/core/hle/kernel/errors.h21
-rw-r--r--src/core/hle/kernel/process.cpp6
-rw-r--r--src/core/hle/kernel/server_port.cpp4
-rw-r--r--src/core/hle/kernel/shared_memory.cpp7
-rw-r--r--src/core/hle/kernel/svc.cpp13
-rw-r--r--src/core/hle/kernel/vm_manager.cpp20
-rw-r--r--src/core/hle/kernel/vm_manager.h8
-rw-r--r--src/core/hle/service/acc/acc.cpp76
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp204
-rw-r--r--src/core/hle/service/acc/profile_manager.h35
-rw-r--r--src/core/hle/service/am/am.cpp33
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp6
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp5
-rw-r--r--src/core/hle/service/ldr/ldr.cpp52
-rw-r--r--src/core/hle/service/usb/usb.cpp43
-rw-r--r--src/core/loader/nro.cpp21
-rw-r--r--src/core/loader/nro.h3
-rw-r--r--src/core/perf_stats.cpp4
-rw-r--r--src/core/settings.h2
23 files changed, 429 insertions, 150 deletions
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index fd0786068..fefc3c747 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -713,7 +713,6 @@ void KeyManager::DeriveBase() {
const auto sbk = GetKey(S128KeyType::SecureBoot);
const auto tsec = GetKey(S128KeyType::TSEC);
- const auto master_source = GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Master));
for (size_t i = 0; i < revisions.size(); ++i) {
if (!revisions[i])
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp
index 554eae9bc..999939d5a 100644
--- a/src/core/file_sys/ips_layer.cpp
+++ b/src/core/file_sys/ips_layer.cpp
@@ -99,7 +99,7 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
u16 rle_size{};
if (ips->ReadObject(&rle_size, offset) != sizeof(u16))
return nullptr;
- rle_size = Common::swap16(data_size);
+ rle_size = Common::swap16(rle_size);
offset += sizeof(u16);
const auto data = ips->ReadByte(offset++);
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index bfe50da73..3824c74e0 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -472,10 +472,14 @@ bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t blo
std::vector<u8> temp(std::min(block_size, src->GetSize()));
for (std::size_t i = 0; i < src->GetSize(); i += block_size) {
const auto read = std::min(block_size, src->GetSize() - i);
- const auto block = src->Read(temp.data(), read, i);
- if (dest->Write(temp.data(), read, i) != read)
+ if (src->Read(temp.data(), read, i) != read) {
return false;
+ }
+
+ if (dest->Write(temp.data(), read, i) != read) {
+ return false;
+ }
}
return true;
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index 419f45896..ed84197b3 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -14,11 +14,6 @@ namespace IPC {
/// Size of the command buffer area, in 32-bit words.
constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
-// These errors are commonly returned by invalid IPC translations, so alias them here for
-// convenience.
-// TODO(yuriks): These will probably go away once translation is implemented inside the kernel.
-constexpr auto ERR_INVALID_HANDLE = Kernel::ERR_INVALID_HANDLE_OS;
-
enum class ControlCommand : u32 {
ConvertSessionToDomain = 0,
ConvertDomainToSession = 1,
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index 885259618..ee698c8a7 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -10,11 +10,6 @@ namespace Kernel {
namespace ErrCodes {
enum {
- // TODO(Subv): Remove these 3DS OS error codes.
- SessionClosedByRemote = 26,
- NoPendingSessions = 35,
- InvalidBufferDescriptor = 48,
-
// Confirmed Switch OS error codes
MaxConnectionsReached = 7,
InvalidSize = 101,
@@ -26,6 +21,7 @@ enum {
InvalidThreadPriority = 112,
InvalidProcessorId = 113,
InvalidHandle = 114,
+ InvalidPointer = 115,
InvalidCombination = 116,
Timeout = 117,
SynchronizationCanceled = 118,
@@ -33,6 +29,7 @@ enum {
InvalidEnumValue = 120,
NoSuchEntry = 121,
AlreadyRegistered = 122,
+ SessionClosed = 123,
InvalidState = 125,
ResourceLimitExceeded = 132,
};
@@ -41,18 +38,14 @@ enum {
// 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.
-// TODO(bunnei): Replace -1 with correct errors for Switch OS
constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull);
-constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1);
+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_ENUM_VALUE_FND(-1);
-constexpr ResultCode ERR_INVALID_COMBINATION(-1);
constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel,
ErrCodes::InvalidCombination);
-constexpr ResultCode ERR_OUT_OF_MEMORY(-1);
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,
@@ -65,14 +58,8 @@ constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::Alrea
constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState);
constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel,
ErrCodes::InvalidThreadPriority);
-constexpr ResultCode ERR_INVALID_POINTER(-1);
-constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1);
-constexpr ResultCode ERR_NOT_AUTHORIZED(-1);
-/// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths.
-constexpr ResultCode ERR_INVALID_HANDLE_OS(-1);
+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);
-/// Returned when Accept() is called on a port with no sessions to be accepted.
-constexpr ResultCode ERR_NO_PENDING_SESSIONS(-1);
} // namespace Kernel
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 073dd5a7d..420218d59 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -232,6 +232,12 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
+
+ // Clear instruction cache in CPU JIT
+ Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
+ Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
+ Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
+ Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
}
ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp
index 3792e3e18..d6ceeb2da 100644
--- a/src/core/hle/kernel/server_port.cpp
+++ b/src/core/hle/kernel/server_port.cpp
@@ -18,7 +18,7 @@ ServerPort::~ServerPort() = default;
ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
if (pending_sessions.empty()) {
- return ERR_NO_PENDING_SESSIONS;
+ return ERR_NOT_FOUND;
}
auto session = std::move(pending_sessions.back());
@@ -28,7 +28,7 @@ ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
bool ServerPort::ShouldWait(Thread* thread) const {
// If there are no pending sessions, we wait until a new one is added.
- return pending_sessions.size() == 0;
+ return pending_sessions.empty();
}
void ServerPort::Acquire(Thread* thread) {
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index d061e6155..a016a86b6 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -80,20 +80,19 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions,
MemoryPermission other_permissions) {
-
- MemoryPermission own_other_permissions =
+ const MemoryPermission own_other_permissions =
target_process == owner_process ? this->permissions : this->other_permissions;
// Automatically allocated memory blocks can only be mapped with other_permissions = DontCare
if (base_address == 0 && other_permissions != MemoryPermission::DontCare) {
- return ERR_INVALID_COMBINATION;
+ return ERR_INVALID_MEMORY_PERMISSIONS;
}
// Error out if the requested permissions don't match what the creator process allows.
if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) {
LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match",
GetObjectId(), address, name);
- return ERR_INVALID_COMBINATION;
+ return ERR_INVALID_MEMORY_PERMISSIONS;
}
// Error out if the provided permissions are not compatible with what the creator process needs.
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 9a783d524..a5302d924 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -594,16 +594,17 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
}
const auto* const current_process = Core::CurrentProcess();
- SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
- if (!thread) {
- return ERR_INVALID_HANDLE;
- }
// Note: The kernel uses the current process's resource limit instead of
// the one from the thread owner's resource limit.
const ResourceLimit& resource_limit = current_process->GetResourceLimit();
if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
- return ERR_NOT_AUTHORIZED;
+ return ERR_INVALID_THREAD_PRIORITY;
+ }
+
+ SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
+ if (!thread) {
+ return ERR_INVALID_HANDLE;
}
thread->SetPriority(priority);
@@ -745,7 +746,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
auto* const current_process = Core::CurrentProcess();
const ResourceLimit& resource_limit = current_process->GetResourceLimit();
if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
- return ERR_NOT_AUTHORIZED;
+ return ERR_INVALID_THREAD_PRIORITY;
}
if (processor_id == THREADPROCESSORID_DEFAULT) {
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index e1a34eef1..1a92c8f70 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -143,6 +143,26 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
return MakeResult<VMAHandle>(MergeAdjacent(vma_handle));
}
+ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const {
+ // Find the first Free VMA.
+ const VAddr base = GetASLRRegionBaseAddress();
+ const VMAHandle vma_handle = std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) {
+ if (vma.second.type != VMAType::Free)
+ return false;
+
+ const VAddr vma_end = vma.second.base + vma.second.size;
+ return vma_end > base && vma_end >= base + size;
+ });
+
+ if (vma_handle == vma_map.end()) {
+ // TODO(Subv): Find the correct error code here.
+ return ResultCode(-1);
+ }
+
+ const VAddr target = std::max(base, vma_handle->second.base);
+ return MakeResult<VAddr>(target);
+}
+
ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size,
MemoryState state,
Memory::MemoryHookPointer mmio_handler) {
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 84c890224..2447cbb8f 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -158,6 +158,14 @@ public:
ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state);
/**
+ * Finds the first free address that can hold a region of the desired size.
+ *
+ * @param size Size of the desired region.
+ * @return The found free address.
+ */
+ ResultVal<VAddr> FindFreeRegion(u64 size) const;
+
+ /**
* Maps a memory-mapped IO region at a given address.
*
* @param target The guest address to start the mapping at.
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index e61748ca3..c6437a671 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -2,9 +2,13 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
#include <array>
+#include "common/common_paths.h"
#include "common/common_types.h"
+#include "common/file_util.h"
#include "common/logging/log.h"
+#include "common/string_util.h"
#include "common/swap.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
@@ -16,6 +20,7 @@
#include "core/hle/service/acc/profile_manager.h"
namespace Service::Account {
+
// TODO: RE this structure
struct UserData {
INSERT_PADDING_WORDS(1);
@@ -27,6 +32,29 @@ struct UserData {
};
static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size");
+// Smallest JPEG https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
+// used as a backup should the one on disk not exist
+constexpr u32 backup_jpeg_size = 107;
+constexpr std::array<u8, backup_jpeg_size> backup_jpeg{{
+ 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
+ 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05,
+ 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e,
+ 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13,
+ 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01,
+ 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08,
+ 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
+}};
+
+static std::string GetImagePath(UUID uuid) {
+ return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
+ "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
+}
+
+static constexpr u32 SanitizeJPEGSize(std::size_t size) {
+ constexpr std::size_t max_jpeg_image_size = 0x20000;
+ return static_cast<u32>(std::min(size, max_jpeg_image_size));
+}
+
class IProfile final : public ServiceFramework<IProfile> {
public:
explicit IProfile(UUID user_id, ProfileManager& profile_manager)
@@ -73,32 +101,42 @@ private:
}
void LoadImage(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_ACC, "(STUBBED) called");
- // smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
- // TODO(mailwl): load actual profile image from disk, width 256px, max size 0x20000
- constexpr u32 jpeg_size = 107;
- static constexpr std::array<u8, jpeg_size> jpeg{
- 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03,
- 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a,
- 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f,
- 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10,
- 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00,
- 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01,
- 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
- };
- ctx.WriteBuffer(jpeg);
+ LOG_DEBUG(Service_ACC, "called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(jpeg_size);
+
+ const FileUtil::IOFile image(GetImagePath(user_id), "rb");
+ if (!image.IsOpen()) {
+ LOG_WARNING(Service_ACC,
+ "Failed to load user provided image! Falling back to built-in backup...");
+ ctx.WriteBuffer(backup_jpeg);
+ rb.Push<u32>(backup_jpeg_size);
+ return;
+ }
+
+ const u32 size = SanitizeJPEGSize(image.GetSize());
+ std::vector<u8> buffer(size);
+ image.ReadBytes(buffer.data(), buffer.size());
+
+ ctx.WriteBuffer(buffer.data(), buffer.size());
+ rb.Push<u32>(size);
}
void GetImageSize(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_ACC, "(STUBBED) called");
- constexpr u32 jpeg_size = 107;
+ LOG_DEBUG(Service_ACC, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(jpeg_size);
+
+ const FileUtil::IOFile image(GetImagePath(user_id), "rb");
+
+ if (!image.IsOpen()) {
+ LOG_WARNING(Service_ACC,
+ "Failed to load user provided image! Falling back to built-in backup...");
+ rb.Push<u32>(backup_jpeg_size);
+ } else {
+ rb.Push<u32>(SanitizeJPEGSize(image.GetSize()));
+ }
}
const ProfileManager& profile_manager;
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index bcb3475db..3cac1b4ff 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -3,41 +3,66 @@
// Refer to the license.txt file included.
#include <random>
-#include <boost/optional.hpp>
+
+#include "common/file_util.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/settings.h"
namespace Service::Account {
+
+struct UserRaw {
+ UUID uuid;
+ UUID uuid2;
+ u64 timestamp;
+ ProfileUsername username;
+ INSERT_PADDING_BYTES(0x80);
+};
+static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size.");
+
+struct ProfileDataRaw {
+ INSERT_PADDING_BYTES(0x10);
+ std::array<UserRaw, MAX_USERS> users;
+};
+static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size.");
+
// TODO(ogniK): Get actual error codes
constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, -1);
constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, -2);
constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20);
-const UUID& UUID::Generate() {
+constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "/system/save/8000000000000010/su/avators/";
+
+UUID UUID::Generate() {
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
- uuid[0] = distribution(gen);
- uuid[1] = distribution(gen);
- return *this;
+ return UUID{distribution(gen), distribution(gen)};
}
ProfileManager::ProfileManager() {
- // TODO(ogniK): Create the default user we have for now until loading/saving users is added
- auto user_uuid = UUID{1, 0};
- ASSERT(CreateNewUser(user_uuid, Settings::values.username).IsSuccess());
- OpenUser(user_uuid);
+ ParseUserSaveFile();
+
+ if (user_count == 0)
+ CreateNewUser(UUID::Generate(), "yuzu");
+
+ auto current = std::clamp<int>(Settings::values.current_user, 0, MAX_USERS - 1);
+ if (UserExistsIndex(current))
+ current = 0;
+
+ OpenUser(*GetUser(current));
}
-ProfileManager::~ProfileManager() = default;
+ProfileManager::~ProfileManager() {
+ WriteUserSaveFile();
+}
/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the
/// internal management of the users profiles
-boost::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) {
+std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) {
if (user_count >= MAX_USERS) {
- return boost::none;
+ return {};
}
- profiles[user_count] = user;
+ profiles[user_count] = profile;
return user_count++;
}
@@ -56,7 +81,7 @@ bool ProfileManager::RemoveProfileAtIndex(std::size_t index) {
/// Helper function to register a user to the system
ResultCode ProfileManager::AddUser(const ProfileInfo& user) {
- if (AddToProfiles(user) == boost::none) {
+ if (!AddToProfiles(user)) {
return ERROR_TOO_MANY_USERS;
}
return RESULT_SUCCESS;
@@ -101,31 +126,40 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username)
return CreateNewUser(uuid, username_output);
}
+std::optional<UUID> ProfileManager::GetUser(std::size_t index) const {
+ if (index >= MAX_USERS) {
+ return {};
+ }
+
+ return profiles[index].user_uuid;
+}
+
/// Returns a users profile index based on their user id.
-boost::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
+std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
if (!uuid) {
- return boost::none;
+ return {};
}
- auto iter = std::find_if(profiles.begin(), profiles.end(),
- [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; });
+
+ const auto iter = std::find_if(profiles.begin(), profiles.end(),
+ [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; });
if (iter == profiles.end()) {
- return boost::none;
+ return {};
}
+
return static_cast<std::size_t>(std::distance(profiles.begin(), iter));
}
/// Returns a users profile index based on their profile
-boost::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const {
+std::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const {
return GetUserIndex(user.user_uuid);
}
/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
-bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index,
- ProfileBase& profile) const {
- if (index == boost::none || index >= MAX_USERS) {
+bool ProfileManager::GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const {
+ if (!index || index >= MAX_USERS) {
return false;
}
- const auto& prof_info = profiles[index.get()];
+ const auto& prof_info = profiles[*index];
profile.user_uuid = prof_info.user_uuid;
profile.username = prof_info.username;
profile.timestamp = prof_info.creation_time;
@@ -134,7 +168,7 @@ bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index,
/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const {
- auto idx = GetUserIndex(uuid);
+ const auto idx = GetUserIndex(uuid);
return GetProfileBase(idx, profile);
}
@@ -161,26 +195,34 @@ std::size_t ProfileManager::GetOpenUserCount() const {
/// Checks if a user id exists in our profile manager
bool ProfileManager::UserExists(UUID uuid) const {
- return (GetUserIndex(uuid) != boost::none);
+ return GetUserIndex(uuid) != std::nullopt;
+}
+
+bool ProfileManager::UserExistsIndex(std::size_t index) const {
+ if (index >= MAX_USERS)
+ return false;
+ return profiles[index].user_uuid.uuid != INVALID_UUID;
}
/// Opens a specific user
void ProfileManager::OpenUser(UUID uuid) {
- auto idx = GetUserIndex(uuid);
- if (idx == boost::none) {
+ const auto idx = GetUserIndex(uuid);
+ if (!idx) {
return;
}
- profiles[idx.get()].is_open = true;
+
+ profiles[*idx].is_open = true;
last_opened_user = uuid;
}
/// Closes a specific user
void ProfileManager::CloseUser(UUID uuid) {
- auto idx = GetUserIndex(uuid);
- if (idx == boost::none) {
+ const auto idx = GetUserIndex(uuid);
+ if (!idx) {
return;
}
- profiles[idx.get()].is_open = false;
+
+ profiles[*idx].is_open = false;
}
/// Gets all valid user ids on the system
@@ -210,10 +252,10 @@ UUID ProfileManager::GetLastOpenedUser() const {
}
/// Return the users profile base and the unknown arbitary data.
-bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile,
+bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
ProfileData& data) const {
if (GetProfileBase(index, profile)) {
- data = profiles[index.get()].data;
+ data = profiles[*index].data;
return true;
}
return false;
@@ -222,7 +264,7 @@ bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, P
/// Return the users profile base and the unknown arbitary data.
bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile,
ProfileData& data) const {
- auto idx = GetUserIndex(uuid);
+ const auto idx = GetUserIndex(uuid);
return GetProfileBaseAndData(idx, profile, data);
}
@@ -239,4 +281,96 @@ bool ProfileManager::CanSystemRegisterUser() const {
// emulate qlaunch. Update this to dynamically change.
}
+bool ProfileManager::RemoveUser(UUID uuid) {
+ const auto index = GetUserIndex(uuid);
+ if (!index) {
+ return false;
+ }
+
+ profiles[*index] = ProfileInfo{};
+ std::stable_partition(profiles.begin(), profiles.end(),
+ [](const ProfileInfo& profile) { return profile.user_uuid; });
+ return true;
+}
+
+bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
+ const auto index = GetUserIndex(uuid);
+ if (!index || profile_new.user_uuid == UUID(INVALID_UUID)) {
+ return false;
+ }
+
+ auto& profile = profiles[*index];
+ profile.user_uuid = profile_new.user_uuid;
+ profile.username = profile_new.username;
+ profile.creation_time = profile_new.timestamp;
+
+ return true;
+}
+
+void ProfileManager::ParseUserSaveFile() {
+ FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
+ ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat",
+ "rb");
+
+ if (!save.IsOpen()) {
+ LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
+ "user 'yuzu' with random UUID.");
+ return;
+ }
+
+ ProfileDataRaw data;
+ if (save.ReadBytes(&data, sizeof(ProfileDataRaw)) != sizeof(ProfileDataRaw)) {
+ LOG_WARNING(Service_ACC, "profiles.dat is smaller than expected... Generating new user "
+ "'yuzu' with random UUID.");
+ return;
+ }
+
+ for (std::size_t i = 0; i < MAX_USERS; ++i) {
+ const auto& user = data.users[i];
+
+ if (user.uuid != UUID(INVALID_UUID))
+ AddUser({user.uuid, user.username, user.timestamp, {}, false});
+ }
+
+ std::stable_partition(profiles.begin(), profiles.end(),
+ [](const ProfileInfo& profile) { return profile.user_uuid; });
+}
+
+void ProfileManager::WriteUserSaveFile() {
+ ProfileDataRaw raw{};
+
+ for (std::size_t i = 0; i < MAX_USERS; ++i) {
+ raw.users[i].username = profiles[i].username;
+ raw.users[i].uuid2 = profiles[i].user_uuid;
+ raw.users[i].uuid = profiles[i].user_uuid;
+ raw.users[i].timestamp = profiles[i].creation_time;
+ }
+
+ const auto raw_path =
+ FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010";
+ if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
+ FileUtil::Delete(raw_path);
+
+ const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
+ ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat";
+
+ if (!FileUtil::CreateFullPath(path)) {
+ LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory "
+ "nand/system/save/8000000000000010/su/avators to mitigate this "
+ "issue.");
+ return;
+ }
+
+ FileUtil::IOFile save(path, "wb");
+
+ if (!save.IsOpen()) {
+ LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data "
+ "made in current session will be saved.");
+ return;
+ }
+
+ save.Resize(sizeof(ProfileDataRaw));
+ save.WriteBytes(&raw, sizeof(ProfileDataRaw));
+}
+
}; // namespace Service::Account
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index bffd4cf4d..1cd2e51b2 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -5,8 +5,8 @@
#pragma once
#include <array>
+#include <optional>
-#include "boost/optional.hpp"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/result.h"
@@ -36,7 +36,7 @@ struct UUID {
}
// TODO(ogniK): Properly generate uuids based on RFC-4122
- const UUID& Generate();
+ static UUID Generate();
// Set the UUID to {0,0} to be considered an invalid user
void Invalidate() {
@@ -45,6 +45,15 @@ struct 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]);
+ }
};
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
@@ -81,18 +90,19 @@ static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase is an invalid size");
/// objects
class ProfileManager {
public:
- ProfileManager(); // TODO(ogniK): Load from system save
+ ProfileManager();
~ProfileManager();
ResultCode AddUser(const ProfileInfo& user);
ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username);
ResultCode CreateNewUser(UUID uuid, const std::string& username);
- boost::optional<std::size_t> GetUserIndex(const UUID& uuid) const;
- boost::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
- bool GetProfileBase(boost::optional<std::size_t> index, ProfileBase& profile) const;
+ std::optional<UUID> GetUser(std::size_t index) const;
+ std::optional<std::size_t> GetUserIndex(const UUID& uuid) const;
+ std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
+ bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const;
bool GetProfileBase(UUID uuid, ProfileBase& profile) const;
bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const;
- bool GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile,
+ bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
ProfileData& data) const;
bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const;
bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
@@ -100,6 +110,7 @@ public:
std::size_t GetUserCount() const;
std::size_t GetOpenUserCount() const;
bool UserExists(UUID uuid) const;
+ bool UserExistsIndex(std::size_t index) const;
void OpenUser(UUID uuid);
void CloseUser(UUID uuid);
UserIDArray GetOpenUsers() const;
@@ -108,11 +119,17 @@ public:
bool CanSystemRegisterUser() const;
+ bool RemoveUser(UUID uuid);
+ bool SetProfileBase(UUID uuid, const ProfileBase& profile_new);
+
private:
+ void ParseUserSaveFile();
+ void WriteUserSaveFile();
+ std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
+ bool RemoveProfileAtIndex(std::size_t index);
+
std::array<ProfileInfo, MAX_USERS> profiles{};
std::size_t user_count = 0;
- boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
- bool RemoveProfileAtIndex(std::size_t index);
UUID last_opened_user{INVALID_UUID};
};
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index ecf72ae24..59aafd616 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -4,11 +4,13 @@
#include <array>
#include <cinttypes>
+#include <cstring>
#include <stack>
#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/service/acc/profile_manager.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
@@ -26,6 +28,16 @@
namespace Service::AM {
+constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
+
+struct LaunchParameters {
+ u32_le magic;
+ u32_le is_account_selected;
+ u128 current_user;
+ INSERT_PADDING_BYTES(0x70);
+};
+static_assert(sizeof(LaunchParameters) == 0x88);
+
IWindowController::IWindowController() : ServiceFramework("IWindowController") {
// clang-format off
static const FunctionInfo functions[] = {
@@ -724,20 +736,23 @@ void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx
}
void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
- constexpr std::array<u8, 0x88> data{{
- 0xca, 0x97, 0x94, 0xc7, // Magic
- 1, 0, 0, 0, // IsAccountSelected (bool)
- 1, 0, 0, 0, // User Id (word 0)
- 0, 0, 0, 0, // User Id (word 1)
- 0, 0, 0, 0, // User Id (word 2)
- 0, 0, 0, 0 // User Id (word 3)
- }};
+ LaunchParameters params{};
- std::vector<u8> buffer(data.begin(), data.end());
+ params.magic = POP_LAUNCH_PARAMETER_MAGIC;
+ params.is_account_selected = 1;
+
+ Account::ProfileManager profile_manager{};
+ const auto uuid = profile_manager.GetUser(Settings::values.current_user);
+ ASSERT(uuid != std::nullopt);
+ params.current_user = uuid->uuid;
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
+
+ std::vector<u8> buffer(sizeof(LaunchParameters));
+ std::memcpy(buffer.data(), &params, buffer.size());
+
rb.PushIpcInterface<AM::IStorage>(buffer);
LOG_DEBUG(Service_AM, "called");
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 428069df2..54305cf05 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -24,8 +24,8 @@ namespace Service::AOC {
constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000;
-static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) {
- return (aoc & DLC_BASE_TITLE_ID_MASK) == base;
+static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
+ return (title_id & DLC_BASE_TITLE_ID_MASK) == base;
}
static std::vector<u64> AccumulateAOCTitleIDs() {
@@ -74,7 +74,7 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID();
rb.Push<u32>(static_cast<u32>(
std::count_if(add_on_content.begin(), add_on_content.end(),
- [&current](u64 tid) { return (tid & DLC_BASE_TITLE_ID_MASK) == current; })));
+ [current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); })));
}
void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index b06e65a77..4b4d1324f 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -108,9 +108,10 @@ void Controller_NPad::OnInit() {
styleset_changed_event =
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged");
- if (!IsControllerActivated())
+ if (!IsControllerActivated()) {
return;
- std::size_t controller{};
+ }
+
if (style.raw == 0) {
// We want to support all controllers
style.handheld.Assign(1);
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index ec32faf15..d607d985e 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -3,9 +3,13 @@
// Refer to the license.txt file included.
#include <memory>
+#include <fmt/format.h>
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/process.h"
#include "core/hle/service/ldr/ldr.h"
#include "core/hle/service/service.h"
+#include "core/loader/nro.h"
namespace Service::LDR {
@@ -59,16 +63,58 @@ public:
explicit RelocatableObject() : ServiceFramework{"ldr:ro"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "LoadNro"},
+ {0, &RelocatableObject::LoadNro, "LoadNro"},
{1, nullptr, "UnloadNro"},
- {2, nullptr, "LoadNrr"},
+ {2, &RelocatableObject::LoadNrr, "LoadNrr"},
{3, nullptr, "UnloadNrr"},
- {4, nullptr, "Initialize"},
+ {4, &RelocatableObject::Initialize, "Initialize"},
};
// clang-format on
RegisterHandlers(functions);
}
+
+ void LoadNrr(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_LDR, "(STUBBED) called");
+ }
+
+ void LoadNro(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ rp.Skip(2, false);
+ const VAddr nro_addr{rp.Pop<VAddr>()};
+ const u64 nro_size{rp.Pop<u64>()};
+ const VAddr bss_addr{rp.Pop<VAddr>()};
+ const u64 bss_size{rp.Pop<u64>()};
+
+ // Read NRO data from memory
+ std::vector<u8> nro_data(nro_size);
+ Memory::ReadBlock(nro_addr, nro_data.data(), nro_size);
+
+ // Load NRO as new executable module
+ const VAddr addr{*Core::CurrentProcess()->VMManager().FindFreeRegion(nro_size + bss_size)};
+ Loader::AppLoader_NRO::LoadNro(nro_data, fmt::format("nro-{:08x}", addr), addr);
+
+ // TODO(bunnei): This is an incomplete implementation. It was tested with Super Mario Party.
+ // It is currently missing:
+ // - Signature checks with LoadNRR
+ // - Checking if a module has already been loaded
+ // - Using/validating BSS, etc. params (these are used from NRO header instead)
+ // - Error checking
+ // - ...Probably other things
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(addr);
+ LOG_WARNING(Service_LDR, "(STUBBED) called");
+ }
+
+ void Initialize(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_LDR, "(STUBBED) called");
+ }
};
void InstallInterfaces(SM::ServiceManager& sm) {
diff --git a/src/core/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp
index e7fb5a419..c489da071 100644
--- a/src/core/hle/service/usb/usb.cpp
+++ b/src/core/hle/service/usb/usb.cpp
@@ -67,15 +67,15 @@ public:
explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "Unknown1"},
- {1, nullptr, "Unknown2"},
- {2, nullptr, "Unknown3"},
- {3, nullptr, "Unknown4"},
+ {0, nullptr, "Open"},
+ {1, nullptr, "Close"},
+ {2, nullptr, "Unknown1"},
+ {3, nullptr, "Populate"},
{4, nullptr, "PostBufferAsync"},
- {5, nullptr, "Unknown5"},
- {6, nullptr, "Unknown6"},
- {7, nullptr, "Unknown7"},
- {8, nullptr, "Unknown8"},
+ {5, nullptr, "GetXferReport"},
+ {6, nullptr, "Unknown2"},
+ {7, nullptr, "Unknown3"},
+ {8, nullptr, "Unknown4"},
};
// clang-format on
@@ -89,15 +89,15 @@ public:
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Unknown1"},
- {1, nullptr, "Unknown2"},
- {2, nullptr, "Unknown3"},
- {3, nullptr, "Unknown4"},
- {4, nullptr, "Unknown5"},
+ {1, nullptr, "SetInterface"},
+ {2, nullptr, "GetInterface"},
+ {3, nullptr, "GetAlternateInterface"},
+ {4, nullptr, "GetCurrentFrame"},
{5, nullptr, "CtrlXferAsync"},
- {6, nullptr, "Unknown6"},
+ {6, nullptr, "Unknown2"},
{7, nullptr, "GetCtrlXferReport"},
- {8, nullptr, "Unknown7"},
- {9, nullptr, "GetClientEpSession"},
+ {8, nullptr, "ResetDevice"},
+ {9, nullptr, "OpenUsbEp"},
};
// clang-format on
@@ -111,13 +111,14 @@ public:
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "BindClientProcess"},
- {1, nullptr, "Unknown1"},
- {2, nullptr, "Unknown2"},
- {3, nullptr, "Unknown3"},
- {4, nullptr, "Unknown4"},
- {5, nullptr, "Unknown5"},
+ {1, nullptr, "QueryAllInterfaces"},
+ {2, nullptr, "QueryAvailableInterfaces"},
+ {3, nullptr, "QueryAcquiredInterfaces"},
+ {4, nullptr, "CreateInterfaceAvailableEvent"},
+ {5, nullptr, "DestroyInterfaceAvailableEvent"},
{6, nullptr, "GetInterfaceStateChangeEvent"},
- {7, nullptr, "GetClientIfSession"},
+ {7, nullptr, "AcquireUsbIf"},
+ {8, nullptr, "Unknown1"},
};
// clang-format on
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 243b499f2..bc8e402a8 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -127,18 +127,23 @@ static constexpr u32 PageAlignSize(u32 size) {
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
}
-bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {
- // Read NSO header
- NroHeader nro_header{};
- if (sizeof(NroHeader) != file.ReadObject(&nro_header)) {
+/*static*/ bool AppLoader_NRO::LoadNro(const std::vector<u8>& data, const std::string& name,
+ VAddr load_base) {
+
+ if (data.size() < sizeof(NroHeader)) {
return {};
}
+
+ // Read NSO header
+ NroHeader nro_header{};
+ std::memcpy(&nro_header, data.data(), sizeof(NroHeader));
if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) {
return {};
}
// Build program image
- std::vector<u8> program_image = file.ReadBytes(PageAlignSize(nro_header.file_size));
+ std::vector<u8> program_image(PageAlignSize(nro_header.file_size));
+ std::memcpy(program_image.data(), data.data(), program_image.size());
if (program_image.size() != PageAlignSize(nro_header.file_size)) {
return {};
}
@@ -182,11 +187,15 @@ bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {
Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
// Register module with GDBStub
- GDBStub::RegisterModule(file.GetName(), load_base, load_base);
+ GDBStub::RegisterModule(name, load_base, load_base);
return true;
}
+bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {
+ return AppLoader_NRO::LoadNro(file.ReadAllBytes(), file.GetName(), load_base);
+}
+
ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
if (is_loaded) {
return ResultStatus::ErrorAlreadyLoaded;
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 50ee5a78a..3e6959302 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -5,6 +5,7 @@
#pragma once
#include <string>
+#include <vector>
#include "common/common_types.h"
#include "core/loader/linker.h"
#include "core/loader/loader.h"
@@ -40,6 +41,8 @@ public:
ResultStatus ReadTitle(std::string& title) override;
bool IsRomFSUpdatable() const override;
+ static bool LoadNro(const std::vector<u8>& data, const std::string& name, VAddr load_base);
+
private:
bool LoadNro(const FileSys::VfsFile& file, VAddr load_base);
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index 7d95816fe..c716a462b 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -74,10 +74,6 @@ double PerfStats::GetLastFrameTimeScale() {
}
void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
- // Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher
- // values increase the time needed to recover and limit framerate again after spikes.
- constexpr microseconds MAX_LAG_TIME_US = 25000us;
-
if (!Settings::values.use_frame_limit) {
return;
}
diff --git a/src/core/settings.h b/src/core/settings.h
index ca80718e2..b5aeff29b 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -114,7 +114,7 @@ struct Values {
// System
bool use_docked_mode;
bool enable_nfc;
- std::string username;
+ int current_user;
int language_index;
// Controls