aboutsummaryrefslogtreecommitdiff
path: root/src/core/hle/service
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/service')
-rw-r--r--src/core/hle/service/acc/acc.cpp6
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp4
-rw-r--r--src/core/hle/service/am/am.cpp164
-rw-r--r--src/core/hle/service/am/am.h10
-rw-r--r--src/core/hle/service/am/applets/applets.cpp14
-rw-r--r--src/core/hle/service/am/applets/applets.h2
-rw-r--r--src/core/hle/service/am/applets/error.cpp11
-rw-r--r--src/core/hle/service/am/applets/web_browser.cpp16
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp6
-rw-r--r--src/core/hle/service/apm/controller.cpp53
-rw-r--r--src/core/hle/service/apm/controller.h8
-rw-r--r--src/core/hle/service/audio/audout_u.cpp6
-rw-r--r--src/core/hle/service/audio/audren_u.cpp12
-rw-r--r--src/core/hle/service/audio/hwopus.cpp4
-rw-r--r--src/core/hle/service/bcat/backend/backend.cpp136
-rw-r--r--src/core/hle/service/bcat/backend/backend.h158
-rw-r--r--src/core/hle/service/bcat/backend/boxcat.cpp513
-rw-r--r--src/core/hle/service/bcat/backend/boxcat.h64
-rw-r--r--src/core/hle/service/bcat/bcat.cpp9
-rw-r--r--src/core/hle/service/bcat/bcat.h7
-rw-r--r--src/core/hle/service/bcat/module.cpp563
-rw-r--r--src/core/hle/service/bcat/module.h31
-rw-r--r--src/core/hle/service/btdrv/btdrv.cpp3
-rw-r--r--src/core/hle/service/btm/btm.cpp14
-rw-r--r--src/core/hle/service/es/es.cpp12
-rw-r--r--src/core/hle/service/fatal/fatal.cpp2
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp55
-rw-r--r--src/core/hle/service/filesystem/filesystem.h10
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp8
-rw-r--r--src/core/hle/service/friend/friend.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp5
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h1
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h1
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h1
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h1
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp82
-rw-r--r--src/core/hle/service/hid/controllers/npad.h10
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.h1
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h1
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h1
-rw-r--r--src/core/hle/service/hid/hid.cpp223
-rw-r--r--src/core/hle/service/hid/hid.h15
-rw-r--r--src/core/hle/service/ldr/ldr.cpp8
-rw-r--r--src/core/hle/service/lm/lm.cpp187
-rw-r--r--src/core/hle/service/lm/lm.h6
-rw-r--r--src/core/hle/service/lm/manager.cpp133
-rw-r--r--src/core/hle/service/lm/manager.h106
-rw-r--r--src/core/hle/service/mii/mii.cpp6
-rw-r--r--src/core/hle/service/nfp/nfp.cpp18
-rw-r--r--src/core/hle/service/nifm/nifm.cpp19
-rw-r--r--src/core/hle/service/nim/nim.cpp3
-rw-r--r--src/core/hle/service/ns/ns.cpp4
-rw-r--r--src/core/hle/service/ns/pl_u.cpp177
-rw-r--r--src/core/hle/service/ns/pl_u.h3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp4
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp38
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp2
-rw-r--r--src/core/hle/service/nvdrv/interface.cpp4
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp3
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp7
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h6
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp12
-rw-r--r--src/core/hle/service/service.cpp4
-rw-r--r--src/core/hle/service/time/time.cpp26
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp4
-rw-r--r--src/core/hle/service/vi/vi.cpp4
74 files changed, 2350 insertions, 703 deletions
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 0c0f7ed6e..7e3e311fb 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -84,7 +84,7 @@ protected:
LOG_ERROR(Service_ACC, "Failed to get profile base and data for user={}",
user_id.Format());
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultCode(-1)); // TODO(ogniK): Get actual error code
+ rb.Push(RESULT_UNKNOWN); // TODO(ogniK): Get actual error code
}
}
@@ -98,7 +98,7 @@ protected:
} else {
LOG_ERROR(Service_ACC, "Failed to get profile base for user={}", user_id.Format());
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultCode(-1)); // TODO(ogniK): Get actual error code
+ rb.Push(RESULT_UNKNOWN); // TODO(ogniK): Get actual error code
}
}
@@ -442,7 +442,7 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex
const auto user_list = profile_manager->GetAllUsers();
if (std::all_of(user_list.begin(), user_list.end(),
[](const auto& user) { return user.uuid == Common::INVALID_UUID; })) {
- rb.Push(ResultCode(-1)); // TODO(ogniK): Find the correct error code
+ rb.Push(RESULT_UNKNOWN); // TODO(ogniK): Find the correct error code
rb.PushRaw<u128>(Common::INVALID_UUID);
return;
}
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index 8f9986326..3e756e59e 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -31,8 +31,8 @@ struct ProfileDataRaw {
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_TOO_MANY_USERS(ErrorModule::Account, u32(-1));
+constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, u32(-2));
constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20);
constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "/system/save/8000000000000010/su/avators/";
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 797c9a06f..701f05019 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -31,6 +31,7 @@
#include "core/hle/service/am/tcap.h"
#include "core/hle/service/apm/controller.h"
#include "core/hle/service/apm/interface.h"
+#include "core/hle/service/bcat/backend/backend.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/nvflinger/nvflinger.h"
@@ -46,15 +47,20 @@ constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2};
constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3};
constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
-constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
+enum class LaunchParameterKind : u32 {
+ ApplicationSpecific = 1,
+ AccountPreselectedUser = 2,
+};
+
+constexpr u32 LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC = 0xC79497CA;
-struct LaunchParameters {
+struct LaunchParameterAccountPreselectedUser {
u32_le magic;
u32_le is_account_selected;
u128 current_user;
INSERT_PADDING_BYTES(0x70);
};
-static_assert(sizeof(LaunchParameters) == 0x88);
+static_assert(sizeof(LaunchParameterAccountPreselectedUser) == 0x88);
IWindowController::IWindowController(Core::System& system_)
: ServiceFramework("IWindowController"), system{system_} {
@@ -283,8 +289,8 @@ ISelfController::ISelfController(Core::System& system,
RegisterHandlers(functions);
auto& kernel = system.Kernel();
- launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
- "ISelfController:LaunchableEvent");
+ launchable_event =
+ Kernel::WritableEvent::CreateEventPair(kernel, "ISelfController:LaunchableEvent");
// This event is created by AM on the first time GetAccumulatedSuspendedTickChangedEvent() is
// called. Yuzu can just create it unconditionally, since it doesn't need to support multiple
@@ -292,7 +298,7 @@ ISelfController::ISelfController(Core::System& system,
// suspended if the event has previously been created by a call to
// GetAccumulatedSuspendedTickChangedEvent.
accumulated_suspended_tick_changed_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::Manual, "ISelfController:AccumulatedSuspendedTickChangedEvent");
+ kernel, "ISelfController:AccumulatedSuspendedTickChangedEvent");
accumulated_suspended_tick_changed_event.writable->Signal();
}
@@ -517,10 +523,10 @@ void ISelfController::GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequest
}
AppletMessageQueue::AppletMessageQueue(Kernel::KernelCore& kernel) {
- on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
- "AMMessageQueue:OnMessageRecieved");
- on_operation_mode_changed = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::Automatic, "AMMessageQueue:OperationModeChanged");
+ on_new_message =
+ Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OnMessageRecieved");
+ on_operation_mode_changed =
+ Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OperationModeChanged");
}
AppletMessageQueue::~AppletMessageQueue() = default;
@@ -985,7 +991,7 @@ void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx)
LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", static_cast<u32>(applet_id));
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultCode(-1));
+ rb.Push(RESULT_UNKNOWN);
return;
}
@@ -1021,7 +1027,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex
if (transfer_mem == nullptr) {
LOG_ERROR(Service_AM, "shared_mem is a nullpr for handle={:08X}", handle);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultCode(-1));
+ rb.Push(RESULT_UNKNOWN);
return;
}
@@ -1067,10 +1073,11 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
{71, nullptr, "RequestToReboot"},
{80, nullptr, "ExitAndRequestToShowThanksMessage"},
{90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"},
- {100, nullptr, "InitializeApplicationCopyrightFrameBuffer"},
- {101, nullptr, "SetApplicationCopyrightImage"},
- {102, nullptr, "SetApplicationCopyrightVisibility"},
- {110, nullptr, "QueryApplicationPlayStatistics"},
+ {100, &IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer, "InitializeApplicationCopyrightFrameBuffer"},
+ {101, &IApplicationFunctions::SetApplicationCopyrightImage, "SetApplicationCopyrightImage"},
+ {102, &IApplicationFunctions::SetApplicationCopyrightVisibility, "SetApplicationCopyrightVisibility"},
+ {110, &IApplicationFunctions::QueryApplicationPlayStatistics, "QueryApplicationPlayStatistics"},
+ {111, &IApplicationFunctions::QueryApplicationPlayStatisticsByUid, "QueryApplicationPlayStatisticsByUid"},
{120, nullptr, "ExecuteProgram"},
{121, nullptr, "ClearUserChannel"},
{122, nullptr, "UnpopToUserChannel"},
@@ -1085,7 +1092,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
auto& kernel = system.Kernel();
gpu_error_detected_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::Manual, "IApplicationFunctions:GpuErrorDetectedSystemEvent");
+ kernel, "IApplicationFunctions:GpuErrorDetectedSystemEvent");
}
IApplicationFunctions::~IApplicationFunctions() = default;
@@ -1097,6 +1104,31 @@ void IApplicationFunctions::EnableApplicationCrashReport(Kernel::HLERequestConte
rb.Push(RESULT_SUCCESS);
}
+void IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer(
+ Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void IApplicationFunctions::SetApplicationCopyrightImage(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void IApplicationFunctions::SetApplicationCopyrightVisibility(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto is_visible = rp.Pop<bool>();
+
+ LOG_WARNING(Service_AM, "(STUBBED) called, is_visible={}", is_visible);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(
Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
@@ -1128,26 +1160,56 @@ void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx
}
void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
+ IPC::RequestParser rp{ctx};
+ const auto kind = rp.PopEnum<LaunchParameterKind>();
+
+ LOG_DEBUG(Service_AM, "called, kind={:08X}", static_cast<u8>(kind));
+
+ if (kind == LaunchParameterKind::ApplicationSpecific && !launch_popped_application_specific) {
+ const auto backend = BCAT::CreateBackendFromSettings(system, [this](u64 tid) {
+ return system.GetFileSystemController().GetBCATDirectory(tid);
+ });
+ const auto build_id_full = system.GetCurrentProcessBuildID();
+ u64 build_id{};
+ std::memcpy(&build_id, build_id_full.data(), sizeof(u64));
+
+ const auto data =
+ backend->GetLaunchParameter({system.CurrentProcess()->GetTitleID(), build_id});
+
+ if (data.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<AM::IStorage>(*data);
+ launch_popped_application_specific = true;
+ return;
+ }
+ } else if (kind == LaunchParameterKind::AccountPreselectedUser &&
+ !launch_popped_account_preselect) {
+ LaunchParameterAccountPreselectedUser params{};
- LaunchParameters params{};
+ params.magic = LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC;
+ params.is_account_selected = 1;
- 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);
+ params.current_user = uuid->uuid;
- Account::ProfileManager profile_manager{};
- const auto uuid = profile_manager.GetUser(Settings::values.current_user);
- ASSERT(uuid);
- params.current_user = uuid->uuid;
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
- rb.Push(RESULT_SUCCESS);
+ std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser));
+ std::memcpy(buffer.data(), &params, buffer.size());
- std::vector<u8> buffer(sizeof(LaunchParameters));
- std::memcpy(buffer.data(), &params, buffer.size());
+ rb.PushIpcInterface<AM::IStorage>(buffer);
+ launch_popped_account_preselect = true;
+ return;
+ }
- rb.PushIpcInterface<AM::IStorage>(buffer);
+ LOG_ERROR(Service_AM, "Attempted to load launch parameter but none was found!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_NO_DATA_IN_CHANNEL);
}
void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(
@@ -1165,7 +1227,7 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]);
FileSys::SaveDataDescriptor descriptor{};
- descriptor.title_id = Core::CurrentProcess()->GetTitleID();
+ descriptor.title_id = system.CurrentProcess()->GetTitleID();
descriptor.user_id = user_id;
descriptor.type = FileSys::SaveDataType::SaveData;
const auto res = system.GetFileSystemController().CreateSaveData(
@@ -1274,12 +1336,16 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
}
void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) {
+ struct Parameters {
+ FileSys::SaveDataType type;
+ u128 user_id;
+ u64 new_normal_size;
+ u64 new_journal_size;
+ };
+ static_assert(sizeof(Parameters) == 40);
+
IPC::RequestParser rp{ctx};
- const auto type{rp.PopRaw<FileSys::SaveDataType>()};
- rp.Skip(1, false);
- const auto user_id{rp.PopRaw<u128>()};
- const auto new_normal_size{rp.PopRaw<u64>()};
- const auto new_journal_size{rp.PopRaw<u64>()};
+ const auto [type, user_id, new_normal_size, new_journal_size] = rp.PopRaw<Parameters>();
LOG_DEBUG(Service_AM,
"called with type={:02X}, user_id={:016X}{:016X}, new_normal={:016X}, "
@@ -1298,10 +1364,14 @@ void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) {
}
void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) {
+ struct Parameters {
+ FileSys::SaveDataType type;
+ u128 user_id;
+ };
+ static_assert(sizeof(Parameters) == 24);
+
IPC::RequestParser rp{ctx};
- const auto type{rp.PopRaw<FileSys::SaveDataType>()};
- rp.Skip(1, false);
- const auto user_id{rp.PopRaw<u128>()};
+ const auto [type, user_id] = rp.PopRaw<Parameters>();
LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type),
user_id[1], user_id[0]);
@@ -1315,6 +1385,22 @@ void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) {
rb.Push(size.journal);
}
+void IApplicationFunctions::QueryApplicationPlayStatistics(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(0);
+}
+
+void IApplicationFunctions::QueryApplicationPlayStatisticsByUid(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(0);
+}
+
void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index a3baeb673..06a65b5ed 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -147,6 +147,7 @@ private:
void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
+ Core::System& system;
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
Kernel::EventPair launchable_event;
Kernel::EventPair accumulated_suspended_tick_changed_event;
@@ -154,8 +155,6 @@ private:
u32 idle_time_detection_extension = 0;
u64 num_fatal_sections_entered = 0;
bool is_auto_sleep_disabled = false;
-
- Core::System& system;
};
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
@@ -253,8 +252,15 @@ private:
void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
void EndBlockingHomeButton(Kernel::HLERequestContext& ctx);
void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx);
+ void InitializeApplicationCopyrightFrameBuffer(Kernel::HLERequestContext& ctx);
+ void SetApplicationCopyrightImage(Kernel::HLERequestContext& ctx);
+ void SetApplicationCopyrightVisibility(Kernel::HLERequestContext& ctx);
+ void QueryApplicationPlayStatistics(Kernel::HLERequestContext& ctx);
+ void QueryApplicationPlayStatisticsByUid(Kernel::HLERequestContext& ctx);
void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx);
+ bool launch_popped_application_specific = false;
+ bool launch_popped_account_preselect = false;
Kernel::EventPair gpu_error_detected_event;
Core::System& system;
};
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index d2e35362f..673ad1f7f 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -24,12 +24,12 @@
namespace Service::AM::Applets {
AppletDataBroker::AppletDataBroker(Kernel::KernelCore& kernel) {
- state_changed_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:StateChangedEvent");
- pop_out_data_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:PopDataOutEvent");
+ state_changed_event =
+ Kernel::WritableEvent::CreateEventPair(kernel, "ILibraryAppletAccessor:StateChangedEvent");
+ pop_out_data_event =
+ Kernel::WritableEvent::CreateEventPair(kernel, "ILibraryAppletAccessor:PopDataOutEvent");
pop_interactive_out_data_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
+ kernel, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
}
AppletDataBroker::~AppletDataBroker() = default;
@@ -157,6 +157,10 @@ AppletManager::AppletManager(Core::System& system_) : system{system_} {}
AppletManager::~AppletManager() = default;
+const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const {
+ return frontend;
+}
+
void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
if (set.parental_controls != nullptr)
frontend.parental_controls = std::move(set.parental_controls);
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index 764c3418c..226be88b1 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -190,6 +190,8 @@ public:
explicit AppletManager(Core::System& system_);
~AppletManager();
+ const AppletFrontendSet& GetAppletFrontendSet() const;
+
void SetAppletFrontendSet(AppletFrontendSet set);
void SetDefaultAppletFrontendSet();
void SetDefaultAppletsIfMissing();
diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp
index a7db26725..eab0d42c9 100644
--- a/src/core/hle/service/am/applets/error.cpp
+++ b/src/core/hle/service/am/applets/error.cpp
@@ -20,9 +20,9 @@ namespace Service::AM::Applets {
struct ShowError {
u8 mode;
bool jump;
- INSERT_PADDING_BYTES(4);
+ INSERT_UNION_PADDING_BYTES(4);
bool use_64bit_error_code;
- INSERT_PADDING_BYTES(1);
+ INSERT_UNION_PADDING_BYTES(1);
u64 error_code_64;
u32 error_code_32;
};
@@ -32,7 +32,7 @@ static_assert(sizeof(ShowError) == 0x14, "ShowError has incorrect size.");
struct ShowErrorRecord {
u8 mode;
bool jump;
- INSERT_PADDING_BYTES(6);
+ INSERT_UNION_PADDING_BYTES(6);
u64 error_code_64;
u64 posix_time;
};
@@ -41,7 +41,7 @@ static_assert(sizeof(ShowErrorRecord) == 0x18, "ShowErrorRecord has incorrect si
struct SystemErrorArg {
u8 mode;
bool jump;
- INSERT_PADDING_BYTES(6);
+ INSERT_UNION_PADDING_BYTES(6);
u64 error_code_64;
std::array<char, 8> language_code;
std::array<char, 0x800> main_text;
@@ -52,7 +52,7 @@ static_assert(sizeof(SystemErrorArg) == 0x1018, "SystemErrorArg has incorrect si
struct ApplicationErrorArg {
u8 mode;
bool jump;
- INSERT_PADDING_BYTES(6);
+ INSERT_UNION_PADDING_BYTES(6);
u32 error_code;
std::array<char, 8> language_code;
std::array<char, 0x800> main_text;
@@ -65,6 +65,7 @@ union Error::ErrorArguments {
ShowErrorRecord error_record;
SystemErrorArg system_error;
ApplicationErrorArg application_error;
+ std::array<u8, 0x1018> raw{};
};
namespace {
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp
index 32283e819..5546ef6e8 100644
--- a/src/core/hle/service/am/applets/web_browser.cpp
+++ b/src/core/hle/service/am/applets/web_browser.cpp
@@ -337,7 +337,7 @@ void WebBrowser::ExecuteInternal() {
void WebBrowser::InitializeShop() {
if (frontend_e_commerce == nullptr) {
LOG_ERROR(Service_AM, "Missing ECommerce Applet frontend!");
- status = ResultCode(-1);
+ status = RESULT_UNKNOWN;
return;
}
@@ -353,7 +353,7 @@ void WebBrowser::InitializeShop() {
if (url == args.end()) {
LOG_ERROR(Service_AM, "Missing EShop Arguments URL for initialization!");
- status = ResultCode(-1);
+ status = RESULT_UNKNOWN;
return;
}
@@ -366,7 +366,7 @@ void WebBrowser::InitializeShop() {
// Less is missing info, More is malformed
if (split_query.size() != 2) {
LOG_ERROR(Service_AM, "EShop Arguments has more than one question mark, malformed");
- status = ResultCode(-1);
+ status = RESULT_UNKNOWN;
return;
}
@@ -390,7 +390,7 @@ void WebBrowser::InitializeShop() {
if (scene == shop_query.end()) {
LOG_ERROR(Service_AM, "No scene parameter was passed via shop query!");
- status = ResultCode(-1);
+ status = RESULT_UNKNOWN;
return;
}
@@ -406,7 +406,7 @@ void WebBrowser::InitializeShop() {
const auto target = target_map.find(scene->second);
if (target == target_map.end()) {
LOG_ERROR(Service_AM, "Scene for shop query is invalid! (scene={})", scene->second);
- status = ResultCode(-1);
+ status = RESULT_UNKNOWN;
return;
}
@@ -427,7 +427,7 @@ void WebBrowser::InitializeOffline() {
if (args.find(WebArgTLVType::DocumentPath) == args.end() ||
args.find(WebArgTLVType::DocumentKind) == args.end() ||
args.find(WebArgTLVType::ApplicationID) == args.end()) {
- status = ResultCode(-1);
+ status = RESULT_UNKNOWN;
LOG_ERROR(Service_AM, "Missing necessary parameters for initialization!");
}
@@ -476,7 +476,7 @@ void WebBrowser::InitializeOffline() {
offline_romfs = GetApplicationRomFS(system, title_id, type);
if (offline_romfs == nullptr) {
- status = ResultCode(-1);
+ status = RESULT_UNKNOWN;
LOG_ERROR(Service_AM, "Failed to find offline data for request!");
}
@@ -496,7 +496,7 @@ void WebBrowser::ExecuteShop() {
const auto check_optional_parameter = [this](const auto& p) {
if (!p.has_value()) {
LOG_ERROR(Service_AM, "Missing one or more necessary parameters for execution!");
- status = ResultCode(-1);
+ status = RESULT_UNKNOWN;
return false;
}
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index e9cf1e840..30076a53e 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -67,8 +67,8 @@ AOC_U::AOC_U(Core::System& system)
RegisterHandlers(functions);
auto& kernel = system.Kernel();
- aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
- "GetAddOnContentListChanged:Event");
+ aoc_change_event =
+ Kernel::WritableEvent::CreateEventPair(kernel, "GetAddOnContentListChanged:Event");
}
AOC_U::~AOC_U() = default;
@@ -131,7 +131,7 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
if (out.size() < offset) {
IPC::ResponseBuilder rb{ctx, 2};
// TODO(DarkLordZach): Find the correct error code.
- rb.Push(ResultCode(-1));
+ rb.Push(RESULT_UNKNOWN);
return;
}
diff --git a/src/core/hle/service/apm/controller.cpp b/src/core/hle/service/apm/controller.cpp
index 4376612eb..25a886238 100644
--- a/src/core/hle/service/apm/controller.cpp
+++ b/src/core/hle/service/apm/controller.cpp
@@ -2,6 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
+#include <array>
+#include <utility>
+
#include "common/logging/log.h"
#include "core/core_timing.h"
#include "core/hle/service/apm/controller.h"
@@ -9,11 +13,10 @@
namespace Service::APM {
-constexpr PerformanceConfiguration DEFAULT_PERFORMANCE_CONFIGURATION =
- PerformanceConfiguration::Config7;
+constexpr auto DEFAULT_PERFORMANCE_CONFIGURATION = PerformanceConfiguration::Config7;
Controller::Controller(Core::Timing::CoreTiming& core_timing)
- : core_timing(core_timing), configs{
+ : core_timing{core_timing}, configs{
{PerformanceMode::Handheld, DEFAULT_PERFORMANCE_CONFIGURATION},
{PerformanceMode::Docked, DEFAULT_PERFORMANCE_CONFIGURATION},
} {}
@@ -22,18 +25,35 @@ Controller::~Controller() = default;
void Controller::SetPerformanceConfiguration(PerformanceMode mode,
PerformanceConfiguration config) {
- static const std::map<PerformanceConfiguration, u32> PCONFIG_TO_SPEED_MAP{
- {PerformanceConfiguration::Config1, 1020}, {PerformanceConfiguration::Config2, 1020},
- {PerformanceConfiguration::Config3, 1224}, {PerformanceConfiguration::Config4, 1020},
- {PerformanceConfiguration::Config5, 1020}, {PerformanceConfiguration::Config6, 1224},
- {PerformanceConfiguration::Config7, 1020}, {PerformanceConfiguration::Config8, 1020},
- {PerformanceConfiguration::Config9, 1020}, {PerformanceConfiguration::Config10, 1020},
- {PerformanceConfiguration::Config11, 1020}, {PerformanceConfiguration::Config12, 1020},
- {PerformanceConfiguration::Config13, 1785}, {PerformanceConfiguration::Config14, 1785},
- {PerformanceConfiguration::Config15, 1020}, {PerformanceConfiguration::Config16, 1020},
- };
-
- SetClockSpeed(PCONFIG_TO_SPEED_MAP.find(config)->second);
+ static constexpr std::array<std::pair<PerformanceConfiguration, u32>, 16> config_to_speed{{
+ {PerformanceConfiguration::Config1, 1020},
+ {PerformanceConfiguration::Config2, 1020},
+ {PerformanceConfiguration::Config3, 1224},
+ {PerformanceConfiguration::Config4, 1020},
+ {PerformanceConfiguration::Config5, 1020},
+ {PerformanceConfiguration::Config6, 1224},
+ {PerformanceConfiguration::Config7, 1020},
+ {PerformanceConfiguration::Config8, 1020},
+ {PerformanceConfiguration::Config9, 1020},
+ {PerformanceConfiguration::Config10, 1020},
+ {PerformanceConfiguration::Config11, 1020},
+ {PerformanceConfiguration::Config12, 1020},
+ {PerformanceConfiguration::Config13, 1785},
+ {PerformanceConfiguration::Config14, 1785},
+ {PerformanceConfiguration::Config15, 1020},
+ {PerformanceConfiguration::Config16, 1020},
+ }};
+
+ const auto iter = std::find_if(config_to_speed.cbegin(), config_to_speed.cend(),
+ [config](const auto& entry) { return entry.first == config; });
+
+ if (iter == config_to_speed.cend()) {
+ LOG_ERROR(Service_APM, "Invalid performance configuration value provided: {}",
+ static_cast<u32>(config));
+ return;
+ }
+
+ SetClockSpeed(iter->second);
configs.insert_or_assign(mode, config);
}
@@ -48,7 +68,7 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) {
BOOST_MODE_TO_CONFIG_MAP.at(static_cast<u32>(mode)));
}
-PerformanceMode Controller::GetCurrentPerformanceMode() {
+PerformanceMode Controller::GetCurrentPerformanceMode() const {
return Settings::values.use_docked_mode ? PerformanceMode::Docked : PerformanceMode::Handheld;
}
@@ -63,6 +83,7 @@ PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(Performa
void Controller::SetClockSpeed(u32 mhz) {
LOG_INFO(Service_APM, "called, mhz={:08X}", mhz);
// TODO(DarkLordZach): Actually signal core_timing to change clock speed.
+ // TODO(Rodrigo): Remove [[maybe_unused]] when core_timing is used.
}
} // namespace Service::APM
diff --git a/src/core/hle/service/apm/controller.h b/src/core/hle/service/apm/controller.h
index 8ac80eaea..af0c4cd34 100644
--- a/src/core/hle/service/apm/controller.h
+++ b/src/core/hle/service/apm/controller.h
@@ -50,21 +50,21 @@ enum class PerformanceMode : u8 {
// system during times of high load -- this simply maps to different PerformanceConfigs to use.
class Controller {
public:
- Controller(Core::Timing::CoreTiming& core_timing);
+ explicit Controller(Core::Timing::CoreTiming& core_timing);
~Controller();
void SetPerformanceConfiguration(PerformanceMode mode, PerformanceConfiguration config);
void SetFromCpuBoostMode(CpuBoostMode mode);
- PerformanceMode GetCurrentPerformanceMode();
+ PerformanceMode GetCurrentPerformanceMode() const;
PerformanceConfiguration GetCurrentPerformanceConfiguration(PerformanceMode mode);
private:
void SetClockSpeed(u32 mhz);
- std::map<PerformanceMode, PerformanceConfiguration> configs;
+ [[maybe_unused]] Core::Timing::CoreTiming& core_timing;
- Core::Timing::CoreTiming& core_timing;
+ std::map<PerformanceMode, PerformanceConfiguration> configs;
};
} // namespace Service::APM
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index fb84a8f13..6a29377e3 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -65,8 +65,8 @@ public:
RegisterHandlers(functions);
// This is the event handle used to check if the audio buffer was released
- buffer_event = Kernel::WritableEvent::CreateEventPair(
- system.Kernel(), Kernel::ResetType::Manual, "IAudioOutBufferReleased");
+ buffer_event =
+ Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioOutBufferReleased");
stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
audio_params.channel_count, std::move(unique_name),
@@ -205,7 +205,7 @@ private:
AudioCore::StreamPtr stream;
std::string device_name;
- AudoutParams audio_params{};
+ [[maybe_unused]] AudoutParams audio_params {};
/// This is the event handle used to check if the audio buffer was released
Kernel::EventPair buffer_event;
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index f162249ed..4ea7ade6e 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -47,8 +47,8 @@ public:
// clang-format on
RegisterHandlers(functions);
- system_event = Kernel::WritableEvent::CreateEventPair(
- system.Kernel(), Kernel::ResetType::Manual, "IAudioRenderer:SystemEvent");
+ system_event =
+ Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioRenderer:SystemEvent");
renderer = std::make_unique<AudioCore::AudioRenderer>(
system.CoreTiming(), audren_params, system_event.writable, instance_number);
}
@@ -180,17 +180,17 @@ public:
RegisterHandlers(functions);
auto& kernel = system.Kernel();
- buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
- "IAudioOutBufferReleasedEvent");
+ buffer_event =
+ Kernel::WritableEvent::CreateEventPair(kernel, "IAudioOutBufferReleasedEvent");
// Should be similar to audio_output_device_switch_event
audio_input_device_switch_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::Automatic, "IAudioDevice:AudioInputDeviceSwitchedEvent");
+ kernel, "IAudioDevice:AudioInputDeviceSwitchedEvent");
// Should only be signalled when an audio output device has been changed, example: speaker
// to headset
audio_output_device_switch_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::Automatic, "IAudioDevice:AudioOutputDeviceSwitchedEvent");
+ kernel, "IAudioDevice:AudioOutputDeviceSwitchedEvent");
}
private:
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index cb4a1160d..cb839e4a2 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -80,7 +80,7 @@ private:
LOG_ERROR(Audio, "Failed to decode opus data");
IPC::ResponseBuilder rb{ctx, 2};
// TODO(ogniK): Use correct error code
- rb.Push(ResultCode(-1));
+ rb.Push(RESULT_UNKNOWN);
return;
}
@@ -278,7 +278,7 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error);
IPC::ResponseBuilder rb{ctx, 2};
// TODO(ogniK): Use correct error code
- rb.Push(ResultCode(-1));
+ rb.Push(RESULT_UNKNOWN);
return;
}
diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp
new file mode 100644
index 000000000..dec0849b8
--- /dev/null
+++ b/src/core/hle/service/bcat/backend/backend.cpp
@@ -0,0 +1,136 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/hex_util.h"
+#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/hle/lock.h"
+#include "core/hle/service/bcat/backend/backend.h"
+
+namespace Service::BCAT {
+
+ProgressServiceBackend::ProgressServiceBackend(Kernel::KernelCore& kernel,
+ std::string_view event_name) {
+ event = Kernel::WritableEvent::CreateEventPair(
+ kernel, std::string("ProgressServiceBackend:UpdateEvent:").append(event_name));
+}
+
+Kernel::SharedPtr<Kernel::ReadableEvent> ProgressServiceBackend::GetEvent() const {
+ return event.readable;
+}
+
+DeliveryCacheProgressImpl& ProgressServiceBackend::GetImpl() {
+ return impl;
+}
+
+void ProgressServiceBackend::SetNeedHLELock(bool need) {
+ need_hle_lock = need;
+}
+
+void ProgressServiceBackend::SetTotalSize(u64 size) {
+ impl.total_bytes = size;
+ SignalUpdate();
+}
+
+void ProgressServiceBackend::StartConnecting() {
+ impl.status = DeliveryCacheProgressImpl::Status::Connecting;
+ SignalUpdate();
+}
+
+void ProgressServiceBackend::StartProcessingDataList() {
+ impl.status = DeliveryCacheProgressImpl::Status::ProcessingDataList;
+ SignalUpdate();
+}
+
+void ProgressServiceBackend::StartDownloadingFile(std::string_view dir_name,
+ std::string_view file_name, u64 file_size) {
+ impl.status = DeliveryCacheProgressImpl::Status::Downloading;
+ impl.current_downloaded_bytes = 0;
+ impl.current_total_bytes = file_size;
+ std::memcpy(impl.current_directory.data(), dir_name.data(),
+ std::min<u64>(dir_name.size(), 0x31ull));
+ std::memcpy(impl.current_file.data(), file_name.data(),
+ std::min<u64>(file_name.size(), 0x31ull));
+ SignalUpdate();
+}
+
+void ProgressServiceBackend::UpdateFileProgress(u64 downloaded) {
+ impl.current_downloaded_bytes = downloaded;
+ SignalUpdate();
+}
+
+void ProgressServiceBackend::FinishDownloadingFile() {
+ impl.total_downloaded_bytes += impl.current_total_bytes;
+ SignalUpdate();
+}
+
+void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) {
+ impl.status = DeliveryCacheProgressImpl::Status::Committing;
+ impl.current_file.fill(0);
+ impl.current_downloaded_bytes = 0;
+ impl.current_total_bytes = 0;
+ std::memcpy(impl.current_directory.data(), dir_name.data(),
+ std::min<u64>(dir_name.size(), 0x31ull));
+ SignalUpdate();
+}
+
+void ProgressServiceBackend::FinishDownload(ResultCode result) {
+ impl.total_downloaded_bytes = impl.total_bytes;
+ impl.status = DeliveryCacheProgressImpl::Status::Done;
+ impl.result = result;
+ SignalUpdate();
+}
+
+void ProgressServiceBackend::SignalUpdate() const {
+ if (need_hle_lock) {
+ std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
+ event.writable->Signal();
+ } else {
+ event.writable->Signal();
+ }
+}
+
+Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {}
+
+Backend::~Backend() = default;
+
+NullBackend::NullBackend(DirectoryGetter getter) : Backend(std::move(getter)) {}
+
+NullBackend::~NullBackend() = default;
+
+bool NullBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) {
+ LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id,
+ title.build_id);
+
+ progress.FinishDownload(RESULT_SUCCESS);
+ return true;
+}
+
+bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name,
+ ProgressServiceBackend& progress) {
+ LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}, name={}", title.title_id,
+ title.build_id, name);
+
+ progress.FinishDownload(RESULT_SUCCESS);
+ return true;
+}
+
+bool NullBackend::Clear(u64 title_id) {
+ LOG_DEBUG(Service_BCAT, "called, title_id={:016X}");
+
+ return true;
+}
+
+void NullBackend::SetPassphrase(u64 title_id, const Passphrase& passphrase) {
+ LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase = {}", title_id,
+ Common::HexToString(passphrase));
+}
+
+std::optional<std::vector<u8>> NullBackend::GetLaunchParameter(TitleIDVersion title) {
+ LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id,
+ title.build_id);
+ return std::nullopt;
+}
+
+} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h
new file mode 100644
index 000000000..ea4b16ad0
--- /dev/null
+++ b/src/core/hle/service/bcat/backend/backend.h
@@ -0,0 +1,158 @@
+// Copyright 2019 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 <string_view>
+
+#include "common/common_types.h"
+#include "core/file_sys/vfs_types.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/writable_event.h"
+#include "core/hle/result.h"
+
+namespace Core {
+class System;
+}
+
+namespace Kernel {
+class KernelCore;
+}
+
+namespace Service::BCAT {
+
+struct DeliveryCacheProgressImpl;
+
+using DirectoryGetter = std::function<FileSys::VirtualDir(u64)>;
+using Passphrase = std::array<u8, 0x20>;
+
+struct TitleIDVersion {
+ u64 title_id;
+ u64 build_id;
+};
+
+using DirectoryName = std::array<char, 0x20>;
+using FileName = std::array<char, 0x20>;
+
+struct DeliveryCacheProgressImpl {
+ enum class Status : s32 {
+ None = 0x0,
+ Queued = 0x1,
+ Connecting = 0x2,
+ ProcessingDataList = 0x3,
+ Downloading = 0x4,
+ Committing = 0x5,
+ Done = 0x9,
+ };
+
+ Status status;
+ ResultCode result = RESULT_SUCCESS;
+ DirectoryName current_directory;
+ FileName current_file;
+ s64 current_downloaded_bytes; ///< Bytes downloaded on current file.
+ s64 current_total_bytes; ///< Bytes total on current file.
+ s64 total_downloaded_bytes; ///< Bytes downloaded on overall download.
+ s64 total_bytes; ///< Bytes total on overall download.
+ INSERT_PADDING_BYTES(
+ 0x198); ///< Appears to be unused in official code, possibly reserved for future use.
+};
+static_assert(sizeof(DeliveryCacheProgressImpl) == 0x200,
+ "DeliveryCacheProgressImpl has incorrect size.");
+
+// A class to manage the signalling to the game about BCAT download progress.
+// Some of this class is implemented in module.cpp to avoid exposing the implementation structure.
+class ProgressServiceBackend {
+ friend class IBcatService;
+
+public:
+ // Clients should call this with true if any of the functions are going to be called from a
+ // non-HLE thread and this class need to lock the hle mutex. (default is false)
+ void SetNeedHLELock(bool need);
+
+ // Sets the number of bytes total in the entire download.
+ void SetTotalSize(u64 size);
+
+ // Notifies the application that the backend has started connecting to the server.
+ void StartConnecting();
+ // Notifies the application that the backend has begun accumulating and processing metadata.
+ void StartProcessingDataList();
+
+ // Notifies the application that a file is starting to be downloaded.
+ void StartDownloadingFile(std::string_view dir_name, std::string_view file_name, u64 file_size);
+ // Updates the progress of the current file to the size passed.
+ void UpdateFileProgress(u64 downloaded);
+ // Notifies the application that the current file has completed download.
+ void FinishDownloadingFile();
+
+ // Notifies the application that all files in this directory have completed and are being
+ // finalized.
+ void CommitDirectory(std::string_view dir_name);
+
+ // Notifies the application that the operation completed with result code result.
+ void FinishDownload(ResultCode result);
+
+private:
+ explicit ProgressServiceBackend(Kernel::KernelCore& kernel, std::string_view event_name);
+
+ Kernel::SharedPtr<Kernel::ReadableEvent> GetEvent() const;
+ DeliveryCacheProgressImpl& GetImpl();
+
+ void SignalUpdate() const;
+
+ DeliveryCacheProgressImpl impl{};
+ Kernel::EventPair event;
+ bool need_hle_lock = false;
+};
+
+// A class representing an abstract backend for BCAT functionality.
+class Backend {
+public:
+ explicit Backend(DirectoryGetter getter);
+ virtual ~Backend();
+
+ // Called when the backend is needed to synchronize the data for the game with title ID and
+ // version in title. A ProgressServiceBackend object is provided to alert the application of
+ // status.
+ virtual bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) = 0;
+ // Very similar to Synchronize, but only for the directory provided. Backends should not alter
+ // the data for any other directories.
+ virtual bool SynchronizeDirectory(TitleIDVersion title, std::string name,
+ ProgressServiceBackend& progress) = 0;
+
+ // Removes all cached data associated with title id provided.
+ virtual bool Clear(u64 title_id) = 0;
+
+ // Sets the BCAT Passphrase to be used with the associated title ID.
+ virtual void SetPassphrase(u64 title_id, const Passphrase& passphrase) = 0;
+
+ // Gets the launch parameter used by AM associated with the title ID and version provided.
+ virtual std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) = 0;
+
+protected:
+ DirectoryGetter dir_getter;
+};
+
+// A backend of BCAT that provides no operation.
+class NullBackend : public Backend {
+public:
+ explicit NullBackend(DirectoryGetter getter);
+ ~NullBackend() override;
+
+ bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override;
+ bool SynchronizeDirectory(TitleIDVersion title, std::string name,
+ ProgressServiceBackend& progress) override;
+
+ bool Clear(u64 title_id) override;
+
+ void SetPassphrase(u64 title_id, const Passphrase& passphrase) override;
+
+ std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override;
+};
+
+std::unique_ptr<Backend> CreateBackendFromSettings(Core::System& system, DirectoryGetter getter);
+
+} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp
new file mode 100644
index 000000000..67e39a5c4
--- /dev/null
+++ b/src/core/hle/service/bcat/backend/boxcat.cpp
@@ -0,0 +1,513 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <fmt/ostream.h>
+#include <httplib.h>
+#include <json.hpp>
+#include <mbedtls/sha256.h>
+#include "common/hex_util.h"
+#include "common/logging/backend.h"
+#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs_libzip.h"
+#include "core/file_sys/vfs_vector.h"
+#include "core/frontend/applets/error.h"
+#include "core/hle/service/am/applets/applets.h"
+#include "core/hle/service/bcat/backend/boxcat.h"
+#include "core/settings.h"
+
+namespace {
+
+// Prevents conflicts with windows macro called CreateFile
+FileSys::VirtualFile VfsCreateFileWrap(FileSys::VirtualDir dir, std::string_view name) {
+ return dir->CreateFile(name);
+}
+
+// Prevents conflicts with windows macro called DeleteFile
+bool VfsDeleteFileWrap(FileSys::VirtualDir dir, std::string_view name) {
+ return dir->DeleteFile(name);
+}
+
+} // Anonymous namespace
+
+namespace Service::BCAT {
+
+constexpr ResultCode ERROR_GENERAL_BCAT_FAILURE{ErrorModule::BCAT, 1};
+
+constexpr char BOXCAT_HOSTNAME[] = "api.yuzu-emu.org";
+
+// Formatted using fmt with arg[0] = hex title id
+constexpr char BOXCAT_PATHNAME_DATA[] = "/game-assets/{:016X}/boxcat";
+constexpr char BOXCAT_PATHNAME_LAUNCHPARAM[] = "/game-assets/{:016X}/launchparam";
+
+constexpr char BOXCAT_PATHNAME_EVENTS[] = "/game-assets/boxcat/events";
+
+constexpr char BOXCAT_API_VERSION[] = "1";
+constexpr char BOXCAT_CLIENT_TYPE[] = "yuzu";
+
+// HTTP status codes for Boxcat
+enum class ResponseStatus {
+ Ok = 200, ///< Operation completed successfully.
+ BadClientVersion = 301, ///< The Boxcat-Client-Version doesn't match the server.
+ NoUpdate = 304, ///< The digest provided would match the new data, no need to update.
+ NoMatchTitleId = 404, ///< The title ID provided doesn't have a boxcat implementation.
+ NoMatchBuildId = 406, ///< The build ID provided is blacklisted (potentially because of format
+ ///< issues or whatnot) and has no data.
+};
+
+enum class DownloadResult {
+ Success = 0,
+ NoResponse,
+ GeneralWebError,
+ NoMatchTitleId,
+ NoMatchBuildId,
+ InvalidContentType,
+ GeneralFSError,
+ BadClientVersion,
+};
+
+constexpr std::array<const char*, 8> DOWNLOAD_RESULT_LOG_MESSAGES{
+ "Success",
+ "There was no response from the server.",
+ "There was a general web error code returned from the server.",
+ "The title ID of the current game doesn't have a boxcat implementation. If you believe an "
+ "implementation should be added, contact yuzu support.",
+ "The build ID of the current version of the game is marked as incompatible with the current "
+ "BCAT distribution. Try upgrading or downgrading your game version or contacting yuzu support.",
+ "The content type of the web response was invalid.",
+ "There was a general filesystem error while saving the zip file.",
+ "The server is either too new or too old to serve the request. Try using the latest version of "
+ "an official release of yuzu.",
+};
+
+std::ostream& operator<<(std::ostream& os, DownloadResult result) {
+ return os << DOWNLOAD_RESULT_LOG_MESSAGES.at(static_cast<std::size_t>(result));
+}
+
+constexpr u32 PORT = 443;
+constexpr u32 TIMEOUT_SECONDS = 30;
+[[maybe_unused]] constexpr u64 VFS_COPY_BLOCK_SIZE = 1ULL << 24; // 4MB
+
+namespace {
+
+std::string GetBINFilePath(u64 title_id) {
+ return fmt::format("{}bcat/{:016X}/launchparam.bin",
+ FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id);
+}
+
+std::string GetZIPFilePath(u64 title_id) {
+ return fmt::format("{}bcat/{:016X}/data.zip",
+ FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id);
+}
+
+// If the error is something the user should know about (build ID mismatch, bad client version),
+// display an error.
+void HandleDownloadDisplayResult(const AM::Applets::AppletManager& applet_manager,
+ DownloadResult res) {
+ if (res == DownloadResult::Success || res == DownloadResult::NoResponse ||
+ res == DownloadResult::GeneralWebError || res == DownloadResult::GeneralFSError ||
+ res == DownloadResult::NoMatchTitleId || res == DownloadResult::InvalidContentType) {
+ return;
+ }
+
+ const auto& frontend{applet_manager.GetAppletFrontendSet()};
+ frontend.error->ShowCustomErrorText(
+ RESULT_UNKNOWN, "There was an error while attempting to use Boxcat.",
+ DOWNLOAD_RESULT_LOG_MESSAGES[static_cast<std::size_t>(res)], [] {});
+}
+
+bool VfsRawCopyProgress(FileSys::VirtualFile src, FileSys::VirtualFile dest,
+ std::string_view dir_name, ProgressServiceBackend& progress,
+ std::size_t block_size = 0x1000) {
+ if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
+ return false;
+ if (!dest->Resize(src->GetSize()))
+ return false;
+
+ progress.StartDownloadingFile(dir_name, src->GetName(), src->GetSize());
+
+ 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);
+
+ if (src->Read(temp.data(), read, i) != read) {
+ return false;
+ }
+
+ if (dest->Write(temp.data(), read, i) != read) {
+ return false;
+ }
+
+ progress.UpdateFileProgress(i);
+ }
+
+ progress.FinishDownloadingFile();
+
+ return true;
+}
+
+bool VfsRawCopyDProgressSingle(FileSys::VirtualDir src, FileSys::VirtualDir dest,
+ ProgressServiceBackend& progress, std::size_t block_size = 0x1000) {
+ if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
+ return false;
+
+ for (const auto& file : src->GetFiles()) {
+ const auto out_file = VfsCreateFileWrap(dest, file->GetName());
+ if (!VfsRawCopyProgress(file, out_file, src->GetName(), progress, block_size)) {
+ return false;
+ }
+ }
+ progress.CommitDirectory(src->GetName());
+
+ return true;
+}
+
+bool VfsRawCopyDProgress(FileSys::VirtualDir src, FileSys::VirtualDir dest,
+ ProgressServiceBackend& progress, std::size_t block_size = 0x1000) {
+ if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
+ return false;
+
+ for (const auto& dir : src->GetSubdirectories()) {
+ const auto out = dest->CreateSubdirectory(dir->GetName());
+ if (!VfsRawCopyDProgressSingle(dir, out, progress, block_size)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // Anonymous namespace
+
+class Boxcat::Client {
+public:
+ Client(std::string path, u64 title_id, u64 build_id)
+ : path(std::move(path)), title_id(title_id), build_id(build_id) {}
+
+ DownloadResult DownloadDataZip() {
+ return DownloadInternal(fmt::format(BOXCAT_PATHNAME_DATA, title_id), TIMEOUT_SECONDS,
+ "application/zip");
+ }
+
+ DownloadResult DownloadLaunchParam() {
+ return DownloadInternal(fmt::format(BOXCAT_PATHNAME_LAUNCHPARAM, title_id),
+ TIMEOUT_SECONDS / 3, "application/octet-stream");
+ }
+
+private:
+ DownloadResult DownloadInternal(const std::string& resolved_path, u32 timeout_seconds,
+ const std::string& content_type_name) {
+ if (client == nullptr) {
+ client = std::make_unique<httplib::SSLClient>(BOXCAT_HOSTNAME, PORT, timeout_seconds);
+ }
+
+ httplib::Headers headers{
+ {std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)},
+ {std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)},
+ {std::string("Game-Build-Id"), fmt::format("{:016X}", build_id)},
+ };
+
+ if (FileUtil::Exists(path)) {
+ FileUtil::IOFile file{path, "rb"};
+ if (file.IsOpen()) {
+ std::vector<u8> bytes(file.GetSize());
+ file.ReadBytes(bytes.data(), bytes.size());
+ const auto digest = DigestFile(bytes);
+ headers.insert({std::string("If-None-Match"), Common::HexToString(digest, false)});
+ }
+ }
+
+ const auto response = client->Get(resolved_path.c_str(), headers);
+ if (response == nullptr)
+ return DownloadResult::NoResponse;
+
+ if (response->status == static_cast<int>(ResponseStatus::NoUpdate))
+ return DownloadResult::Success;
+ if (response->status == static_cast<int>(ResponseStatus::BadClientVersion))
+ return DownloadResult::BadClientVersion;
+ if (response->status == static_cast<int>(ResponseStatus::NoMatchTitleId))
+ return DownloadResult::NoMatchTitleId;
+ if (response->status == static_cast<int>(ResponseStatus::NoMatchBuildId))
+ return DownloadResult::NoMatchBuildId;
+ if (response->status != static_cast<int>(ResponseStatus::Ok))
+ return DownloadResult::GeneralWebError;
+
+ const auto content_type = response->headers.find("content-type");
+ if (content_type == response->headers.end() ||
+ content_type->second.find(content_type_name) == std::string::npos) {
+ return DownloadResult::InvalidContentType;
+ }
+
+ FileUtil::CreateFullPath(path);
+ FileUtil::IOFile file{path, "wb"};
+ if (!file.IsOpen())
+ return DownloadResult::GeneralFSError;
+ if (!file.Resize(response->body.size()))
+ return DownloadResult::GeneralFSError;
+ if (file.WriteBytes(response->body.data(), response->body.size()) != response->body.size())
+ return DownloadResult::GeneralFSError;
+
+ return DownloadResult::Success;
+ }
+
+ using Digest = std::array<u8, 0x20>;
+ static Digest DigestFile(std::vector<u8> bytes) {
+ Digest out{};
+ mbedtls_sha256_ret(bytes.data(), bytes.size(), out.data(), 0);
+ return out;
+ }
+
+ std::unique_ptr<httplib::Client> client;
+ std::string path;
+ u64 title_id;
+ u64 build_id;
+};
+
+Boxcat::Boxcat(AM::Applets::AppletManager& applet_manager_, DirectoryGetter getter)
+ : Backend(std::move(getter)), applet_manager{applet_manager_} {}
+
+Boxcat::~Boxcat() = default;
+
+void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGetter dir_getter,
+ TitleIDVersion title, ProgressServiceBackend& progress,
+ std::optional<std::string> dir_name = {}) {
+ progress.SetNeedHLELock(true);
+
+ if (Settings::values.bcat_boxcat_local) {
+ LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping download.");
+ const auto dir = dir_getter(title.title_id);
+ if (dir)
+ progress.SetTotalSize(dir->GetSize());
+ progress.FinishDownload(RESULT_SUCCESS);
+ return;
+ }
+
+ const auto zip_path{GetZIPFilePath(title.title_id)};
+ Boxcat::Client client{zip_path, title.title_id, title.build_id};
+
+ progress.StartConnecting();
+
+ const auto res = client.DownloadDataZip();
+ if (res != DownloadResult::Success) {
+ LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
+
+ if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
+ FileUtil::Delete(zip_path);
+ }
+
+ HandleDownloadDisplayResult(applet_manager, res);
+ progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
+ return;
+ }
+
+ progress.StartProcessingDataList();
+
+ FileUtil::IOFile zip{zip_path, "rb"};
+ const auto size = zip.GetSize();
+ std::vector<u8> bytes(size);
+ if (!zip.IsOpen() || size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) {
+ LOG_ERROR(Service_BCAT, "Boxcat failed to read ZIP file at path '{}'!", zip_path);
+ progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
+ return;
+ }
+
+ const auto extracted = FileSys::ExtractZIP(std::make_shared<FileSys::VectorVfsFile>(bytes));
+ if (extracted == nullptr) {
+ LOG_ERROR(Service_BCAT, "Boxcat failed to extract ZIP file!");
+ progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
+ return;
+ }
+
+ if (dir_name == std::nullopt) {
+ progress.SetTotalSize(extracted->GetSize());
+
+ const auto target_dir = dir_getter(title.title_id);
+ if (target_dir == nullptr || !VfsRawCopyDProgress(extracted, target_dir, progress)) {
+ LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!");
+ progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
+ return;
+ }
+ } else {
+ const auto target_dir = dir_getter(title.title_id);
+ if (target_dir == nullptr) {
+ LOG_ERROR(Service_BCAT, "Boxcat failed to get directory for title ID!");
+ progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
+ return;
+ }
+
+ const auto target_sub = target_dir->GetSubdirectory(*dir_name);
+ const auto source_sub = extracted->GetSubdirectory(*dir_name);
+
+ progress.SetTotalSize(source_sub->GetSize());
+
+ std::vector<std::string> filenames;
+ {
+ const auto files = target_sub->GetFiles();
+ std::transform(files.begin(), files.end(), std::back_inserter(filenames),
+ [](const auto& vfile) { return vfile->GetName(); });
+ }
+
+ for (const auto& filename : filenames) {
+ VfsDeleteFileWrap(target_sub, filename);
+ }
+
+ if (target_sub == nullptr || source_sub == nullptr ||
+ !VfsRawCopyDProgressSingle(source_sub, target_sub, progress)) {
+ LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!");
+ progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
+ return;
+ }
+ }
+
+ progress.FinishDownload(RESULT_SUCCESS);
+}
+
+bool Boxcat::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) {
+ is_syncing.exchange(true);
+
+ std::thread([this, title, &progress] {
+ SynchronizeInternal(applet_manager, dir_getter, title, progress);
+ })
+ .detach();
+
+ return true;
+}
+
+bool Boxcat::SynchronizeDirectory(TitleIDVersion title, std::string name,
+ ProgressServiceBackend& progress) {
+ is_syncing.exchange(true);
+
+ std::thread([this, title, name, &progress] {
+ SynchronizeInternal(applet_manager, dir_getter, title, progress, name);
+ })
+ .detach();
+
+ return true;
+}
+
+bool Boxcat::Clear(u64 title_id) {
+ if (Settings::values.bcat_boxcat_local) {
+ LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping clear.");
+ return true;
+ }
+
+ const auto dir = dir_getter(title_id);
+
+ std::vector<std::string> dirnames;
+
+ for (const auto& subdir : dir->GetSubdirectories())
+ dirnames.push_back(subdir->GetName());
+
+ for (const auto& subdir : dirnames) {
+ if (!dir->DeleteSubdirectoryRecursive(subdir))
+ return false;
+ }
+
+ return true;
+}
+
+void Boxcat::SetPassphrase(u64 title_id, const Passphrase& passphrase) {
+ LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id,
+ Common::HexToString(passphrase));
+}
+
+std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title) {
+ const auto path{GetBINFilePath(title.title_id)};
+
+ if (Settings::values.bcat_boxcat_local) {
+ LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping download.");
+ } else {
+ Boxcat::Client client{path, title.title_id, title.build_id};
+
+ const auto res = client.DownloadLaunchParam();
+ if (res != DownloadResult::Success) {
+ LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
+
+ if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
+ FileUtil::Delete(path);
+ }
+
+ HandleDownloadDisplayResult(applet_manager, res);
+ return std::nullopt;
+ }
+ }
+
+ FileUtil::IOFile bin{path, "rb"};
+ const auto size = bin.GetSize();
+ std::vector<u8> bytes(size);
+ if (!bin.IsOpen() || size == 0 || bin.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) {
+ LOG_ERROR(Service_BCAT, "Boxcat failed to read launch parameter binary at path '{}'!",
+ path);
+ return std::nullopt;
+ }
+
+ return bytes;
+}
+
+Boxcat::StatusResult Boxcat::GetStatus(std::optional<std::string>& global,
+ std::map<std::string, EventStatus>& games) {
+ httplib::SSLClient client{BOXCAT_HOSTNAME, static_cast<int>(PORT),
+ static_cast<int>(TIMEOUT_SECONDS)};
+
+ httplib::Headers headers{
+ {std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)},
+ {std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)},
+ };
+
+ const auto response = client.Get(BOXCAT_PATHNAME_EVENTS, headers);
+ if (response == nullptr)
+ return StatusResult::Offline;
+
+ if (response->status == static_cast<int>(ResponseStatus::BadClientVersion))
+ return StatusResult::BadClientVersion;
+
+ try {
+ nlohmann::json json = nlohmann::json::parse(response->body);
+
+ if (!json["online"].get<bool>())
+ return StatusResult::Offline;
+
+ if (json["global"].is_null())
+ global = std::nullopt;
+ else
+ global = json["global"].get<std::string>();
+
+ if (json["games"].is_array()) {
+ for (const auto object : json["games"]) {
+ if (object.is_object() && object.find("name") != object.end()) {
+ EventStatus detail{};
+ if (object["header"].is_string()) {
+ detail.header = object["header"].get<std::string>();
+ } else {
+ detail.header = std::nullopt;
+ }
+
+ if (object["footer"].is_string()) {
+ detail.footer = object["footer"].get<std::string>();
+ } else {
+ detail.footer = std::nullopt;
+ }
+
+ if (object["events"].is_array()) {
+ for (const auto& event : object["events"]) {
+ if (!event.is_string())
+ continue;
+ detail.events.push_back(event.get<std::string>());
+ }
+ }
+
+ games.insert_or_assign(object["name"], std::move(detail));
+ }
+ }
+ }
+
+ return StatusResult::Success;
+ } catch (const nlohmann::json::parse_error& error) {
+ LOG_ERROR(Service_BCAT, "{}", error.what());
+ return StatusResult::ParseError;
+ }
+}
+
+} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/backend/boxcat.h b/src/core/hle/service/bcat/backend/boxcat.h
new file mode 100644
index 000000000..d65b42e58
--- /dev/null
+++ b/src/core/hle/service/bcat/backend/boxcat.h
@@ -0,0 +1,64 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+#include <map>
+#include <optional>
+#include "core/hle/service/bcat/backend/backend.h"
+
+namespace Service::AM::Applets {
+class AppletManager;
+}
+
+namespace Service::BCAT {
+
+struct EventStatus {
+ std::optional<std::string> header;
+ std::optional<std::string> footer;
+ std::vector<std::string> events;
+};
+
+/// Boxcat is yuzu's custom backend implementation of Nintendo's BCAT service. It is free to use and
+/// doesn't require a switch or nintendo account. The content is controlled by the yuzu team.
+class Boxcat final : public Backend {
+ friend void SynchronizeInternal(AM::Applets::AppletManager& applet_manager,
+ DirectoryGetter dir_getter, TitleIDVersion title,
+ ProgressServiceBackend& progress,
+ std::optional<std::string> dir_name);
+
+public:
+ explicit Boxcat(AM::Applets::AppletManager& applet_manager_, DirectoryGetter getter);
+ ~Boxcat() override;
+
+ bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override;
+ bool SynchronizeDirectory(TitleIDVersion title, std::string name,
+ ProgressServiceBackend& progress) override;
+
+ bool Clear(u64 title_id) override;
+
+ void SetPassphrase(u64 title_id, const Passphrase& passphrase) override;
+
+ std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override;
+
+ enum class StatusResult {
+ Success,
+ Offline,
+ ParseError,
+ BadClientVersion,
+ };
+
+ static StatusResult GetStatus(std::optional<std::string>& global,
+ std::map<std::string, EventStatus>& games);
+
+private:
+ std::atomic_bool is_syncing{false};
+
+ class Client;
+ std::unique_ptr<Client> client;
+ AM::Applets::AppletManager& applet_manager;
+};
+
+} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat.cpp b/src/core/hle/service/bcat/bcat.cpp
index 179aa4949..8bb2528c9 100644
--- a/src/core/hle/service/bcat/bcat.cpp
+++ b/src/core/hle/service/bcat/bcat.cpp
@@ -6,11 +6,16 @@
namespace Service::BCAT {
-BCAT::BCAT(std::shared_ptr<Module> module, const char* name)
- : Module::Interface(std::move(module), name) {
+BCAT::BCAT(Core::System& system, std::shared_ptr<Module> module,
+ FileSystem::FileSystemController& fsc, const char* name)
+ : Interface(system, std::move(module), fsc, name) {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, &BCAT::CreateBcatService, "CreateBcatService"},
+ {1, &BCAT::CreateDeliveryCacheStorageService, "CreateDeliveryCacheStorageService"},
+ {2, &BCAT::CreateDeliveryCacheStorageServiceWithApplicationId, "CreateDeliveryCacheStorageServiceWithApplicationId"},
};
+ // clang-format on
RegisterHandlers(functions);
}
diff --git a/src/core/hle/service/bcat/bcat.h b/src/core/hle/service/bcat/bcat.h
index 802bd689a..6354465fc 100644
--- a/src/core/hle/service/bcat/bcat.h
+++ b/src/core/hle/service/bcat/bcat.h
@@ -6,11 +6,16 @@
#include "core/hle/service/bcat/module.h"
+namespace Core {
+class System;
+}
+
namespace Service::BCAT {
class BCAT final : public Module::Interface {
public:
- explicit BCAT(std::shared_ptr<Module> module, const char* name);
+ explicit BCAT(Core::System& system, std::shared_ptr<Module> module,
+ FileSystem::FileSystemController& fsc, const char* name);
~BCAT() override;
};
diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp
index b7bd738fc..8a7304f86 100644
--- a/src/core/hle/service/bcat/module.cpp
+++ b/src/core/hle/service/bcat/module.cpp
@@ -2,34 +2,258 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <cctype>
+#include <mbedtls/md5.h>
+#include "backend/boxcat.h"
+#include "common/hex_util.h"
#include "common/logging/log.h"
+#include "common/string_util.h"
+#include "core/file_sys/vfs.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/writable_event.h"
+#include "core/hle/service/bcat/backend/backend.h"
#include "core/hle/service/bcat/bcat.h"
#include "core/hle/service/bcat/module.h"
+#include "core/hle/service/filesystem/filesystem.h"
+#include "core/settings.h"
namespace Service::BCAT {
+constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::BCAT, 1};
+constexpr ResultCode ERROR_FAILED_OPEN_ENTITY{ErrorModule::BCAT, 2};
+constexpr ResultCode ERROR_ENTITY_ALREADY_OPEN{ErrorModule::BCAT, 6};
+constexpr ResultCode ERROR_NO_OPEN_ENTITY{ErrorModule::BCAT, 7};
+
+// The command to clear the delivery cache just calls fs IFileSystem DeleteFile on all of the files
+// and if any of them have a non-zero result it just forwards that result. This is the FS error code
+// for permission denied, which is the closest approximation of this scenario.
+constexpr ResultCode ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400};
+
+using BCATDigest = std::array<u8, 0x10>;
+
+namespace {
+
+u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) {
+ u64 out{};
+ std::memcpy(&out, id.data(), sizeof(u64));
+ return out;
+}
+
+// The digest is only used to determine if a file is unique compared to others of the same name.
+// Since the algorithm isn't ever checked in game, MD5 is safe.
+BCATDigest DigestFile(const FileSys::VirtualFile& file) {
+ BCATDigest out{};
+ const auto bytes = file->ReadAllBytes();
+ mbedtls_md5_ret(bytes.data(), bytes.size(), out.data());
+ return out;
+}
+
+// For a name to be valid it must be non-empty, must have a null terminating character as the final
+// char, can only contain numbers, letters, underscores and a hyphen if directory and a period if
+// file.
+bool VerifyNameValidInternal(Kernel::HLERequestContext& ctx, std::array<char, 0x20> name,
+ char match_char) {
+ const auto null_chars = std::count(name.begin(), name.end(), 0);
+ const auto bad_chars = std::count_if(name.begin(), name.end(), [match_char](char c) {
+ return !std::isalnum(static_cast<u8>(c)) && c != '_' && c != match_char && c != '\0';
+ });
+ if (null_chars == 0x20 || null_chars == 0 || bad_chars != 0 || name[0x1F] != '\0') {
+ LOG_ERROR(Service_BCAT, "Name passed was invalid!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ARGUMENT);
+ return false;
+ }
+
+ return true;
+}
+
+bool VerifyNameValidDir(Kernel::HLERequestContext& ctx, DirectoryName name) {
+ return VerifyNameValidInternal(ctx, name, '-');
+}
+
+bool VerifyNameValidFile(Kernel::HLERequestContext& ctx, FileName name) {
+ return VerifyNameValidInternal(ctx, name, '.');
+}
+
+} // Anonymous namespace
+
+struct DeliveryCacheDirectoryEntry {
+ FileName name;
+ u64 size;
+ BCATDigest digest;
+};
+
+class IDeliveryCacheProgressService final : public ServiceFramework<IDeliveryCacheProgressService> {
+public:
+ IDeliveryCacheProgressService(Kernel::SharedPtr<Kernel::ReadableEvent> event,
+ const DeliveryCacheProgressImpl& impl)
+ : ServiceFramework{"IDeliveryCacheProgressService"}, event(std::move(event)), impl(impl) {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &IDeliveryCacheProgressService::GetEvent, "GetEvent"},
+ {1, &IDeliveryCacheProgressService::GetImpl, "GetImpl"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void GetEvent(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_BCAT, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(event);
+ }
+
+ void GetImpl(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_BCAT, "called");
+
+ ctx.WriteBuffer(&impl, sizeof(DeliveryCacheProgressImpl));
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ Kernel::SharedPtr<Kernel::ReadableEvent> event;
+ const DeliveryCacheProgressImpl& impl;
+};
+
class IBcatService final : public ServiceFramework<IBcatService> {
public:
- IBcatService() : ServiceFramework("IBcatService") {
+ explicit IBcatService(Core::System& system_, Backend& backend_)
+ : ServiceFramework("IBcatService"), system{system_}, backend{backend_},
+ progress{{
+ ProgressServiceBackend{system_.Kernel(), "Normal"},
+ ProgressServiceBackend{system_.Kernel(), "Directory"},
+ }} {
+ // clang-format off
static const FunctionInfo functions[] = {
- {10100, nullptr, "RequestSyncDeliveryCache"},
- {10101, nullptr, "RequestSyncDeliveryCacheWithDirectoryName"},
+ {10100, &IBcatService::RequestSyncDeliveryCache, "RequestSyncDeliveryCache"},
+ {10101, &IBcatService::RequestSyncDeliveryCacheWithDirectoryName, "RequestSyncDeliveryCacheWithDirectoryName"},
{10200, nullptr, "CancelSyncDeliveryCacheRequest"},
{20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"},
{20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"},
- {30100, nullptr, "SetPassphrase"},
+ {30100, &IBcatService::SetPassphrase, "SetPassphrase"},
{30200, nullptr, "RegisterBackgroundDeliveryTask"},
{30201, nullptr, "UnregisterBackgroundDeliveryTask"},
{30202, nullptr, "BlockDeliveryTask"},
{30203, nullptr, "UnblockDeliveryTask"},
{90100, nullptr, "EnumerateBackgroundDeliveryTask"},
{90200, nullptr, "GetDeliveryList"},
- {90201, nullptr, "ClearDeliveryCacheStorage"},
+ {90201, &IBcatService::ClearDeliveryCacheStorage, "ClearDeliveryCacheStorage"},
{90300, nullptr, "GetPushNotificationLog"},
};
+ // clang-format on
RegisterHandlers(functions);
}
+
+private:
+ enum class SyncType {
+ Normal,
+ Directory,
+ Count,
+ };
+
+ std::shared_ptr<IDeliveryCacheProgressService> CreateProgressService(SyncType type) {
+ auto& backend{progress.at(static_cast<std::size_t>(type))};
+ return std::make_shared<IDeliveryCacheProgressService>(backend.GetEvent(),
+ backend.GetImpl());
+ }
+
+ void RequestSyncDeliveryCache(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_BCAT, "called");
+
+ backend.Synchronize({system.CurrentProcess()->GetTitleID(),
+ GetCurrentBuildID(system.GetCurrentProcessBuildID())},
+ progress.at(static_cast<std::size_t>(SyncType::Normal)));
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface(CreateProgressService(SyncType::Normal));
+ }
+
+ void RequestSyncDeliveryCacheWithDirectoryName(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto name_raw = rp.PopRaw<DirectoryName>();
+ const auto name =
+ Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size());
+
+ LOG_DEBUG(Service_BCAT, "called, name={}", name);
+
+ backend.SynchronizeDirectory({system.CurrentProcess()->GetTitleID(),
+ GetCurrentBuildID(system.GetCurrentProcessBuildID())},
+ name,
+ progress.at(static_cast<std::size_t>(SyncType::Directory)));
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface(CreateProgressService(SyncType::Directory));
+ }
+
+ void SetPassphrase(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto title_id = rp.PopRaw<u64>();
+
+ const auto passphrase_raw = ctx.ReadBuffer();
+
+ LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id,
+ Common::HexToString(passphrase_raw));
+
+ if (title_id == 0) {
+ LOG_ERROR(Service_BCAT, "Invalid title ID!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ARGUMENT);
+ }
+
+ if (passphrase_raw.size() > 0x40) {
+ LOG_ERROR(Service_BCAT, "Passphrase too large!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ARGUMENT);
+ return;
+ }
+
+ Passphrase passphrase{};
+ std::memcpy(passphrase.data(), passphrase_raw.data(),
+ std::min(passphrase.size(), passphrase_raw.size()));
+
+ backend.SetPassphrase(title_id, passphrase);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void ClearDeliveryCacheStorage(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto title_id = rp.PopRaw<u64>();
+
+ LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
+
+ if (title_id == 0) {
+ LOG_ERROR(Service_BCAT, "Invalid title ID!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ARGUMENT);
+ return;
+ }
+
+ if (!backend.Clear(title_id)) {
+ LOG_ERROR(Service_BCAT, "Could not clear the directory successfully!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_FAILED_CLEAR_CACHE);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ Core::System& system;
+ Backend& backend;
+
+ std::array<ProgressServiceBackend, static_cast<std::size_t>(SyncType::Count)> progress;
};
void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) {
@@ -37,20 +261,333 @@ void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IBcatService>();
+ rb.PushIpcInterface<IBcatService>(system, *backend);
+}
+
+class IDeliveryCacheFileService final : public ServiceFramework<IDeliveryCacheFileService> {
+public:
+ IDeliveryCacheFileService(FileSys::VirtualDir root_)
+ : ServiceFramework{"IDeliveryCacheFileService"}, root(std::move(root_)) {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &IDeliveryCacheFileService::Open, "Open"},
+ {1, &IDeliveryCacheFileService::Read, "Read"},
+ {2, &IDeliveryCacheFileService::GetSize, "GetSize"},
+ {3, &IDeliveryCacheFileService::GetDigest, "GetDigest"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void Open(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto dir_name_raw = rp.PopRaw<DirectoryName>();
+ const auto file_name_raw = rp.PopRaw<FileName>();
+
+ const auto dir_name =
+ Common::StringFromFixedZeroTerminatedBuffer(dir_name_raw.data(), dir_name_raw.size());
+ const auto file_name =
+ Common::StringFromFixedZeroTerminatedBuffer(file_name_raw.data(), file_name_raw.size());
+
+ LOG_DEBUG(Service_BCAT, "called, dir_name={}, file_name={}", dir_name, file_name);
+
+ if (!VerifyNameValidDir(ctx, dir_name_raw) || !VerifyNameValidFile(ctx, file_name_raw))
+ return;
+
+ if (current_file != nullptr) {
+ LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_ENTITY_ALREADY_OPEN);
+ return;
+ }
+
+ const auto dir = root->GetSubdirectory(dir_name);
+
+ if (dir == nullptr) {
+ LOG_ERROR(Service_BCAT, "The directory of name={} couldn't be opened!", dir_name);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_FAILED_OPEN_ENTITY);
+ return;
+ }
+
+ current_file = dir->GetFile(file_name);
+
+ if (current_file == nullptr) {
+ LOG_ERROR(Service_BCAT, "The file of name={} couldn't be opened!", file_name);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_FAILED_OPEN_ENTITY);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void Read(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto offset{rp.PopRaw<u64>()};
+
+ auto size = ctx.GetWriteBufferSize();
+
+ LOG_DEBUG(Service_BCAT, "called, offset={:016X}, size={:016X}", offset, size);
+
+ if (current_file == nullptr) {
+ LOG_ERROR(Service_BCAT, "There is no file currently open!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_NO_OPEN_ENTITY);
+ }
+
+ size = std::min<u64>(current_file->GetSize() - offset, size);
+ const auto buffer = current_file->ReadBytes(size, offset);
+ ctx.WriteBuffer(buffer);
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u64>(buffer.size());
+ }
+
+ void GetSize(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_BCAT, "called");
+
+ if (current_file == nullptr) {
+ LOG_ERROR(Service_BCAT, "There is no file currently open!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_NO_OPEN_ENTITY);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u64>(current_file->GetSize());
+ }
+
+ void GetDigest(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_BCAT, "called");
+
+ if (current_file == nullptr) {
+ LOG_ERROR(Service_BCAT, "There is no file currently open!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_NO_OPEN_ENTITY);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 6};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw(DigestFile(current_file));
+ }
+
+ FileSys::VirtualDir root;
+ FileSys::VirtualFile current_file;
+};
+
+class IDeliveryCacheDirectoryService final
+ : public ServiceFramework<IDeliveryCacheDirectoryService> {
+public:
+ IDeliveryCacheDirectoryService(FileSys::VirtualDir root_)
+ : ServiceFramework{"IDeliveryCacheDirectoryService"}, root(std::move(root_)) {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &IDeliveryCacheDirectoryService::Open, "Open"},
+ {1, &IDeliveryCacheDirectoryService::Read, "Read"},
+ {2, &IDeliveryCacheDirectoryService::GetCount, "GetCount"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void Open(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto name_raw = rp.PopRaw<DirectoryName>();
+ const auto name =
+ Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size());
+
+ LOG_DEBUG(Service_BCAT, "called, name={}", name);
+
+ if (!VerifyNameValidDir(ctx, name_raw))
+ return;
+
+ if (current_dir != nullptr) {
+ LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_ENTITY_ALREADY_OPEN);
+ return;
+ }
+
+ current_dir = root->GetSubdirectory(name);
+
+ if (current_dir == nullptr) {
+ LOG_ERROR(Service_BCAT, "Failed to open the directory name={}!", name);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_FAILED_OPEN_ENTITY);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void Read(Kernel::HLERequestContext& ctx) {
+ auto write_size = ctx.GetWriteBufferSize() / sizeof(DeliveryCacheDirectoryEntry);
+
+ LOG_DEBUG(Service_BCAT, "called, write_size={:016X}", write_size);
+
+ if (current_dir == nullptr) {
+ LOG_ERROR(Service_BCAT, "There is no open directory!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_NO_OPEN_ENTITY);
+ return;
+ }
+
+ const auto files = current_dir->GetFiles();
+ write_size = std::min<u64>(write_size, files.size());
+ std::vector<DeliveryCacheDirectoryEntry> entries(write_size);
+ std::transform(
+ files.begin(), files.begin() + write_size, entries.begin(), [](const auto& file) {
+ FileName name{};
+ std::memcpy(name.data(), file->GetName().data(),
+ std::min(file->GetName().size(), name.size()));
+ return DeliveryCacheDirectoryEntry{name, file->GetSize(), DigestFile(file)};
+ });
+
+ ctx.WriteBuffer(entries);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(static_cast<u32>(write_size * sizeof(DeliveryCacheDirectoryEntry)));
+ }
+
+ void GetCount(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_BCAT, "called");
+
+ if (current_dir == nullptr) {
+ LOG_ERROR(Service_BCAT, "There is no open directory!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_NO_OPEN_ENTITY);
+ return;
+ }
+
+ const auto files = current_dir->GetFiles();
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(static_cast<u32>(files.size()));
+ }
+
+ FileSys::VirtualDir root;
+ FileSys::VirtualDir current_dir;
+};
+
+class IDeliveryCacheStorageService final : public ServiceFramework<IDeliveryCacheStorageService> {
+public:
+ IDeliveryCacheStorageService(FileSys::VirtualDir root_)
+ : ServiceFramework{"IDeliveryCacheStorageService"}, root(std::move(root_)) {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &IDeliveryCacheStorageService::CreateFileService, "CreateFileService"},
+ {1, &IDeliveryCacheStorageService::CreateDirectoryService, "CreateDirectoryService"},
+ {10, &IDeliveryCacheStorageService::EnumerateDeliveryCacheDirectory, "EnumerateDeliveryCacheDirectory"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+
+ for (const auto& subdir : root->GetSubdirectories()) {
+ DirectoryName name{};
+ std::memcpy(name.data(), subdir->GetName().data(),
+ std::min(sizeof(DirectoryName) - 1, subdir->GetName().size()));
+ entries.push_back(name);
+ }
+ }
+
+private:
+ void CreateFileService(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_BCAT, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IDeliveryCacheFileService>(root);
+ }
+
+ void CreateDirectoryService(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_BCAT, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IDeliveryCacheDirectoryService>(root);
+ }
+
+ void EnumerateDeliveryCacheDirectory(Kernel::HLERequestContext& ctx) {
+ auto size = ctx.GetWriteBufferSize() / sizeof(DirectoryName);
+
+ LOG_DEBUG(Service_BCAT, "called, size={:016X}", size);
+
+ size = std::min<u64>(size, entries.size() - next_read_index);
+ ctx.WriteBuffer(entries.data() + next_read_index, size * sizeof(DirectoryName));
+ next_read_index += size;
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(static_cast<u32>(size));
+ }
+
+ FileSys::VirtualDir root;
+ std::vector<DirectoryName> entries;
+ u64 next_read_index = 0;
+};
+
+void Module::Interface::CreateDeliveryCacheStorageService(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_BCAT, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IDeliveryCacheStorageService>(
+ fsc.GetBCATDirectory(system.CurrentProcess()->GetTitleID()));
+}
+
+void Module::Interface::CreateDeliveryCacheStorageServiceWithApplicationId(
+ Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto title_id = rp.PopRaw<u64>();
+
+ LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IDeliveryCacheStorageService>(fsc.GetBCATDirectory(title_id));
+}
+
+std::unique_ptr<Backend> CreateBackendFromSettings([[maybe_unused]] Core::System& system,
+ DirectoryGetter getter) {
+#ifdef YUZU_ENABLE_BOXCAT
+ if (Settings::values.bcat_backend == "boxcat") {
+ return std::make_unique<Boxcat>(system.GetAppletManager(), std::move(getter));
+ }
+#endif
+
+ return std::make_unique<NullBackend>(std::move(getter));
}
-Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
- : ServiceFramework(name), module(std::move(module)) {}
+Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_,
+ FileSystem::FileSystemController& fsc_, const char* name)
+ : ServiceFramework(name), fsc{fsc_}, module{std::move(module_)},
+ backend{CreateBackendFromSettings(system_,
+ [&fsc_](u64 tid) { return fsc_.GetBCATDirectory(tid); })},
+ system{system_} {}
Module::Interface::~Interface() = default;
-void InstallInterfaces(SM::ServiceManager& service_manager) {
+void InstallInterfaces(Core::System& system) {
auto module = std::make_shared<Module>();
- std::make_shared<BCAT>(module, "bcat:a")->InstallAsService(service_manager);
- std::make_shared<BCAT>(module, "bcat:m")->InstallAsService(service_manager);
- std::make_shared<BCAT>(module, "bcat:u")->InstallAsService(service_manager);
- std::make_shared<BCAT>(module, "bcat:s")->InstallAsService(service_manager);
+ std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:a")
+ ->InstallAsService(system.ServiceManager());
+ std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:m")
+ ->InstallAsService(system.ServiceManager());
+ std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:u")
+ ->InstallAsService(system.ServiceManager());
+ std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:s")
+ ->InstallAsService(system.ServiceManager());
}
} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/module.h b/src/core/hle/service/bcat/module.h
index f0d63cab0..e4ba23ba0 100644
--- a/src/core/hle/service/bcat/module.h
+++ b/src/core/hle/service/bcat/module.h
@@ -6,23 +6,46 @@
#include "core/hle/service/service.h"
-namespace Service::BCAT {
+namespace Core {
+class System;
+}
+
+namespace Service {
+
+namespace FileSystem {
+class FileSystemController;
+} // namespace FileSystem
+
+namespace BCAT {
+
+class Backend;
class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
- explicit Interface(std::shared_ptr<Module> module, const char* name);
+ explicit Interface(Core::System& system_, std::shared_ptr<Module> module_,
+ FileSystem::FileSystemController& fsc_, const char* name);
~Interface() override;
void CreateBcatService(Kernel::HLERequestContext& ctx);
+ void CreateDeliveryCacheStorageService(Kernel::HLERequestContext& ctx);
+ void CreateDeliveryCacheStorageServiceWithApplicationId(Kernel::HLERequestContext& ctx);
protected:
+ FileSystem::FileSystemController& fsc;
+
std::shared_ptr<Module> module;
+ std::unique_ptr<Backend> backend;
+
+ private:
+ Core::System& system;
};
};
/// Registers all BCAT services with the specified service manager.
-void InstallInterfaces(SM::ServiceManager& service_manager);
+void InstallInterfaces(Core::System& system);
+
+} // namespace BCAT
-} // namespace Service::BCAT
+} // namespace Service
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
index afce581e5..4574d9572 100644
--- a/src/core/hle/service/btdrv/btdrv.cpp
+++ b/src/core/hle/service/btdrv/btdrv.cpp
@@ -34,8 +34,7 @@ public:
RegisterHandlers(functions);
auto& kernel = system.Kernel();
- register_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::Automatic, "BT:RegisterEvent");
+ register_event = Kernel::WritableEvent::CreateEventPair(kernel, "BT:RegisterEvent");
}
private:
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index 920fc6ff7..251b3c9df 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -57,14 +57,12 @@ public:
RegisterHandlers(functions);
auto& kernel = system.Kernel();
- scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
- "IBtmUserCore:ScanEvent");
- connection_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::Automatic, "IBtmUserCore:ConnectionEvent");
- service_discovery = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::Automatic, "IBtmUserCore:Discovery");
- config_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
- "IBtmUserCore:ConfigEvent");
+ scan_event = Kernel::WritableEvent::CreateEventPair(kernel, "IBtmUserCore:ScanEvent");
+ connection_event =
+ Kernel::WritableEvent::CreateEventPair(kernel, "IBtmUserCore:ConnectionEvent");
+ service_discovery =
+ Kernel::WritableEvent::CreateEventPair(kernel, "IBtmUserCore:Discovery");
+ config_event = Kernel::WritableEvent::CreateEventPair(kernel, "IBtmUserCore:ConfigEvent");
}
private:
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp
index af70d174d..f77ddd739 100644
--- a/src/core/hle/service/es/es.cpp
+++ b/src/core/hle/service/es/es.cpp
@@ -128,7 +128,7 @@ private:
void CountCommonTicket(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_ETicket, "called");
- const auto count = keys.GetCommonTickets().size();
+ const u32 count = static_cast<u32>(keys.GetCommonTickets().size());
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
@@ -138,7 +138,7 @@ private:
void CountPersonalizedTicket(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_ETicket, "called");
- const auto count = keys.GetPersonalizedTickets().size();
+ const u32 count = static_cast<u32>(keys.GetPersonalizedTickets().size());
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
@@ -150,7 +150,7 @@ private:
if (keys.GetCommonTickets().empty())
out_entries = 0;
else
- out_entries = ctx.GetWriteBufferSize() / sizeof(u128);
+ out_entries = static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(u128));
LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries);
@@ -160,7 +160,7 @@ private:
std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids),
[](const auto& pair) { return pair.first; });
- out_entries = std::min<u32>(ids.size(), out_entries);
+ out_entries = static_cast<u32>(std::min<std::size_t>(ids.size(), out_entries));
ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128));
IPC::ResponseBuilder rb{ctx, 3};
@@ -173,7 +173,7 @@ private:
if (keys.GetPersonalizedTickets().empty())
out_entries = 0;
else
- out_entries = ctx.GetWriteBufferSize() / sizeof(u128);
+ out_entries = static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(u128));
LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries);
@@ -183,7 +183,7 @@ private:
std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids),
[](const auto& pair) { return pair.first; });
- out_entries = std::min<u32>(ids.size(), out_entries);
+ out_entries = static_cast<u32>(std::min<std::size_t>(ids.size(), out_entries));
ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128));
IPC::ResponseBuilder rb{ctx, 3};
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index b2ebf6240..2546d7595 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -66,7 +66,7 @@ enum class FatalType : u32 {
static void GenerateErrorReport(Core::System& system, ResultCode error_code,
const FatalInfo& info) {
- const auto title_id = Core::CurrentProcess()->GetTitleID();
+ const auto title_id = system.CurrentProcess()->GetTitleID();
std::string crash_report = fmt::format(
"Yuzu {}-{} crash report\n"
"Title ID: {:016x}\n"
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 14cd0e322..102017d73 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -58,11 +58,11 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64
auto file = dir->CreateFile(FileUtil::GetFilename(path));
if (file == nullptr) {
// TODO(DarkLordZach): Find a better error code for this
- return ResultCode(-1);
+ return RESULT_UNKNOWN;
}
if (!file->Resize(size)) {
// TODO(DarkLordZach): Find a better error code for this
- return ResultCode(-1);
+ return RESULT_UNKNOWN;
}
return RESULT_SUCCESS;
}
@@ -80,7 +80,7 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) cons
}
if (!dir->DeleteFile(FileUtil::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
- return ResultCode(-1);
+ return RESULT_UNKNOWN;
}
return RESULT_SUCCESS;
@@ -94,7 +94,7 @@ ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_)
auto new_dir = dir->CreateSubdirectory(FileUtil::GetFilename(path));
if (new_dir == nullptr) {
// TODO(DarkLordZach): Find a better error code for this
- return ResultCode(-1);
+ return RESULT_UNKNOWN;
}
return RESULT_SUCCESS;
}
@@ -104,7 +104,7 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_)
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
- return ResultCode(-1);
+ return RESULT_UNKNOWN;
}
return RESULT_SUCCESS;
}
@@ -114,7 +114,7 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::str
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
- return ResultCode(-1);
+ return RESULT_UNKNOWN;
}
return RESULT_SUCCESS;
}
@@ -125,7 +125,7 @@ ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::stri
if (!dir->CleanSubdirectoryRecursive(FileUtil::GetFilename(sanitized_path))) {
// TODO(DarkLordZach): Find a better error code for this
- return ResultCode(-1);
+ return RESULT_UNKNOWN;
}
return RESULT_SUCCESS;
@@ -142,7 +142,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
return FileSys::ERROR_PATH_NOT_FOUND;
if (!src->Rename(FileUtil::GetFilename(dest_path))) {
// TODO(DarkLordZach): Find a better error code for this
- return ResultCode(-1);
+ return RESULT_UNKNOWN;
}
return RESULT_SUCCESS;
}
@@ -160,7 +160,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
if (!src->GetContainingDirectory()->DeleteFile(FileUtil::GetFilename(src_path))) {
// TODO(DarkLordZach): Find a better error code for this
- return ResultCode(-1);
+ return RESULT_UNKNOWN;
}
return RESULT_SUCCESS;
@@ -177,7 +177,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_pa
return FileSys::ERROR_PATH_NOT_FOUND;
if (!src->Rename(FileUtil::GetFilename(dest_path))) {
// TODO(DarkLordZach): Find a better error code for this
- return ResultCode(-1);
+ return RESULT_UNKNOWN;
}
return RESULT_SUCCESS;
}
@@ -189,7 +189,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_pa
src_path, dest_path);
// TODO(DarkLordZach): Find a better error code for this
- return ResultCode(-1);
+ return RESULT_UNKNOWN;
}
ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_,
@@ -241,7 +241,7 @@ ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
return FileSys::ERROR_PATH_NOT_FOUND;
}
-FileSystemController::FileSystemController() = default;
+FileSystemController::FileSystemController(Core::System& system_) : system{system_} {}
FileSystemController::~FileSystemController() = default;
@@ -287,10 +287,10 @@ ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFSCurrentProcess()
if (romfs_factory == nullptr) {
// TODO(bunnei): Find a better error code for this
- return ResultCode(-1);
+ return RESULT_UNKNOWN;
}
- return romfs_factory->OpenCurrentProcess();
+ return romfs_factory->OpenCurrentProcess(system.CurrentProcess()->GetTitleID());
}
ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS(
@@ -300,7 +300,7 @@ ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS(
if (romfs_factory == nullptr) {
// TODO(bunnei): Find a better error code for this
- return ResultCode(-1);
+ return RESULT_UNKNOWN;
}
return romfs_factory->Open(title_id, storage_id, type);
@@ -447,10 +447,10 @@ FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataTy
FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE};
FileSys::NACP nacp;
- const auto res = Core::System::GetInstance().GetAppLoader().ReadControlData(nacp);
+ const auto res = system.GetAppLoader().ReadControlData(nacp);
if (res != Loader::ResultStatus::Success) {
- FileSys::PatchManager pm{Core::CurrentProcess()->GetTitleID()};
+ FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
auto [nacp_unique, discard] = pm.GetControlMetadata();
if (nacp_unique != nullptr) {
@@ -674,6 +674,15 @@ FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id)
return bis_factory->GetModificationDumpRoot(title_id);
}
+FileSys::VirtualDir FileSystemController::GetBCATDirectory(u64 title_id) const {
+ LOG_TRACE(Service_FS, "Opening BCAT root for tid={:016X}", title_id);
+
+ if (bis_factory == nullptr)
+ return nullptr;
+
+ return bis_factory->GetBCATDirectory(title_id);
+}
+
void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
if (overwrite) {
bis_factory = nullptr;
@@ -693,10 +702,10 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
if (bis_factory == nullptr) {
bis_factory =
std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory);
- Core::System::GetInstance().RegisterContentProvider(
- FileSys::ContentProviderUnionSlot::SysNAND, bis_factory->GetSystemNANDContents());
- Core::System::GetInstance().RegisterContentProvider(
- FileSys::ContentProviderUnionSlot::UserNAND, bis_factory->GetUserNANDContents());
+ system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SysNAND,
+ bis_factory->GetSystemNANDContents());
+ system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::UserNAND,
+ bis_factory->GetUserNANDContents());
}
if (save_data_factory == nullptr) {
@@ -705,8 +714,8 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
if (sdmc_factory == nullptr) {
sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
- Core::System::GetInstance().RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC,
- sdmc_factory->GetSDMCContents());
+ system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC,
+ sdmc_factory->GetSDMCContents());
}
}
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 3e0c03ec0..1b0a6a949 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -10,6 +10,10 @@
#include "core/file_sys/vfs.h"
#include "core/hle/result.h"
+namespace Core {
+class System;
+}
+
namespace FileSys {
class BISFactory;
class RegisteredCache;
@@ -52,7 +56,7 @@ enum class ImageDirectoryId : u32 {
class FileSystemController {
public:
- FileSystemController();
+ explicit FileSystemController(Core::System& system_);
~FileSystemController();
ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory);
@@ -110,6 +114,8 @@ public:
FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const;
FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const;
+ FileSys::VirtualDir GetBCATDirectory(u64 title_id) const;
+
// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
// above is called.
void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
@@ -123,6 +129,8 @@ private:
std::unique_ptr<FileSys::XCI> gamecard;
std::unique_ptr<FileSys::RegisteredCache> gamecard_registered;
std::unique_ptr<FileSys::PlaceholderCache> gamecard_placeholder;
+
+ Core::System& system;
};
void InstallInterfaces(Core::System& system);
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index eb982ad49..92162d3e1 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -785,7 +785,7 @@ void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) {
static_cast<u8>(type), title_id);
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
- rb.Push(ResultCode(-1));
+ rb.Push(RESULT_UNKNOWN);
}
void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) {
@@ -803,7 +803,7 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
- auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
+ [[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
u128 uid = rp.PopRaw<u128>();
LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(),
@@ -891,7 +891,7 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
// TODO (bunnei): Find the right error code to use here
LOG_CRITICAL(Service_FS, "no file system interface available!");
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultCode(-1));
+ rb.Push(RESULT_UNKNOWN);
return;
}
@@ -928,7 +928,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
"could not open data storage with title_id={:016X}, storage_id={:02X}", title_id,
static_cast<u8>(storage_id));
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultCode(-1));
+ rb.Push(RESULT_UNKNOWN);
return;
}
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index 42b4ee861..1a0214f08 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -162,7 +162,7 @@ public:
RegisterHandlers(functions);
notification_event = Kernel::WritableEvent::CreateEventPair(
- system.Kernel(), Kernel::ResetType::Manual, "INotificationService:NotifyEvent");
+ system.Kernel(), "INotificationService:NotifyEvent");
}
private:
@@ -237,7 +237,6 @@ private:
};
Common::UUID uuid;
- bool is_event_created = false;
Kernel::EventPair notification_event;
std::queue<SizedNotificationInfo> notifications;
States states{};
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index 8e8263f5b..1f2131ec8 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -11,11 +11,10 @@
namespace Service::HID {
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
-constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
+[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
-Controller_DebugPad::Controller_DebugPad(Core::System& system)
- : ControllerBase(system), system(system) {}
+Controller_DebugPad::Controller_DebugPad(Core::System& system) : ControllerBase(system) {}
Controller_DebugPad::~Controller_DebugPad() = default;
void Controller_DebugPad::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index 6c4de817e..555b29d76 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -89,6 +89,5 @@ private:
buttons;
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>
analogs;
- Core::System& system;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index 80da0a0d3..6e990dd00 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -10,8 +10,7 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
-Controller_Gesture::Controller_Gesture(Core::System& system)
- : ControllerBase(system), system(system) {}
+Controller_Gesture::Controller_Gesture(Core::System& system) : ControllerBase(system) {}
Controller_Gesture::~Controller_Gesture() = default;
void Controller_Gesture::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index 396897527..f650b8338 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -59,6 +59,5 @@ private:
std::array<GestureState, 17> gesture_states;
};
SharedMemory shared_memory{};
- Core::System& system;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index e587b2e15..358cb9329 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -12,8 +12,7 @@ namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
constexpr u8 KEYS_PER_BYTE = 8;
-Controller_Keyboard::Controller_Keyboard(Core::System& system)
- : ControllerBase(system), system(system) {}
+Controller_Keyboard::Controller_Keyboard(Core::System& system) : ControllerBase(system) {}
Controller_Keyboard::~Controller_Keyboard() = default;
void Controller_Keyboard::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index ef586f7eb..f3eef5936 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -53,6 +53,5 @@ private:
keyboard_keys;
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods>
keyboard_mods;
- Core::System& system;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 88f2ca4c1..93d88ea50 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -11,7 +11,7 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
-Controller_Mouse::Controller_Mouse(Core::System& system) : ControllerBase(system), system(system) {}
+Controller_Mouse::Controller_Mouse(Core::System& system) : ControllerBase(system) {}
Controller_Mouse::~Controller_Mouse() = default;
void Controller_Mouse::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index df2da6ae3..357ab7107 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -53,6 +53,5 @@ private:
std::unique_ptr<Input::MouseDevice> mouse_device;
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons>
mouse_button_devices;
- Core::System& system;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 44b668fbf..79fff517e 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -20,7 +20,7 @@
namespace Service::HID {
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
-constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
+[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
constexpr std::size_t NPAD_OFFSET = 0x9A00;
constexpr u32 BATTERY_FULL = 2;
constexpr u32 MAX_NPAD_ID = 7;
@@ -105,6 +105,8 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
controller.joy_styles.raw = 0; // Zero out
controller.device_type.raw = 0;
switch (controller_type) {
+ case NPadControllerType::None:
+ UNREACHABLE();
case NPadControllerType::Handheld:
controller.joy_styles.handheld.Assign(1);
controller.device_type.handheld.Assign(1);
@@ -165,13 +167,14 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
controller.battery_level[0] = BATTERY_FULL;
controller.battery_level[1] = BATTERY_FULL;
controller.battery_level[2] = BATTERY_FULL;
+ styleset_changed_events[controller_idx].writable->Signal();
}
void Controller_NPad::OnInit() {
auto& kernel = system.Kernel();
for (std::size_t i = 0; i < styleset_changed_events.size(); i++) {
styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::Automatic, fmt::format("npad:NpadStyleSetChanged_{}", i));
+ kernel, fmt::format("npad:NpadStyleSetChanged_{}", i));
}
if (!IsControllerActivated()) {
@@ -238,7 +241,7 @@ void Controller_NPad::OnRelease() {}
void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
const auto controller_idx = NPadIdToIndex(npad_id);
- const auto controller_type = connected_controllers[controller_idx].type;
+ [[maybe_unused]] const auto controller_type = connected_controllers[controller_idx].type;
if (!connected_controllers[controller_idx].is_connected) {
return;
}
@@ -345,6 +348,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
libnx_entry.connection_status.raw = 0;
switch (controller_type) {
+ case NPadControllerType::None:
+ UNREACHABLE();
case NPadControllerType::Handheld:
handheld_entry.connection_status.raw = 0;
handheld_entry.connection_status.IsWired.Assign(1);
@@ -433,7 +438,6 @@ 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) {
@@ -452,10 +456,6 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
controller.type = requested_controller;
InitNewlyAddedControler(i);
}
- had_controller_update = true;
- }
- if (had_controller_update) {
- styleset_changed_events[i].writable->Signal();
}
}
}
@@ -481,7 +481,6 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode)
const std::size_t npad_index = NPadIdToIndex(npad_id);
ASSERT(npad_index < shared_memory_entries.size());
if (shared_memory_entries[npad_index].pad_assignment != assignment_mode) {
- styleset_changed_events[npad_index].writable->Signal();
shared_memory_entries[npad_index].pad_assignment = assignment_mode;
}
}
@@ -507,7 +506,6 @@ Kernel::SharedPtr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEven
// TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
// be signalled at least once, and signaled after a new controller is connected?
const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)];
- styleset_event.writable->Signal();
return styleset_event.readable;
}
@@ -585,36 +583,6 @@ bool Controller_NPad::SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2) {
return true;
}
-bool Controller_NPad::IsControllerSupported(NPadControllerType controller) {
- if (controller == NPadControllerType::Handheld) {
- // Handheld is not even a supported type, lets stop here
- if (std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
- NPAD_HANDHELD) == supported_npad_id_types.end()) {
- return false;
- }
- // Handheld should not be supported in docked mode
- if (Settings::values.use_docked_mode) {
- return false;
- }
- }
- switch (controller) {
- case NPadControllerType::ProController:
- return style.pro_controller;
- case NPadControllerType::Handheld:
- return style.handheld;
- case NPadControllerType::JoyDual:
- return style.joycon_dual;
- case NPadControllerType::JoyLeft:
- return style.joycon_left;
- case NPadControllerType::JoyRight:
- return style.joycon_right;
- case NPadControllerType::Pokeball:
- return style.pokeball;
- default:
- return false;
- }
-}
-
Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) {
// These are controllers without led patterns
@@ -661,25 +629,24 @@ void Controller_NPad::ClearAllConnectedControllers() {
}
void Controller_NPad::DisconnectAllConnectedControllers() {
- std::for_each(connected_controllers.begin(), connected_controllers.end(),
- [](ControllerHolder& controller) { controller.is_connected = false; });
+ for (ControllerHolder& controller : connected_controllers) {
+ controller.is_connected = false;
+ }
}
void Controller_NPad::ConnectAllDisconnectedControllers() {
- std::for_each(connected_controllers.begin(), connected_controllers.end(),
- [](ControllerHolder& controller) {
- if (controller.type != NPadControllerType::None && !controller.is_connected) {
- controller.is_connected = false;
- }
- });
+ for (ControllerHolder& controller : connected_controllers) {
+ if (controller.type != NPadControllerType::None && !controller.is_connected) {
+ controller.is_connected = true;
+ }
+ }
}
void Controller_NPad::ClearAllControllers() {
- std::for_each(connected_controllers.begin(), connected_controllers.end(),
- [](ControllerHolder& controller) {
- controller.type = NPadControllerType::None;
- controller.is_connected = false;
- });
+ for (ControllerHolder& controller : connected_controllers) {
+ controller.type = NPadControllerType::None;
+ controller.is_connected = false;
+ }
}
u32 Controller_NPad::GetAndResetPressState() {
@@ -687,10 +654,10 @@ u32 Controller_NPad::GetAndResetPressState() {
}
bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const {
- const bool support_handheld =
- std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) !=
- supported_npad_id_types.end();
if (controller == NPadControllerType::Handheld) {
+ const bool support_handheld =
+ std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
+ NPAD_HANDHELD) != supported_npad_id_types.end();
// Handheld is not even a supported type, lets stop here
if (!support_handheld) {
return false;
@@ -702,6 +669,7 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
return true;
}
+
if (std::any_of(supported_npad_id_types.begin(), supported_npad_id_types.end(),
[](u32 npad_id) { return npad_id <= MAX_NPAD_ID; })) {
switch (controller) {
@@ -719,6 +687,7 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
return false;
}
}
+
return false;
}
@@ -797,6 +766,7 @@ Controller_NPad::NPadControllerType Controller_NPad::DecideBestController(
priority_list.push_back(NPadControllerType::JoyLeft);
priority_list.push_back(NPadControllerType::JoyRight);
priority_list.push_back(NPadControllerType::JoyDual);
+ break;
}
const auto iter = std::find_if(priority_list.begin(), priority_list.end(),
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 1bc3d55d6..16c4caa1f 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -301,6 +301,11 @@ private:
bool is_connected;
};
+ void InitNewlyAddedControler(std::size_t controller_idx);
+ bool IsControllerSupported(NPadControllerType controller) const;
+ NPadControllerType DecideBestController(NPadControllerType priority) const;
+ void RequestPadStateUpdate(u32 npad_id);
+
u32 press_state{};
NPadType style{};
@@ -321,12 +326,7 @@ private:
std::array<ControllerHolder, 10> connected_controllers{};
bool can_controllers_vibrate{true};
- void InitNewlyAddedControler(std::size_t controller_idx);
- bool IsControllerSupported(NPadControllerType controller) const;
- NPadControllerType DecideBestController(NPadControllerType priority) const;
- void RequestPadStateUpdate(u32 npad_id);
std::array<ControllerPad, 10> npad_pad_states{};
- bool IsControllerSupported(NPadControllerType controller);
bool is_in_lr_assignment_mode{false};
Core::System& system;
};
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
index 9b829341e..9e527d176 100644
--- a/src/core/hle/service/hid/controllers/stubbed.cpp
+++ b/src/core/hle/service/hid/controllers/stubbed.cpp
@@ -9,8 +9,7 @@
namespace Service::HID {
-Controller_Stubbed::Controller_Stubbed(Core::System& system)
- : ControllerBase(system), system(system) {}
+Controller_Stubbed::Controller_Stubbed(Core::System& system) : ControllerBase(system) {}
Controller_Stubbed::~Controller_Stubbed() = default;
void Controller_Stubbed::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h
index 37d7d8538..4fa83ac85 100644
--- a/src/core/hle/service/hid/controllers/stubbed.h
+++ b/src/core/hle/service/hid/controllers/stubbed.h
@@ -30,6 +30,5 @@ public:
private:
bool smart_update{};
std::size_t common_offset{};
- Core::System& system;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 25912fd69..1c6e55566 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -13,8 +13,7 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
-Controller_Touchscreen::Controller_Touchscreen(Core::System& system)
- : ControllerBase(system), system(system) {}
+Controller_Touchscreen::Controller_Touchscreen(Core::System& system) : ControllerBase(system) {}
Controller_Touchscreen::~Controller_Touchscreen() = default;
void Controller_Touchscreen::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 3429c84db..a1d97269e 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -69,6 +69,5 @@ private:
TouchScreenSharedMemory shared_memory{};
std::unique_ptr<Input::TouchDevice> touch_device;
s64_le last_touch{};
- Core::System& system;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
index 1bce044b4..27511b27b 100644
--- a/src/core/hle/service/hid/controllers/xpad.cpp
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -10,7 +10,7 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
-Controller_XPad::Controller_XPad(Core::System& system) : ControllerBase(system), system(system) {}
+Controller_XPad::Controller_XPad(Core::System& system) : ControllerBase(system) {}
Controller_XPad::~Controller_XPad() = default;
void Controller_XPad::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
index c445ebec0..ad229787c 100644
--- a/src/core/hle/service/hid/controllers/xpad.h
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -56,6 +56,5 @@ private:
};
static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size");
SharedMemory shared_memory{};
- Core::System& system;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 8d76ba746..ecc130f6c 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -38,8 +38,10 @@ namespace Service::HID {
// Updating period for each HID device.
// TODO(ogniK): Find actual polling rate of hid
constexpr s64 pad_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 66);
-constexpr s64 accelerometer_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
-constexpr s64 gyroscope_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
+[[maybe_unused]] constexpr s64 accelerometer_update_ticks =
+ static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
+[[maybe_unused]] constexpr s64 gyroscope_update_ticks =
+ static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
IAppletResource::IAppletResource(Core::System& system)
@@ -193,7 +195,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
{101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
{102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"},
{103, &Hid::ActivateNpad, "ActivateNpad"},
- {104, nullptr, "DeactivateNpad"},
+ {104, &Hid::DeactivateNpad, "DeactivateNpad"},
{106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"},
{107, &Hid::DisconnectNpad, "DisconnectNpad"},
{108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"},
@@ -201,13 +203,13 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
{120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
{121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
{122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"},
- {123, nullptr, "SetNpadJoyAssignmentModeSingleByDefault"},
+ {123, &Hid::SetNpadJoyAssignmentModeSingle, "SetNpadJoyAssignmentModeSingle"},
{124, &Hid::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"},
{125, &Hid::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"},
{126, &Hid::StartLrAssignmentMode, "StartLrAssignmentMode"},
{127, &Hid::StopLrAssignmentMode, "StopLrAssignmentMode"},
{128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"},
- {129, nullptr, "GetNpadHandheldActivationMode"},
+ {129, &Hid::GetNpadHandheldActivationMode, "GetNpadHandheldActivationMode"},
{130, &Hid::SwapNpadAssignment, "SwapNpadAssignment"},
{131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"},
{132, nullptr, "EnableUnintendedHomeButtonInputProtection"},
@@ -468,6 +470,17 @@ void Hid::ActivateNpad(Kernel::HLERequestContext& ctx) {
applet_resource->ActivateController(HidController::NPad);
}
+void Hid::DeactivateNpad(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ applet_resource->DeactivateController(HidController::NPad);
+}
+
void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id{rp.Pop<u32>()};
@@ -544,10 +557,126 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx
LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", npad_id,
applet_resource_user_id);
+ auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
+ controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Single);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
+ // TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault
+ IPC::RequestParser rp{ctx};
+ const auto npad_id{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+ const auto npad_joy_device_type{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_HID,
+ "(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
+ npad_id, applet_resource_user_id, npad_joy_device_type);
+
+ auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
+ controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Single);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
+void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto npad_id{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id,
+ applet_resource_user_id);
+
+ auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
+ controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto unknown_1{rp.Pop<u32>()};
+ const auto unknown_2{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_HID,
+ "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
+ unknown_1, unknown_2, applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+ auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
+ controller.StartLRAssignmentMode();
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Hid::StopLrAssignmentMode(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+ auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
+ controller.StopLRAssignmentMode();
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Hid::SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+ const auto mode{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, mode={}",
+ applet_resource_user_id, mode);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Hid::GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
+ applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto npad_1{rp.Pop<u32>()};
+ const auto npad_2{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, npad_1={}, npad_2={}",
+ applet_resource_user_id, npad_1, npad_2);
+
+ auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
+ IPC::ResponseBuilder rb{ctx, 2};
+ if (controller.SwapNpadAssignment(npad_1, npad_2)) {
+ rb.Push(RESULT_SUCCESS);
+ } else {
+ LOG_ERROR(Service_HID, "Npads are not connected!");
+ rb.Push(ERR_NPAD_NOT_CONNECTED);
+ }
+}
+
void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
@@ -622,47 +751,6 @@ void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
applet_resource->GetController<Controller_NPad>(HidController::NPad).GetLastVibration());
}
-void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto npad_id{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
-
- LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id,
- applet_resource_user_id);
-
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
- controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
-}
-
-void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto unknown_1{rp.Pop<u32>()};
- const auto unknown_2{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
-
- LOG_WARNING(Service_HID,
- "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
- unknown_1, unknown_2, applet_resource_user_id);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
-}
-
-void Hid::SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto applet_resource_user_id{rp.Pop<u64>()};
- const auto mode{rp.Pop<u64>()};
-
- LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, mode={}",
- applet_resource_user_id, mode);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
-}
-
void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called");
@@ -756,49 +844,6 @@ void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
}
-void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto applet_resource_user_id{rp.Pop<u64>()};
-
- LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
- controller.StartLRAssignmentMode();
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
-}
-
-void Hid::StopLrAssignmentMode(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto applet_resource_user_id{rp.Pop<u64>()};
-
- LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
- controller.StopLRAssignmentMode();
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
-}
-
-void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto npad_1{rp.Pop<u32>()};
- const auto npad_2{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
-
- LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, npad_1={}, npad_2={}",
- applet_resource_user_id, npad_1, npad_2);
-
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
- IPC::ResponseBuilder rb{ctx, 2};
- if (controller.SwapNpadAssignment(npad_1, npad_2)) {
- rb.Push(RESULT_SUCCESS);
- } else {
- LOG_ERROR(Service_HID, "Npads are not connected!");
- rb.Push(ERR_NPAD_NOT_CONNECTED);
- }
-}
-
class HidDbg final : public ServiceFramework<HidDbg> {
public:
explicit HidDbg() : ServiceFramework{"hid:dbg"} {
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 35b663679..f08e036a3 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -99,20 +99,26 @@ private:
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx);
void ActivateNpad(Kernel::HLERequestContext& ctx);
+ void DeactivateNpad(Kernel::HLERequestContext& ctx);
void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx);
void DisconnectNpad(Kernel::HLERequestContext& ctx);
void GetPlayerLedPattern(Kernel::HLERequestContext& ctx);
void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx);
+ void SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx);
+ void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx);
+ void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx);
+ void StartLrAssignmentMode(Kernel::HLERequestContext& ctx);
+ void StopLrAssignmentMode(Kernel::HLERequestContext& ctx);
+ void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
+ void GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
+ void SwapNpadAssignment(Kernel::HLERequestContext& ctx);
void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
void SendVibrationValue(Kernel::HLERequestContext& ctx);
void SendVibrationValues(Kernel::HLERequestContext& ctx);
void GetActualVibrationValue(Kernel::HLERequestContext& ctx);
- void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx);
- void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx);
- void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx);
void PermitVibration(Kernel::HLERequestContext& ctx);
@@ -122,9 +128,6 @@ private:
void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
- void StartLrAssignmentMode(Kernel::HLERequestContext& ctx);
- void StopLrAssignmentMode(Kernel::HLERequestContext& ctx);
- void SwapNpadAssignment(Kernel::HLERequestContext& ctx);
std::shared_ptr<IAppletResource> applet_resource;
Core::System& system;
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 3164ca26e..88f903bfd 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -163,7 +163,7 @@ public:
return;
}
- if (Core::CurrentProcess()->GetTitleID() != header.title_id) {
+ if (system.CurrentProcess()->GetTitleID() != header.title_id) {
LOG_ERROR(Service_LDR,
"Attempting to load NRR with title ID other than current process. (actual "
"{:016X})!",
@@ -294,7 +294,7 @@ public:
Memory::ReadBlock(nro_address, nro_data.data(), nro_size);
SHA256Hash hash{};
- mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0);
+ mbedtls_sha256_ret(nro_data.data(), nro_data.size(), hash.data(), 0);
// NRO Hash is already loaded
if (std::any_of(nro.begin(), nro.end(), [&hash](const std::pair<VAddr, NROInfo>& info) {
@@ -327,7 +327,7 @@ public:
}
// Load NRO as new executable module
- auto* process = Core::CurrentProcess();
+ auto* process = system.CurrentProcess();
auto& vm_manager = process->VMManager();
auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size);
@@ -411,7 +411,7 @@ public:
return;
}
- auto& vm_manager = Core::CurrentProcess()->VMManager();
+ auto& vm_manager = system.CurrentProcess()->VMManager();
const auto& nro_info = iter->second;
// Unmap the mirrored memory
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp
index 2a61593e2..435f2d286 100644
--- a/src/core/hle/service/lm/lm.cpp
+++ b/src/core/hle/service/lm/lm.cpp
@@ -6,8 +6,10 @@
#include <string>
#include "common/logging/log.h"
+#include "common/scope_exit.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/lm/lm.h"
+#include "core/hle/service/lm/manager.h"
#include "core/hle/service/service.h"
#include "core/memory.h"
@@ -15,65 +17,16 @@ namespace Service::LM {
class ILogger final : public ServiceFramework<ILogger> {
public:
- ILogger() : ServiceFramework("ILogger") {
+ ILogger(Manager& manager) : ServiceFramework("ILogger"), manager(manager) {
static const FunctionInfo functions[] = {
- {0x00000000, &ILogger::Initialize, "Initialize"},
- {0x00000001, &ILogger::SetDestination, "SetDestination"},
+ {0, &ILogger::Log, "Log"},
+ {1, &ILogger::SetDestination, "SetDestination"},
};
RegisterHandlers(functions);
}
private:
- struct MessageHeader {
- enum Flags : u32_le {
- IsHead = 1,
- IsTail = 2,
- };
- enum Severity : u32_le {
- Trace,
- Info,
- Warning,
- Error,
- Critical,
- };
-
- u64_le pid;
- u64_le threadContext;
- union {
- BitField<0, 16, Flags> flags;
- BitField<16, 8, Severity> severity;
- BitField<24, 8, u32> verbosity;
- };
- u32_le payload_size;
-
- bool IsHeadLog() const {
- return flags & Flags::IsHead;
- }
- bool IsTailLog() const {
- return flags & Flags::IsTail;
- }
- };
- static_assert(sizeof(MessageHeader) == 0x18, "MessageHeader is incorrect size");
-
- /// Log field type
- enum class Field : u8 {
- Skip = 1,
- Message = 2,
- Line = 3,
- Filename = 4,
- Function = 5,
- Module = 6,
- Thread = 7,
- };
-
- /**
- * ILogger::Initialize service function
- * Inputs:
- * 0: 0x00000000
- * Outputs:
- * 0: ResultCode
- */
- void Initialize(Kernel::HLERequestContext& ctx) {
+ void Log(Kernel::HLERequestContext& ctx) {
// This function only succeeds - Get that out of the way
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -85,140 +38,70 @@ private:
Memory::ReadBlock(addr, &header, sizeof(MessageHeader));
addr += sizeof(MessageHeader);
- if (header.IsHeadLog()) {
- log_stream.str("");
- log_stream.clear();
- }
-
- // Parse out log metadata
- u32 line{};
- std::string module;
- std::string message;
- std::string filename;
- std::string function;
- std::string thread;
+ FieldMap fields;
while (addr < end_addr) {
- const Field field{static_cast<Field>(Memory::Read8(addr++))};
- const std::size_t length{Memory::Read8(addr++)};
+ const auto field = static_cast<Field>(Memory::Read8(addr++));
+ const auto length = Memory::Read8(addr++);
if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) {
++addr;
}
- switch (field) {
- case Field::Skip:
- break;
- case Field::Message:
- message = Memory::ReadCString(addr, length);
- break;
- case Field::Line:
- line = Memory::Read32(addr);
- break;
- case Field::Filename:
- filename = Memory::ReadCString(addr, length);
- break;
- case Field::Function:
- function = Memory::ReadCString(addr, length);
- break;
- case Field::Module:
- module = Memory::ReadCString(addr, length);
- break;
- case Field::Thread:
- thread = Memory::ReadCString(addr, length);
- break;
- }
+ SCOPE_EXIT({ addr += length; });
- addr += length;
- }
+ if (field == Field::Skip) {
+ continue;
+ }
- // Empty log - nothing to do here
- if (log_stream.str().empty() && message.empty()) {
- return;
+ std::vector<u8> data(length);
+ Memory::ReadBlock(addr, data.data(), length);
+ fields.emplace(field, std::move(data));
}
- // Format a nicely printable string out of the log metadata
- if (!filename.empty()) {
- log_stream << filename << ':';
- }
- if (!module.empty()) {
- log_stream << module << ':';
- }
- if (!function.empty()) {
- log_stream << function << ':';
- }
- if (line) {
- log_stream << std::to_string(line) << ':';
- }
- if (!thread.empty()) {
- log_stream << thread << ':';
- }
- if (log_stream.str().length() > 0 && log_stream.str().back() == ':') {
- log_stream << ' ';
- }
- log_stream << message;
-
- if (header.IsTailLog()) {
- switch (header.severity) {
- case MessageHeader::Severity::Trace:
- LOG_DEBUG(Debug_Emulated, "{}", log_stream.str());
- break;
- case MessageHeader::Severity::Info:
- LOG_INFO(Debug_Emulated, "{}", log_stream.str());
- break;
- case MessageHeader::Severity::Warning:
- LOG_WARNING(Debug_Emulated, "{}", log_stream.str());
- break;
- case MessageHeader::Severity::Error:
- LOG_ERROR(Debug_Emulated, "{}", log_stream.str());
- break;
- case MessageHeader::Severity::Critical:
- LOG_CRITICAL(Debug_Emulated, "{}", log_stream.str());
- break;
- }
- }
+ manager.Log({header, std::move(fields)});
}
- // This service function is intended to be used as a way to
- // redirect logging output to different destinations, however,
- // given we always want to see the logging output, it's sufficient
- // to do nothing and return success here.
void SetDestination(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_LM, "called");
+ IPC::RequestParser rp{ctx};
+ const auto destination = rp.PopEnum<DestinationFlag>();
+
+ LOG_DEBUG(Service_LM, "called, destination={:08X}", static_cast<u32>(destination));
+
+ manager.SetDestination(destination);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
- std::ostringstream log_stream;
+ Manager& manager;
};
class LM final : public ServiceFramework<LM> {
public:
- explicit LM() : ServiceFramework{"lm"} {
+ explicit LM(Manager& manager) : ServiceFramework{"lm"}, manager(manager) {
+ // clang-format off
static const FunctionInfo functions[] = {
- {0x00000000, &LM::OpenLogger, "OpenLogger"},
+ {0, &LM::OpenLogger, "OpenLogger"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
- /**
- * LM::OpenLogger service function
- * Inputs:
- * 0: 0x00000000
- * Outputs:
- * 0: ResultCode
- */
+private:
void OpenLogger(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_LM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ILogger>();
+ rb.PushIpcInterface<ILogger>(manager);
}
+
+ Manager& manager;
};
-void InstallInterfaces(SM::ServiceManager& service_manager) {
- std::make_shared<LM>()->InstallAsService(service_manager);
+void InstallInterfaces(Core::System& system) {
+ std::make_shared<LM>(system.GetLogManager())->InstallAsService(system.ServiceManager());
}
} // namespace Service::LM
diff --git a/src/core/hle/service/lm/lm.h b/src/core/hle/service/lm/lm.h
index 7806ae27b..d40410b5c 100644
--- a/src/core/hle/service/lm/lm.h
+++ b/src/core/hle/service/lm/lm.h
@@ -4,13 +4,13 @@
#pragma once
-namespace Service::SM {
-class ServiceManager;
+namespace Core {
+class System;
}
namespace Service::LM {
/// Registers all LM services with the specified service manager.
-void InstallInterfaces(SM::ServiceManager& service_manager);
+void InstallInterfaces(Core::System& system);
} // namespace Service::LM
diff --git a/src/core/hle/service/lm/manager.cpp b/src/core/hle/service/lm/manager.cpp
new file mode 100644
index 000000000..b67081b86
--- /dev/null
+++ b/src/core/hle/service/lm/manager.cpp
@@ -0,0 +1,133 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "common/string_util.h"
+#include "core/hle/service/lm/manager.h"
+#include "core/reporter.h"
+
+namespace Service::LM {
+
+std::ostream& operator<<(std::ostream& os, DestinationFlag dest) {
+ std::vector<std::string> array;
+ const auto check_single_flag = [dest, &array](DestinationFlag check, std::string name) {
+ if ((static_cast<u32>(check) & static_cast<u32>(dest)) != 0) {
+ array.emplace_back(std::move(name));
+ }
+ };
+
+ check_single_flag(DestinationFlag::Default, "Default");
+ check_single_flag(DestinationFlag::UART, "UART");
+ check_single_flag(DestinationFlag::UARTSleeping, "UART (Sleeping)");
+
+ os << "[";
+ for (const auto& entry : array) {
+ os << entry << ", ";
+ }
+ return os << "]";
+}
+
+std::ostream& operator<<(std::ostream& os, MessageHeader::Severity severity) {
+ switch (severity) {
+ case MessageHeader::Severity::Trace:
+ return os << "Trace";
+ case MessageHeader::Severity::Info:
+ return os << "Info";
+ case MessageHeader::Severity::Warning:
+ return os << "Warning";
+ case MessageHeader::Severity::Error:
+ return os << "Error";
+ case MessageHeader::Severity::Critical:
+ return os << "Critical";
+ default:
+ return os << fmt::format("{:08X}", static_cast<u32>(severity));
+ }
+}
+
+std::ostream& operator<<(std::ostream& os, Field field) {
+ switch (field) {
+ case Field::Skip:
+ return os << "Skip";
+ case Field::Message:
+ return os << "Message";
+ case Field::Line:
+ return os << "Line";
+ case Field::Filename:
+ return os << "Filename";
+ case Field::Function:
+ return os << "Function";
+ case Field::Module:
+ return os << "Module";
+ case Field::Thread:
+ return os << "Thread";
+ default:
+ return os << fmt::format("{:08X}", static_cast<u32>(field));
+ }
+}
+
+std::string FormatField(Field type, const std::vector<u8>& data) {
+ switch (type) {
+ case Field::Skip:
+ return "";
+ case Field::Line:
+ if (data.size() >= sizeof(u32)) {
+ u32 line;
+ std::memcpy(&line, data.data(), sizeof(u32));
+ return fmt::format("{}", line);
+ }
+ return "[ERROR DECODING LINE NUMBER]";
+ case Field::Message:
+ case Field::Filename:
+ case Field::Function:
+ case Field::Module:
+ case Field::Thread:
+ return Common::StringFromFixedZeroTerminatedBuffer(
+ reinterpret_cast<const char*>(data.data()), data.size());
+ default:
+ UNIMPLEMENTED();
+ }
+}
+
+Manager::Manager(Core::Reporter& reporter) : reporter(reporter) {}
+
+Manager::~Manager() = default;
+
+void Manager::SetEnabled(bool enabled) {
+ this->enabled = enabled;
+}
+
+void Manager::SetDestination(DestinationFlag destination) {
+ this->destination = destination;
+}
+
+void Manager::Log(LogMessage message) {
+ if (message.header.IsHeadLog()) {
+ InitializeLog();
+ }
+
+ current_log.emplace_back(std::move(message));
+
+ if (current_log.back().header.IsTailLog()) {
+ FinalizeLog();
+ }
+}
+
+void Manager::Flush() {
+ FinalizeLog();
+}
+
+void Manager::InitializeLog() {
+ current_log.clear();
+
+ LOG_INFO(Service_LM, "Initialized new log session");
+}
+
+void Manager::FinalizeLog() {
+ reporter.SaveLogReport(static_cast<u32>(destination), std::move(current_log));
+
+ LOG_INFO(Service_LM, "Finalized current log session");
+}
+
+} // namespace Service::LM
diff --git a/src/core/hle/service/lm/manager.h b/src/core/hle/service/lm/manager.h
new file mode 100644
index 000000000..544e636ba
--- /dev/null
+++ b/src/core/hle/service/lm/manager.h
@@ -0,0 +1,106 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <map>
+#include <ostream>
+#include <vector>
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "common/swap.h"
+
+namespace Core {
+class Reporter;
+}
+
+namespace Service::LM {
+
+enum class DestinationFlag : u32 {
+ Default = 1,
+ UART = 2,
+ UARTSleeping = 4,
+
+ All = 0xFFFF,
+};
+
+struct MessageHeader {
+ enum Flags : u32_le {
+ IsHead = 1,
+ IsTail = 2,
+ };
+ enum Severity : u32_le {
+ Trace,
+ Info,
+ Warning,
+ Error,
+ Critical,
+ };
+
+ u64_le pid;
+ u64_le thread_context;
+ union {
+ BitField<0, 16, Flags> flags;
+ BitField<16, 8, Severity> severity;
+ BitField<24, 8, u32> verbosity;
+ };
+ u32_le payload_size;
+
+ bool IsHeadLog() const {
+ return flags & IsHead;
+ }
+ bool IsTailLog() const {
+ return flags & IsTail;
+ }
+};
+static_assert(sizeof(MessageHeader) == 0x18, "MessageHeader is incorrect size");
+
+enum class Field : u8 {
+ Skip = 1,
+ Message = 2,
+ Line = 3,
+ Filename = 4,
+ Function = 5,
+ Module = 6,
+ Thread = 7,
+};
+
+std::ostream& operator<<(std::ostream& os, DestinationFlag dest);
+std::ostream& operator<<(std::ostream& os, MessageHeader::Severity severity);
+std::ostream& operator<<(std::ostream& os, Field field);
+
+using FieldMap = std::map<Field, std::vector<u8>>;
+
+struct LogMessage {
+ MessageHeader header;
+ FieldMap fields;
+};
+
+std::string FormatField(Field type, const std::vector<u8>& data);
+
+class Manager {
+public:
+ explicit Manager(Core::Reporter& reporter);
+ ~Manager();
+
+ void SetEnabled(bool enabled);
+ void SetDestination(DestinationFlag destination);
+
+ void Log(LogMessage message);
+
+ void Flush();
+
+private:
+ void InitializeLog();
+ void FinalizeLog();
+
+ bool enabled = true;
+ DestinationFlag destination = DestinationFlag::All;
+
+ std::vector<LogMessage> current_log;
+
+ Core::Reporter& reporter;
+};
+
+} // namespace Service::LM
diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp
index 0b3923ad9..0ffc5009e 100644
--- a/src/core/hle/service/mii/mii.cpp
+++ b/src/core/hle/service/mii/mii.cpp
@@ -242,7 +242,7 @@ private:
const auto index = db.IndexOf(uuid);
if (index > MAX_MIIS) {
// TODO(DarkLordZach): Find a better error code
- rb.Push(ResultCode(-1));
+ rb.Push(RESULT_UNKNOWN);
rb.Push(index);
} else {
rb.Push(RESULT_SUCCESS);
@@ -268,7 +268,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2};
// TODO(DarkLordZach): Find a better error code
- rb.Push(success ? RESULT_SUCCESS : ResultCode(-1));
+ rb.Push(success ? RESULT_SUCCESS : RESULT_UNKNOWN);
}
void AddOrReplace(Kernel::HLERequestContext& ctx) {
@@ -282,7 +282,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2};
// TODO(DarkLordZach): Find a better error code
- rb.Push(success ? RESULT_SUCCESS : ResultCode(-1));
+ rb.Push(success ? RESULT_SUCCESS : RESULT_UNKNOWN);
}
void Delete(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index a42c22d44..3bf753dee 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -16,18 +16,14 @@
#include "core/hle/service/nfp/nfp_user.h"
namespace Service::NFP {
-
namespace ErrCodes {
-constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP,
- -1); // TODO(ogniK): Find the actual error code
constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152);
} // namespace ErrCodes
Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
: ServiceFramework(name), module(std::move(module)), system(system) {
auto& kernel = system.Kernel();
- nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
- "IUser:NFCTagDetected");
+ nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, "IUser:NFCTagDetected");
}
Module::Interface::~Interface() = default;
@@ -35,7 +31,7 @@ Module::Interface::~Interface() = default;
class IUser final : public ServiceFramework<IUser> {
public:
IUser(Module::Interface& nfp_interface, Core::System& system)
- : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface), system(system) {
+ : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface) {
static const FunctionInfo functions[] = {
{0, &IUser::Initialize, "Initialize"},
{1, &IUser::Finalize, "Finalize"},
@@ -66,10 +62,9 @@ public:
RegisterHandlers(functions);
auto& kernel = system.Kernel();
- deactivate_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::Automatic, "IUser:DeactivateEvent");
- availability_change_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::Automatic, "IUser:AvailabilityChangeEvent");
+ deactivate_event = Kernel::WritableEvent::CreateEventPair(kernel, "IUser:DeactivateEvent");
+ availability_change_event =
+ Kernel::WritableEvent::CreateEventPair(kernel, "IUser:AvailabilityChangeEvent");
}
private:
@@ -183,6 +178,8 @@ private:
case DeviceState::TagRemoved:
device_state = DeviceState::Initialized;
break;
+ default:
+ break;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -324,7 +321,6 @@ private:
Kernel::EventPair deactivate_event;
Kernel::EventPair availability_change_event;
const Module::Interface& nfp_interface;
- Core::System& system;
};
void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 24d1813a7..01d557c7a 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -12,6 +12,13 @@
namespace Service::NIFM {
+enum class RequestState : u32 {
+ NotSubmitted = 1,
+ Error = 1, ///< The duplicate 1 is intentional; it means both not submitted and error on HW.
+ Pending = 2,
+ Connected = 3,
+};
+
class IScanRequest final : public ServiceFramework<IScanRequest> {
public:
explicit IScanRequest() : ServiceFramework("IScanRequest") {
@@ -62,10 +69,8 @@ public:
RegisterHandlers(functions);
auto& kernel = system.Kernel();
- event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
- "IRequest:Event1");
- event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
- "IRequest:Event2");
+ event1 = Kernel::WritableEvent::CreateEventPair(kernel, "IRequest:Event1");
+ event2 = Kernel::WritableEvent::CreateEventPair(kernel, "IRequest:Event2");
}
private:
@@ -81,7 +86,7 @@ private:
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(0);
+ rb.PushEnum(RequestState::Connected);
}
void GetResult(Kernel::HLERequestContext& ctx) {
@@ -189,14 +194,14 @@ private:
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u8>(0);
+ rb.Push<u8>(1);
}
void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u8>(0);
+ rb.Push<u8>(1);
}
Core::System& system;
};
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index 75d414952..7d6cf2070 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -141,8 +141,7 @@ public:
auto& kernel = system.Kernel();
finished_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::Automatic,
- "IEnsureNetworkClockAvailabilityService:FinishEvent");
+ kernel, "IEnsureNetworkClockAvailabilityService:FinishEvent");
}
private:
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 15c156ce1..eeba0aa19 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -271,7 +271,7 @@ void IApplicationManagerInterface::GetApplicationControlData(Kernel::HLERequestC
"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));
+ rb.Push(RESULT_UNKNOWN);
return;
}
@@ -291,7 +291,7 @@ void IApplicationManagerInterface::GetApplicationControlData(Kernel::HLERequestC
0x4000 + control.second->GetSize());
IPC::ResponseBuilder rb{ctx, 2};
// TODO(DarkLordZach): Find a better error code for this.
- rb.Push(ResultCode(-1));
+ rb.Push(RESULT_UNKNOWN);
return;
}
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 7dcdb4a07..db433305f 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -6,13 +6,6 @@
#include <cstring>
#include <vector>
-#include <FontChineseSimplified.h>
-#include <FontChineseTraditional.h>
-#include <FontExtendedChineseSimplified.h>
-#include <FontKorean.h>
-#include <FontNintendoExtended.h>
-#include <FontStandard.h>
-
#include "common/assert.h"
#include "common/common_paths.h"
#include "common/common_types.h"
@@ -24,7 +17,9 @@
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
+#include "core/file_sys/system_archive/system_archive.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/physical_memory.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ns/pl_u.h"
@@ -94,15 +89,20 @@ static void DecryptSharedFont(const std::vector<u32>& input, Kernel::PhysicalMem
offset += transformed_font.size() * sizeof(u32);
}
-static void EncryptSharedFont(const std::vector<u8>& input, Kernel::PhysicalMemory& output,
- std::size_t& offset) {
- ASSERT_MSG(offset + input.size() + 8 < SHARED_FONT_MEM_SIZE, "Shared fonts exceeds 17mb!");
- const u32 KEY = EXPECTED_MAGIC ^ EXPECTED_RESULT;
- std::memcpy(output.data() + offset, &EXPECTED_RESULT, sizeof(u32)); // Magic header
- const u32 ENC_SIZE = static_cast<u32>(input.size()) ^ KEY;
- std::memcpy(output.data() + offset + sizeof(u32), &ENC_SIZE, sizeof(u32));
- std::memcpy(output.data() + offset + (sizeof(u32) * 2), input.data(), input.size());
- offset += input.size() + (sizeof(u32) * 2);
+void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output,
+ std::size_t& offset) {
+ ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE,
+ "Shared fonts exceeds 17mb!");
+
+ const auto key = Common::swap32(EXPECTED_RESULT ^ EXPECTED_MAGIC);
+ std::vector<u32> transformed_font(input.size() + 2);
+ transformed_font[0] = Common::swap32(EXPECTED_MAGIC);
+ transformed_font[1] = Common::swap32(static_cast<u32>(input.size() * sizeof(u32))) ^ key;
+ std::transform(input.begin(), input.end(), transformed_font.begin() + 2,
+ [key](u32 in) { return in ^ key; });
+ std::memcpy(output.data() + offset, transformed_font.data(),
+ transformed_font.size() * sizeof(u32));
+ offset += transformed_font.size() * sizeof(u32);
}
// Helper function to make BuildSharedFontsRawRegions a bit nicer
@@ -168,114 +168,49 @@ PL_U::PL_U(Core::System& system)
// Attempt to load shared font data from disk
const auto* nand = fsc.GetSystemNANDContents();
std::size_t offset = 0;
- // Rebuild shared fonts from data ncas
- if (nand->HasEntry(static_cast<u64>(FontArchives::Standard),
- FileSys::ContentRecordType::Data)) {
- impl->shared_font = std::make_shared<Kernel::PhysicalMemory>(SHARED_FONT_MEM_SIZE);
- for (auto font : SHARED_FONTS) {
- const auto nca =
- nand->GetEntry(static_cast<u64>(font.first), FileSys::ContentRecordType::Data);
- if (!nca) {
- LOG_ERROR(Service_NS, "Failed to find {:016X}! Skipping",
- static_cast<u64>(font.first));
- continue;
- }
- const auto romfs = nca->GetRomFS();
- if (!romfs) {
- LOG_ERROR(Service_NS, "{:016X} has no RomFS! Skipping",
- static_cast<u64>(font.first));
- continue;
- }
- const auto extracted_romfs = FileSys::ExtractRomFS(romfs);
- if (!extracted_romfs) {
- LOG_ERROR(Service_NS, "Failed to extract RomFS for {:016X}! Skipping",
- static_cast<u64>(font.first));
- continue;
- }
- const auto font_fp = extracted_romfs->GetFile(font.second);
- if (!font_fp) {
- LOG_ERROR(Service_NS, "{:016X} has no file \"{}\"! Skipping",
- static_cast<u64>(font.first), font.second);
- continue;
- }
- std::vector<u32> font_data_u32(font_fp->GetSize() / sizeof(u32));
- font_fp->ReadBytes<u32>(font_data_u32.data(), font_fp->GetSize());
- // We need to be BigEndian as u32s for the xor encryption
- std::transform(font_data_u32.begin(), font_data_u32.end(), font_data_u32.begin(),
- Common::swap32);
- FontRegion region{
- static_cast<u32>(offset + 8),
- static_cast<u32>((font_data_u32.size() * sizeof(u32)) -
- 8)}; // Font offset and size do not account for the header
- DecryptSharedFont(font_data_u32, *impl->shared_font, offset);
- impl->shared_font_regions.push_back(region);
+ // Rebuild shared fonts from data ncas or synthesize
+
+ impl->shared_font = std::make_shared<Kernel::PhysicalMemory>(SHARED_FONT_MEM_SIZE);
+ for (auto font : SHARED_FONTS) {
+ FileSys::VirtualFile romfs;
+ const auto nca =
+ nand->GetEntry(static_cast<u64>(font.first), FileSys::ContentRecordType::Data);
+ if (nca) {
+ romfs = nca->GetRomFS();
}
- } else {
- impl->shared_font = std::make_shared<Kernel::PhysicalMemory>(
- SHARED_FONT_MEM_SIZE); // Shared memory needs to always be allocated and a fixed size
-
- const std::string user_path = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir);
- const std::string filepath{user_path + SHARED_FONT};
+ if (!romfs) {
+ romfs = FileSys::SystemArchive::SynthesizeSystemArchive(static_cast<u64>(font.first));
+ }
- // Create path if not already created
- if (!FileUtil::CreateFullPath(filepath)) {
- LOG_ERROR(Service_NS, "Failed to create sharedfonts path \"{}\"!", filepath);
- return;
+ if (!romfs) {
+ LOG_ERROR(Service_NS, "Failed to find or synthesize {:016X}! Skipping",
+ static_cast<u64>(font.first));
+ continue;
}
- bool using_ttf = false;
- for (const char* font_ttf : SHARED_FONTS_TTF) {
- if (FileUtil::Exists(user_path + font_ttf)) {
- using_ttf = true;
- FileUtil::IOFile file(user_path + font_ttf, "rb");
- if (file.IsOpen()) {
- std::vector<u8> ttf_bytes(file.GetSize());
- file.ReadBytes<u8>(ttf_bytes.data(), ttf_bytes.size());
- FontRegion region{
- static_cast<u32>(offset + 8),
- static_cast<u32>(ttf_bytes.size())}; // Font offset and size do not account
- // for the header
- EncryptSharedFont(ttf_bytes, *impl->shared_font, offset);
- impl->shared_font_regions.push_back(region);
- } else {
- LOG_WARNING(Service_NS, "Unable to load font: {}", font_ttf);
- }
- } else if (using_ttf) {
- LOG_WARNING(Service_NS, "Unable to find font: {}", font_ttf);
- }
+ const auto extracted_romfs = FileSys::ExtractRomFS(romfs);
+ if (!extracted_romfs) {
+ LOG_ERROR(Service_NS, "Failed to extract RomFS for {:016X}! Skipping",
+ static_cast<u64>(font.first));
+ continue;
}
- if (using_ttf)
- return;
- FileUtil::IOFile file(filepath, "rb");
-
- if (file.IsOpen()) {
- // Read shared font data
- ASSERT(file.GetSize() == SHARED_FONT_MEM_SIZE);
- file.ReadBytes(impl->shared_font->data(), impl->shared_font->size());
- impl->BuildSharedFontsRawRegions(*impl->shared_font);
- } else {
- LOG_WARNING(Service_NS,
- "Shared Font file missing. Loading open source replacement from memory");
-
- // clang-format off
- const std::vector<std::vector<u8>> open_source_shared_fonts_ttf = {
- {std::begin(FontChineseSimplified), std::end(FontChineseSimplified)},
- {std::begin(FontChineseTraditional), std::end(FontChineseTraditional)},
- {std::begin(FontExtendedChineseSimplified), std::end(FontExtendedChineseSimplified)},
- {std::begin(FontKorean), std::end(FontKorean)},
- {std::begin(FontNintendoExtended), std::end(FontNintendoExtended)},
- {std::begin(FontStandard), std::end(FontStandard)},
- };
- // clang-format on
-
- for (const std::vector<u8>& font_ttf : open_source_shared_fonts_ttf) {
- const FontRegion region{static_cast<u32>(offset + 8),
- static_cast<u32>(font_ttf.size())};
- EncryptSharedFont(font_ttf, *impl->shared_font, offset);
- impl->shared_font_regions.push_back(region);
- }
+ const auto font_fp = extracted_romfs->GetFile(font.second);
+ if (!font_fp) {
+ LOG_ERROR(Service_NS, "{:016X} has no file \"{}\"! Skipping",
+ static_cast<u64>(font.first), font.second);
+ continue;
}
+ std::vector<u32> font_data_u32(font_fp->GetSize() / sizeof(u32));
+ font_fp->ReadBytes<u32>(font_data_u32.data(), font_fp->GetSize());
+ // We need to be BigEndian as u32s for the xor encryption
+ std::transform(font_data_u32.begin(), font_data_u32.end(), font_data_u32.begin(),
+ Common::swap32);
+ // Font offset and size do not account for the header
+ const FontRegion region{static_cast<u32>(offset + 8),
+ static_cast<u32>((font_data_u32.size() * sizeof(u32)) - 8)};
+ DecryptSharedFont(font_data_u32, *impl->shared_font, offset);
+ impl->shared_font_regions.push_back(region);
}
}
@@ -324,14 +259,14 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) {
void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
// Map backing memory for the font data
LOG_DEBUG(Service_NS, "called");
- Core::CurrentProcess()->VMManager().MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0,
- SHARED_FONT_MEM_SIZE,
- Kernel::MemoryState::Shared);
+ system.CurrentProcess()->VMManager().MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0,
+ SHARED_FONT_MEM_SIZE,
+ Kernel::MemoryState::Shared);
// Create shared font memory object
auto& kernel = system.Kernel();
impl->shared_font_mem = Kernel::SharedMemory::Create(
- kernel, Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite,
+ kernel, system.CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite,
Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE,
"PL_U:shared_font_mem");
diff --git a/src/core/hle/service/ns/pl_u.h b/src/core/hle/service/ns/pl_u.h
index 1063f4204..27161bd7a 100644
--- a/src/core/hle/service/ns/pl_u.h
+++ b/src/core/hle/service/ns/pl_u.h
@@ -5,6 +5,7 @@
#pragma once
#include <memory>
+#include <vector>
#include "core/hle/service/service.h"
namespace Service {
@@ -15,6 +16,8 @@ class FileSystemController;
namespace NS {
+void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, std::size_t& offset);
+
class PL_U final : public ServiceFramework<PL_U> {
public:
explicit PL_U(Core::System& system);
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index f764388bc..3f7b8e670 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -5,6 +5,7 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
+#include "core/core_timing.h"
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvdrv/devices/nvmap.h"
#include "core/perf_stats.h"
@@ -38,7 +39,10 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
transform, crop_rect};
system.GetPerfStats().EndGameFrame();
+ system.GetPerfStats().EndSystemFrame();
system.GPU().SwapBuffers(&framebuffer);
+ system.FrameLimiter().DoFrameLimiting(system.CoreTiming().GetGlobalTimeUs());
+ system.GetPerfStats().BeginSystemFrame();
}
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index 6bc053f27..07c88465e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -45,6 +45,8 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std:
return GetVARegions(input, output);
case IoctlCommand::IocUnmapBufferCommand:
return UnmapBuffer(input, output);
+ default:
+ break;
}
if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand)
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index ff6b1abae..b27ee0502 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -38,9 +38,10 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::v
return IocCtrlEventUnregister(input, output);
case IoctlCommand::IocCtrlEventSignalCommand:
return IocCtrlEventSignal(input, output);
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented ioctl");
+ return 0;
}
- UNIMPLEMENTED_MSG("Unimplemented ioctl");
- return 0;
}
u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) {
@@ -62,16 +63,26 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
return NvResult::BadParameter;
}
+ u32 event_id = params.value & 0x00FF;
+
+ if (event_id >= MaxNvEvents) {
+ std::memcpy(output.data(), &params, sizeof(params));
+ return NvResult::BadParameter;
+ }
+
+ auto event = events_interface.events[event_id];
auto& gpu = system.GPU();
// This is mostly to take into account unimplemented features. As synced
// gpu is always synced.
if (!gpu.IsAsync()) {
+ event.writable->Signal();
return NvResult::Success;
}
auto lock = gpu.LockSync();
const u32 current_syncpoint_value = gpu.GetSyncpointValue(params.syncpt_id);
const s32 diff = current_syncpoint_value - params.threshold;
if (diff >= 0) {
+ event.writable->Signal();
params.value = current_syncpoint_value;
std::memcpy(output.data(), &params, sizeof(params));
return NvResult::Success;
@@ -87,27 +98,6 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
return NvResult::Timeout;
}
- u32 event_id;
- if (is_async) {
- event_id = params.value & 0x00FF;
- if (event_id >= MaxNvEvents) {
- std::memcpy(output.data(), &params, sizeof(params));
- return NvResult::BadParameter;
- }
- } else {
- if (ctrl.fresh_call) {
- const auto result = events_interface.GetFreeEvent();
- if (result) {
- event_id = *result;
- } else {
- LOG_CRITICAL(Service_NVDRV, "No Free Events available!");
- event_id = params.value & 0x00FF;
- }
- } else {
- event_id = ctrl.event_id;
- }
- }
-
EventState status = events_interface.status[event_id];
if (event_id < MaxNvEvents || status == EventState::Free || status == EventState::Registered) {
events_interface.SetEventStatus(event_id, EventState::Waiting);
@@ -119,7 +109,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000;
}
params.value |= event_id;
- events_interface.events[event_id].writable->Clear();
+ event.writable->Clear();
gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
if (!is_async && ctrl.fresh_call) {
ctrl.must_delay = true;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 389ace76f..cc2192e5c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -40,9 +40,10 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input,
return FlushL2(input, output);
case IoctlCommand::IocGetGpuTime:
return GetGpuTime(input, output);
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented ioctl");
+ return 0;
}
- UNIMPLEMENTED_MSG("Unimplemented ioctl");
- return 0;
}
u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 2b8d1bef6..9de0ace22 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -44,6 +44,8 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::ve
return GetWaitbase(input, output);
case IoctlCommand::IocChannelSetTimeoutCommand:
return ChannelSetTimeout(input, output);
+ default:
+ break;
}
if (command.group == NVGPU_IOCTL_MAGIC) {
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index 5e0c23602..68d139cfb 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -134,7 +134,9 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3, 1};
rb.Push(RESULT_SUCCESS);
if (event_id < MaxNvEvents) {
- rb.PushCopyObjects(nvdrv->GetEvent(event_id));
+ auto event = nvdrv->GetEvent(event_id);
+ event->Clear();
+ rb.PushCopyObjects(event);
rb.Push<u32>(NvResult::Success);
} else {
rb.Push<u32>(0);
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 307a7e928..cc9cd3fd1 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -40,8 +40,7 @@ Module::Module(Core::System& system) {
auto& kernel = system.Kernel();
for (u32 i = 0; i < MaxNvEvents; i++) {
std::string event_label = fmt::format("NVDRV::NvEvent_{}", i);
- events_interface.events[i] = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::Automatic, event_label);
+ events_interface.events[i] = Kernel::WritableEvent::CreateEventPair(kernel, event_label);
events_interface.status[i] = EventState::Free;
events_interface.registered[i] = false;
}
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index e1a07d3ee..1af11e80c 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -14,10 +14,9 @@
namespace Service::NVFlinger {
-BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
- auto& kernel = Core::System::GetInstance().Kernel();
- buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
- "BufferQueue NativeHandle");
+BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id, u64 layer_id)
+ : id(id), layer_id(layer_id) {
+ buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, "BufferQueue NativeHandle");
}
BufferQueue::~BufferQueue() = default;
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index 356bedb81..8f9b18547 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -15,6 +15,10 @@
#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nvdrv/nvdata.h"
+namespace Kernel {
+class KernelCore;
+}
+
namespace Service::NVFlinger {
struct IGBPBuffer {
@@ -44,7 +48,7 @@ public:
NativeWindowFormat = 2,
};
- BufferQueue(u32 id, u64 layer_id);
+ explicit BufferQueue(Kernel::KernelCore& kernel, u32 id, u64 layer_id);
~BufferQueue();
enum class BufferTransformFlags : u32 {
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 2e4d707b9..cc9522aad 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -83,7 +83,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
const u64 layer_id = next_layer_id++;
const u32 buffer_queue_id = next_buffer_queue_id++;
- buffer_queues.emplace_back(buffer_queue_id, layer_id);
+ buffer_queues.emplace_back(system.Kernel(), buffer_queue_id, layer_id);
display->CreateLayer(layer_id, buffer_queues.back());
return layer_id;
}
@@ -187,14 +187,18 @@ void NVFlinger::Compose() {
MicroProfileFlip();
if (!buffer) {
- // There was no queued buffer to draw, render previous frame
- system.GetPerfStats().EndGameFrame();
- system.GPU().SwapBuffers({});
continue;
}
const auto& igbp_buffer = buffer->get().igbp_buffer;
+ const auto& gpu = system.GPU();
+ const auto& multi_fence = buffer->get().multi_fence;
+ for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) {
+ const auto& fence = multi_fence.fences[fence_id];
+ gpu.WaitFence(fence.id, fence.value);
+ }
+
// Now send the buffer to the GPU for drawing.
// TODO(Subv): Support more than just disp0. The display device selection is probably based
// on which display we're drawing (Default, Internal, External, etc)
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 831a427de..7c5302017 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -208,7 +208,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
AOC::InstallInterfaces(*sm, system);
APM::InstallInterfaces(system);
Audio::InstallInterfaces(*sm, system);
- BCAT::InstallInterfaces(*sm);
+ BCAT::InstallInterfaces(system);
BPC::InstallInterfaces(*sm);
BtDrv::InstallInterfaces(*sm, system);
BTM::InstallInterfaces(*sm, system);
@@ -226,7 +226,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
LBL::InstallInterfaces(*sm);
LDN::InstallInterfaces(*sm);
LDR::InstallInterfaces(*sm, system);
- LM::InstallInterfaces(*sm);
+ LM::InstallInterfaces(system);
Migration::InstallInterfaces(*sm);
Mii::InstallInterfaces(*sm);
MM::InstallInterfaces(*sm);
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 1b9ab8401..62efe021e 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -34,12 +34,12 @@ static void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time,
additional_info = {};
return;
}
- calendar_time.year = tm->tm_year + 1900;
- calendar_time.month = tm->tm_mon + 1;
- calendar_time.day = tm->tm_mday;
- calendar_time.hour = tm->tm_hour;
- calendar_time.minute = tm->tm_min;
- calendar_time.second = tm->tm_sec;
+ calendar_time.year = static_cast<u16_le>(tm->tm_year + 1900);
+ calendar_time.month = static_cast<u8>(tm->tm_mon + 1);
+ calendar_time.day = static_cast<u8>(tm->tm_mday);
+ calendar_time.hour = static_cast<u8>(tm->tm_hour);
+ calendar_time.minute = static_cast<u8>(tm->tm_min);
+ calendar_time.second = static_cast<u8>(tm->tm_sec);
additional_info.day_of_week = tm->tm_wday;
additional_info.day_of_year = tm->tm_yday;
@@ -322,7 +322,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
if (tm == nullptr) {
LOG_ERROR(Service_Time, "tm is a nullptr");
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultCode(-1)); // TODO(ogniK): Find appropriate error code
+ rb.Push(RESULT_UNKNOWN); // TODO(ogniK): Find appropriate error code
return;
}
@@ -331,12 +331,12 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000), {}};
CalendarTime calendar_time{};
- calendar_time.year = tm->tm_year + 1900;
- calendar_time.month = tm->tm_mon + 1;
- calendar_time.day = tm->tm_mday;
- calendar_time.hour = tm->tm_hour;
- calendar_time.minute = tm->tm_min;
- calendar_time.second = tm->tm_sec;
+ calendar_time.year = static_cast<u16_le>(tm->tm_year + 1900);
+ calendar_time.month = static_cast<u8>(tm->tm_mon + 1);
+ calendar_time.day = static_cast<u8>(tm->tm_mday);
+ calendar_time.hour = static_cast<u8>(tm->tm_hour);
+ calendar_time.minute = static_cast<u8>(tm->tm_min);
+ calendar_time.second = static_cast<u8>(tm->tm_sec);
ClockSnapshot clock_snapshot{};
clock_snapshot.system_posix_time = time_since_epoch;
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index 006a6d9ff..07033fb98 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -17,8 +17,8 @@ namespace Service::VI {
Display::Display(u64 id, std::string name, Core::System& system) : id{id}, name{std::move(name)} {
auto& kernel = system.Kernel();
- vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
- fmt::format("Display VSync Event {}", id));
+ vsync_event =
+ Kernel::WritableEvent::CreateEventPair(kernel, fmt::format("Display VSync Event {}", id));
}
Display::~Display() = default;
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 199b30635..abfc3a801 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -45,7 +45,7 @@ struct DisplayInfo {
/// Whether or not the display has a limited number of layers.
u8 has_limited_layers{1};
- INSERT_PADDING_BYTES(7){};
+ INSERT_PADDING_BYTES(7);
/// Indicates the total amount of layers supported by the display.
/// @note This is only valid if has_limited_layers is set.
@@ -541,7 +541,7 @@ private:
} else {
// Wait the current thread until a buffer becomes available
ctx.SleepClientThread(
- "IHOSBinderDriver::DequeueBuffer", -1,
+ "IHOSBinderDriver::DequeueBuffer", UINT64_MAX,
[=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
Kernel::ThreadWakeupReason reason) {
// Repeat TransactParcel DequeueBuffer when a buffer is available