First Commit

This commit is contained in:
2025-02-06 22:24:29 +08:00
parent ed7df4c81e
commit 7539e6a53c
18116 changed files with 6181499 additions and 0 deletions

View File

@@ -0,0 +1,217 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <vector>
#include "common/archives.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/hle/ipc.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/result.h"
#include "core/hle/service/ac/ac.h"
#include "core/hle/service/ac/ac_i.h"
#include "core/hle/service/ac/ac_u.h"
#include "core/hle/service/soc/soc_u.h"
#include "core/memory.h"
SERIALIZE_EXPORT_IMPL(Service::AC::Module)
SERVICE_CONSTRUCT_IMPL(Service::AC::Module)
namespace Service::AC {
void Module::Interface::CreateDefaultConfig(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
std::vector<u8> buffer(sizeof(ACConfig));
std::memcpy(buffer.data(), &ac->default_config, buffer.size());
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(ResultSuccess);
rb.PushStaticBuffer(std::move(buffer), 0);
LOG_WARNING(Service_AC, "(STUBBED) called");
}
void Module::Interface::ConnectAsync(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
rp.Skip(2, false); // ProcessId descriptor
ac->connect_event = rp.PopObject<Kernel::Event>();
rp.Skip(2, false); // Buffer descriptor
if (ac->connect_event) {
ac->connect_event->SetName("AC:connect_event");
ac->connect_event->Signal();
ac->ac_connected = true;
}
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_WARNING(Service_AC, "(STUBBED) called");
}
void Module::Interface::GetConnectResult(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
rp.Skip(2, false); // ProcessId descriptor
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
}
void Module::Interface::CloseAsync(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
rp.Skip(2, false); // ProcessId descriptor
ac->close_event = rp.PopObject<Kernel::Event>();
if (ac->ac_connected && ac->disconnect_event) {
ac->disconnect_event->Signal();
}
if (ac->close_event) {
ac->close_event->SetName("AC:close_event");
ac->close_event->Signal();
}
ac->ac_connected = false;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
}
void Module::Interface::GetCloseResult(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
rp.Skip(2, false); // ProcessId descriptor
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_WARNING(Service_AC, "(STUBBED) called");
}
void Module::Interface::GetWifiStatus(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
bool can_reach_internet = false;
std::shared_ptr<SOC::SOC_U> socu_module = SOC::GetService(ac->system);
if (socu_module) {
can_reach_internet = socu_module->GetDefaultInterfaceInfo().has_value();
}
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push<u32>(static_cast<u32>(can_reach_internet ? (Settings::values.is_new_3ds
? WifiStatus::STATUS_CONNECTED_N3DS
: WifiStatus::STATUS_CONNECTED_O3DS)
: WifiStatus::STATUS_DISCONNECTED));
}
void Module::Interface::GetInfraPriority(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
[[maybe_unused]] const std::vector<u8>& ac_config = rp.PopStaticBuffer();
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push<u32>(0); // Infra Priority, default 0
LOG_WARNING(Service_AC, "(STUBBED) called");
}
void Module::Interface::SetRequestEulaVersion(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
u32 major = rp.Pop<u8>();
u32 minor = rp.Pop<u8>();
const std::vector<u8>& ac_config = rp.PopStaticBuffer();
// TODO(Subv): Copy over the input ACConfig to the stored ACConfig.
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(ResultSuccess);
rb.PushStaticBuffer(std::move(ac_config), 0);
LOG_WARNING(Service_AC, "(STUBBED) called, major={}, minor={}", major, minor);
}
void Module::Interface::RegisterDisconnectEvent(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
rp.Skip(2, false); // ProcessId descriptor
ac->disconnect_event = rp.PopObject<Kernel::Event>();
if (ac->disconnect_event) {
ac->disconnect_event->SetName("AC:disconnect_event");
}
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_WARNING(Service_AC, "(STUBBED) called");
}
void Module::Interface::GetConnectingProxyEnable(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
constexpr bool proxy_enabled = false;
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push(proxy_enabled);
LOG_WARNING(Service_AC, "(STUBBED) called");
}
void Module::Interface::IsConnected(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
u32 unk = rp.Pop<u32>();
u32 unk_descriptor = rp.Pop<u32>();
u32 unk_param = rp.Pop<u32>();
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push(ac->ac_connected);
LOG_WARNING(Service_AC, "(STUBBED) called unk=0x{:08X} descriptor=0x{:08X} param=0x{:08X}", unk,
unk_descriptor, unk_param);
}
void Module::Interface::SetClientVersion(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
u32 version = rp.Pop<u32>();
rp.Skip(2, false); // ProcessId descriptor
LOG_WARNING(Service_AC, "(STUBBED) called, version: 0x{:08X}", version);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
}
Module::Interface::Interface(std::shared_ptr<Module> ac, const char* name, u32 max_session)
: ServiceFramework(name, max_session), ac(std::move(ac)) {}
void InstallInterfaces(Core::System& system) {
auto& service_manager = system.ServiceManager();
auto ac = std::make_shared<Module>(system);
std::make_shared<AC_I>(ac)->InstallAsService(service_manager);
std::make_shared<AC_U>(ac)->InstallAsService(service_manager);
}
Module::Module(Core::System& system_) : system(system_) {}
template <class Archive>
void Module::serialize(Archive& ar, const unsigned int) {
ar& ac_connected;
ar& close_event;
ar& connect_event;
ar& disconnect_event;
// default_config is never written to
}
SERIALIZE_IMPL(Module)
} // namespace Service::AC

View File

@@ -0,0 +1,187 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <memory>
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Kernel {
class Event;
}
namespace Service::AC {
class Module final {
public:
explicit Module(Core::System& system_);
~Module() = default;
class Interface : public ServiceFramework<Interface> {
public:
Interface(std::shared_ptr<Module> ac, const char* name, u32 max_session);
/**
* AC::CreateDefaultConfig service function
* Inputs:
* 64 : ACConfig size << 14 | 2
* 65 : pointer to ACConfig struct
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void CreateDefaultConfig(Kernel::HLERequestContext& ctx);
/**
* AC::ConnectAsync service function
* Inputs:
* 1 : ProcessId Header
* 3 : Copy Handle Header
* 4 : Connection Event handle
* 5 : ACConfig size << 14 | 2
* 6 : pointer to ACConfig struct
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void ConnectAsync(Kernel::HLERequestContext& ctx);
/**
* AC::GetConnectResult service function
* Inputs:
* 1 : ProcessId Header
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void GetConnectResult(Kernel::HLERequestContext& ctx);
/**
* AC::CloseAsync service function
* Inputs:
* 1 : ProcessId Header
* 3 : Copy Handle Header
* 4 : Event handle, should be signaled when AC connection is closed
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void CloseAsync(Kernel::HLERequestContext& ctx);
/**
* AC::GetCloseResult service function
* Inputs:
* 1 : ProcessId Header
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void GetCloseResult(Kernel::HLERequestContext& ctx);
/**
* AC::GetWifiStatus service function
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Output connection type, 0 = none, 1 = Old3DS Internet, 2 = New3DS Internet.
*/
void GetWifiStatus(Kernel::HLERequestContext& ctx);
/**
* AC::GetInfraPriority service function
* Inputs:
* 1 : ACConfig size << 14 | 2
* 2 : pointer to ACConfig struct
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Infra Priority
*/
void GetInfraPriority(Kernel::HLERequestContext& ctx);
/**
* AC::SetRequestEulaVersion service function
* Inputs:
* 1 : Eula Version major
* 2 : Eula Version minor
* 3 : ACConfig size << 14 | 2
* 4 : Input pointer to ACConfig struct
* 64 : ACConfig size << 14 | 2
* 65 : Output pointer to ACConfig struct
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Infra Priority
*/
void SetRequestEulaVersion(Kernel::HLERequestContext& ctx);
/**
* AC::RegisterDisconnectEvent service function
* Inputs:
* 1 : ProcessId Header
* 3 : Copy Handle Header
* 4 : Event handle, should be signaled when AC connection is closed
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void RegisterDisconnectEvent(Kernel::HLERequestContext& ctx);
/**
* AC::GetConnectingProxyEnable service function
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : bool, is proxy enabled
*/
void GetConnectingProxyEnable(Kernel::HLERequestContext& ctx);
/**
* AC::IsConnected service function
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : bool, is connected
*/
void IsConnected(Kernel::HLERequestContext& ctx);
/**
* AC::SetClientVersion service function
* Inputs:
* 1 : Used SDK Version
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void SetClientVersion(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> ac;
};
protected:
enum class WifiStatus {
STATUS_DISCONNECTED = 0,
STATUS_CONNECTED_O3DS = 1,
STATUS_CONNECTED_N3DS = 2,
};
struct ACConfig {
std::array<u8, 0x200> data;
};
ACConfig default_config{};
bool ac_connected = false;
std::shared_ptr<Kernel::Event> close_event;
std::shared_ptr<Kernel::Event> connect_event;
std::shared_ptr<Kernel::Event> disconnect_event;
private:
Core::System& system;
template <class Archive>
void serialize(Archive& ar, const unsigned int);
friend class boost::serialization::access;
};
void InstallInterfaces(Core::System& system);
} // namespace Service::AC
BOOST_CLASS_EXPORT_KEY(Service::AC::Module)
SERVICE_CONSTRUCT(Service::AC::Module)

View File

@@ -0,0 +1,41 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/ac/ac_i.h"
namespace Service::AC {
AC_I::AC_I(std::shared_ptr<Module> ac) : Module::Interface(std::move(ac), "ac:i", 10) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, &AC_I::CreateDefaultConfig, "CreateDefaultConfig"},
{0x0004, &AC_I::ConnectAsync, "ConnectAsync"},
{0x0005, &AC_I::GetConnectResult, "GetConnectResult"},
{0x0007, nullptr, "CancelConnectAsync"},
{0x0008, &AC_I::CloseAsync, "CloseAsync"},
{0x0009, &AC_I::GetCloseResult, "GetCloseResult"},
{0x000A, nullptr, "GetLastErrorCode"},
{0x000C, nullptr, "GetStatus"},
{0x000D, &AC_I::GetWifiStatus, "GetWifiStatus"},
{0x000E, nullptr, "GetCurrentAPInfo"},
{0x0010, nullptr, "GetCurrentNZoneInfo"},
{0x0011, nullptr, "GetNZoneApNumService"},
{0x001D, nullptr, "ScanAPs"},
{0x0024, nullptr, "AddDenyApType"},
{0x0027, &AC_I::GetInfraPriority, "GetInfraPriority"},
{0x002D, &AC_I::SetRequestEulaVersion, "SetRequestEulaVersion"},
{0x0030, &AC_I::RegisterDisconnectEvent, "RegisterDisconnectEvent"},
{0x0036, &AC_I::GetConnectingProxyEnable, "GetConnectingProxyEnable"},
{0x003C, nullptr, "GetAPSSIDList"},
{0x003E, &AC_I::IsConnected, "IsConnected"},
{0x0040, &AC_I::SetClientVersion, "SetClientVersion"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::AC
SERIALIZE_EXPORT_IMPL(Service::AC::AC_I)

View File

@@ -0,0 +1,23 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include "core/hle/service/ac/ac.h"
namespace Service::AC {
class AC_I final : public Module::Interface {
public:
explicit AC_I(std::shared_ptr<Module> ac);
private:
SERVICE_SERIALIZATION(AC_I, ac, Module)
};
} // namespace Service::AC
BOOST_CLASS_EXPORT_KEY(Service::AC::AC_I)
BOOST_SERIALIZATION_CONSTRUCT(Service::AC::AC_I)

View File

@@ -0,0 +1,41 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/ac/ac_u.h"
namespace Service::AC {
AC_U::AC_U(std::shared_ptr<Module> ac) : Module::Interface(std::move(ac), "ac:u", 10) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, &AC_U::CreateDefaultConfig, "CreateDefaultConfig"},
{0x0004, &AC_U::ConnectAsync, "ConnectAsync"},
{0x0005, &AC_U::GetConnectResult, "GetConnectResult"},
{0x0007, nullptr, "CancelConnectAsync"},
{0x0008, &AC_U::CloseAsync, "CloseAsync"},
{0x0009, &AC_U::GetCloseResult, "GetCloseResult"},
{0x000A, nullptr, "GetLastErrorCode"},
{0x000C, nullptr, "GetStatus"},
{0x000D, &AC_U::GetWifiStatus, "GetWifiStatus"},
{0x000E, nullptr, "GetCurrentAPInfo"},
{0x0010, nullptr, "GetCurrentNZoneInfo"},
{0x0011, nullptr, "GetNZoneApNumService"},
{0x001D, nullptr, "ScanAPs"},
{0x0024, nullptr, "AddDenyApType"},
{0x0027, &AC_U::GetInfraPriority, "GetInfraPriority"},
{0x002D, &AC_U::SetRequestEulaVersion, "SetRequestEulaVersion"},
{0x0030, &AC_U::RegisterDisconnectEvent, "RegisterDisconnectEvent"},
{0x0036, &AC_U::GetConnectingProxyEnable, "GetConnectingProxyEnable"},
{0x003C, nullptr, "GetAPSSIDList"},
{0x003E, &AC_U::IsConnected, "IsConnected"},
{0x0040, &AC_U::SetClientVersion, "SetClientVersion"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::AC
SERIALIZE_EXPORT_IMPL(Service::AC::AC_U)

View File

@@ -0,0 +1,23 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include "core/hle/service/ac/ac.h"
namespace Service::AC {
class AC_U final : public Module::Interface {
public:
explicit AC_U(std::shared_ptr<Module> ac);
private:
SERVICE_SERIALIZATION(AC_U, ac, Module)
};
} // namespace Service::AC
BOOST_CLASS_EXPORT_KEY(Service::AC::AC_U)
BOOST_SERIALIZATION_CONSTRUCT(Service::AC::AC_U)

View File

@@ -0,0 +1,69 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/act/act.h"
#include "core/hle/service/act/act_a.h"
#include "core/hle/service/act/act_errors.h"
#include "core/hle/service/act/act_u.h"
namespace Service::ACT {
Module::Interface::Interface(std::shared_ptr<Module> act, const char* name)
: ServiceFramework(name, 3), act(std::move(act)) {}
Module::Interface::~Interface() = default;
void Module::Interface::Initialize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const auto sdk_version = rp.Pop<u32>();
const auto shared_memory_size = rp.Pop<u32>();
const auto caller_pid = rp.PopPID();
[[maybe_unused]] const auto shared_memory = rp.PopObject<Kernel::SharedMemory>();
LOG_DEBUG(Service_ACT,
"(STUBBED) called sdk_version={:08X}, shared_memory_size={:08X}, caller_pid={}",
sdk_version, shared_memory_size, caller_pid);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
}
void Module::Interface::GetErrorCode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const auto result = rp.Pop<Result>();
LOG_DEBUG(Service_ACT, "called result={:08X}", result.raw);
const u32 error_code = GetACTErrorCode(result);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push(error_code);
}
void Module::Interface::GetAccountInfo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const auto account_slot = rp.Pop<u8>();
const auto size = rp.Pop<u32>();
const auto block_id = rp.Pop<u32>();
[[maybe_unused]] auto output_buffer = rp.PopMappedBuffer();
LOG_DEBUG(Service_ACT, "(STUBBED) called account_slot={:02X}, size={:08X}, block_id={:08X}",
account_slot, size, block_id);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
}
void InstallInterfaces(Core::System& system) {
auto& service_manager = system.ServiceManager();
auto act = std::make_shared<Module>();
std::make_shared<ACT_A>(act)->InstallAsService(service_manager);
std::make_shared<ACT_U>(act)->InstallAsService(service_manager);
}
} // namespace Service::ACT

View File

@@ -0,0 +1,72 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::ACT {
/// Initializes all ACT services
class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
Interface(std::shared_ptr<Module> act, const char* name);
~Interface();
protected:
std::shared_ptr<Module> act;
/**
* ACT::Initialize service function.
* Inputs:
* 1 : SDK version
* 2 : Shared Memory Size
* 3 : PID Translation Header (0x20)
* 4 : Caller PID
* 5 : Handle Translation Header (0x0)
* 6 : Shared Memory Handle
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void Initialize(Kernel::HLERequestContext& ctx);
/**
* ACT::GetErrorCode service function.
* Inputs:
* 1 : Result code
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Error code
*/
void GetErrorCode(Kernel::HLERequestContext& ctx);
/**
* ACT::GetAccountInfo service function.
* Inputs:
* 1 : Account slot
* 2 : Size
* 3 : Block ID
* 4 : Output Buffer Mapping Translation Header ((Size << 4) | 0xC)
* 5 : Output Buffer Pointer
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void GetAccountInfo(Kernel::HLERequestContext& ctx);
};
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {}
friend class boost::serialization::access;
};
void InstallInterfaces(Core::System& system);
} // namespace Service::ACT

View File

@@ -0,0 +1,77 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/act/act_a.h"
namespace Service::ACT {
ACT_A::ACT_A(std::shared_ptr<Module> act) : Module::Interface(std::move(act), "act:a") {
const FunctionInfo functions[] = {
// act:u shared commands
// clang-format off
{0x0001, &ACT_A::Initialize, "Initialize"},
{0x0002, &ACT_A::GetErrorCode, "GetErrorCode"},
{0x0003, nullptr, "GetLastResponseCode"},
{0x0005, nullptr, "GetCommonInfo"},
{0x0006, &ACT_A::GetAccountInfo, "GetAccountInfo"},
{0x0007, nullptr, "GetResultAsync"},
{0x0008, nullptr, "GetMiiImageData"},
{0x0009, nullptr, "SetNfsPassword"},
{0x000B, nullptr, "AcquireEulaList"},
{0x000C, nullptr, "AcquireTimeZoneList"},
{0x000D, nullptr, "GenerateUuid"},
{0x000F, nullptr, "FindSlotNoByUuid"},
{0x0010, nullptr, "SaveData"},
{0x0011, nullptr, "GetTransferableId"},
{0x0012, nullptr, "AcquireNexServiceToken"},
{0x0013, nullptr, "GetNexServiceToken"},
{0x0014, nullptr, "AcquireIndependentServiceToken"},
{0x0015, nullptr, "GetIndependentServiceToken"},
{0x0016, nullptr, "AcquireAccountInfo"},
{0x0017, nullptr, "AcquireAccountIdByPrincipalId"},
{0x0018, nullptr, "AcquirePrincipalIdByAccountId"},
{0x0019, nullptr, "AcquireMii"},
{0x001A, nullptr, "AcquireAccountInfoEx"},
{0x001D, nullptr, "InquireMailAddress"},
{0x001E, nullptr, "AcquireEula"},
{0x001F, nullptr, "AcquireEulaLanguageList"},
// act:a
{0x0402, nullptr, "CreateConsoleAccount"},
{0x0403, nullptr, "CommitConsoleAccount"},
{0x0404, nullptr, "UnbindServerAccount"},
{0x0405, nullptr, "DeleteConsoleAccount"},
{0x0407, nullptr, "UnloadConsoleAccount"},
{0x0408, nullptr, "EnableAccountPasswordCache"},
{0x0409, nullptr, "SetDefaultAccount"},
{0x040A, nullptr, "ReplaceAccountId"},
{0x040B, nullptr, "GetSupportContext"},
{0x0412, nullptr, "UpdateMii"},
{0x0413, nullptr, "UpdateMiiImage"},
{0x0414, nullptr, "InquireAccountIdAvailability"},
{0x0415, nullptr, "BindToNewServerAccount"},
{0x0416, nullptr, "BindToExistentServerAccount"},
{0x0417, nullptr, "InquireBindingToExistentServerAccount"},
{0x041A, nullptr, "AcquireAccountTokenEx"},
{0x041B, nullptr, "AgreeEula"},
{0x041C, nullptr, "SyncAccountInfo"},
{0x041E, nullptr, "UpdateAccountPassword"},
{0x041F, nullptr, "ReissueAccountPassword"},
{0x0420, nullptr, "SetAccountPasswordInput"},
{0x0421, nullptr, "UploadMii"},
{0x0423, nullptr, "ValidateMailAddress"},
{0x0423, nullptr, "SendConfirmationMail"},
{0x0428, nullptr, "ApproveByCreditCard"},
{0x0428, nullptr, "SendCoppaCodeMail"},
{0x042F, nullptr, "UpdateAccountInfoEx"},
{0x0430, nullptr, "UpdateAccountMailAddress"},
{0x0435, nullptr, "DeleteServerAccount"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::ACT
SERIALIZE_EXPORT_IMPL(Service::ACT::ACT_A)

View File

@@ -0,0 +1,22 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/act/act.h"
namespace Service::ACT {
class ACT_A final : public Module::Interface {
public:
explicit ACT_A(std::shared_ptr<Module> act);
private:
SERVICE_SERIALIZATION(ACT_A, act, Module)
};
} // namespace Service::ACT
BOOST_CLASS_EXPORT_KEY(Service::ACT::ACT_A)
BOOST_SERIALIZATION_CONSTRUCT(Service::ACT::ACT_A)

View File

@@ -0,0 +1,757 @@
// Copyright 2024 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/act/act_errors.h"
namespace Service::ACT {
u32 GetACTErrorCode(Result result) {
u32 error_code = ErrCodes::Unknown;
if (result.module == ErrorModule::ACT) {
switch (result.description) {
case ErrDescriptions::MySuccess:
error_code = ErrCodes::MySuccess;
break;
case ErrDescriptions::MailAddressNotConfirmed:
error_code = ErrCodes::MailAddressNotConfirmed;
break;
case ErrDescriptions::LibraryError:
error_code = ErrCodes::LibraryError;
break;
case ErrDescriptions::NotInitialized:
error_code = ErrCodes::NotInitialized;
break;
case ErrDescriptions::AlreadyInitialized:
error_code = ErrCodes::AlreadyInitialized;
break;
case ErrDescriptions::ErrDesc103:
error_code = ErrCodes::ErrCode225103;
break;
case ErrDescriptions::ErrDesc104:
error_code = ErrCodes::ErrCode225104;
break;
case ErrDescriptions::Busy:
error_code = ErrCodes::Busy;
break;
case ErrDescriptions::ErrDesc112:
error_code = ErrCodes::ErrCode225112;
break;
case ErrDescriptions::NotImplemented:
error_code = ErrCodes::NotImplemented;
break;
case ErrDescriptions::Deprecated:
error_code = ErrCodes::Deprecated;
break;
case ErrDescriptions::DevelopmentOnly:
error_code = ErrCodes::DevelopmentOnly;
break;
case ErrDescriptions::InvalidArgument:
error_code = ErrCodes::InvalidArgument;
break;
case ErrDescriptions::InvalidPointer:
error_code = ErrCodes::InvalidPointer;
break;
case ErrDescriptions::OutOfRange:
error_code = ErrCodes::OutOfRange;
break;
case ErrDescriptions::InvalidSize:
error_code = ErrCodes::InvalidSize;
break;
case ErrDescriptions::InvalidFormat:
error_code = ErrCodes::InvalidFormat;
break;
case ErrDescriptions::InvalidHandle:
error_code = ErrCodes::InvalidHandle;
break;
case ErrDescriptions::InvalidValue:
error_code = ErrCodes::InvalidValue;
break;
case ErrDescriptions::InternalError:
error_code = ErrCodes::InternalError;
break;
case ErrDescriptions::EndOfStream:
error_code = ErrCodes::EndOfStream;
break;
case ErrDescriptions::FileError:
error_code = ErrCodes::FileError;
break;
case ErrDescriptions::FileNotFound:
error_code = ErrCodes::FileNotFound;
break;
case ErrDescriptions::FileVersionMismatch:
error_code = ErrCodes::FileVersionMismatch;
break;
case ErrDescriptions::FileIOError:
error_code = ErrCodes::FileIOError;
break;
case ErrDescriptions::FileTypeMismatch:
error_code = ErrCodes::FileTypeMismatch;
break;
case ErrDescriptions::ErrDesc315:
error_code = ErrCodes::ErrCode225315;
break;
case ErrDescriptions::OutOfResource:
error_code = ErrCodes::OutOfResource;
break;
case ErrDescriptions::ShortOfBuffer:
error_code = ErrCodes::ShortOfBuffer;
break;
case ErrDescriptions::OutOfMemory:
error_code = ErrCodes::OutOfMemory;
break;
case ErrDescriptions::OutOfGlobalHeap:
error_code = ErrCodes::OutOfGlobalHeap;
break;
case ErrDescriptions::ErrDesc350:
error_code = ErrCodes::ErrCode225350;
break;
case ErrDescriptions::ErrDesc351:
error_code = ErrCodes::ErrCode225351;
break;
case ErrDescriptions::ErrDesc352:
error_code = ErrCodes::ErrCode225352;
break;
case ErrDescriptions::ErrDesc360:
error_code = ErrCodes::ErrCode225360;
break;
case ErrDescriptions::ErrDesc361:
error_code = ErrCodes::ErrCode225361;
break;
case ErrDescriptions::ErrDesc362:
error_code = ErrCodes::ErrCode225362;
break;
case ErrDescriptions::ErrDesc363:
error_code = ErrCodes::ErrCode225363;
break;
case ErrDescriptions::AccountManagementError:
error_code = ErrCodes::AccountManagementError;
break;
case ErrDescriptions::AccountNotFound:
error_code = ErrCodes::AccountNotFound;
break;
case ErrDescriptions::SlotsFull:
error_code = ErrCodes::SlotsFull;
break;
case ErrDescriptions::AccountNotLoaded:
error_code = ErrCodes::AccountNotLoaded;
break;
case ErrDescriptions::AccountAlreadyLoaded:
error_code = ErrCodes::AccountAlreadyLoaded;
break;
case ErrDescriptions::AccountLocked:
error_code = ErrCodes::AccountLocked;
break;
case ErrDescriptions::NotNetworkAccount:
error_code = ErrCodes::NotNetworkAccount;
break;
case ErrDescriptions::NotLocalAccount:
error_code = ErrCodes::NotLocalAccount;
break;
case ErrDescriptions::AccountNotCommited:
error_code = ErrCodes::AccountCommited;
break;
case ErrDescriptions::ErrDesc431:
error_code = ErrCodes::ErrCode225431;
break;
case ErrDescriptions::ErrDesc432:
error_code = ErrCodes::ErrCode225432;
break;
case ErrDescriptions::ErrDesc433:
error_code = ErrCodes::ErrCode225433;
break;
case ErrDescriptions::ErrDesc451:
error_code = ErrCodes::ErrCode221101;
break;
case ErrDescriptions::AuthenticationError:
error_code = ErrCodes::AuthenticationError;
break;
case ErrDescriptions::HttpError:
error_code = ErrCodes::HttpError;
break;
case ErrDescriptions::ErrDesc502:
error_code = ErrCodes::ErrCode225502;
break;
case ErrDescriptions::ErrDesc503:
error_code = ErrCodes::ErrCode225503;
break;
case ErrDescriptions::ErrDesc504:
error_code = ErrCodes::ErrCode225504;
break;
case ErrDescriptions::ErrDesc505:
error_code = ErrCodes::ErrCode225505;
break;
case ErrDescriptions::ErrDesc506:
error_code = ErrCodes::ErrCode225506;
break;
case ErrDescriptions::ErrDesc507:
error_code = ErrCodes::ErrCode225507;
break;
case ErrDescriptions::ErrDesc508:
error_code = ErrCodes::ErrCode225508;
break;
case ErrDescriptions::ErrDesc509:
error_code = ErrCodes::ErrCode225509;
break;
case ErrDescriptions::ErrDesc510:
error_code = ErrCodes::ErrCode225510;
break;
case ErrDescriptions::ErrDesc511:
error_code = ErrCodes::ErrCode225511;
break;
case ErrDescriptions::ErrDesc512:
error_code = ErrCodes::ErrCode225512;
break;
case ErrDescriptions::ErrDesc513:
error_code = ErrCodes::ErrCode225513;
break;
case ErrDescriptions::ErrDesc514:
error_code = ErrCodes::ErrCode225514;
break;
case ErrDescriptions::ErrDesc515:
error_code = ErrCodes::ErrCode225515;
break;
case ErrDescriptions::ErrDesc516:
error_code = ErrCodes::ErrCode225516;
break;
case ErrDescriptions::ErrDesc517:
error_code = ErrCodes::ErrCode225517;
break;
case ErrDescriptions::ErrDesc518:
error_code = ErrCodes::ErrCode225518;
break;
case ErrDescriptions::ErrDesc519:
error_code = ErrCodes::ErrCode225519;
break;
case ErrDescriptions::ErrDesc520:
error_code = ErrCodes::ErrCode225520;
break;
case ErrDescriptions::ErrDesc521:
error_code = ErrCodes::ErrCode225521;
break;
case ErrDescriptions::ErrDesc522:
error_code = ErrCodes::ErrCode225522;
break;
case ErrDescriptions::ErrDesc523:
error_code = ErrCodes::ErrCode225523;
break;
case ErrDescriptions::ErrDesc524:
error_code = ErrCodes::ErrCode225524;
break;
case ErrDescriptions::ErrDesc525:
error_code = ErrCodes::ErrCode225525;
break;
case ErrDescriptions::ErrDesc526:
error_code = ErrCodes::ErrCode225526;
break;
case ErrDescriptions::ErrDesc527:
error_code = ErrCodes::ErrCode225527;
break;
case ErrDescriptions::ErrDesc528:
error_code = ErrCodes::ErrCode225528;
break;
case ErrDescriptions::ErrDesc529:
error_code = ErrCodes::ErrCode225529;
break;
case ErrDescriptions::ErrDesc530:
error_code = ErrCodes::ErrCode225530;
break;
case ErrDescriptions::ErrDesc531:
error_code = ErrCodes::ErrCode225531;
break;
case ErrDescriptions::ErrDesc532:
error_code = ErrCodes::ErrCode225532;
break;
case ErrDescriptions::ErrDesc533:
error_code = ErrCodes::ErrCode225533;
break;
case ErrDescriptions::ErrDesc534:
error_code = ErrCodes::ErrCode225534;
break;
case ErrDescriptions::ErrDesc535:
error_code = ErrCodes::ErrCode225535;
break;
case ErrDescriptions::ErrDesc536:
error_code = ErrCodes::ErrCode225536;
break;
case ErrDescriptions::ErrDesc537:
error_code = ErrCodes::ErrCode225537;
break;
case ErrDescriptions::ErrDesc538:
error_code = ErrCodes::ErrCode225538;
break;
case ErrDescriptions::ErrDesc539:
error_code = ErrCodes::ErrCode225539;
break;
case ErrDescriptions::ErrDesc540:
error_code = ErrCodes::ErrCode225540;
break;
case ErrDescriptions::ErrDesc541:
error_code = ErrCodes::ErrCode225541;
break;
case ErrDescriptions::ErrDesc542:
error_code = ErrCodes::ErrCode225542;
break;
case ErrDescriptions::ErrDesc543:
error_code = ErrCodes::ErrCode225543;
break;
case ErrDescriptions::ErrDesc544:
error_code = ErrCodes::ErrCode225544;
break;
case ErrDescriptions::ErrDesc545:
error_code = ErrCodes::ErrCode225545;
break;
case ErrDescriptions::ErrDesc546:
error_code = ErrCodes::ErrCode225546;
break;
case ErrDescriptions::ErrDesc547:
error_code = ErrCodes::ErrCode225547;
break;
case ErrDescriptions::ErrDesc548:
error_code = ErrCodes::ErrCode225548;
break;
case ErrDescriptions::ErrDesc549:
error_code = ErrCodes::ErrCode225549;
break;
case ErrDescriptions::ErrDesc550:
error_code = ErrCodes::ErrCode225550;
break;
case ErrDescriptions::ErrDesc551:
error_code = ErrCodes::ErrCode225551;
break;
case ErrDescriptions::ErrDesc552:
error_code = ErrCodes::ErrCode225552;
break;
case ErrDescriptions::ErrDesc553:
error_code = ErrCodes::ErrCode225553;
break;
case ErrDescriptions::RequestError:
error_code = ErrCodes::RequestError;
break;
case ErrDescriptions::BadFormatParameter:
error_code = ErrCodes::BadFormatParameter;
break;
case ErrDescriptions::BadFormatRequest:
error_code = ErrCodes::BadFormatRequest;
break;
case ErrDescriptions::RequestParameterMissing:
error_code = ErrCodes::RequestParameterMissing;
break;
case ErrDescriptions::WrongHttpMethod:
error_code = ErrCodes::WrongHttpMethod;
break;
case ErrDescriptions::ResponseError:
error_code = ErrCodes::ResponseError;
break;
case ErrDescriptions::BadFormatResponse:
error_code = ErrCodes::BadFormatResponse;
break;
case ErrDescriptions::ResponseItemMissing:
error_code = ErrCodes::ResponseItemMissing;
break;
case ErrDescriptions::ResponseTooLarge:
error_code = ErrCodes::ResponseTooLarge;
break;
case ErrDescriptions::InvalidCommonParameter:
error_code = ErrCodes::InvalidCommonParameter;
break;
case ErrDescriptions::InvalidPlatformId:
error_code = ErrCodes::InvalidPlatformId;
break;
case ErrDescriptions::UnauthorizedDevice:
error_code = ErrCodes::UnauthorizedDevice;
break;
case ErrDescriptions::InvalidSerialId:
error_code = ErrCodes::InvalidSerialId;
break;
case ErrDescriptions::InvalidMacAddress:
error_code = ErrCodes::InvalidMacAddress;
break;
case ErrDescriptions::InvalidRegion:
error_code = ErrCodes::InvalidRegion;
break;
case ErrDescriptions::InvalidCountry:
error_code = ErrCodes::InvalidCountry;
break;
case ErrDescriptions::InvalidLanguage:
error_code = ErrCodes::InvalidLanguage;
break;
case ErrDescriptions::UnauthorizedClient:
error_code = ErrCodes::UnauthorizedClient;
break;
case ErrDescriptions::DeviceIdEmpty:
error_code = ErrCodes::DeviceIdEmpty;
break;
case ErrDescriptions::SerialIdEmpty:
error_code = ErrCodes::SerialIdEmpty;
break;
case ErrDescriptions::PlatformIdEmpty:
error_code = ErrCodes::PlatformIdEmpty;
break;
case ErrDescriptions::InvalidUniqueId:
error_code = ErrCodes::InvalidUniqueId;
break;
case ErrDescriptions::InvalidClientId:
error_code = ErrCodes::InvalidClientId;
break;
case ErrDescriptions::InvalidClientKey:
error_code = ErrCodes::InvalidClientKey;
break;
case ErrDescriptions::InvalidNexClientId:
error_code = ErrCodes::InvalidNexClientId;
break;
case ErrDescriptions::InvalidGameServerId:
error_code = ErrCodes::InvalidGameServerId;
break;
case ErrDescriptions::GameServerIdEnvironmentNotFound:
error_code = ErrCodes::GameServerIdEnvironmentNotFound;
break;
case ErrDescriptions::GameServerIdUniqueIdNotLinked:
error_code = ErrCodes::GameServerIdUniqueIdNotLinked;
break;
case ErrDescriptions::ClientIdUniqueIdNotLinked:
error_code = ErrCodes::ClientIdUniqueIdNotLinked;
break;
case ErrDescriptions::DeviceMismatch:
error_code = ErrCodes::DeviceMismatch;
break;
case ErrDescriptions::CountryMismatch:
error_code = ErrCodes::CountryMismatch;
break;
case ErrDescriptions::EulaNotAccepted:
error_code = ErrCodes::EulaNotAccepted;
break;
case ErrDescriptions::UpdateRequired:
error_code = ErrCodes::UpdateRequired;
break;
case ErrDescriptions::SystemUpdateRequired:
error_code = ErrCodes::SystemUpdateRequired;
break;
case ErrDescriptions::ApplicationUpdateRequired:
error_code = ErrCodes::ApplicationUpdateRequired;
break;
case ErrDescriptions::UnauthorizedRequest:
error_code = ErrCodes::UnauthorizedRequest;
break;
case ErrDescriptions::RequestForbidden:
error_code = ErrCodes::RequestForbidden;
break;
case ErrDescriptions::ResourceNotFound:
error_code = ErrCodes::ResourceNotFound;
break;
case ErrDescriptions::PidNotFound:
error_code = ErrCodes::PidNotFound;
break;
case ErrDescriptions::NexAccountNotFound:
error_code = ErrCodes::NexAccountNotFound;
break;
case ErrDescriptions::GenerateTokenFailure:
error_code = ErrCodes::GenerateTokenFailure;
break;
case ErrDescriptions::RequestNotFound:
error_code = ErrCodes::RequestNotFound;
break;
case ErrDescriptions::MasterPinNotFound:
error_code = ErrCodes::MasterPinNotFound;
break;
case ErrDescriptions::MailTextNotFound:
error_code = ErrCodes::MailTextNotFound;
break;
case ErrDescriptions::SendMailFailure:
error_code = ErrCodes::SendMailFailure;
break;
case ErrDescriptions::ApprovalIdNotFound:
error_code = ErrCodes::ApprovalIdNotFound;
break;
case ErrDescriptions::InvalidEulaParameter:
error_code = ErrCodes::InvalidEulaParameter;
break;
case ErrDescriptions::InvalidEulaCountry:
error_code = ErrCodes::InvalidEulaCountry;
break;
case ErrDescriptions::InvalidEulaCountryAndVersion:
error_code = ErrCodes::InvalidEulaCountryAndVersion;
break;
case ErrDescriptions::EulaNotFound:
error_code = ErrCodes::EulaNotFound;
break;
case ErrDescriptions::PhraseNotAcceptable:
error_code = ErrCodes::PhraseNotAcceptable;
break;
case ErrDescriptions::AccountIdAlreadyExists:
error_code = ErrCodes::AccountIdAlreadyExists;
break;
case ErrDescriptions::AccountIdNotAcceptable:
error_code = ErrCodes::AccountIdNotAcceptable;
break;
case ErrDescriptions::AccountPasswordNotAcceptable:
error_code = ErrCodes::AccountPasswordNotAcceptable;
break;
case ErrDescriptions::MiiNameNotAcceptable:
error_code = ErrCodes::MiiNameNotAcceptable;
break;
case ErrDescriptions::MailAddressNotAcceptable:
error_code = ErrCodes::MailAddressNotAcceptable;
break;
case ErrDescriptions::AccountIdFormatInvalid:
error_code = ErrCodes::AccountIdFormatInvalid;
break;
case ErrDescriptions::AccountIdPasswordSame:
error_code = ErrCodes::AccountIdPasswordSame;
break;
case ErrDescriptions::AccountIdCharNotAcceptable:
error_code = ErrCodes::AccountIdCharNotAcceptable;
break;
case ErrDescriptions::AccountIdSuccessiveSymbol:
error_code = ErrCodes::AccountIdSuccessiveSymbol;
break;
case ErrDescriptions::AccountIdSymbolPositionNotAcceptable:
error_code = ErrCodes::AccountIdSymbolPositionNotAcceptable;
break;
case ErrDescriptions::AccountIdTooManyDigit:
error_code = ErrCodes::AccountIdTooManyDigit;
break;
case ErrDescriptions::AccountPasswordCharNotAcceptable:
error_code = ErrCodes::AccountPasswordCharNotAcceptable;
break;
case ErrDescriptions::AccountPasswordTooFewCharTypes:
error_code = ErrCodes::AccountPasswordTooFewCharTypes;
break;
case ErrDescriptions::AccountPasswordSuccessiveSameChar:
error_code = ErrCodes::AccountPasswordSuccessiveSameChar;
break;
case ErrDescriptions::MailAddressDomainNameNotAcceptable:
error_code = ErrCodes::MailAddressDomainNameNotAcceptable;
break;
case ErrDescriptions::MailAddressDomainNameNotResolved:
error_code = ErrCodes::MailAddressDomainNameNotResolved;
break;
case ErrDescriptions::ErrDesc787:
error_code = ErrCodes::ErrCode222587;
break;
case ErrDescriptions::ReachedAssociationLimit:
error_code = ErrCodes::ReachedAssociationLimit;
break;
case ErrDescriptions::ReachedRegistrationLimit:
error_code = ErrCodes::ReachedRegistrationLimit;
break;
case ErrDescriptions::CoppaNotAccepted:
error_code = ErrCodes::CoppaNotAccepted;
break;
case ErrDescriptions::ParentalControlsRequired:
error_code = ErrCodes::ParentalControlsRequired;
break;
case ErrDescriptions::MiiNotRegistered:
error_code = ErrCodes::MiiNotRegistered;
break;
case ErrDescriptions::DeviceEulaCountryMismatch:
error_code = ErrCodes::DeviceEulaCountryMismatch;
break;
case ErrDescriptions::PendingMigration:
error_code = ErrCodes::PendingMigration;
break;
case ErrDescriptions::WrongUserInput:
error_code = ErrCodes::WrongUserInput;
break;
case ErrDescriptions::WrongAccountPassword:
error_code = ErrCodes::WrongAccountPassword;
break;
case ErrDescriptions::WrongMailAddress:
error_code = ErrCodes::WrongMailAddress;
break;
case ErrDescriptions::WrongAccountPasswordOrMailAddress:
error_code = ErrCodes::WrongAccountPasswordOrMailAddress;
break;
case ErrDescriptions::WrongConfirmationCode:
error_code = ErrCodes::WrongConfirmationCode;
break;
case ErrDescriptions::WrongBirthDateOrMailAddress:
error_code = ErrCodes::WrongBirthDateOrMailAddress;
break;
case ErrDescriptions::WrongAccountMail:
error_code = ErrCodes::WrongAccountMail;
break;
case ErrDescriptions::AccountAlreadyDeleted:
error_code = ErrCodes::AccountAlreadyDeleted;
break;
case ErrDescriptions::AccountIdChanged:
error_code = ErrCodes::AccountIdChanged;
break;
case ErrDescriptions::AuthenticationLocked:
error_code = ErrCodes::AuthenticationLocked;
break;
case ErrDescriptions::DeviceInactive:
error_code = ErrCodes::DeviceInactive;
break;
case ErrDescriptions::CoppaAgreementCanceled:
error_code = ErrCodes::CoppaAgreementCanceled;
break;
case ErrDescriptions::DomainAccountAlreadyExists:
error_code = ErrCodes::DomainAccountAlreadyExists;
break;
case ErrDescriptions::AccountTokenExpired:
error_code = ErrCodes::AccountTokenExpired;
break;
case ErrDescriptions::InvalidAccountToken:
error_code = ErrCodes::InvalidAccountToken;
break;
case ErrDescriptions::AuthenticationRequired:
error_code = ErrCodes::AuthenticationRequired;
break;
case ErrDescriptions::ErrDesc844:
error_code = ErrCodes::ErrCode225844;
break;
case ErrDescriptions::ConfirmationCodeExpired:
error_code = ErrCodes::ConfirmationCodeExpired;
break;
case ErrDescriptions::MailAddressNotValidated:
error_code = ErrCodes::MailAddressNotValidated;
break;
case ErrDescriptions::ExcessiveMailSendRequest:
error_code = ErrCodes::ExcessiveMailSendRequest;
break;
case ErrDescriptions::CreditCardError:
error_code = ErrCodes::CreditCardError;
break;
case ErrDescriptions::CreditCardGeneralFailure:
error_code = ErrCodes::CreditCardGeneralFailure;
break;
case ErrDescriptions::CreditCardDeclined:
error_code = ErrCodes::CreditCardDeclined;
break;
case ErrDescriptions::CreditCardBlacklisted:
error_code = ErrCodes::CreditCardBlacklisted;
break;
case ErrDescriptions::InvalidCreditCardNumber:
error_code = ErrCodes::InvalidCreditCardNumber;
break;
case ErrDescriptions::InvalidCreditCardDate:
error_code = ErrCodes::InvalidCreditCardDate;
break;
case ErrDescriptions::InvalidCreditCardPin:
error_code = ErrCodes::InvalidCreditCardPin;
break;
case ErrDescriptions::InvalidPostalCode:
error_code = ErrCodes::InvalidPostalCode;
break;
case ErrDescriptions::InvalidLocation:
error_code = ErrCodes::InvalidLocation;
break;
case ErrDescriptions::CreditCardDateExpired:
error_code = ErrCodes::CreditCardDateExpired;
break;
case ErrDescriptions::CreditCardNumberWrong:
error_code = ErrCodes::CreditCardNumberWrong;
break;
case ErrDescriptions::CreditCardPinWrong:
error_code = ErrCodes::CreditCardPinWrong;
break;
case ErrDescriptions::Banned:
error_code = ErrCodes::Banned;
break;
case ErrDescriptions::BannedAccount:
error_code = ErrCodes::BannedAccount;
break;
case ErrDescriptions::BannedAccountAll:
error_code = ErrCodes::BannedAccountAll;
break;
case ErrDescriptions::BannedAccountInApplication:
error_code = ErrCodes::BannedAccountInApplication;
break;
case ErrDescriptions::BannedAccountInNexService:
error_code = ErrCodes::BannedAccountInNexService;
break;
case ErrDescriptions::BannedAccountInIndependentService:
error_code = ErrCodes::BannedAccountInIndependentService;
break;
case ErrDescriptions::BannedDevice:
error_code = ErrCodes::BannedDevice;
break;
case ErrDescriptions::BannedDeviceAll:
error_code = ErrCodes::BannedDeviceAll;
break;
case ErrDescriptions::BannedDeviceInApplication:
error_code = ErrCodes::BannedDeviceInApplication;
break;
case ErrDescriptions::BannedDeviceInNexService:
error_code = ErrCodes::BannedDeviceInNexService;
break;
case ErrDescriptions::BannedDeviceInIndependentService:
error_code = ErrCodes::BannedDeviceInIndependentService;
break;
case ErrDescriptions::BannedAccountTemporarily:
error_code = ErrCodes::BannedAccountTemporarily;
break;
case ErrDescriptions::BannedAccountAllTemporarily:
error_code = ErrCodes::BannedAccountAllTemporarily;
break;
case ErrDescriptions::BannedAccountInApplicationTemporarily:
error_code = ErrCodes::BannedAccountInApplicationTemporarily;
break;
case ErrDescriptions::BannedAccountInNexServiceTemporarily:
error_code = ErrCodes::BannedAccountInNexServiceTemporarily;
break;
case ErrDescriptions::BannedAccountInIndependentServiceTemporarily:
error_code = ErrCodes::BannedAccountInIndependentServiceTemporarily;
break;
case ErrDescriptions::BannedDeviceTemporarily:
error_code = ErrCodes::BannedDeviceTemporarily;
break;
case ErrDescriptions::BannedDeviceAllTemporarily:
error_code = ErrCodes::BannedDeviceAllTemporarily;
break;
case ErrDescriptions::BannedDeviceInApplicationTemporarily:
error_code = ErrCodes::BannedDeviceInApplicationTemporarily;
break;
case ErrDescriptions::BannedDeviceInNexServiceTemporarily:
error_code = ErrCodes::BannedDeviceInNexServiceTemporarily;
break;
case ErrDescriptions::BannedDeviceInIndependentServiceTemporarily:
error_code = ErrCodes::BannedDeviceInIndependentServiceTemporarily;
break;
case ErrDescriptions::ServiceNotProvided:
error_code = ErrCodes::ServiceNotProvided;
break;
case ErrDescriptions::UnderMaintenance:
error_code = ErrCodes::UnderMaintenance;
break;
case ErrDescriptions::ServiceClosed:
error_code = ErrCodes::ServiceClosed;
break;
case ErrDescriptions::NintendoNetworkClosed:
error_code = ErrCodes::NintendoNetworkClosed;
break;
case ErrDescriptions::NotProvidedCountry:
error_code = ErrCodes::NotProvidedCountry;
break;
case ErrDescriptions::RestrictionError:
error_code = ErrCodes::RestrictionError;
break;
case ErrDescriptions::RestrictedByAge:
error_code = ErrCodes::RestrictedByAge;
break;
case ErrDescriptions::RestrictedByParentalControls:
error_code = ErrCodes::RestrictedByParentalControls;
break;
case ErrDescriptions::OnGameInternetCommunicationRestricted:
error_code = ErrCodes::OnGameInternetCommunicationRestricted;
break;
case ErrDescriptions::InternalServerError:
error_code = ErrCodes::InternalServerError;
break;
case ErrDescriptions::UnknownServerError:
error_code = ErrCodes::UnknownServerError;
break;
case ErrDescriptions::UnauthenticatedAfterSalvage:
error_code = ErrCodes::UnauthenticatedAfterSalvage;
break;
case ErrDescriptions::AuthenticationFailureUnknown:
error_code = ErrCodes::AuthenticationFailureUnknown;
break;
}
}
return error_code;
}
} // namespace Service::ACT

View File

@@ -0,0 +1,613 @@
// Copyright 2024 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Service::ACT {
namespace ErrDescriptions {
enum {
MySuccess = 0,
MailAddressNotConfirmed = 1,
// Library errors
LibraryError = 100,
NotInitialized = 101,
AlreadyInitialized = 102,
ErrDesc103 = 103,
ErrDesc104 = 104,
Busy = 111,
ErrDesc112 = 112,
NotImplemented = 191,
Deprecated = 192,
DevelopmentOnly = 193,
InvalidArgument = 200,
InvalidPointer = 201,
OutOfRange = 202,
InvalidSize = 203,
InvalidFormat = 204,
InvalidHandle = 205,
InvalidValue = 206,
InternalError = 300,
EndOfStream = 301,
FileError = 310,
FileNotFound = 311,
FileVersionMismatch = 312,
FileIOError = 313,
FileTypeMismatch = 314,
ErrDesc315 = 315,
OutOfResource = 330,
ShortOfBuffer = 331,
OutOfMemory = 340,
OutOfGlobalHeap = 341,
ErrDesc350 = 350,
ErrDesc351 = 351,
ErrDesc352 = 352,
ErrDesc360 = 360,
ErrDesc361 = 361,
ErrDesc362 = 362,
ErrDesc363 = 363,
// Account management errors
AccountManagementError = 400,
AccountNotFound = 401,
SlotsFull = 402,
AccountNotLoaded = 411,
AccountAlreadyLoaded = 412,
AccountLocked = 413,
NotNetworkAccount = 421,
NotLocalAccount = 422,
AccountNotCommited = 423,
ErrDesc431 = 431,
ErrDesc432 = 432,
ErrDesc433 = 433,
ErrDesc451 = 451,
AuthenticationError = 500,
// HTTP errors
HttpError = 501,
ErrDesc502 = 502,
ErrDesc503 = 503,
ErrDesc504 = 504,
ErrDesc505 = 505,
ErrDesc506 = 506,
ErrDesc507 = 507,
ErrDesc508 = 508,
ErrDesc509 = 509,
ErrDesc510 = 510,
ErrDesc511 = 511,
ErrDesc512 = 512,
ErrDesc513 = 513,
ErrDesc514 = 514,
ErrDesc515 = 515,
ErrDesc516 = 516,
ErrDesc517 = 517,
ErrDesc518 = 518,
ErrDesc519 = 519,
ErrDesc520 = 520,
ErrDesc521 = 521,
ErrDesc522 = 522,
ErrDesc523 = 523,
ErrDesc524 = 524,
ErrDesc525 = 525,
ErrDesc526 = 526,
ErrDesc527 = 527,
ErrDesc528 = 528,
ErrDesc529 = 529,
ErrDesc530 = 530,
ErrDesc531 = 531,
ErrDesc532 = 532,
ErrDesc533 = 533,
ErrDesc534 = 534,
ErrDesc535 = 535,
ErrDesc536 = 536,
ErrDesc537 = 537,
ErrDesc538 = 538,
ErrDesc539 = 539,
ErrDesc540 = 540,
ErrDesc541 = 541,
ErrDesc542 = 542,
ErrDesc543 = 543,
ErrDesc544 = 544,
ErrDesc545 = 545,
ErrDesc546 = 546,
ErrDesc547 = 547,
ErrDesc548 = 548,
ErrDesc549 = 549,
ErrDesc550 = 550,
ErrDesc551 = 551,
ErrDesc552 = 552,
ErrDesc553 = 553,
// Request errors
RequestError = 600,
BadFormatParameter = 601,
BadFormatRequest = 602,
RequestParameterMissing = 603,
WrongHttpMethod = 604,
// Response errors
ResponseError = 620,
BadFormatResponse = 621,
ResponseItemMissing = 622,
ResponseTooLarge = 623,
// Invalid parameter errors
InvalidCommonParameter = 650,
InvalidPlatformId = 651,
UnauthorizedDevice = 652,
InvalidSerialId = 653,
InvalidMacAddress = 654,
InvalidRegion = 655,
InvalidCountry = 656,
InvalidLanguage = 657,
UnauthorizedClient = 658,
DeviceIdEmpty = 659,
SerialIdEmpty = 660,
PlatformIdEmpty = 661,
InvalidUniqueId = 671,
InvalidClientId = 672,
InvalidClientKey = 673,
InvalidNexClientId = 681,
InvalidGameServerId = 682,
GameServerIdEnvironmentNotFound = 683,
GameServerIdUniqueIdNotLinked = 684,
ClientIdUniqueIdNotLinked = 685,
DeviceMismatch = 701,
CountryMismatch = 702,
EulaNotAccepted = 703,
// Update required errors
UpdateRequired = 710,
SystemUpdateRequired = 711,
ApplicationUpdateRequired = 712,
UnauthorizedRequest = 720,
RequestForbidden = 722,
// Resource not found errors
ResourceNotFound = 730,
PidNotFound = 731,
NexAccountNotFound = 732,
GenerateTokenFailure = 733,
RequestNotFound = 734,
MasterPinNotFound = 735,
MailTextNotFound = 736,
SendMailFailure = 737,
ApprovalIdNotFound = 738,
// EULA errors
InvalidEulaParameter = 740,
InvalidEulaCountry = 741,
InvalidEulaCountryAndVersion = 742,
EulaNotFound = 743,
// Not acceptable errors
PhraseNotAcceptable = 770,
AccountIdAlreadyExists = 771,
AccountIdNotAcceptable = 772,
AccountPasswordNotAcceptable = 773,
MiiNameNotAcceptable = 774,
MailAddressNotAcceptable = 775,
AccountIdFormatInvalid = 776,
AccountIdPasswordSame = 777,
AccountIdCharNotAcceptable = 778,
AccountIdSuccessiveSymbol = 779,
AccountIdSymbolPositionNotAcceptable = 780,
AccountIdTooManyDigit = 781,
AccountPasswordCharNotAcceptable = 782,
AccountPasswordTooFewCharTypes = 783,
AccountPasswordSuccessiveSameChar = 784,
MailAddressDomainNameNotAcceptable = 785,
MailAddressDomainNameNotResolved = 786,
ErrDesc787 = 787,
ReachedAssociationLimit = 791,
ReachedRegistrationLimit = 792,
CoppaNotAccepted = 793,
ParentalControlsRequired = 794,
MiiNotRegistered = 795,
DeviceEulaCountryMismatch = 796,
PendingMigration = 797,
// Wrong user input errors
WrongUserInput = 810,
WrongAccountPassword = 811,
WrongMailAddress = 812,
WrongAccountPasswordOrMailAddress = 813,
WrongConfirmationCode = 814,
WrongBirthDateOrMailAddress = 815,
WrongAccountMail = 816,
AccountAlreadyDeleted = 831,
AccountIdChanged = 832,
AuthenticationLocked = 833,
DeviceInactive = 834,
CoppaAgreementCanceled = 835,
DomainAccountAlreadyExists = 836,
AccountTokenExpired = 841,
InvalidAccountToken = 842,
AuthenticationRequired = 843,
ErrDesc844 = 844,
ConfirmationCodeExpired = 851,
MailAddressNotValidated = 861,
ExcessiveMailSendRequest = 862,
// Credit card errors
CreditCardError = 870,
CreditCardGeneralFailure = 871,
CreditCardDeclined = 872,
CreditCardBlacklisted = 873,
InvalidCreditCardNumber = 874,
InvalidCreditCardDate = 875,
InvalidCreditCardPin = 876,
InvalidPostalCode = 877,
InvalidLocation = 878,
CreditCardDateExpired = 879,
CreditCardNumberWrong = 880,
CreditCardPinWrong = 881,
// Ban errors
Banned = 900,
BannedAccount = 901,
BannedAccountAll = 902,
BannedAccountInApplication = 903,
BannedAccountInNexService = 904,
BannedAccountInIndependentService = 905,
BannedDevice = 911,
BannedDeviceAll = 912,
BannedDeviceInApplication = 913,
BannedDeviceInNexService = 914,
BannedDeviceInIndependentService = 915,
BannedAccountTemporarily = 921,
BannedAccountAllTemporarily = 922,
BannedAccountInApplicationTemporarily = 923,
BannedAccountInNexServiceTemporarily = 924,
BannedAccountInIndependentServiceTemporarily = 925,
BannedDeviceTemporarily = 931,
BannedDeviceAllTemporarily = 932,
BannedDeviceInApplicationTemporarily = 933,
BannedDeviceInNexServiceTemporarily = 934,
BannedDeviceInIndependentServiceTemporarily = 935,
// Service not provided errors
ServiceNotProvided = 950,
UnderMaintenance = 951,
ServiceClosed = 952,
NintendoNetworkClosed = 953,
NotProvidedCountry = 954,
// Restriction errors
RestrictionError = 970,
RestrictedByAge = 971,
RestrictedByParentalControls = 980,
OnGameInternetCommunicationRestricted = 981,
InternalServerError = 991,
UnknownServerError = 992,
UnauthenticatedAfterSalvage = 998,
AuthenticationFailureUnknown = 999,
};
}
namespace ErrCodes {
enum {
MySuccess = 220000, // 022-0000
MailAddressNotConfirmed = 220001, // 022-0001
// Library errors
LibraryError = 220500, // 022-0500
NotInitialized = 220501, // 022-0501
AlreadyInitialized = 220502, // 022-0502
ErrCode225103 = 225103, // 022-5103
ErrCode225104 = 225104, // 022-5104
Busy = 220511, // 022-0511
ErrCode225112 = 225112, // 022-5112
NotImplemented = 220591, // 022-0591
Deprecated = 220592, // 022-0592
DevelopmentOnly = 220593, // 022-0593
InvalidArgument = 220600, // 022-0600
InvalidPointer = 220601, // 022-0601
OutOfRange = 220602, // 022-0602
InvalidSize = 220603, // 022-0603
InvalidFormat = 220604, // 022-0604
InvalidHandle = 220605, // 022-0605
InvalidValue = 220606, // 022-0606
InternalError = 220700, // 022-0700
EndOfStream = 220701, // 022-0701
FileError = 220710, // 022-0710
FileNotFound = 220711, // 022-0711
FileVersionMismatch = 220712, // 022-0712
FileIOError = 220713, // 022-0713
FileTypeMismatch = 220714, // 022-0714
ErrCode225315 = 225315, // 022-5315
OutOfResource = 220730, // 022-0730
ShortOfBuffer = 220731, // 022-0731
OutOfMemory = 220740, // 022-0740
OutOfGlobalHeap = 220741, // 022-0741
ErrCode225350 = 225350, // 022-5350
ErrCode225351 = 225351, // 022-5351
ErrCode225352 = 225352, // 022-5352
ErrCode225360 = 225360, // 022-5360
ErrCode225361 = 225361, // 022-5361
ErrCode225362 = 225362, // 022-5362
ErrCode225363 = 225363, // 022-5363
// Account management errors
AccountManagementError = 221000, // 022-1000
AccountNotFound = 221001, // 022-1001
SlotsFull = 221002, // 022-1002
AccountNotLoaded = 221011, // 022-1011
AccountAlreadyLoaded = 221012, // 022-1012
AccountLocked = 221013, // 022-1013
NotNetworkAccount = 221021, // 022-1021
NotLocalAccount = 221022, // 022-1022
AccountCommited = 221023, // 022-1023
ErrCode225431 = 225431, // 022-5431
ErrCode225432 = 225432, // 022-5432
ErrCode225433 = 225433, // 022-5433
ErrCode221101 = 221101, // 022-1101
AuthenticationError = 222000, // 022-2000
// HTTP errors
HttpError = 222100, // 022-2100
ErrCode225502 = 225502, // 022-5502
ErrCode225503 = 225503, // 022-5503
ErrCode225504 = 225504, // 022-5504
ErrCode225505 = 225505, // 022-5505
ErrCode225506 = 225506, // 022-5506
ErrCode225507 = 225507, // 022-5507
ErrCode225508 = 225508, // 022-5508
ErrCode225509 = 225509, // 022-5509
ErrCode225510 = 225510, // 022-5510
ErrCode225511 = 225511, // 022-5511
ErrCode225512 = 225512, // 022-5512
ErrCode225513 = 225513, // 022-5513
ErrCode225514 = 225514, // 022-5514
ErrCode225515 = 225515, // 022-5515
ErrCode225516 = 225516, // 022-5516
ErrCode225517 = 225517, // 022-5517
ErrCode225518 = 225518, // 022-5518
ErrCode225519 = 225519, // 022-5519
ErrCode225520 = 225520, // 022-5520
ErrCode225521 = 225521, // 022-5521
ErrCode225522 = 225522, // 022-5522
ErrCode225523 = 225523, // 022-5523
ErrCode225524 = 225524, // 022-5524
ErrCode225525 = 225525, // 022-5525
ErrCode225526 = 225526, // 022-5526
ErrCode225527 = 225527, // 022-5527
ErrCode225528 = 225528, // 022-5528
ErrCode225529 = 225529, // 022-5529
ErrCode225530 = 225530, // 022-5530
ErrCode225531 = 225531, // 022-5531
ErrCode225532 = 225532, // 022-5532
ErrCode225533 = 225533, // 022-5533
ErrCode225534 = 225534, // 022-5534
ErrCode225535 = 225535, // 022-5535
ErrCode225536 = 225536, // 022-5536
ErrCode225537 = 225537, // 022-5537
ErrCode225538 = 225538, // 022-5538
ErrCode225539 = 225539, // 022-5539
ErrCode225540 = 225540, // 022-5540
ErrCode225541 = 225541, // 022-5541
ErrCode225542 = 225542, // 022-5542
ErrCode225543 = 225543, // 022-5543
ErrCode225544 = 225544, // 022-5544
ErrCode225545 = 225545, // 022-5545
ErrCode225546 = 225546, // 022-5546
ErrCode225547 = 225547, // 022-5547
ErrCode225548 = 225548, // 022-5548
ErrCode225549 = 225549, // 022-5549
ErrCode225550 = 225550, // 022-5550
ErrCode225551 = 225551, // 022-5551
ErrCode225552 = 225552, // 022-5552
ErrCode225553 = 225553, // 022-5553
// Request errors
RequestError = 222400, // 022-2400
BadFormatParameter = 222401, // 022-2401
BadFormatRequest = 222402, // 022-2402
RequestParameterMissing = 222403, // 022-2403
WrongHttpMethod = 222404, // 022-2404
// Response errors
ResponseError = 222420, // 022-2420
BadFormatResponse = 222421, // 022-2421
ResponseItemMissing = 222422, // 022-2422
ResponseTooLarge = 222423, // 022-2423
// Invalid parameter errors
InvalidCommonParameter = 222450, // 022-2450
InvalidPlatformId = 222451, // 022-2451
UnauthorizedDevice = 222452, // 022-2452
InvalidSerialId = 222453, // 022-2453
InvalidMacAddress = 222454, // 022-2454
InvalidRegion = 222455, // 022-2455
InvalidCountry = 222456, // 022-2456
InvalidLanguage = 222457, // 022-2457
UnauthorizedClient = 222458, // 022-2458
DeviceIdEmpty = 222459, // 022-2459
SerialIdEmpty = 222460, // 022-2460
PlatformIdEmpty = 222461, // 022-2461
InvalidUniqueId = 222471, // 022-2471
InvalidClientId = 222472, // 022-2472
InvalidClientKey = 222473, // 022-2473
InvalidNexClientId = 222481, // 022-2481
InvalidGameServerId = 222482, // 022-2482
GameServerIdEnvironmentNotFound = 222483, // 022-2483
GameServerIdUniqueIdNotLinked = 222484, // 022-2484
ClientIdUniqueIdNotLinked = 222485, // 022-2485
DeviceMismatch = 222501, // 022-2501
CountryMismatch = 222502, // 022-2502
EulaNotAccepted = 222503, // 022-2503
// Update required errors
UpdateRequired = 222510, // 022-2510
SystemUpdateRequired = 222511, // 022-2511
ApplicationUpdateRequired = 222512, // 022-2512
UnauthorizedRequest = 222520, // 022-2520
RequestForbidden = 222522, // 022-2522
// Resource not found errors
ResourceNotFound = 222530, // 022-2530
PidNotFound = 222531, // 022-2531
NexAccountNotFound = 222532, // 022-2532
GenerateTokenFailure = 222533, // 022-2533
RequestNotFound = 222534, // 022-2534
MasterPinNotFound = 222535, // 022-2535
MailTextNotFound = 222536, // 022-2536
SendMailFailure = 222537, // 022-2537
ApprovalIdNotFound = 222538, // 022-2538
// EULA errors
InvalidEulaParameter = 222540, // 022-2540
InvalidEulaCountry = 222541, // 022-2541
InvalidEulaCountryAndVersion = 222542, // 022-2542
EulaNotFound = 222543, // 022-2543
// Not acceptable errors
PhraseNotAcceptable = 222570, // 022-2570
AccountIdAlreadyExists = 222571, // 022-2571
AccountIdNotAcceptable = 222572, // 022-2572
AccountPasswordNotAcceptable = 222573, // 022-2573
MiiNameNotAcceptable = 222574, // 022-2574
MailAddressNotAcceptable = 222575, // 022-2575
AccountIdFormatInvalid = 222576, // 022-2576
AccountIdPasswordSame = 222577, // 022-2577
AccountIdCharNotAcceptable = 222578, // 022-2578
AccountIdSuccessiveSymbol = 222579, // 022-2579
AccountIdSymbolPositionNotAcceptable = 222580, // 022-2580
AccountIdTooManyDigit = 222581, // 022-2581
AccountPasswordCharNotAcceptable = 222582, // 022-2582
AccountPasswordTooFewCharTypes = 222583, // 022-2583
AccountPasswordSuccessiveSameChar = 222584, // 022-2584
MailAddressDomainNameNotAcceptable = 222585, // 022-2585
MailAddressDomainNameNotResolved = 222586, // 022-2586
ErrCode222587 = 222587, // 022-2587
ReachedAssociationLimit = 222591, // 022-2591
ReachedRegistrationLimit = 222592, // 022-2592
CoppaNotAccepted = 222593, // 022-2593
ParentalControlsRequired = 222594, // 022-2594
MiiNotRegistered = 222595, // 022-2595
DeviceEulaCountryMismatch = 222596, // 022-2596
PendingMigration = 222597, // 022-2597
// Wrong user input errors
WrongUserInput = 222610, // 022-2610
WrongAccountPassword = 222611, // 022-2611
WrongMailAddress = 222612, // 022-2612
WrongAccountPasswordOrMailAddress = 222613, // 022-2613
WrongConfirmationCode = 222614, // 022-2614
WrongBirthDateOrMailAddress = 222615, // 022-2615
WrongAccountMail = 222616, // 022-2616
AccountAlreadyDeleted = 222631, // 022-2631
AccountIdChanged = 222632, // 022-2632
AuthenticationLocked = 222633, // 022-2633
DeviceInactive = 222634, // 022-2634
CoppaAgreementCanceled = 222635, // 022-2635
DomainAccountAlreadyExists = 222636, // 022-2636
AccountTokenExpired = 222641, // 022-2641
InvalidAccountToken = 222642, // 022-2642
AuthenticationRequired = 222643, // 022-2643
ErrCode225844 = 225844, // 022-5844
ConfirmationCodeExpired = 222651, // 022-2651
MailAddressNotValidated = 222661, // 022-2661
ExcessiveMailSendRequest = 222662, // 022-2662
// Credit card errors
CreditCardError = 222670, // 022-2670
CreditCardGeneralFailure = 222671, // 022-2671
CreditCardDeclined = 222672, // 022-2672
CreditCardBlacklisted = 222673, // 022-2673
InvalidCreditCardNumber = 222674, // 022-2674
InvalidCreditCardDate = 222675, // 022-2675
InvalidCreditCardPin = 222676, // 022-2676
InvalidPostalCode = 222677, // 022-2677
InvalidLocation = 222678, // 022-2678
CreditCardDateExpired = 222679, // 022-2679
CreditCardNumberWrong = 222680, // 022-2680
CreditCardPinWrong = 222681, // 022-2681
// Ban errors
Banned = 222800, // 022-2800
BannedAccount = 222801, // 022-2801
BannedAccountAll = 222802, // 022-2802
BannedAccountInApplication = 222803, // 022-2803
BannedAccountInNexService = 222804, // 022-2804
BannedAccountInIndependentService = 222805, // 022-2805
BannedDevice = 222811, // 022-2811
BannedDeviceAll = 222812, // 022-2812
BannedDeviceInApplication = 222813, // 022-2813
BannedDeviceInNexService = 222814, // 022-2814
BannedDeviceInIndependentService = 222815, // 022-2815
BannedAccountTemporarily = 222821, // 022-2821
BannedAccountAllTemporarily = 222822, // 022-2822
BannedAccountInApplicationTemporarily = 222823, // 022-2823
BannedAccountInNexServiceTemporarily = 222824, // 022-2824
BannedAccountInIndependentServiceTemporarily = 222825, // 022-2825
BannedDeviceTemporarily = 222831, // 022-2831
BannedDeviceAllTemporarily = 222832, // 022-2832
BannedDeviceInApplicationTemporarily = 222833, // 022-2833
BannedDeviceInNexServiceTemporarily = 222834, // 022-2834
BannedDeviceInIndependentServiceTemporarily = 222835, // 022-2835
// Service not provided errors
ServiceNotProvided = 222880, // 022-2880
UnderMaintenance = 222881, // 022-2881
ServiceClosed = 222882, // 022-2882
NintendoNetworkClosed = 222883, // 022-2883
NotProvidedCountry = 222884, // 022-2884
// Restriction errors
RestrictionError = 222900, // 022-2900
RestrictedByAge = 222901, // 022-2901
RestrictedByParentalControls = 222910, // 022-2910
OnGameInternetCommunicationRestricted = 222911, // 022-2911
InternalServerError = 222931, // 022-2931
UnknownServerError = 222932, // 022-2932
UnauthenticatedAfterSalvage = 222998, // 022-2998
AuthenticationFailureUnknown = 222999, // 022-2999
Unknown = 229999, // 022-9999
};
}
/// Gets the ACT error code for the given result
u32 GetACTErrorCode(Result result);
} // namespace Service::ACT

View File

@@ -0,0 +1,46 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/act/act_u.h"
namespace Service::ACT {
ACT_U::ACT_U(std::shared_ptr<Module> act) : Module::Interface(std::move(act), "act:u") {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, &ACT_U::Initialize, "Initialize"},
{0x0002, &ACT_U::GetErrorCode, "GetErrorCode"},
{0x0003, nullptr, "GetLastResponseCode"},
{0x0005, nullptr, "GetCommonInfo"},
{0x0006, &ACT_U::GetAccountInfo, "GetAccountInfo"},
{0x0007, nullptr, "GetResultAsync"},
{0x0008, nullptr, "GetMiiImageData"},
{0x0009, nullptr, "SetNfsPassword"},
{0x000B, nullptr, "AcquireEulaList"},
{0x000C, nullptr, "AcquireTimeZoneList"},
{0x000D, nullptr, "GenerateUuid"},
{0x000F, nullptr, "FindSlotNoByUuid"},
{0x0010, nullptr, "SaveData"},
{0x0011, nullptr, "GetTransferableId"},
{0x0012, nullptr, "AcquireNexServiceToken"},
{0x0013, nullptr, "GetNexServiceToken"},
{0x0014, nullptr, "AcquireIndependentServiceToken"},
{0x0015, nullptr, "GetIndependentServiceToken"},
{0x0016, nullptr, "AcquireAccountInfo"},
{0x0017, nullptr, "AcquireAccountIdByPrincipalId"},
{0x0018, nullptr, "AcquirePrincipalIdByAccountId"},
{0x0019, nullptr, "AcquireMii"},
{0x001A, nullptr, "AcquireAccountInfoEx"},
{0x001D, nullptr, "InquireMailAddress"},
{0x001E, nullptr, "AcquireEula"},
{0x001F, nullptr, "AcquireEulaLanguageList"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::ACT
SERIALIZE_EXPORT_IMPL(Service::ACT::ACT_U)

View File

@@ -0,0 +1,22 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/act/act.h"
namespace Service::ACT {
class ACT_U final : public Module::Interface {
public:
explicit ACT_U(std::shared_ptr<Module> act);
private:
SERVICE_SERIALIZATION(ACT_U, act, Module)
};
} // namespace Service::ACT
BOOST_CLASS_EXPORT_KEY(Service::ACT::ACT_U)
BOOST_SERIALIZATION_CONSTRUCT(Service::ACT::ACT_U)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,810 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include <boost/serialization/array.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include "common/common_types.h"
#include "common/construct.h"
#include "common/swap.h"
#include "core/file_sys/cia_container.h"
#include "core/file_sys/file_backend.h"
#include "core/global.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/result.h"
#include "core/hle/service/service.h"
#include "network/artic_base/artic_base_client.h"
namespace Core {
class System;
}
namespace FileUtil {
class IOFile;
}
namespace Service::FS {
enum class MediaType : u32;
}
namespace Kernel {
class Mutex;
}
namespace Service::AM {
namespace ErrCodes {
enum {
CIACurrentlyInstalling = 4,
InvalidTID = 31,
EmptyCIA = 32,
TryingToUninstallSystemApp = 44,
InvalidTIDInList = 60,
InvalidCIAHeader = 104,
};
} // namespace ErrCodes
enum class CIAInstallState : u32 {
InstallStarted,
HeaderLoaded,
CertLoaded,
TicketLoaded,
TMDLoaded,
ContentWritten,
};
enum class InstallStatus : u32 {
Success,
ErrorFailedToOpenFile,
ErrorFileNotFound,
ErrorAborted,
ErrorInvalid,
ErrorEncrypted,
};
enum class CTCertLoadStatus {
Loaded,
NotFound,
Invalid,
IOError,
};
struct CTCert {
u32_be signature_type{};
std::array<u8, 0x1E> signature_r{};
std::array<u8, 0x1E> signature_s{};
INSERT_PADDING_BYTES(0x40){};
std::array<char, 0x40> issuer{};
u32_be key_type{};
std::array<char, 0x40> key_id{};
u32_be expiration_time{};
std::array<u8, 0x1E> public_key_x{};
std::array<u8, 0x1E> public_key_y{};
INSERT_PADDING_BYTES(0x3C){};
bool IsValid() const;
u32 GetDeviceID() const;
};
static_assert(sizeof(CTCert) == 0x180, "Invalid CTCert size.");
// Title ID valid length
constexpr std::size_t TITLE_ID_VALID_LENGTH = 16;
constexpr u64 TWL_TITLE_ID_FLAG = 0x0000800000000000ULL;
// Progress callback for InstallCIA, receives bytes written and total bytes
using ProgressCallback = void(std::size_t, std::size_t);
// A file handled returned for CIAs to be written into and subsequently installed.
class CIAFile final : public FileSys::FileBackend {
public:
explicit CIAFile(Core::System& system_, Service::FS::MediaType media_type);
~CIAFile();
ResultVal<std::size_t> Read(u64 offset, std::size_t length, u8* buffer) const override;
Result WriteTicket();
Result WriteTitleMetadata();
ResultVal<std::size_t> WriteContentData(u64 offset, std::size_t length, const u8* buffer);
ResultVal<std::size_t> Write(u64 offset, std::size_t length, bool flush, bool update_timestamp,
const u8* buffer) override;
u64 GetSize() const override;
bool SetSize(u64 size) const override;
bool Close() override;
void Flush() const override;
private:
Core::System& system;
// Whether it's installing an update, and what step of installation it is at
bool is_update = false;
CIAInstallState install_state = CIAInstallState::InstallStarted;
// How much has been written total, CIAContainer for the installing CIA, buffer of all data
// prior to content data, how much of each content index has been written, and where the CIA
// is being installed to
u64 written = 0;
FileSys::CIAContainer container;
std::vector<u8> data;
std::vector<u64> content_written;
std::vector<FileUtil::IOFile> content_files;
Service::FS::MediaType media_type;
class DecryptionState;
std::unique_ptr<DecryptionState> decryption_state;
};
// A file handled returned for Tickets to be written into and subsequently installed.
class TicketFile final : public FileSys::FileBackend {
public:
explicit TicketFile();
~TicketFile();
ResultVal<std::size_t> Read(u64 offset, std::size_t length, u8* buffer) const override;
ResultVal<std::size_t> Write(u64 offset, std::size_t length, bool flush, bool update_timestamp,
const u8* buffer) override;
u64 GetSize() const override;
bool SetSize(u64 size) const override;
bool Close() override;
void Flush() const override;
private:
u64 written = 0;
std::vector<u8> data;
};
/**
* Installs a CIA file from a specified file path.
* @param path file path of the CIA file to install
* @param update_callback callback function called during filesystem write
* @returns bool whether the install was successful
*/
InstallStatus InstallCIA(const std::string& path,
std::function<ProgressCallback>&& update_callback = nullptr);
/**
* Downloads and installs title form the Nintendo Update Service.
* @param title_id the title_id to download
* @returns whether the install was successful or error code
*/
InstallStatus InstallFromNus(u64 title_id, int version = -1);
/**
* Get the update title ID for a title
* @param titleId the title ID
* @returns The update title ID
*/
u64 GetTitleUpdateId(u64 title_id);
/**
* Get the mediatype for an installed title
* @param titleId the installed title ID
* @returns MediaType which the installed title will reside on
*/
Service::FS::MediaType GetTitleMediaType(u64 titleId);
/**
* Get the .tmd path for a title
* @param media_type the media the title exists on
* @param tid the title ID to get
* @param update set true if the incoming TMD should be used instead of the current TMD
* @returns string path to the .tmd file if it exists, otherwise a path to create one is given.
*/
std::string GetTitleMetadataPath(Service::FS::MediaType media_type, u64 tid, bool update = false);
/**
* Get the .app path for a title's installed content index.
* @param media_type the media the title exists on
* @param tid the title ID to get
* @param index the content index to get
* @param update set true if the incoming TMD should be used instead of the current TMD
* @returns string path to the .app file
*/
std::string GetTitleContentPath(FS::MediaType media_type, u64 tid, std::size_t index = 0,
bool update = false);
/**
* Get the folder for a title's installed content.
* @param media_type the media the title exists on
* @param tid the title ID to get
* @returns string path to the title folder
*/
std::string GetTitlePath(Service::FS::MediaType media_type, u64 tid);
/**
* Get the title/ folder for a storage medium.
* @param media_type the storage medium to get the path for
* @returns string path to the folder
*/
std::string GetMediaTitlePath(Service::FS::MediaType media_type);
/**
* Uninstalls the specified title.
* @param media_type the storage medium the title is installed to
* @param title_id the title ID to uninstall
* @return result of the uninstall operation
*/
Result UninstallProgram(const FS::MediaType media_type, const u64 title_id);
class Module final {
public:
explicit Module(Core::System& system);
~Module();
class Interface : public ServiceFramework<Interface> {
public:
Interface(std::shared_ptr<Module> am, const char* name, u32 max_session);
~Interface();
std::shared_ptr<Module> GetModule() const {
return am;
}
void UseArticClient(std::shared_ptr<Network::ArticBase::Client>& client) {
artic_client = client;
}
protected:
void GetProgramInfosImpl(Kernel::HLERequestContext& ctx, bool ignore_platform);
/**
* AM::GetNumPrograms service function
* Gets the number of installed titles in the requested media type
* Inputs:
* 0 : Command header (0x00010040)
* 1 : Media type to load the titles from
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2 : The number of titles in the requested media type
*/
void GetNumPrograms(Kernel::HLERequestContext& ctx);
/**
* AM::FindDLCContentInfos service function
* Explicitly checks that TID high value is 0004008C or an error is returned.
* Inputs:
* 1 : MediaType
* 2-3 : u64, Title ID
* 4 : Content count
* 6 : Content IDs pointer
* 8 : Content Infos pointer
* Outputs:
* 1 : Result, 0 on success, otherwise error code
*/
void FindDLCContentInfos(Kernel::HLERequestContext& ctx);
/**
* AM::ListDLCContentInfos service function
* Explicitly checks that TID high value is 0004008C or an error is returned.
* Inputs:
* 1 : Content count
* 2 : MediaType
* 3-4 : u64, Title ID
* 5 : Start Index
* 7 : Content Infos pointer
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2 : Number of content infos returned
*/
void ListDLCContentInfos(Kernel::HLERequestContext& ctx);
/**
* AM::DeleteContents service function
* Inputs:
* 1 : MediaType
* 2-3 : u64, Title ID
* 4 : Content count
* 6 : Content IDs pointer
* Outputs:
* 1 : Result, 0 on success, otherwise error code
*/
void DeleteContents(Kernel::HLERequestContext& ctx);
/**
* AM::GetProgramList service function
* Loads information about the desired number of titles from the desired media type into an
* array
* Inputs:
* 1 : Title count
* 2 : Media type to load the titles from
* 4 : Title IDs output pointer
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2 : The number of titles loaded from the requested media type
*/
void GetProgramList(Kernel::HLERequestContext& ctx);
/**
* AM::GetProgramInfos service function
* Inputs:
* 1 : u8 Mediatype
* 2 : Total titles
* 4 : TitleIDList pointer
* 6 : TitleList pointer
* Outputs:
* 1 : Result, 0 on success, otherwise error code
*/
void GetProgramInfos(Kernel::HLERequestContext& ctx);
/**
* AM::GetProgramInfosIgnorePlatform service function
* Inputs:
* 1 : u8 Mediatype
* 2 : Total titles
* 4 : TitleIDList pointer
* 6 : TitleList pointer
* Outputs:
* 1 : Result, 0 on success, otherwise error code
*/
void GetProgramInfosIgnorePlatform(Kernel::HLERequestContext& ctx);
/**
* AM::DeleteUserProgram service function
* Deletes a user program
* Inputs:
* 1 : Media Type
* 2-3 : Title ID
* Outputs:
* 1 : Result, 0 on success, otherwise error code
*/
void DeleteUserProgram(Kernel::HLERequestContext& ctx);
/**
* AM::GetProductCode service function
* Gets the product code of a title
* Inputs:
* 1 : Media Type
* 2-3 : Title ID
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2-5 : Product Code
*/
void GetProductCode(Kernel::HLERequestContext& ctx);
/**
* AM::GetDLCTitleInfos service function
* Wrapper for AM::GetProgramInfos, explicitly checks that TID high value is 0004008C.
* Inputs:
* 1 : u8 Mediatype
* 2 : Total titles
* 4 : TitleIDList pointer
* 6 : TitleList pointer
* Outputs:
* 1 : Result, 0 on success, otherwise error code
*/
void GetDLCTitleInfos(Kernel::HLERequestContext& ctx);
/**
* AM::GetPatchTitleInfos service function
* Wrapper for AM::GetProgramInfos, explicitly checks that TID high value is 0004000E.
* Inputs:
* 1 : u8 Mediatype
* 2 : Total titles
* 4 : TitleIDList input pointer
* 6 : TitleList output pointer
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2 : TitleIDList input pointer
* 4 : TitleList output pointer
*/
void GetPatchTitleInfos(Kernel::HLERequestContext& ctx);
/**
* AM::ListDataTitleTicketInfos service function
* Inputs:
* 1 : Ticket count
* 2-3 : u64, Title ID
* 4 : Start Index?
* 5 : (TicketCount * 24) << 8 | 0x4
* 6 : Ticket Infos pointer
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2 : Number of ticket infos returned
*/
void ListDataTitleTicketInfos(Kernel::HLERequestContext& ctx);
/**
* AM::GetDLCContentInfoCount service function
* Explicitly checks that TID high value is 0004008C or an error is returned.
* Inputs:
* 0 : Command header (0x100100C0)
* 1 : MediaType
* 2-3 : u64, Title ID
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2 : Number of content infos plus one
*/
void GetDLCContentInfoCount(Kernel::HLERequestContext& ctx);
/**
* AM::DeleteTicket service function
* Inputs:
* 1-2 : u64, Title ID
* Outputs:
* 1 : Result, 0 on success, otherwise error code
*/
void DeleteTicket(Kernel::HLERequestContext& ctx);
/**
* AM::GetNumTickets service function
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2 : Number of tickets
*/
void GetNumTickets(Kernel::HLERequestContext& ctx);
/**
* AM::GetTicketList service function
* Inputs:
* 1 : Number of TicketList
* 2 : Number to skip
* 4 : TicketList pointer
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2 : Total TicketList
*/
void GetTicketList(Kernel::HLERequestContext& ctx);
/**
* AM::GetDeviceID service function
* Inputs:
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2 : Unknown
* 3 : DeviceID
*/
void GetDeviceID(Kernel::HLERequestContext& ctx);
/**
* AM::NeedsCleanup service function
* Inputs:
* 1 : Media Type
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2 : bool, Needs Cleanup
*/
void NeedsCleanup(Kernel::HLERequestContext& ctx);
/**
* AM::DoCleanup service function
* Inputs:
* 1 : Media Type
* Outputs:
* 1 : Result, 0 on success, otherwise error code
*/
void DoCleanup(Kernel::HLERequestContext& ctx);
/**
* AM::QueryAvailableTitleDatabase service function
* Inputs:
* 1 : Media Type
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2 : Boolean, database availability
*/
void QueryAvailableTitleDatabase(Kernel::HLERequestContext& ctx);
/**
* AM::GetPersonalizedTicketInfoList service function
* Inputs:
* 1 : Count
* 2-3 : Buffer
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2 : Out count
*/
void GetPersonalizedTicketInfoList(Kernel::HLERequestContext& ctx);
/**
* AM::GetNumImportTitleContextsFiltered service function
* Inputs:
* 1 : Count
* 2 : Filter
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2 : Num import titles
*/
void GetNumImportTitleContextsFiltered(Kernel::HLERequestContext& ctx);
/**
* AM::GetImportTitleContextListFiltered service function
* Inputs:
* 1 : Count
* 2 : Media type
* 3 : filter
* 4-5 : Buffer
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2 : Out count
* 3-4 : Out buffer
*/
void GetImportTitleContextListFiltered(Kernel::HLERequestContext& ctx);
/**
* AM::CheckContentRights service function
* Inputs:
* 1-2 : Title ID
* 3 : Content Index
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2 : Boolean, whether we have rights to this content
*/
void CheckContentRights(Kernel::HLERequestContext& ctx);
/**
* AM::CheckContentRightsIgnorePlatform service function
* Inputs:
* 1-2 : Title ID
* 3 : Content Index
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2 : Boolean, whether we have rights to this content
*/
void CheckContentRightsIgnorePlatform(Kernel::HLERequestContext& ctx);
/**
* AM::BeginImportProgram service function
* Begin importing from a CTR Installable Archive
* Inputs:
* 0 : Command header (0x04020040)
* 1 : Media type to install title to
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2-3 : CIAFile handle for application to write to
*/
void BeginImportProgram(Kernel::HLERequestContext& ctx);
/**
* AM::BeginImportProgramTemporarily service function
* Begin importing from a CTR Installable Archive into the temporary title database
* Inputs:
* 0 : Command header (0x04030000)
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2-3 : CIAFile handle for application to write to
*/
void BeginImportProgramTemporarily(Kernel::HLERequestContext& ctx);
/**
* AM::EndImportProgram service function
* Finish importing from a CTR Installable Archive
* Inputs:
* 0 : Command header (0x04050002)
* 1-2 : CIAFile handle application wrote to
* Outputs:
* 1 : Result, 0 on success, otherwise error code
*/
void EndImportProgram(Kernel::HLERequestContext& ctx);
/**
* AM::EndImportProgramWithoutCommit service function
* Finish importing from a CTR Installable Archive
* Inputs:
* 0 : Command header (0x04060002)
* 1-2 : CIAFile handle application wrote to
* Outputs:
* 1 : Result, 0 on success, otherwise error code
*/
void EndImportProgramWithoutCommit(Kernel::HLERequestContext& ctx);
/**
* AM::CommitImportPrograms service function
* Commits changes from the temporary title database to the real title database (title.db).
* This is a no-op for us, we don't use title.db
* Inputs:
* 0 : Command header (0x040700C2)
* 1 : Media type
* 2 : Title count
* 3 : Database type
* 4-5 : Title list buffer
* Outputs:
* 1 : Result, 0 on success, otherwise error code
*/
void CommitImportPrograms(Kernel::HLERequestContext& ctx);
/**
* AM::GetProgramInfoFromCia service function
* Get TitleInfo from a CIA file handle
* Inputs:
* 0 : Command header (0x04080042)
* 1 : Media type of the title
* 2-3 : File handle CIA data can be read from
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2-8: TitleInfo structure
*/
void GetProgramInfoFromCia(Kernel::HLERequestContext& ctx);
/**
* AM::GetSystemMenuDataFromCia service function
* Loads a CIA file's SMDH data into a specified buffer
* Inputs:
* 0 : Command header (0x04090004)
* 1-2 : File handle CIA data can be read from
* 3-4 : Output buffer
* Outputs:
* 1 : Result, 0 on success, otherwise error code
*/
void GetSystemMenuDataFromCia(Kernel::HLERequestContext& ctx);
/**
* AM::GetDependencyListFromCia service function
* Loads a CIA's dependency list into a specified buffer
* Inputs:
* 0 : Command header (0x040A0002)
* 1-2 : File handle CIA data can be read from
* 64-65 : Output buffer
* Outputs:
* 1 : Result, 0 on success, otherwise error code
*/
void GetDependencyListFromCia(Kernel::HLERequestContext& ctx);
/**
* AM::GetTransferSizeFromCia service function
* Returns the total expected transfer size up to the CIA meta offset from a CIA
* Inputs:
* 0 : Command header (0x040B0002)
* 1-2 : File handle CIA data can be read from
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2-3 : Transfer size
*/
void GetTransferSizeFromCia(Kernel::HLERequestContext& ctx);
/**
* AM::GetCoreVersionFromCia service function
* Returns the core version from a CIA
* Inputs:
* 0 : Command header (0x040C0002)
* 1-2 : File handle CIA data can be read from
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2 : Core version
*/
void GetCoreVersionFromCia(Kernel::HLERequestContext& ctx);
/**
* AM::GetRequiredSizeFromCia service function
* Returns the required amount of free space required to install a given CIA file
* Inputs:
* 0 : Command header (0x040D0042)
* 1 : Media type to install title to
* 2-3 : File handle CIA data can be read from
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2-3 : Required free space for CIA
*/
void GetRequiredSizeFromCia(Kernel::HLERequestContext& ctx);
/**
* AM::DeleteProgram service function
* Deletes a program
* Inputs:
* 0 : Command header (0x041000C0)
* 1 : Media type
* 2-3 : Title ID
* Outputs:
* 1 : Result, 0 on success, otherwise error code
*/
void DeleteProgram(Kernel::HLERequestContext& ctx);
/**
* AM::GetSystemUpdaterMutex service function
* Inputs:
* 0 : Command header (0x04120000)
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2 : Copy handle descriptor
* 3 : System updater mutex
*/
void GetSystemUpdaterMutex(Kernel::HLERequestContext& ctx);
/**
* AM::GetMetaSizeFromCia service function
* Returns the size of a given CIA's meta section
* Inputs:
* 0 : Command header (0x04130002)
* 1-2 : File handle CIA data can be read from
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2 : Meta section size
*/
void GetMetaSizeFromCia(Kernel::HLERequestContext& ctx);
/**
* AM::GetMetaDataFromCia service function
* Loads meta section data from a CIA file into a given buffer
* Inputs:
* 0 : Command header (0x04140044)
* 1-2 : File handle CIA data can be read from
* 3-4 : Output buffer
* Outputs:
* 1 : Result, 0 on success, otherwise error code
*/
void GetMetaDataFromCia(Kernel::HLERequestContext& ctx);
/**
* AM::BeginImportTicket service function
* Inputs:
* 1 : Media type to install title to
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2-3 : TicketHandle handle for application to write to
*/
void BeginImportTicket(Kernel::HLERequestContext& ctx);
/**
* AM::EndImportTicket service function
* Inputs:
* 1-2 : TicketHandle handle application wrote to
* Outputs:
* 1 : Result, 0 on success, otherwise error code
*/
void EndImportTicket(Kernel::HLERequestContext& ctx);
/**
* AM::GetDeviceCert service function
* Inputs:
* Outputs:
* 1 : Result, 0 on success, otherwise error code
* 2 : Unknown
* 3-4 : Device cert
*/
void GetDeviceCert(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> am;
// Placed on the interface level so that only am:net and am:app have it.
std::shared_ptr<Network::ArticBase::Client> artic_client = nullptr;
};
/**
* Gets the CTCert.bin path in the host filesystem
* @returns std::string CTCert.bin path in the host filesystem
*/
static std::string GetCTCertPath();
/**
* Loads the CTCert.bin file from the filesystem.
* @returns CTCertLoadStatus indicating the file load status.
*/
static CTCertLoadStatus LoadCTCertFile(CTCert& output);
private:
/**
* Scans the for titles in a storage medium for listing.
* @param media_type the storage medium to scan
*/
void ScanForTitles(Service::FS::MediaType media_type);
/**
* Scans all storage mediums for titles for listing.
*/
void ScanForAllTitles();
Core::System& system;
bool cia_installing = false;
std::array<std::vector<u64_le>, 3> am_title_list;
std::shared_ptr<Kernel::Mutex> system_updater_mutex;
CTCert ct_cert{};
template <class Archive>
void serialize(Archive& ar, const unsigned int);
friend class boost::serialization::access;
};
std::shared_ptr<Module> GetModule(Core::System& system);
void InstallInterfaces(Core::System& system);
} // namespace Service::AM
BOOST_CLASS_EXPORT_KEY(Service::AM::Module)
SERVICE_CONSTRUCT(Service::AM::Module)

View File

@@ -0,0 +1,33 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/am/am_app.h"
namespace Service::AM {
AM_APP::AM_APP(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "am:app", 5) {
static const FunctionInfo functions[] = {
// clang-format off
{0x1001, &AM_APP::GetDLCContentInfoCount, "GetDLCContentInfoCount"},
{0x1002, &AM_APP::FindDLCContentInfos, "FindDLCContentInfos"},
{0x1003, &AM_APP::ListDLCContentInfos, "ListDLCContentInfos"},
{0x1004, &AM_APP::DeleteContents, "DeleteContents"},
{0x1005, &AM_APP::GetDLCTitleInfos, "GetDLCTitleInfos"},
{0x1006, nullptr, "GetNumDataTitleTickets"},
{0x1007, &AM_APP::ListDataTitleTicketInfos, "ListDataTitleTicketInfos"},
{0x1008, nullptr, "GetItemRights"},
{0x1009, nullptr, "IsDataTitleInUse"},
{0x100A, nullptr, "IsExternalTitleDatabaseInitialized"},
{0x100B, nullptr, "GetNumExistingContentInfos"},
{0x100C, nullptr, "ListExistingContentInfos"},
{0x100D, &AM_APP::GetPatchTitleInfos, "GetPatchTitleInfos"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::AM
SERIALIZE_EXPORT_IMPL(Service::AM::AM_APP)

View File

@@ -0,0 +1,22 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included..
#pragma once
#include "core/hle/service/am/am.h"
namespace Service::AM {
class AM_APP final : public Module::Interface {
public:
explicit AM_APP(std::shared_ptr<Module> am);
private:
SERVICE_SERIALIZATION(AM_APP, am, Module)
};
} // namespace Service::AM
BOOST_CLASS_EXPORT_KEY(Service::AM::AM_APP)
BOOST_SERIALIZATION_CONSTRUCT(Service::AM::AM_APP)

View File

@@ -0,0 +1,130 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/am/am_net.h"
namespace Service::AM {
AM_NET::AM_NET(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "am:net", 5) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, &AM_NET::GetNumPrograms, "GetNumPrograms"},
{0x0002, &AM_NET::GetProgramList, "GetProgramList"},
{0x0003, &AM_NET::GetProgramInfos, "GetProgramInfos"},
{0x0004, &AM_NET::DeleteUserProgram, "DeleteUserProgram"},
{0x0005, &AM_NET::GetProductCode, "GetProductCode"},
{0x0006, nullptr, "GetStorageId"},
{0x0007, &AM_NET::DeleteTicket, "DeleteTicket"},
{0x0008, &AM_NET::GetNumTickets, "GetNumTickets"},
{0x0009, &AM_NET::GetTicketList, "GetTicketList"},
{0x000A, &AM_NET::GetDeviceID, "GetDeviceID"},
{0x000B, nullptr, "GetNumImportTitleContexts"},
{0x000C, nullptr, "GetImportTitleContextList"},
{0x000D, nullptr, "GetImportTitleContexts"},
{0x000E, nullptr, "DeleteImportTitleContext"},
{0x000F, nullptr, "GetNumImportContentContexts"},
{0x0010, nullptr, "GetImportContentContextList"},
{0x0011, nullptr, "GetImportContentContexts"},
{0x0012, nullptr, "DeleteImportContentContexts"},
{0x0013, &AM_NET::NeedsCleanup, "NeedsCleanup"},
{0x0014, nullptr, "DoCleanup"},
{0x0015, nullptr, "DeleteAllImportContexts"},
{0x0016, nullptr, "DeleteAllTemporaryPrograms"},
{0x0017, nullptr, "ImportTwlBackupLegacy"},
{0x0018, nullptr, "InitializeTitleDatabase"},
{0x0019, nullptr, "QueryAvailableTitleDatabase"},
{0x001A, nullptr, "CalcTwlBackupSize"},
{0x001B, nullptr, "ExportTwlBackup"},
{0x001C, nullptr, "ImportTwlBackup"},
{0x001D, nullptr, "DeleteAllTwlUserPrograms"},
{0x001E, nullptr, "ReadTwlBackupInfo"},
{0x001F, nullptr, "DeleteAllExpiredUserPrograms"},
{0x0020, nullptr, "GetTwlArchiveResourceInfo"},
{0x0021, &AM_NET::GetPersonalizedTicketInfoList, "GetPersonalizedTicketInfoList"},
{0x0022, nullptr, "DeleteAllImportContextsFiltered"},
{0x0023, &AM_NET::GetNumImportTitleContextsFiltered, "GetNumImportTitleContextsFiltered"},
{0x0024, &AM_NET::GetImportTitleContextListFiltered, "GetImportTitleContextListFiltered"},
{0x0025, &AM_NET::CheckContentRights, "CheckContentRights"},
{0x0026, nullptr, "GetTicketLimitInfos"},
{0x0027, nullptr, "GetDemoLaunchInfos"},
{0x0028, nullptr, "ReadTwlBackupInfoEx"},
{0x0029, nullptr, "DeleteUserProgramsAtomically"},
{0x002A, nullptr, "GetNumExistingContentInfosSystem"},
{0x002B, nullptr, "ListExistingContentInfosSystem"},
{0x002C, &AM_NET::GetProgramInfosIgnorePlatform, "GetProgramInfosIgnorePlatform"},
{0x002D, &AM_NET::CheckContentRightsIgnorePlatform, "CheckContentRightsIgnorePlatform"},
{0x0401, nullptr, "UpdateFirmwareTo"},
{0x0402, &AM_NET::BeginImportProgram, "BeginImportProgram"},
{0x0403, nullptr, "BeginImportProgramTemporarily"},
{0x0404, nullptr, "CancelImportProgram"},
{0x0405, &AM_NET::EndImportProgram, "EndImportProgram"},
{0x0406, nullptr, "EndImportProgramWithoutCommit"},
{0x0407, nullptr, "CommitImportPrograms"},
{0x0408, &AM_NET::GetProgramInfoFromCia, "GetProgramInfoFromCia"},
{0x0409, &AM_NET::GetSystemMenuDataFromCia, "GetSystemMenuDataFromCia"},
{0x040A, &AM_NET::GetDependencyListFromCia, "GetDependencyListFromCia"},
{0x040B, &AM_NET::GetTransferSizeFromCia, "GetTransferSizeFromCia"},
{0x040C, &AM_NET::GetCoreVersionFromCia, "GetCoreVersionFromCia"},
{0x040D, &AM_NET::GetRequiredSizeFromCia, "GetRequiredSizeFromCia"},
{0x040E, nullptr, "CommitImportProgramsAndUpdateFirmwareAuto"},
{0x040F, nullptr, "UpdateFirmwareAuto"},
{0x0410, &AM_NET::DeleteProgram, "DeleteProgram"},
{0x0411, nullptr, "GetTwlProgramListForReboot"},
{0x0412, &AM_NET::GetSystemUpdaterMutex, "GetSystemUpdaterMutex"},
{0x0413, &AM_NET::GetMetaSizeFromCia, "GetMetaSizeFromCia"},
{0x0414, &AM_NET::GetMetaDataFromCia, "GetMetaDataFromCia"},
{0x0415, nullptr, "CheckDemoLaunchRights"},
{0x0416, nullptr, "GetInternalTitleLocationInfo"},
{0x0417, nullptr, "PerpetuateAgbSaveData"},
{0x0418, nullptr, "BeginImportProgramForOverWrite"},
{0x0419, nullptr, "BeginImportSystemProgram"},
{0x0801, &AM_NET::BeginImportTicket, "BeginImportTicket"},
{0x0802, nullptr, "CancelImportTicket"},
{0x0803, &AM_NET::EndImportTicket, "EndImportTicket"},
{0x0804, nullptr, "BeginImportTitle"},
{0x0805, nullptr, "StopImportTitle"},
{0x0806, nullptr, "ResumeImportTitle"},
{0x0807, nullptr, "CancelImportTitle"},
{0x0808, nullptr, "EndImportTitle"},
{0x0809, nullptr, "CommitImportTitles"},
{0x080A, nullptr, "BeginImportTmd"},
{0x080B, nullptr, "CancelImportTmd"},
{0x080C, nullptr, "EndImportTmd"},
{0x080D, nullptr, "CreateImportContentContexts"},
{0x080E, nullptr, "BeginImportContent"},
{0x080F, nullptr, "StopImportContent"},
{0x0810, nullptr, "ResumeImportContent"},
{0x0811, nullptr, "CancelImportContent"},
{0x0812, nullptr, "EndImportContent"},
{0x0813, nullptr, "GetNumCurrentImportContentContexts"},
{0x0814, nullptr, "GetCurrentImportContentContextList"},
{0x0815, nullptr, "GetCurrentImportContentContexts"},
{0x0816, nullptr, "Sign"},
{0x0817, nullptr, "Verify"},
{0x0818, &AM_NET::GetDeviceCert, "GetDeviceCert"},
{0x0819, nullptr, "ImportCertificates"},
{0x081A, nullptr, "ImportCertificate"},
{0x081B, nullptr, "CommitImportTitlesAndUpdateFirmwareAuto"},
{0x081C, nullptr, "DeleteTicketId"},
{0x081D, nullptr, "GetNumTicketIds"},
{0x081E, nullptr, "GetTicketIdList"},
{0x081F, nullptr, "GetNumTicketsOfProgram"},
{0x0820, nullptr, "ListTicketInfos"},
{0x0821, nullptr, "GetRightsOnlyTicketData"},
{0x0822, nullptr, "GetNumCurrentContentInfos"},
{0x0823, nullptr, "FindCurrentContentInfos"},
{0x0824, nullptr, "ListCurrentContentInfos"},
{0x0825, nullptr, "CalculateContextRequiredSize"},
{0x0826, nullptr, "UpdateImportContentContexts"},
{0x0827, nullptr, "DeleteAllDemoLaunchInfos"},
{0x0828, nullptr, "BeginImportTitleForOverWrite"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::AM
SERIALIZE_EXPORT_IMPL(Service::AM::AM_NET)

View File

@@ -0,0 +1,22 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included..
#pragma once
#include "core/hle/service/am/am.h"
namespace Service::AM {
class AM_NET final : public Module::Interface {
public:
explicit AM_NET(std::shared_ptr<Module> am);
private:
SERVICE_SERIALIZATION(AM_NET, am, Module)
};
} // namespace Service::AM
BOOST_CLASS_EXPORT_KEY(Service::AM::AM_NET)
BOOST_SERIALIZATION_CONSTRUCT(Service::AM::AM_NET)

View File

@@ -0,0 +1,78 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/am/am_sys.h"
namespace Service::AM {
AM_SYS::AM_SYS(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "am:sys", 5) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, &AM_SYS::GetNumPrograms, "GetNumPrograms"},
{0x0002, &AM_SYS::GetProgramList, "GetProgramList"},
{0x0003, &AM_SYS::GetProgramInfos, "GetProgramInfos"},
{0x0004, &AM_SYS::DeleteUserProgram, "DeleteUserProgram"},
{0x0005, &AM_SYS::GetProductCode, "GetProductCode"},
{0x0006, nullptr, "GetStorageId"},
{0x0007, &AM_SYS::DeleteTicket, "DeleteTicket"},
{0x0008, &AM_SYS::GetNumTickets, "GetNumTickets"},
{0x0009, &AM_SYS::GetTicketList, "GetTicketList"},
{0x000A, &AM_SYS::GetDeviceID, "GetDeviceID"},
{0x000B, nullptr, "GetNumImportTitleContexts"},
{0x000C, nullptr, "GetImportTitleContextList"},
{0x000D, nullptr, "GetImportTitleContexts"},
{0x000E, nullptr, "DeleteImportTitleContext"},
{0x000F, nullptr, "GetNumImportContentContexts"},
{0x0010, nullptr, "GetImportContentContextList"},
{0x0011, nullptr, "GetImportContentContexts"},
{0x0012, nullptr, "DeleteImportContentContexts"},
{0x0013, &AM_SYS::NeedsCleanup, "NeedsCleanup"},
{0x0014, &AM_SYS::DoCleanup, "DoCleanup"},
{0x0015, nullptr, "DeleteAllImportContexts"},
{0x0016, nullptr, "DeleteAllTemporaryPrograms"},
{0x0017, nullptr, "ImportTwlBackupLegacy"},
{0x0018, nullptr, "InitializeTitleDatabase"},
{0x0019, &AM_SYS::QueryAvailableTitleDatabase, "QueryAvailableTitleDatabase"},
{0x001A, nullptr, "CalcTwlBackupSize"},
{0x001B, nullptr, "ExportTwlBackup"},
{0x001C, nullptr, "ImportTwlBackup"},
{0x001D, nullptr, "DeleteAllTwlUserPrograms"},
{0x001E, nullptr, "ReadTwlBackupInfo"},
{0x001F, nullptr, "DeleteAllExpiredUserPrograms"},
{0x0020, nullptr, "GetTwlArchiveResourceInfo"},
{0x0021, nullptr, "GetPersonalizedTicketInfoList"},
{0x0022, nullptr, "DeleteAllImportContextsFiltered"},
{0x0023, nullptr, "GetNumImportTitleContextsFiltered"},
{0x0024, nullptr, "GetImportTitleContextListFiltered"},
{0x0025, &AM_SYS::CheckContentRights, "CheckContentRights"},
{0x0026, nullptr, "GetTicketLimitInfos"},
{0x0027, nullptr, "GetDemoLaunchInfos"},
{0x0028, nullptr, "ReadTwlBackupInfoEx"},
{0x0029, nullptr, "DeleteUserProgramsAtomically"},
{0x002A, nullptr, "GetNumExistingContentInfosSystem"},
{0x002B, nullptr, "ListExistingContentInfosSystem"},
{0x002C, nullptr, "GetProgramInfosIgnorePlatform"},
{0x002D, &AM_SYS::CheckContentRightsIgnorePlatform, "CheckContentRightsIgnorePlatform"},
{0x1001, &AM_SYS::GetDLCContentInfoCount, "GetDLCContentInfoCount"},
{0x1002, &AM_SYS::FindDLCContentInfos, "FindDLCContentInfos"},
{0x1003, &AM_SYS::ListDLCContentInfos, "ListDLCContentInfos"},
{0x1004, &AM_SYS::DeleteContents, "DeleteContents"},
{0x1005, &AM_SYS::GetDLCTitleInfos, "GetDLCTitleInfos"},
{0x1006, nullptr, "GetNumDataTitleTickets"},
{0x1007, &AM_SYS::ListDataTitleTicketInfos, "ListDataTitleTicketInfos"},
{0x1008, nullptr, "GetItemRights"},
{0x1009, nullptr, "IsDataTitleInUse"},
{0x100A, nullptr, "IsExternalTitleDatabaseInitialized"},
{0x100B, nullptr, "GetNumExistingContentInfos"},
{0x100C, nullptr, "ListExistingContentInfos"},
{0x100D, &AM_SYS::GetPatchTitleInfos, "GetPatchTitleInfos"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::AM
SERIALIZE_EXPORT_IMPL(Service::AM::AM_SYS)

View File

@@ -0,0 +1,22 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included..
#pragma once
#include "core/hle/service/am/am.h"
namespace Service::AM {
class AM_SYS final : public Module::Interface {
public:
explicit AM_SYS(std::shared_ptr<Module> am);
private:
SERVICE_SERIALIZATION(AM_SYS, am, Module)
};
} // namespace Service::AM
BOOST_CLASS_EXPORT_KEY(Service::AM::AM_SYS)
BOOST_SERIALIZATION_CONSTRUCT(Service::AM::AM_SYS)

View File

@@ -0,0 +1,90 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/am/am_u.h"
namespace Service::AM {
AM_U::AM_U(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "am:u", 5) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, &AM_U::GetNumPrograms, "GetNumPrograms"},
{0x0002, &AM_U::GetProgramList, "GetProgramList"},
{0x0003, &AM_U::GetProgramInfos, "GetProgramInfos"},
{0x0004, &AM_U::DeleteUserProgram, "DeleteUserProgram"},
{0x0005, &AM_U::GetProductCode, "GetProductCode"},
{0x0006, nullptr, "GetStorageId"},
{0x0007, &AM_U::DeleteTicket, "DeleteTicket"},
{0x0008, &AM_U::GetNumTickets, "GetNumTickets"},
{0x0009, &AM_U::GetTicketList, "GetTicketList"},
{0x000A, &AM_U::GetDeviceID, "GetDeviceID"},
{0x000B, nullptr, "GetNumImportTitleContexts"},
{0x000C, nullptr, "GetImportTitleContextList"},
{0x000D, nullptr, "GetImportTitleContexts"},
{0x000E, nullptr, "DeleteImportTitleContext"},
{0x000F, nullptr, "GetNumImportContentContexts"},
{0x0010, nullptr, "GetImportContentContextList"},
{0x0011, nullptr, "GetImportContentContexts"},
{0x0012, nullptr, "DeleteImportContentContexts"},
{0x0013, &AM_U::NeedsCleanup, "NeedsCleanup"},
{0x0014, nullptr, "DoCleanup"},
{0x0015, nullptr, "DeleteAllImportContexts"},
{0x0016, nullptr, "DeleteAllTemporaryPrograms"},
{0x0017, nullptr, "ImportTwlBackupLegacy"},
{0x0018, nullptr, "InitializeTitleDatabase"},
{0x0019, nullptr, "QueryAvailableTitleDatabase"},
{0x001A, nullptr, "CalcTwlBackupSize"},
{0x001B, nullptr, "ExportTwlBackup"},
{0x001C, nullptr, "ImportTwlBackup"},
{0x001D, nullptr, "DeleteAllTwlUserPrograms"},
{0x001E, nullptr, "ReadTwlBackupInfo"},
{0x001F, nullptr, "DeleteAllExpiredUserPrograms"},
{0x0020, nullptr, "GetTwlArchiveResourceInfo"},
{0x0021, nullptr, "GetPersonalizedTicketInfoList"},
{0x0022, nullptr, "DeleteAllImportContextsFiltered"},
{0x0023, nullptr, "GetNumImportTitleContextsFiltered"},
{0x0024, nullptr, "GetImportTitleContextListFiltered"},
{0x0025, nullptr, "CheckContentRights"},
{0x0026, nullptr, "GetTicketLimitInfos"},
{0x0027, nullptr, "GetDemoLaunchInfos"},
{0x0028, nullptr, "ReadTwlBackupInfoEx"},
{0x0029, nullptr, "DeleteUserProgramsAtomically"},
{0x002A, nullptr, "GetNumExistingContentInfosSystem"},
{0x002B, nullptr, "ListExistingContentInfosSystem"},
{0x002C, nullptr, "GetProgramInfosIgnorePlatform"},
{0x002D, nullptr, "CheckContentRightsIgnorePlatform"},
{0x0401, nullptr, "UpdateFirmwareTo"},
{0x0402, &AM_U::BeginImportProgram, "BeginImportProgram"},
{0x0403, &AM_U::BeginImportProgramTemporarily, "BeginImportProgramTemporarily"},
{0x0404, nullptr, "CancelImportProgram"},
{0x0405, &AM_U::EndImportProgram, "EndImportProgram"},
{0x0406, &AM_U::EndImportProgramWithoutCommit, "EndImportProgramWithoutCommit"},
{0x0407, &AM_U::CommitImportPrograms, "CommitImportPrograms"},
{0x0408, &AM_U::GetProgramInfoFromCia, "GetProgramInfoFromCia"},
{0x0409, &AM_U::GetSystemMenuDataFromCia, "GetSystemMenuDataFromCia"},
{0x040A, &AM_U::GetDependencyListFromCia, "GetDependencyListFromCia"},
{0x040B, &AM_U::GetTransferSizeFromCia, "GetTransferSizeFromCia"},
{0x040C, &AM_U::GetCoreVersionFromCia, "GetCoreVersionFromCia"},
{0x040D, &AM_U::GetRequiredSizeFromCia, "GetRequiredSizeFromCia"},
{0x040E, nullptr, "CommitImportProgramsAndUpdateFirmwareAuto"},
{0x040F, nullptr, "UpdateFirmwareAuto"},
{0x0410, &AM_U::DeleteProgram, "DeleteProgram"},
{0x0411, nullptr, "GetTwlProgramListForReboot"},
{0x0412, &AM_U::GetSystemUpdaterMutex, "GetSystemUpdaterMutex"},
{0x0413, &AM_U::GetMetaSizeFromCia, "GetMetaSizeFromCia"},
{0x0414, &AM_U::GetMetaDataFromCia, "GetMetaDataFromCia"},
{0x0415, nullptr, "CheckDemoLaunchRights"},
{0x0416, nullptr, "GetInternalTitleLocationInfo"},
{0x0417, nullptr, "PerpetuateAgbSaveData"},
{0x0418, nullptr, "BeginImportProgramForOverWrite"},
{0x0419, nullptr, "BeginImportSystemProgram"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::AM
SERIALIZE_EXPORT_IMPL(Service::AM::AM_U)

View File

@@ -0,0 +1,22 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included..
#pragma once
#include "core/hle/service/am/am.h"
namespace Service::AM {
class AM_U final : public Module::Interface {
public:
explicit AM_U(std::shared_ptr<Module> am);
private:
SERVICE_SERIALIZATION(AM_U, am, Module)
};
} // namespace Service::AM
BOOST_CLASS_EXPORT_KEY(Service::AM::AM_U)
BOOST_SERIALIZATION_CONSTRUCT(Service::AM::AM_U)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,564 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <limits>
#include <memory>
#include <optional>
#include <vector>
#include <boost/serialization/array.hpp>
#include <boost/serialization/optional.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/vector.hpp>
#include "core/frontend/input.h"
#include "core/global.h"
#include "core/hle/kernel/event.h"
#include "core/hle/result.h"
#include "core/hle/service/fs/archive.h"
namespace Core {
class System;
}
namespace HLE::Applets {
class Applet;
}
namespace Service::APT {
/// Signals used by APT functions
enum class SignalType : u32 {
None = 0x0,
Wakeup = 0x1,
Request = 0x2,
Response = 0x3,
Exit = 0x4,
Message = 0x5,
HomeButtonSingle = 0x6,
HomeButtonDouble = 0x7,
DspSleep = 0x8,
DspWakeup = 0x9,
WakeupByExit = 0xA,
WakeupByPause = 0xB,
WakeupByCancel = 0xC,
WakeupByCancelAll = 0xD,
WakeupByPowerButtonClick = 0xE,
WakeupToJumpHome = 0xF,
RequestForSysApplet = 0x10,
WakeupToLaunchApplication = 0x11,
};
enum class Notification : u32 {
None = 0,
HomeButtonSingle = 1,
HomeButtonDouble = 2,
SleepQuery = 3,
SleepCancelledByOpen = 4,
SleepAccepted = 5,
SleepAwake = 6,
Shutdown = 7,
PowerButtonClick = 8,
PowerButtonClear = 9,
TrySleep = 10,
OrderToClose = 11,
};
/// App Id's used by APT functions
enum class AppletId : u32 {
None = 0,
AnySystemApplet = 0x100,
HomeMenu = 0x101,
AlternateMenu = 0x103,
Camera = 0x110,
FriendList = 0x112,
GameNotes = 0x113,
InternetBrowser = 0x114,
InstructionManual = 0x115,
Notifications = 0x116,
Miiverse = 0x117,
MiiversePost = 0x118,
AmiiboSettings = 0x119,
AnySysLibraryApplet = 0x200,
SoftwareKeyboard1 = 0x201,
Ed1 = 0x202,
PnoteApp = 0x204,
SnoteApp = 0x205,
Error = 0x206,
Mint = 0x207,
Extrapad = 0x208,
Memolib = 0x209,
Application = 0x300,
Tiger = 0x301,
AnyLibraryApplet = 0x400,
SoftwareKeyboard2 = 0x401,
Ed2 = 0x402,
PnoteApp2 = 0x404,
SnoteApp2 = 0x405,
Error2 = 0x406,
Mint2 = 0x407,
Extrapad2 = 0x408,
Memolib2 = 0x409,
};
/// Application Old/New 3DS target platforms
enum class TargetPlatform : u8 {
Old3ds = 0,
New3ds = 1,
};
/// Application Old/New 3DS running modes
enum class ApplicationRunningMode : u8 {
NoApplication = 0,
Old3dsRegistered = 1,
New3dsRegistered = 2,
Old3dsUnregistered = 3,
New3dsUnregistered = 4,
};
/// Holds information about the parameters used in Send/Glance/ReceiveParameter
struct MessageParameter {
AppletId sender_id = AppletId::None;
AppletId destination_id = AppletId::None;
SignalType signal = SignalType::None;
std::shared_ptr<Kernel::Object> object = nullptr;
std::vector<u8> buffer;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& sender_id;
ar& destination_id;
ar& signal;
ar& object;
ar& buffer;
}
friend class boost::serialization::access;
};
enum class AppletPos : u32 {
Application = 0,
Library = 1,
System = 2,
SysLibrary = 3,
Resident = 4,
AutoLibrary = 5,
Invalid = 0xFF,
};
union AppletAttributes {
u32 raw;
BitField<0, 3, AppletPos> applet_pos;
BitField<28, 1, u32> no_exit_on_system_applet;
BitField<29, 1, u32> is_home_menu;
AppletAttributes() : raw(0) {}
AppletAttributes(u32 attributes) : raw(attributes) {}
};
enum class ApplicationJumpFlags : u8 {
UseInputParameters = 0,
UseStoredParameters = 1,
UseCurrentParameters = 2
};
struct DeliverArg {
std::vector<u8> param;
std::vector<u8> hmac;
u64 source_program_id = std::numeric_limits<u64>::max();
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& param;
ar& hmac;
ar& source_program_id;
}
friend class boost::serialization::access;
};
struct ApplicationJumpParameters {
u64 next_title_id;
FS::MediaType next_media_type;
ApplicationJumpFlags flags;
u64 current_title_id;
FS::MediaType current_media_type;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& next_title_id;
ar& next_media_type;
ar& flags;
ar& current_title_id;
ar& current_media_type;
}
friend class boost::serialization::access;
};
struct ApplicationStartParameters {
u64 next_title_id;
FS::MediaType next_media_type;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& next_title_id;
ar& next_media_type;
}
friend class boost::serialization::access;
};
enum class DisplayBufferMode : u32_le {
R8G8B8A8 = 0,
R8G8B8 = 1,
R5G6B5 = 2,
R5G5B5A1 = 3,
R4G4B4A4 = 4,
Unimportable = 0xFFFFFFFF,
};
/// Used by the application to pass information about the current framebuffer to applets.
struct CaptureBufferInfo {
u32_le size;
u8 is_3d;
INSERT_PADDING_BYTES(0x3); // Padding for alignment
u32_le top_screen_left_offset;
u32_le top_screen_right_offset;
DisplayBufferMode top_screen_format;
u32_le bottom_screen_left_offset;
u32_le bottom_screen_right_offset;
DisplayBufferMode bottom_screen_format;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& size;
ar& is_3d;
ar& top_screen_left_offset;
ar& top_screen_right_offset;
ar& top_screen_format;
ar& bottom_screen_left_offset;
ar& bottom_screen_right_offset;
ar& bottom_screen_format;
}
friend class boost::serialization::access;
};
static_assert(sizeof(CaptureBufferInfo) == 0x20, "CaptureBufferInfo struct has incorrect size");
enum class SleepQueryReply : u32 {
Reject = 0,
Accept = 1,
Later = 2,
};
class AppletManager : public std::enable_shared_from_this<AppletManager> {
public:
explicit AppletManager(Core::System& system);
~AppletManager();
void ReloadInputDevices();
/**
* Clears any existing parameter and places a new one. This function is currently only used by
* HLE Applets and should be likely removed in the future
*/
void CancelAndSendParameter(const MessageParameter& parameter);
Result SendParameter(const MessageParameter& parameter);
ResultVal<MessageParameter> GlanceParameter(AppletId app_id);
ResultVal<MessageParameter> ReceiveParameter(AppletId app_id);
bool CancelParameter(bool check_sender, AppletId sender_appid, bool check_receiver,
AppletId receiver_appid);
struct GetLockHandleResult {
AppletAttributes corrected_attributes;
u32 state;
std::shared_ptr<Kernel::Mutex> lock;
};
ResultVal<GetLockHandleResult> GetLockHandle(AppletAttributes attributes);
struct InitializeResult {
std::shared_ptr<Kernel::Event> notification_event;
std::shared_ptr<Kernel::Event> parameter_event;
};
ResultVal<InitializeResult> Initialize(AppletId app_id, AppletAttributes attributes);
Result Enable(AppletAttributes attributes);
Result Finalize(AppletId app_id);
u32 CountRegisteredApplet();
bool IsRegistered(AppletId app_id);
ResultVal<AppletAttributes> GetAttribute(AppletId app_id);
ResultVal<Notification> InquireNotification(AppletId app_id);
Result SendNotification(Notification notification);
Result PrepareToStartLibraryApplet(AppletId applet_id);
Result PreloadLibraryApplet(AppletId applet_id);
Result FinishPreloadingLibraryApplet(AppletId applet_id);
Result StartLibraryApplet(AppletId applet_id, std::shared_ptr<Kernel::Object> object,
const std::vector<u8>& buffer);
Result PrepareToCloseLibraryApplet(bool not_pause, bool exiting, bool jump_home);
Result CloseLibraryApplet(std::shared_ptr<Kernel::Object> object,
const std::vector<u8>& buffer);
Result CancelLibraryApplet(bool app_exiting);
Result SendDspSleep(AppletId from_applet_id, std::shared_ptr<Kernel::Object> object);
Result SendDspWakeUp(AppletId from_applet_id, std::shared_ptr<Kernel::Object> object);
Result PrepareToStartSystemApplet(AppletId applet_id);
Result StartSystemApplet(AppletId applet_id, std::shared_ptr<Kernel::Object> object,
const std::vector<u8>& buffer);
Result PrepareToCloseSystemApplet();
Result CloseSystemApplet(std::shared_ptr<Kernel::Object> object, const std::vector<u8>& buffer);
Result OrderToCloseSystemApplet();
Result PrepareToJumpToHomeMenu();
Result JumpToHomeMenu(std::shared_ptr<Kernel::Object> object, const std::vector<u8>& buffer);
Result PrepareToLeaveHomeMenu();
Result LeaveHomeMenu(std::shared_ptr<Kernel::Object> object, const std::vector<u8>& buffer);
Result OrderToCloseApplication();
Result PrepareToCloseApplication(bool return_to_sys);
Result CloseApplication(std::shared_ptr<Kernel::Object> object, const std::vector<u8>& buffer);
Result PrepareToDoApplicationJump(u64 title_id, FS::MediaType media_type,
ApplicationJumpFlags flags);
Result DoApplicationJump(const DeliverArg& arg);
boost::optional<DeliverArg> ReceiveDeliverArg() {
auto arg = deliver_arg;
deliver_arg = boost::none;
return arg;
}
void SetDeliverArg(boost::optional<DeliverArg> arg) {
deliver_arg = std::move(arg);
}
std::vector<u8> GetCaptureInfo() {
std::vector<u8> buffer;
if (capture_info) {
buffer.resize(sizeof(CaptureBufferInfo));
std::memcpy(buffer.data(), &capture_info.get(), sizeof(CaptureBufferInfo));
}
return buffer;
}
void SetCaptureInfo(std::vector<u8> buffer) {
ASSERT_MSG(buffer.size() >= sizeof(CaptureBufferInfo), "CaptureBufferInfo is too small.");
capture_info.emplace();
std::memcpy(&capture_info.get(), buffer.data(), sizeof(CaptureBufferInfo));
}
std::vector<u8> ReceiveCaptureBufferInfo() {
std::vector<u8> buffer;
if (capture_buffer_info) {
buffer.resize(sizeof(CaptureBufferInfo));
std::memcpy(buffer.data(), &capture_buffer_info.get(), sizeof(CaptureBufferInfo));
capture_buffer_info.reset();
}
return buffer;
}
void SendCaptureBufferInfo(std::vector<u8> buffer) {
ASSERT_MSG(buffer.size() >= sizeof(CaptureBufferInfo), "CaptureBufferInfo is too small.");
capture_buffer_info.emplace();
std::memcpy(&capture_buffer_info.get(), buffer.data(), sizeof(CaptureBufferInfo));
}
Result PrepareToStartApplication(u64 title_id, FS::MediaType media_type);
Result StartApplication(const std::vector<u8>& parameter, const std::vector<u8>& hmac,
bool paused);
Result WakeupApplication(std::shared_ptr<Kernel::Object> object, const std::vector<u8>& buffer);
Result CancelApplication();
struct AppletManInfo {
AppletPos active_applet_pos;
AppletId requested_applet_id;
AppletId home_menu_applet_id;
AppletId active_applet_id;
};
ResultVal<AppletManInfo> GetAppletManInfo(AppletPos requested_applet_pos);
struct AppletInfo {
u64 title_id;
Service::FS::MediaType media_type;
bool registered;
bool loaded;
u32 attributes;
};
ResultVal<AppletInfo> GetAppletInfo(AppletId app_id);
ApplicationJumpParameters GetApplicationJumpParameters() const {
return app_jump_parameters;
}
ResultVal<Service::FS::MediaType> Unknown54(u32 in_param);
TargetPlatform GetTargetPlatform();
ApplicationRunningMode GetApplicationRunningMode();
private:
/// APT lock retrieved via GetLockHandle.
std::shared_ptr<Kernel::Mutex> lock;
/// Parameter data to be returned in the next call to Glance/ReceiveParameter.
// NOTE: A bug in gcc prevents serializing std::optional
boost::optional<MessageParameter> next_parameter;
/// This parameter will be sent to the application/applet once they register themselves by using
/// APT::Initialize.
boost::optional<MessageParameter> delayed_parameter;
ApplicationJumpParameters app_jump_parameters{};
boost::optional<ApplicationStartParameters> app_start_parameters{};
boost::optional<DeliverArg> deliver_arg{};
boost::optional<CaptureBufferInfo> capture_info;
boost::optional<CaptureBufferInfo> capture_buffer_info;
static constexpr std::size_t NumAppletSlot = 4;
enum class AppletSlot : u8 {
Application,
SystemApplet,
HomeMenu,
LibraryApplet,
// An invalid tag
Error,
};
struct AppletSlotData {
AppletId applet_id;
AppletSlot slot;
u64 title_id;
bool registered;
bool loaded;
AppletAttributes attributes;
Notification notification;
std::shared_ptr<Kernel::Event> notification_event;
std::shared_ptr<Kernel::Event> parameter_event;
void Reset() {
applet_id = AppletId::None;
registered = false;
title_id = 0;
attributes.raw = 0;
}
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& applet_id;
ar& slot;
ar& title_id;
ar& registered;
ar& loaded;
ar& attributes.raw;
ar& notification;
ar& notification_event;
ar& parameter_event;
}
friend class boost::serialization::access;
};
// Holds data about the concurrently running applets in the system.
std::array<AppletSlotData, NumAppletSlot> applet_slots = {};
AppletSlot active_slot = AppletSlot::Error;
AppletSlot last_library_launcher_slot = AppletSlot::Error;
SignalType library_applet_closing_command = SignalType::None;
AppletId last_prepared_library_applet = AppletId::None;
AppletSlot last_system_launcher_slot = AppletSlot::Error;
AppletSlot last_jump_to_home_slot = AppletSlot::Error;
bool ordered_to_close_sys_applet = false;
bool ordered_to_close_application = false;
bool application_cancelled = false;
AppletSlot application_close_target = AppletSlot::Error;
// This flag is used to determine if an app that supports New 3DS capabilities should use them.
// It also affects the results of APT:GetTargetPlatform and APT:GetApplicationRunningMode.
bool new_3ds_mode_blocked = false;
std::unordered_map<AppletId, std::shared_ptr<HLE::Applets::Applet>> hle_applets;
Core::TimingEventType* hle_applet_update_event;
Core::TimingEventType* button_update_event;
std::atomic<bool> is_device_reload_pending{true};
std::unique_ptr<Input::ButtonDevice> home_button;
std::unique_ptr<Input::ButtonDevice> power_button;
bool last_home_button_state = false;
bool last_power_button_state = false;
Core::System& system;
AppletSlotData* GetAppletSlot(AppletSlot slot) {
return &applet_slots[static_cast<std::size_t>(slot)];
}
AppletId GetAppletSlotId(AppletSlot slot) {
return slot != AppletSlot::Error ? GetAppletSlot(slot)->applet_id : AppletId::None;
}
AppletSlot GetAppletSlotFromId(AppletId id);
AppletSlot GetAppletSlotFromAttributes(AppletAttributes attributes);
AppletSlot GetAppletSlotFromPos(AppletPos pos);
/// Checks if the Application slot has already been registered and sends the parameter to it,
/// otherwise it queues for sending when the application registers itself with APT::Enable.
void SendApplicationParameterAfterRegistration(const MessageParameter& parameter);
void SendNotificationToAll(Notification notification);
void EnsureHomeMenuLoaded();
void CaptureFrameBuffers();
Result CreateHLEApplet(AppletId id, AppletId parent, bool preload);
void HLEAppletUpdateEvent(std::uintptr_t user_data, s64 cycles_late);
void LoadInputDevices();
void ButtonUpdateEvent(std::uintptr_t user_data, s64 cycles_late);
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& next_parameter;
ar& app_jump_parameters;
ar& delayed_parameter;
ar& app_start_parameters;
ar& deliver_arg;
ar& capture_info;
ar& capture_buffer_info;
ar& active_slot;
ar& last_library_launcher_slot;
ar& last_prepared_library_applet;
ar& last_system_launcher_slot;
ar& last_jump_to_home_slot;
ar& ordered_to_close_sys_applet;
ar& ordered_to_close_application;
ar& application_cancelled;
ar& application_close_target;
ar& new_3ds_mode_blocked;
ar& lock;
ar& capture_info;
ar& applet_slots;
ar& library_applet_closing_command;
if (Archive::is_loading::value) {
LoadInputDevices();
}
}
friend class boost::serialization::access;
};
} // namespace Service::APT
BOOST_CLASS_VERSION(Service::APT::ApplicationJumpParameters, 1)
BOOST_CLASS_VERSION(Service::APT::AppletManager, 1)
SERVICE_CONSTRUCT(Service::APT::AppletManager)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,114 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/apt/apt_a.h"
namespace Service::APT {
APT_A::APT_A(std::shared_ptr<Module> apt)
: Module::APTInterface(std::move(apt), "APT:A", MaxAPTSessions) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, &APT_A::GetLockHandle, "GetLockHandle"},
{0x0002, &APT_A::Initialize, "Initialize"},
{0x0003, &APT_A::Enable, "Enable"},
{0x0004, &APT_A::Finalize, "Finalize"},
{0x0005, &APT_A::GetAppletManInfo, "GetAppletManInfo"},
{0x0006, &APT_A::GetAppletInfo, "GetAppletInfo"},
{0x0007, nullptr, "GetLastSignaledAppletId"},
{0x0008, &APT_A::CountRegisteredApplet, "CountRegisteredApplet"},
{0x0009, &APT_A::IsRegistered, "IsRegistered"},
{0x000A, &APT_A::GetAttribute, "GetAttribute"},
{0x000B, &APT_A::InquireNotification, "InquireNotification"},
{0x000C, &APT_A::SendParameter, "SendParameter"},
{0x000D, &APT_A::ReceiveParameter, "ReceiveParameter"},
{0x000E, &APT_A::GlanceParameter, "GlanceParameter"},
{0x000F, &APT_A::CancelParameter, "CancelParameter"},
{0x0010, nullptr, "DebugFunc"},
{0x0011, nullptr, "MapProgramIdForDebug"},
{0x0012, nullptr, "SetHomeMenuAppletIdForDebug"},
{0x0013, nullptr, "GetPreparationState"},
{0x0014, nullptr, "SetPreparationState"},
{0x0015, &APT_A::PrepareToStartApplication, "PrepareToStartApplication"},
{0x0016, &APT_A::PreloadLibraryApplet, "PreloadLibraryApplet"},
{0x0017, &APT_A::FinishPreloadingLibraryApplet, "FinishPreloadingLibraryApplet"},
{0x0018, &APT_A::PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
{0x0019, &APT_A::PrepareToStartSystemApplet, "PrepareToStartSystemApplet"},
{0x001A, &APT_A::PrepareToStartNewestHomeMenu, "PrepareToStartNewestHomeMenu"},
{0x001B, &APT_A::StartApplication, "StartApplication"},
{0x001C, &APT_A::WakeupApplication, "WakeupApplication"},
{0x001D, &APT_A::CancelApplication, "CancelApplication"},
{0x001E, &APT_A::StartLibraryApplet, "StartLibraryApplet"},
{0x001F, &APT_A::StartSystemApplet, "StartSystemApplet"},
{0x0020, nullptr, "StartNewestHomeMenu"},
{0x0021, &APT_A::OrderToCloseApplication, "OrderToCloseApplication"},
{0x0022, &APT_A::PrepareToCloseApplication, "PrepareToCloseApplication"},
{0x0023, nullptr, "PrepareToJumpToApplication"},
{0x0024, nullptr, "JumpToApplication"},
{0x0025, &APT_A::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"},
{0x0026, &APT_A::PrepareToCloseSystemApplet, "PrepareToCloseSystemApplet"},
{0x0027, &APT_A::CloseApplication, "CloseApplication"},
{0x0028, &APT_A::CloseLibraryApplet, "CloseLibraryApplet"},
{0x0029, &APT_A::CloseSystemApplet, "CloseSystemApplet"},
{0x002A, &APT_A::OrderToCloseSystemApplet, "OrderToCloseSystemApplet"},
{0x002B, &APT_A::PrepareToJumpToHomeMenu, "PrepareToJumpToHomeMenu"},
{0x002C, &APT_A::JumpToHomeMenu, "JumpToHomeMenu"},
{0x002D, &APT_A::PrepareToLeaveHomeMenu, "PrepareToLeaveHomeMenu"},
{0x002E, &APT_A::LeaveHomeMenu, "LeaveHomeMenu"},
{0x002F, nullptr, "PrepareToLeaveResidentApplet"},
{0x0030, nullptr, "LeaveResidentApplet"},
{0x0031, &APT_A::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"},
{0x0032, &APT_A::DoApplicationJump, "DoApplicationJump"},
{0x0033, &APT_A::GetProgramIdOnApplicationJump, "GetProgramIdOnApplicationJump"},
{0x0034, &APT_A::SendDeliverArg, "SendDeliverArg"},
{0x0035, &APT_A::ReceiveDeliverArg, "ReceiveDeliverArg"},
{0x0036, &APT_A::LoadSysMenuArg, "LoadSysMenuArg"},
{0x0037, &APT_A::StoreSysMenuArg, "StoreSysMenuArg"},
{0x0038, nullptr, "PreloadResidentApplet"},
{0x0039, nullptr, "PrepareToStartResidentApplet"},
{0x003A, nullptr, "StartResidentApplet"},
{0x003B, &APT_A::CancelLibraryApplet, "CancelLibraryApplet"},
{0x003C, &APT_A::SendDspSleep, "SendDspSleep"},
{0x003D, &APT_A::SendDspWakeUp, "SendDspWakeUp"},
{0x003E, &APT_A::ReplySleepQuery, "ReplySleepQuery"},
{0x003F, &APT_A::ReplySleepNotificationComplete, "ReplySleepNotificationComplete"},
{0x0040, &APT_A::SendCaptureBufferInfo, "SendCaptureBufferInfo"},
{0x0041, &APT_A::ReceiveCaptureBufferInfo, "ReceiveCaptureBufferInfo"},
{0x0042, nullptr, "SleepSystem"},
{0x0043, &APT_A::NotifyToWait, "NotifyToWait"},
{0x0044, &APT_A::GetSharedFont, "GetSharedFont"},
{0x0045, &APT_A::GetWirelessRebootInfo, "GetWirelessRebootInfo"},
{0x0046, &APT_A::Wrap, "Wrap"},
{0x0047, &APT_A::Unwrap, "Unwrap"},
{0x0048, &APT_A::GetProgramInfo, "GetProgramInfo"},
{0x0049, &APT_A::Reboot, "Reboot"},
{0x004A, &APT_A::GetCaptureInfo, "GetCaptureInfo"},
{0x004B, &APT_A::AppletUtility, "AppletUtility"},
{0x004C, nullptr, "SetFatalErrDispMode"},
{0x004D, nullptr, "GetAppletProgramInfo"},
{0x004E, &APT_A::HardwareResetAsync, "HardwareResetAsync"},
{0x004F, &APT_A::SetAppCpuTimeLimit, "SetAppCpuTimeLimit"},
{0x0050, &APT_A::GetAppCpuTimeLimit, "GetAppCpuTimeLimit"},
{0x0051, &APT_A::GetStartupArgument, "GetStartupArgument"},
{0x0052, nullptr, "Wrap1"},
{0x0053, nullptr, "Unwrap1"},
{0x0054, &APT_A::Unknown54, "Unknown54"},
{0x0055, &APT_A::SetScreenCapturePostPermission, "SetScreenCapturePostPermission"},
{0x0056, &APT_A::GetScreenCapturePostPermission, "GetScreenCapturePostPermission"},
{0x0057, &APT_A::WakeupApplication2, "WakeupApplication2"},
{0x0058, &APT_A::GetProgramId, "GetProgramId"},
{0x0101, &APT_A::GetTargetPlatform, "GetTargetPlatform"},
{0x0102, &APT_A::CheckNew3DS, "CheckNew3DS"},
{0x0103, &APT_A::GetApplicationRunningMode, "GetApplicationRunningMode"},
{0x0104, &APT_A::IsStandardMemoryLayout, "IsStandardMemoryLayout"},
{0x0105, &APT_A::IsTitleAllowed, "IsTitleAllowed"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::APT
SERIALIZE_EXPORT_IMPL(Service::APT::APT_A)

View File

@@ -0,0 +1,22 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/apt/apt.h"
namespace Service::APT {
class APT_A final : public Module::APTInterface {
public:
explicit APT_A(std::shared_ptr<Module> apt);
private:
SERVICE_SERIALIZATION(APT_A, apt, Module)
};
} // namespace Service::APT
BOOST_CLASS_EXPORT_KEY(Service::APT::APT_A)
BOOST_SERIALIZATION_CONSTRUCT(Service::APT::APT_A)

View File

@@ -0,0 +1,114 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/apt/apt_s.h"
namespace Service::APT {
APT_S::APT_S(std::shared_ptr<Module> apt)
: Module::APTInterface(std::move(apt), "APT:S", MaxAPTSessions) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, &APT_S::GetLockHandle, "GetLockHandle"},
{0x0002, &APT_S::Initialize, "Initialize"},
{0x0003, &APT_S::Enable, "Enable"},
{0x0004, &APT_S::Finalize, "Finalize"},
{0x0005, &APT_S::GetAppletManInfo, "GetAppletManInfo"},
{0x0006, &APT_S::GetAppletInfo, "GetAppletInfo"},
{0x0007, nullptr, "GetLastSignaledAppletId"},
{0x0008, &APT_S::CountRegisteredApplet, "CountRegisteredApplet"},
{0x0009, &APT_S::IsRegistered, "IsRegistered"},
{0x000A, &APT_S::GetAttribute, "GetAttribute"},
{0x000B, &APT_S::InquireNotification, "InquireNotification"},
{0x000C, &APT_S::SendParameter, "SendParameter"},
{0x000D, &APT_S::ReceiveParameter, "ReceiveParameter"},
{0x000E, &APT_S::GlanceParameter, "GlanceParameter"},
{0x000F, &APT_S::CancelParameter, "CancelParameter"},
{0x0010, nullptr, "DebugFunc"},
{0x0011, nullptr, "MapProgramIdForDebug"},
{0x0012, nullptr, "SetHomeMenuAppletIdForDebug"},
{0x0013, nullptr, "GetPreparationState"},
{0x0014, nullptr, "SetPreparationState"},
{0x0015, &APT_S::PrepareToStartApplication, "PrepareToStartApplication"},
{0x0016, &APT_S::PreloadLibraryApplet, "PreloadLibraryApplet"},
{0x0017, &APT_S::FinishPreloadingLibraryApplet, "FinishPreloadingLibraryApplet"},
{0x0018, &APT_S::PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
{0x0019, &APT_S::PrepareToStartSystemApplet, "PrepareToStartSystemApplet"},
{0x001A, &APT_S::PrepareToStartNewestHomeMenu, "PrepareToStartNewestHomeMenu"},
{0x001B, &APT_S::StartApplication, "StartApplication"},
{0x001C, &APT_S::WakeupApplication, "WakeupApplication"},
{0x001D, &APT_S::CancelApplication, "CancelApplication"},
{0x001E, &APT_S::StartLibraryApplet, "StartLibraryApplet"},
{0x001F, &APT_S::StartSystemApplet, "StartSystemApplet"},
{0x0020, nullptr, "StartNewestHomeMenu"},
{0x0021, &APT_S::OrderToCloseApplication, "OrderToCloseApplication"},
{0x0022, &APT_S::PrepareToCloseApplication, "PrepareToCloseApplication"},
{0x0023, nullptr, "PrepareToJumpToApplication"},
{0x0024, nullptr, "JumpToApplication"},
{0x0025, &APT_S::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"},
{0x0026, &APT_S::PrepareToCloseSystemApplet, "PrepareToCloseSystemApplet"},
{0x0027, &APT_S::CloseApplication, "CloseApplication"},
{0x0028, &APT_S::CloseLibraryApplet, "CloseLibraryApplet"},
{0x0029, &APT_S::CloseSystemApplet, "CloseSystemApplet"},
{0x002A, &APT_S::OrderToCloseSystemApplet, "OrderToCloseSystemApplet"},
{0x002B, &APT_S::PrepareToJumpToHomeMenu, "PrepareToJumpToHomeMenu"},
{0x002C, &APT_S::JumpToHomeMenu, "JumpToHomeMenu"},
{0x002D, &APT_S::PrepareToLeaveHomeMenu, "PrepareToLeaveHomeMenu"},
{0x002E, &APT_S::LeaveHomeMenu, "LeaveHomeMenu"},
{0x002F, nullptr, "PrepareToLeaveResidentApplet"},
{0x0030, nullptr, "LeaveResidentApplet"},
{0x0031, &APT_S::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"},
{0x0032, &APT_S::DoApplicationJump, "DoApplicationJump"},
{0x0033, &APT_S::GetProgramIdOnApplicationJump, "GetProgramIdOnApplicationJump"},
{0x0034, &APT_S::SendDeliverArg, "SendDeliverArg"},
{0x0035, &APT_S::ReceiveDeliverArg, "ReceiveDeliverArg"},
{0x0036, &APT_S::LoadSysMenuArg, "LoadSysMenuArg"},
{0x0037, &APT_S::StoreSysMenuArg, "StoreSysMenuArg"},
{0x0038, nullptr, "PreloadResidentApplet"},
{0x0039, nullptr, "PrepareToStartResidentApplet"},
{0x003A, nullptr, "StartResidentApplet"},
{0x003B, &APT_S::CancelLibraryApplet, "CancelLibraryApplet"},
{0x003C, &APT_S::SendDspSleep, "SendDspSleep"},
{0x003D, &APT_S::SendDspWakeUp, "SendDspWakeUp"},
{0x003E, &APT_S::ReplySleepQuery, "ReplySleepQuery"},
{0x003F, &APT_S::ReplySleepNotificationComplete, "ReplySleepNotificationComplete"},
{0x0040, &APT_S::SendCaptureBufferInfo, "SendCaptureBufferInfo"},
{0x0041, &APT_S::ReceiveCaptureBufferInfo, "ReceiveCaptureBufferInfo"},
{0x0042, nullptr, "SleepSystem"},
{0x0043, &APT_S::NotifyToWait, "NotifyToWait"},
{0x0044, &APT_S::GetSharedFont, "GetSharedFont"},
{0x0045, &APT_S::GetWirelessRebootInfo, "GetWirelessRebootInfo"},
{0x0046, &APT_S::Wrap, "Wrap"},
{0x0047, &APT_S::Unwrap, "Unwrap"},
{0x0048, &APT_S::GetProgramInfo, "GetProgramInfo"},
{0x0049, &APT_S::Reboot, "Reboot"},
{0x004A, &APT_S::GetCaptureInfo, "GetCaptureInfo"},
{0x004B, &APT_S::AppletUtility, "AppletUtility"},
{0x004C, nullptr, "SetFatalErrDispMode"},
{0x004D, nullptr, "GetAppletProgramInfo"},
{0x004E, &APT_S::HardwareResetAsync, "HardwareResetAsync"},
{0x004F, &APT_S::SetAppCpuTimeLimit, "SetAppCpuTimeLimit"},
{0x0050, &APT_S::GetAppCpuTimeLimit, "GetAppCpuTimeLimit"},
{0x0051, &APT_S::GetStartupArgument, "GetStartupArgument"},
{0x0052, nullptr, "Wrap1"},
{0x0053, nullptr, "Unwrap1"},
{0x0054, &APT_S::Unknown54, "Unknown54"},
{0x0055, &APT_S::SetScreenCapturePostPermission, "SetScreenCapturePostPermission"},
{0x0056, &APT_S::GetScreenCapturePostPermission, "GetScreenCapturePostPermission"},
{0x0057, &APT_S::WakeupApplication2, "WakeupApplication2"},
{0x0058, &APT_S::GetProgramId, "GetProgramId"},
{0x0101, &APT_S::GetTargetPlatform, "GetTargetPlatform"},
{0x0102, &APT_S::CheckNew3DS, "CheckNew3DS"},
{0x0103, &APT_S::GetApplicationRunningMode, "GetApplicationRunningMode"},
{0x0104, &APT_S::IsStandardMemoryLayout, "IsStandardMemoryLayout"},
{0x0105, &APT_S::IsTitleAllowed, "IsTitleAllowed"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::APT
SERIALIZE_EXPORT_IMPL(Service::APT::APT_S)

View File

@@ -0,0 +1,29 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/apt/apt.h"
namespace Service::APT {
// Application and title launching service. These services handle signaling for home/power button as
// well. Only one session for either APT service can be open at a time, normally processes close the
// service handle immediately once finished using the service. The commands for APT:U and APT:S are
// exactly the same, however certain commands are only accessible with APT:S(NS module will call
// svcBreak when the command isn't accessible). See http://3dbrew.org/wiki/NS#APT_Services.
/// Interface to "APT:S" service
class APT_S final : public Module::APTInterface {
public:
explicit APT_S(std::shared_ptr<Module> apt);
private:
SERVICE_SERIALIZATION(APT_S, apt, Module)
};
} // namespace Service::APT
BOOST_CLASS_EXPORT_KEY(Service::APT::APT_S)
BOOST_SERIALIZATION_CONSTRUCT(Service::APT::APT_S)

View File

@@ -0,0 +1,114 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/apt/apt_u.h"
namespace Service::APT {
APT_U::APT_U(std::shared_ptr<Module> apt)
: Module::APTInterface(std::move(apt), "APT:U", MaxAPTSessions) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, &APT_U::GetLockHandle, "GetLockHandle"},
{0x0002, &APT_U::Initialize, "Initialize"},
{0x0003, &APT_U::Enable, "Enable"},
{0x0004, &APT_U::Finalize, "Finalize"},
{0x0005, &APT_U::GetAppletManInfo, "GetAppletManInfo"},
{0x0006, &APT_U::GetAppletInfo, "GetAppletInfo"},
{0x0007, nullptr, "GetLastSignaledAppletId"},
{0x0008, &APT_U::CountRegisteredApplet, "CountRegisteredApplet"},
{0x0009, &APT_U::IsRegistered, "IsRegistered"},
{0x000A, &APT_U::GetAttribute, "GetAttribute"},
{0x000B, &APT_U::InquireNotification, "InquireNotification"},
{0x000C, &APT_U::SendParameter, "SendParameter"},
{0x000D, &APT_U::ReceiveParameter, "ReceiveParameter"},
{0x000E, &APT_U::GlanceParameter, "GlanceParameter"},
{0x000F, &APT_U::CancelParameter, "CancelParameter"},
{0x0010, nullptr, "DebugFunc"},
{0x0011, nullptr, "MapProgramIdForDebug"},
{0x0012, nullptr, "SetHomeMenuAppletIdForDebug"},
{0x0013, nullptr, "GetPreparationState"},
{0x0014, nullptr, "SetPreparationState"},
{0x0015, &APT_U::PrepareToStartApplication, "PrepareToStartApplication"},
{0x0016, &APT_U::PreloadLibraryApplet, "PreloadLibraryApplet"},
{0x0017, &APT_U::FinishPreloadingLibraryApplet, "FinishPreloadingLibraryApplet"},
{0x0018, &APT_U::PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
{0x0019, &APT_U::PrepareToStartSystemApplet, "PrepareToStartSystemApplet"},
{0x001A, &APT_U::PrepareToStartNewestHomeMenu, "PrepareToStartNewestHomeMenu"},
{0x001B, &APT_U::StartApplication, "StartApplication"},
{0x001C, &APT_U::WakeupApplication, "WakeupApplication"},
{0x001D, &APT_U::CancelApplication, "CancelApplication"},
{0x001E, &APT_U::StartLibraryApplet, "StartLibraryApplet"},
{0x001F, &APT_U::StartSystemApplet, "StartSystemApplet"},
{0x0020, nullptr, "StartNewestHomeMenu"},
{0x0021, &APT_U::OrderToCloseApplication, "OrderToCloseApplication"},
{0x0022, &APT_U::PrepareToCloseApplication, "PrepareToCloseApplication"},
{0x0023, nullptr, "PrepareToJumpToApplication"},
{0x0024, nullptr, "JumpToApplication"},
{0x0025, &APT_U::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"},
{0x0026, &APT_U::PrepareToCloseSystemApplet, "PrepareToCloseSystemApplet"},
{0x0027, &APT_U::CloseApplication, "CloseApplication"},
{0x0028, &APT_U::CloseLibraryApplet, "CloseLibraryApplet"},
{0x0029, &APT_U::CloseSystemApplet, "CloseSystemApplet"},
{0x002A, &APT_U::OrderToCloseSystemApplet, "OrderToCloseSystemApplet"},
{0x002B, &APT_U::PrepareToJumpToHomeMenu, "PrepareToJumpToHomeMenu"},
{0x002C, &APT_U::JumpToHomeMenu, "JumpToHomeMenu"},
{0x002D, &APT_U::PrepareToLeaveHomeMenu, "PrepareToLeaveHomeMenu"},
{0x002E, &APT_U::LeaveHomeMenu, "LeaveHomeMenu"},
{0x002F, nullptr, "PrepareToLeaveResidentApplet"},
{0x0030, nullptr, "LeaveResidentApplet"},
{0x0031, &APT_U::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"},
{0x0032, &APT_U::DoApplicationJump, "DoApplicationJump"},
{0x0033, &APT_U::GetProgramIdOnApplicationJump, "GetProgramIdOnApplicationJump"},
{0x0034, &APT_U::SendDeliverArg, "SendDeliverArg"},
{0x0035, &APT_U::ReceiveDeliverArg, "ReceiveDeliverArg"},
{0x0036, &APT_U::LoadSysMenuArg, "LoadSysMenuArg"},
{0x0037, &APT_U::StoreSysMenuArg, "StoreSysMenuArg"},
{0x0038, nullptr, "PreloadResidentApplet"},
{0x0039, nullptr, "PrepareToStartResidentApplet"},
{0x003A, nullptr, "StartResidentApplet"},
{0x003B, &APT_U::CancelLibraryApplet, "CancelLibraryApplet"},
{0x003C, &APT_U::SendDspSleep, "SendDspSleep"},
{0x003D, &APT_U::SendDspWakeUp, "SendDspWakeUp"},
{0x003E, &APT_U::ReplySleepQuery, "ReplySleepQuery"},
{0x003F, &APT_U::ReplySleepNotificationComplete, "ReplySleepNotificationComplete"},
{0x0040, &APT_U::SendCaptureBufferInfo, "SendCaptureBufferInfo"},
{0x0041, &APT_U::ReceiveCaptureBufferInfo, "ReceiveCaptureBufferInfo"},
{0x0042, nullptr, "SleepSystem"},
{0x0043, &APT_U::NotifyToWait, "NotifyToWait"},
{0x0044, &APT_U::GetSharedFont, "GetSharedFont"},
{0x0045, &APT_U::GetWirelessRebootInfo, "GetWirelessRebootInfo"},
{0x0046, &APT_U::Wrap, "Wrap"},
{0x0047, &APT_U::Unwrap, "Unwrap"},
{0x0048, &APT_U::GetProgramInfo, "GetProgramInfo"},
{0x0049, &APT_U::Reboot, "Reboot"},
{0x004A, &APT_U::GetCaptureInfo, "GetCaptureInfo"},
{0x004B, &APT_U::AppletUtility, "AppletUtility"},
{0x004C, nullptr, "SetFatalErrDispMode"},
{0x004D, nullptr, "GetAppletProgramInfo"},
{0x004E, &APT_U::HardwareResetAsync, "HardwareResetAsync"},
{0x004F, &APT_U::SetAppCpuTimeLimit, "SetAppCpuTimeLimit"},
{0x0050, &APT_U::GetAppCpuTimeLimit, "GetAppCpuTimeLimit"},
{0x0051, &APT_U::GetStartupArgument, "GetStartupArgument"},
{0x0052, nullptr, "Wrap1"},
{0x0053, nullptr, "Unwrap1"},
{0x0054, &APT_U::Unknown54, "Unknown54"},
{0x0055, &APT_U::SetScreenCapturePostPermission, "SetScreenCapturePostPermission"},
{0x0056, &APT_U::GetScreenCapturePostPermission, "GetScreenCapturePostPermission"},
{0x0057, &APT_U::WakeupApplication2, "WakeupApplication2"},
{0x0058, &APT_U::GetProgramId, "GetProgramId"},
{0x0101, &APT_U::GetTargetPlatform, "GetTargetPlatform"},
{0x0102, &APT_U::CheckNew3DS, "CheckNew3DS"},
{0x0103, &APT_U::GetApplicationRunningMode, "GetApplicationRunningMode"},
{0x0104, &APT_U::IsStandardMemoryLayout, "IsStandardMemoryLayout"},
{0x0105, &APT_U::IsTitleAllowed, "IsTitleAllowed"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::APT
SERIALIZE_EXPORT_IMPL(Service::APT::APT_U)

View File

@@ -0,0 +1,29 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/apt/apt.h"
namespace Service::APT {
// Application and title launching service. These services handle signaling for home/power button as
// well. Only one session for either APT service can be open at a time, normally processes close the
// service handle immediately once finished using the service. The commands for APT:U and APT:S are
// exactly the same, however certain commands are only accessible with APT:S(NS module will call
// svcBreak when the command isn't accessible). See http://3dbrew.org/wiki/NS#APT_Services.
/// Interface to "APT:U" service
class APT_U final : public Module::APTInterface {
public:
explicit APT_U(std::shared_ptr<Module> apt);
private:
SERVICE_SERIALIZATION(APT_U, apt, Module)
};
} // namespace Service::APT
BOOST_CLASS_EXPORT_KEY(Service::APT::APT_U)
BOOST_SERIALIZATION_CONSTRUCT(Service::APT::APT_U)

View File

@@ -0,0 +1,106 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/apt/bcfnt/bcfnt.h"
#include "core/hle/service/service.h"
namespace Service::APT::BCFNT {
void RelocateSharedFont(std::shared_ptr<Kernel::SharedMemory> shared_font, VAddr new_address) {
static const u32 SharedFontStartOffset = 0x80;
const u8* cfnt_ptr = shared_font->GetPointer(SharedFontStartOffset);
CFNT cfnt;
std::memcpy(&cfnt, cfnt_ptr, sizeof(cfnt));
u32 assumed_cmap_offset = 0;
u32 assumed_cwdh_offset = 0;
u32 assumed_tglp_offset = 0;
u32 first_cmap_offset = 0;
u32 first_cwdh_offset = 0;
u32 first_tglp_offset = 0;
// First discover the location of sections so that the rebase offset can be auto-detected
u32 current_offset = SharedFontStartOffset + cfnt.header_size;
for (unsigned block = 0; block < cfnt.num_blocks; ++block) {
const u8* data = shared_font->GetPointer(current_offset);
SectionHeader section_header;
std::memcpy(&section_header, data, sizeof(section_header));
if (first_cmap_offset == 0 && std::memcmp(section_header.magic, "CMAP", 4) == 0) {
first_cmap_offset = current_offset;
} else if (first_cwdh_offset == 0 && std::memcmp(section_header.magic, "CWDH", 4) == 0) {
first_cwdh_offset = current_offset;
} else if (first_tglp_offset == 0 && std::memcmp(section_header.magic, "TGLP", 4) == 0) {
first_tglp_offset = current_offset;
} else if (std::memcmp(section_header.magic, "FINF", 4) == 0) {
BCFNT::FINF finf;
std::memcpy(&finf, data, sizeof(finf));
assumed_cmap_offset = finf.cmap_offset - sizeof(SectionHeader);
assumed_cwdh_offset = finf.cwdh_offset - sizeof(SectionHeader);
assumed_tglp_offset = finf.tglp_offset - sizeof(SectionHeader);
}
current_offset += section_header.section_size;
}
u32 previous_base = assumed_cmap_offset - first_cmap_offset;
ASSERT(previous_base == assumed_cwdh_offset - first_cwdh_offset);
ASSERT(previous_base == assumed_tglp_offset - first_tglp_offset);
u32 offset = new_address - previous_base;
// Reset pointer back to start of sections and do the actual rebase
current_offset = SharedFontStartOffset + cfnt.header_size;
for (unsigned block = 0; block < cfnt.num_blocks; ++block) {
u8* data = shared_font->GetPointer(current_offset);
SectionHeader section_header;
std::memcpy(&section_header, data, sizeof(section_header));
if (std::memcmp(section_header.magic, "FINF", 4) == 0) {
BCFNT::FINF finf;
std::memcpy(&finf, data, sizeof(finf));
// Relocate the offsets in the FINF section
finf.cmap_offset += offset;
finf.cwdh_offset += offset;
finf.tglp_offset += offset;
std::memcpy(data, &finf, sizeof(finf));
} else if (std::memcmp(section_header.magic, "CMAP", 4) == 0) {
BCFNT::CMAP cmap;
std::memcpy(&cmap, data, sizeof(cmap));
// Relocate the offsets in the CMAP section
if (cmap.next_cmap_offset != 0)
cmap.next_cmap_offset += offset;
std::memcpy(data, &cmap, sizeof(cmap));
} else if (std::memcmp(section_header.magic, "CWDH", 4) == 0) {
BCFNT::CWDH cwdh;
std::memcpy(&cwdh, data, sizeof(cwdh));
// Relocate the offsets in the CWDH section
if (cwdh.next_cwdh_offset != 0)
cwdh.next_cwdh_offset += offset;
std::memcpy(data, &cwdh, sizeof(cwdh));
} else if (std::memcmp(section_header.magic, "TGLP", 4) == 0) {
BCFNT::TGLP tglp;
std::memcpy(&tglp, data, sizeof(tglp));
// Relocate the offsets in the TGLP section
tglp.sheet_data_offset += offset;
std::memcpy(data, &tglp, sizeof(tglp));
}
current_offset += section_header.section_size;
}
}
} // namespace Service::APT::BCFNT

View File

@@ -0,0 +1,89 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include "common/swap.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/service.h"
namespace Service::APT::BCFNT { ///< BCFNT Shared Font file structures
struct CFNT {
u8 magic[4];
u16_le endianness;
u16_le header_size;
u32_le version;
u32_le file_size;
u32_le num_blocks;
};
struct SectionHeader {
u8 magic[4];
u32_le section_size;
};
struct FINF {
u8 magic[4];
u32_le section_size;
u8 font_type;
u8 line_feed;
u16_le alter_char_index;
u8 default_width[3];
u8 encoding;
u32_le tglp_offset;
u32_le cwdh_offset;
u32_le cmap_offset;
u8 height;
u8 width;
u8 ascent;
u8 reserved;
};
struct TGLP {
u8 magic[4];
u32_le section_size;
u8 cell_width;
u8 cell_height;
u8 baseline_position;
u8 max_character_width;
u32_le sheet_size;
u16_le num_sheets;
u16_le sheet_image_format;
u16_le num_columns;
u16_le num_rows;
u16_le sheet_width;
u16_le sheet_height;
u32_le sheet_data_offset;
};
struct CMAP {
u8 magic[4];
u32_le section_size;
u16_le code_begin;
u16_le code_end;
u16_le mapping_method;
u16_le reserved;
u32_le next_cmap_offset;
};
struct CWDH {
u8 magic[4];
u32_le section_size;
u16_le start_index;
u16_le end_index;
u32_le next_cwdh_offset;
};
/**
* Relocates the internal addresses of the BCFNT Shared Font to the new base. The current base will
* be auto-detected based on the file headers.
*
* @param shared_font SharedMemory object that contains the Shared Font
* @param new_address New base for the offsets in the structure.
*/
void RelocateSharedFont(std::shared_ptr<Kernel::SharedMemory> shared_font, VAddr new_address);
} // namespace Service::APT::BCFNT

View File

@@ -0,0 +1,13 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
namespace Service::APT::ErrCodes {
enum {
ParameterPresent = 2,
InvalidAppletSlot = 4,
AppNotRunning = 11,
};
} // namespace Service::APT::ErrCodes

View File

@@ -0,0 +1,57 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/core.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/apt/ns.h"
#include "core/loader/loader.h"
namespace Service::NS {
std::shared_ptr<Kernel::Process> LaunchTitle(Core::System& system, FS::MediaType media_type,
u64 title_id) {
std::string path = AM::GetTitleContentPath(media_type, title_id);
auto loader = Loader::GetLoader(path);
if (!loader) {
LOG_WARNING(Service_NS, "Could not find .app for title 0x{:016x}", title_id);
return nullptr;
}
auto plg_ldr = Service::PLGLDR::GetService(system);
if (plg_ldr) {
const auto& plg_context = plg_ldr->GetPluginLoaderContext();
if (plg_context.is_enabled && plg_context.use_user_load_parameters &&
plg_context.user_load_parameters.low_title_Id == static_cast<u32>(title_id) &&
plg_context.user_load_parameters.plugin_memory_strategy ==
PLGLDR::PLG_LDR::PluginMemoryStrategy::PLG_STRATEGY_MODE3) {
loader->SetKernelMemoryModeOverride(Kernel::MemoryMode::Dev2);
}
}
std::shared_ptr<Kernel::Process> process;
Loader::ResultStatus result = loader->Load(process);
if (result != Loader::ResultStatus::Success) {
LOG_WARNING(Service_NS, "Error loading .app for title 0x{:016x}", title_id);
return nullptr;
}
return process;
}
void RebootToTitle(Core::System& system, FS::MediaType media_type, u64 title_id) {
auto new_path = AM::GetTitleContentPath(media_type, title_id);
if (new_path.empty() || !FileUtil::Exists(new_path)) {
// TODO: This can happen if the requested title is not installed. Need a way to find
// non-installed titles in the game list.
LOG_CRITICAL(Service_APT,
"Failed to find title '{}' to jump to, resetting current title instead.",
new_path);
new_path.clear();
}
system.RequestReset(new_path);
}
} // namespace Service::NS

View File

@@ -0,0 +1,25 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/fs/archive.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::NS {
/// Loads and launches the title identified by title_id in the specified media type.
std::shared_ptr<Kernel::Process> LaunchTitle(Core::System& system, FS::MediaType media_type,
u64 title_id);
/// Reboots the system to the specified title.
void RebootToTitle(Core::System& system, FS::MediaType media_type, u64 title_id);
} // namespace Service::NS

View File

@@ -0,0 +1,23 @@
// Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/apt/ns_c.h"
namespace Service::NS {
NS_C::NS_C(std::shared_ptr<Service::APT::Module> apt)
: Service::APT::Module::NSInterface(std::move(apt), "ns:c", 1) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, nullptr, "LockSpecialContent"},
{0x0002, nullptr, "UnlockSpecialContent"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::NS
SERIALIZE_EXPORT_IMPL(Service::NS::NS_C)

View File

@@ -0,0 +1,25 @@
// Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/apt/apt.h"
#include "core/hle/service/service.h"
namespace Service::NS {
/// Interface to "ns:c" service
class NS_C final : public Service::APT::Module::NSInterface {
public:
explicit NS_C(std::shared_ptr<Service::APT::Module> apt);
private:
SERVICE_SERIALIZATION(NS_C, apt, Service::APT::Module)
};
} // namespace Service::NS
BOOST_CLASS_EXPORT_KEY(Service::NS::NS_C)
BOOST_SERIALIZATION_CONSTRUCT(Service::NS::NS_C)

View File

@@ -0,0 +1,36 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/apt/ns_s.h"
namespace Service::NS {
NS_S::NS_S(std::shared_ptr<Service::APT::Module> apt)
: Service::APT::Module::NSInterface(std::move(apt), "ns:s", 3) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, nullptr, "LaunchFIRM"},
{0x0002, nullptr, "LaunchTitle"},
{0x0003, nullptr, "TerminateApplication"},
{0x0004, nullptr, "TerminateProcess"},
{0x0005, nullptr, "LaunchApplicationFIRM"},
{0x0006, &NS_S::SetWirelessRebootInfo, "SetWirelessRebootInfo"},
{0x0007, nullptr, "CardUpdateInitialize"},
{0x0008, nullptr, "CardUpdateShutdown"},
{0x000D, nullptr, "SetTWLBannerHMAC"},
{0x000E, &NS_S::ShutdownAsync, "ShutdownAsync"},
{0x0010, &NS_S::RebootSystem, "RebootSystem"},
{0x0011, nullptr, "TerminateTitle"},
{0x0012, nullptr, "SetApplicationCpuTimeLimit"},
{0x0015, nullptr, "LaunchApplication"},
{0x0016, &NS_S::RebootSystemClean, "RebootSystemClean"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::NS
SERIALIZE_EXPORT_IMPL(Service::NS::NS_S)

View File

@@ -0,0 +1,25 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/apt/apt.h"
#include "core/hle/service/service.h"
namespace Service::NS {
/// Interface to "ns:s" service
class NS_S final : public Service::APT::Module::NSInterface {
public:
explicit NS_S(std::shared_ptr<Service::APT::Module> apt);
private:
SERVICE_SERIALIZATION(NS_S, apt, Service::APT::Module)
};
} // namespace Service::NS
BOOST_CLASS_EXPORT_KEY(Service::NS::NS_S)
BOOST_SERIALIZATION_CONSTRUCT(Service::NS::NS_S)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,999 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <boost/serialization/export.hpp>
#include "core/global.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/service/boss/online_service.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace IPC {
class RequestParser;
}
namespace Service::BOSS {
class Module final {
public:
explicit Module(Core::System& system_);
~Module() = default;
struct SessionData : public Kernel::SessionRequestHandler::SessionDataBase {
std::shared_ptr<OnlineService> online_service{};
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int);
friend class boost::serialization::access;
};
class Interface : public ServiceFramework<Interface, SessionData> {
public:
Interface(std::shared_ptr<Module> boss, const char* name, u32 max_session);
~Interface() = default;
protected:
/**
* BOSS::InitializeSession service function
* Inputs:
* 0 : Header Code[0x00010082]
* 1-2 : programID, normally zero for using the programID determined from the input PID
* 3 : 0x20, ARM11-kernel processID translate-header.
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void InitializeSession(Kernel::HLERequestContext& ctx);
/**
* BOSS::RegisterStorage service function
* Inputs:
* 0 : Header Code[0x00020100]
* 1-2 : u64 extdataID
* 3 : u32 boss_size
* 4 : u8 extdata_type: 0 = NAND, 1 = SD
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void SetStorageInfo(Kernel::HLERequestContext& ctx);
/**
* BOSS::UnregisterStorage service function
* Inputs:
* 0 : Header Code[0x00030000]
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void UnregisterStorage(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetStorageInfo service function
* Inputs:
* 0 : Header Code[0x00040000]
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u32 unknown value
*/
void GetStorageInfo(Kernel::HLERequestContext& ctx);
/**
* BOSS::RegisterPrivateRootCa service function
* Inputs:
* 0 : Header Code[0x00050042]
* 1 : u32 Size
* 2 : MappedBufferDesc(permission = R)
* 3 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : buff_size << 4 | 0xA
* 3 : u32 buff_addr
*/
void RegisterPrivateRootCa(Kernel::HLERequestContext& ctx);
/**
* BOSS::RegisterPrivateClientCert service function
* Inputs:
* 0 : Header Code[0x00060084]
* 1 : u32 buffer 1 size
* 2 : u32 buffer 2 size
* 3 : MappedBufferDesc1(permission = R)
* 4 : u32 buff_addr1
* 5 : MappedBufferDesc2(permission = R)
* 6 : u32 buff_addr2
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : buff1_size << 4 | 0xA
* 3 : u32 buff_addr1
* 4 : buff2_size << 4 | 0xA
* 5 : u32 buff_addr2
*/
void RegisterPrivateClientCert(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetNewArrivalFlag service function
* Inputs:
* 0 : Header Code[0x00070000]
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u8 flag
*/
void GetNewArrivalFlag(Kernel::HLERequestContext& ctx);
/**
* BOSS::RegisterNewArrivalEvent service function
* Inputs:
* 0 : Header Code[0x00080002]
* 1 : u32 unknown1
* 2 : u32 unknown2
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void RegisterNewArrivalEvent(Kernel::HLERequestContext& ctx);
/**
* BOSS::SetOptoutFlag service function
* Inputs:
* 0 : Header Code[0x00090040]
* 1 : u8 output_flag
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void SetOptoutFlag(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetOptoutFlag service function
* Inputs:
* 0 : Header Code[0x000A0000]
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u8 output_flag
*/
void GetOptoutFlag(Kernel::HLERequestContext& ctx);
/**
* BOSS::RegisterTask service function
* Inputs:
* 0 : Header Code[0x000B00C2]
* 1 : TaskID buffer size
* 2 : u8 unknown value, Usually zero, regardless of HTTP GET/POST.
* 3 : u8 unknown value, Usually zero, regardless of HTTP GET/POST.
* 4 : MappedBufferDesc1(permission = R)
* 5 : buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : buff_size << 4 | 0xA
* 3 : u32 buff_addr
*/
void RegisterTask(Kernel::HLERequestContext& ctx);
/**
* BOSS::UnregisterTask service function
* Inputs:
* 0 : Header Code[0x000C0082]
* 1 : TaskID buffer size
* 2 : u8 unknown value
* 3 : MappedBufferDesc1(permission = R)
* 4 : buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : buff_size << 4 | 0xA
* 3 : u32 buff_addr
*/
void UnregisterTask(Kernel::HLERequestContext& ctx);
/**
* BOSS::ReconfigureTask service function
* Inputs:
* 0 : Header Code[0x000D0082]
* 1 : TaskID buffer size
* 2 : u8 unknown value
* 3 : MappedBufferDesc1(permission = R)
* 4 : buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : buff_size << 4 | 0xA
* 3 : u32 buff_addr
*/
void ReconfigureTask(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetTaskIdList service function
* Inputs:
* 0 : Header Code[0x000E0000]
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void GetTaskIdList(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetStepIdList service function
* Inputs:
* 0 : Header Code[0x000F0042]
* 1 : u32 buffer size
* 2 : MappedBufferDesc(permission = R)
* 3 : u32 buff_addr
*
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : buff_size << 4 | 0xA
* 3 : u32 buff_addr
*/
void GetStepIdList(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetNsDataIdList service function
* Inputs:
* 0 : Header Code[0x00100102]
* 1 : u32 filter
* 2 : u32 Buffer size in words(max entries)
* 3 : u16, starting word-index in the internal NsDataId list
* 4 : u32, start_NsDataId
* 5 : MappedBufferDesc(permission = W)
* 6 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u16 Actual number of output entries
* 3 : u16 Last word-index copied to output in the internal NsDataId list
* 4 : buff_size << 4 | 0xC
* 5 : u32 buff_addr
*/
void GetNsDataIdList(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetNsDataIdList1 service function
* Inputs:
* 0 : Header Code[0x00110102]
* 1 : u32 filter
* 2 : u32 Buffer size in words(max entries)
* 3 : u16, starting word-index in the internal NsDataId list
* 4 : u32, start_NsDataId
* 5 : MappedBufferDesc(permission = W)
* 6 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u16 Actual number of output entries
* 3 : u16 Last word-index copied to output in the internal NsDataId list
* 4 : buff_size << 4 | 0xC
* 5 : u32 buff_addr
*/
void GetNsDataIdList1(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetNsDataIdList2 service function
* Inputs:
* 0 : Header Code[0x00120102]
* 1 : u32 filter
* 2 : u32 Buffer size in words(max entries)
* 3 : u16, starting word-index in the internal NsDataId list
* 4 : u32, start_NsDataId
* 5 : MappedBufferDesc(permission = W)
* 6 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u16 Actual number of output entries
* 3 : u16 Last word-index copied to output in the internal NsDataId list
* 4 : buff_size << 4 | 0xC
* 5 : u32 buff_addr
*/
void GetNsDataIdList2(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetNsDataIdList3 service function
* Inputs:
* 0 : Header Code[0x00130102]
* 1 : u32 filter
* 2 : u32 Buffer size in words(max entries)
* 3 : u16, starting word-index in the internal NsDataId list
* 4 : u32, start_NsDataId
* 5 : MappedBufferDesc(permission = W)
* 6 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u16 Actual number of output entries
* 3 : u16 Last word-index copied to output in the internal NsDataId list
* 4 : buff_size << 4 | 0xC
* 5 : u32 buff_addr
*/
void GetNsDataIdList3(Kernel::HLERequestContext& ctx);
/**
* BOSS::SendProperty service function
* Inputs:
* 0 : Header Code[0x00140082]
* 1 : u16 PropertyID
* 2 : u32 size
* 3 : MappedBufferDesc(permission = R)
* 4 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : buff_size << 4 | 0xA
* 3 : u32 buff_addr
*/
void SendProperty(Kernel::HLERequestContext& ctx);
/**
* BOSS::SendPropertyHandle service function
* Inputs:
* 0 : Header Code[0x00150042]
* 2 : u16 PropertyID
* 3 : 0x0
* 4 : Handle
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void SendPropertyHandle(Kernel::HLERequestContext& ctx);
/**
* BOSS::ReceiveProperty service function
* Inputs:
* 0 : Header Code[0x00160082]
* 1 : u16 PropertyID
* 2 : u32 Size
* 3 : MappedBufferDesc(permission = W)
* 4 : u32 buff addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Actual read size
* 4 : buff_size << 4 | 0xC
* 5 : u32 buff_addr
*/
void ReceiveProperty(Kernel::HLERequestContext& ctx);
/**
* BOSS::UpdateTaskInterval service function
* Inputs:
* 0 : Header Code[0x00170082]
* 1 : u32 unknown value
* 2 : u8 unknown value
* 3 : MappedBufferDesc1(permission = R)
* 4 : buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : buff_size << 4 | 0xA
* 3 : u32 buff_addr
*/
void UpdateTaskInterval(Kernel::HLERequestContext& ctx);
/**
* BOSS::UpdateTaskCount service function
* Inputs:
* 0 : Header Code[0x00180082]
* 1 : u32 buff_size
* 2 : u32 unknown2
* 3 : MappedBufferDesc(permission = R)
* 4 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : buff_size << 4 | 0xA
* 3 : u32 buff_addr
*/
void UpdateTaskCount(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetTaskInterval service function
* Inputs:
* 0 : Header Code[0x00190042]
* 1 : u32 size
* 2 : MappedBufferDesc(permission = R)
* 3 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u32 unknown value
* 3 : buff_size << 4 | 0xA
* 4 : u32 buff_addr
*/
void GetTaskInterval(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetTaskCount service function
* Inputs:
* 0 : Header Code[0x001A0042]
* 1 : u32 size
* 2 : MappedBufferDesc(permission = R)
* 3 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u32 unknown value
* 3 : buff_size << 4 | 0xA
* 4 : u32 buff_addr
*/
void GetTaskCount(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetTaskServiceStatus service function
* Inputs:
* 0 : Header Code[0x001B0042]
* 1 : u32 size
* 2 : MappedBufferDesc(permission = R)
* 3 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u8 unknown value
* 3 : buff_size << 4 | 0xA
* 4 : u32 buff_addr
*/
void GetTaskServiceStatus(Kernel::HLERequestContext& ctx);
/**
* BOSS::StartTask service function
* Inputs:
* 0 : Header Code[0x001C0042]
* 1 : TaskID buffer size
* 2 : MappedBufferDesc(permission = R)
* 3 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : buff_size << 4 | 0xA
* 3 : u32 buff_addr
*/
void StartTask(Kernel::HLERequestContext& ctx);
/**
* BOSS::StartTaskImmediate service function
* Inputs:
* 0 : Header Code[0x001D0042]
* 1 : TaskID buffer size
* 2 : MappedBufferDesc(permission = R)
* 3 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : buff_size << 4 | 0xA
* 3 : u32 buff_addr
*/
void StartTaskImmediate(Kernel::HLERequestContext& ctx);
/**
* BOSS::CancelTask service function
* Inputs:
* 0 : Header Code[0x001E0042]
* 1 : TaskID buffer size
* 2 : MappedBufferDesc(permission = R)
* 3 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : buff_size << 4 | 0xA
* 3 : u32 buff_addr
*/
void CancelTask(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetTaskFinishHandle service function
* Inputs:
* 0 : Header Code[0x001F0000]
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : 0x0
* 3 : Task Finish Handle
*/
void GetTaskFinishHandle(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetTaskState service function
* Inputs:
* 0 : Header Code[0x00200082]
* 1 : TaskID buffer size
* 2 : u8 state
* 3 : MappedBufferDesc(permission = R)
* 4 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u8 TaskStatus
* 3 : u32 Current state value for task PropertyID 0x4
* 4 : u8 unknown value
* 5 : buff_size << 4 | 0xA
* 6 : u32 buff_addr
*/
void GetTaskState(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetTaskResult service function
* Inputs:
* 0 : Header Code[0x00210042]
* 1 : u32 size
* 2 : MappedBufferDesc(permission = R)
* 3 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u8 unknown value
* 3 : u32 unknown value
* 4 : u8 unknown value
* 5 : buff_size << 4 | 0xA
* 6 : u32 buff_addr
*/
void GetTaskResult(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetTaskCommErrorCode service function
* Inputs:
* 0 : Header Code[0x00220042]
* 1 : u32 size
* 2 : MappedBufferDesc(permission = R)
* 3 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u32 unknown value
* 3 : u32 unknown value
* 4 : u8 unknown value
* 5 : buff_size << 4 | 0xA
* 6 : u32 buff_addr
*/
void GetTaskCommErrorCode(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetTaskStatus service function
* Inputs:
* 0 : Header Code[0x002300C2]
* 1 : u32 size
* 2 : u8 unknown value
* 3 : u8 unknown value
* 4 : MappedBufferDesc(permission = R)
* 5 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u8 unknown value
* 3 : buff_size << 4 | 0xA
* 4 : u32 buff_addr
*/
void GetTaskStatus(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetTaskError service function
* Inputs:
* 0 : Header Code[0x00240082]
* 1 : u32 size
* 2 : u8 unknown value
* 3 : MappedBufferDesc(permission = R)
* 4 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u8 unknown value
* 3 : buff_size << 4 | 0xA
* 4 : u32 buff_addr
*/
void GetTaskError(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetTaskInfo service function
* Inputs:
* 0 : Header Code[0x00250082]
* 1 : u32 size
* 2 : u8 unknown value
* 3 : MappedBufferDesc(permission = R)
* 4 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : buff_size << 4 | 0xA
* 3 : u32 buff_addr
*/
void GetTaskInfo(Kernel::HLERequestContext& ctx);
/**
* BOSS::DeleteNsData service function
* Inputs:
* 0 : Header Code[0x00260040]
* 1 : u32 NsDataID
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void DeleteNsData(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetNsDataHeaderInfo service function
* Inputs:
* 0 : Header Code[0x002700C2]
* 1 : u32, NsDataID
* 2 : u8, type
* 3 : u32, Size
* 4 : MappedBufferDesc(permission = W)
* 5 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : buff_size << 4 | 0xC
* 3 : u32, buff_addr
*/
void GetNsDataHeaderInfo(Kernel::HLERequestContext& ctx);
/**
* BOSS::ReadNsData service function
* Inputs:
* 0 : Header Code[0x00280102]
* 1 : u32, NsDataID
* 2-3 : u64, offset
* 4 : u32, Size
* 5 : MappedBufferDesc(permission = W)
* 6 : u32, buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u32 Actual read size
* 3 : u32, unknown value
* 4 : buff_size << 4 | 0xC
* 5 : u32, buff_addr
*/
void ReadNsData(Kernel::HLERequestContext& ctx);
/**
* BOSS::SetNsDataAdditionalInfo service function
* Inputs:
* 0 : Header Code[0x00290080]
* 1 : u32 unknown value
* 2 : u32 unknown value
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void SetNsDataAdditionalInfo(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetNsDataAdditionalInfo service function
* Inputs:
* 0 : Header Code[0x002A0040]
* 1 : u32 unknown value
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u32 unknown value
*/
void GetNsDataAdditionalInfo(Kernel::HLERequestContext& ctx);
/**
* BOSS::SetNsDataNewFlag service function
* Inputs:
* 0 : Header Code[0x002B0080]
* 1 : u32 unknown value
* 2 : u8 flag
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void SetNsDataNewFlag(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetNsDataNewFlag service function
* Inputs:
* 0 : Header Code[0x002C0040]
* 1 : u32 unknown value
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u8 flag
*/
void GetNsDataNewFlag(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetNsDataLastUpdate service function
* Inputs:
* 0 : Header Code[0x002D0040]
* 1 : u32 unknown value
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u32 unknown value
* 3 : u32 unknown value
*/
void GetNsDataLastUpdate(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetErrorCode service function
* Inputs:
* 0 : Header Code[0x002E0040]
* 1 : u8 input
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u32 unknown value
*/
void GetErrorCode(Kernel::HLERequestContext& ctx);
/**
* BOSS::RegisterStorageEntry service function
* Inputs:
* 0 : Header Code[0x002F0140]
* 1 : u32 unknown value
* 2 : u32 unknown value
* 3 : u32 unknown value
* 4 : u16 unknown value
* 5 : u8 unknown value
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void RegisterStorageEntry(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetStorageEntryInfo service function
* Inputs:
* 0 : Header Code[0x00300000]
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u32 unknown value
* 3 : u16 unknown value
*/
void GetStorageEntryInfo(Kernel::HLERequestContext& ctx);
/**
* BOSS::SetStorageOption service function
* Inputs:
* 0 : Header Code[0x00310100]
* 1 : u8 unknown value
* 2 : u32 unknown value
* 3 : u16 unknown value
* 4 : u16 unknown value
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void SetStorageOption(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetStorageOption service function
* Inputs:
* 0 : Header Code[0x00320000]
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u8 unknown value
* 3 : u32 unknown value
* 4 : u16 unknown value
* 5 : u16 unknown value
*/
void GetStorageOption(Kernel::HLERequestContext& ctx);
/**
* BOSS::StartBgImmediate service function
* Inputs:
* 0 : Header Code[0x00330042]
* 1 : TaskID buffer size
* 2 : MappedBufferDesc(permission = R)
* 3 : u32, buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : buff_size << 4 | 0xA
* 3 : u32, buff_addr
*/
void StartBgImmediate(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetTaskProperty0 service function
* Inputs:
* 0 : Header Code[0x00340042]
* 1 : u32 size
* 2 : MappedBufferDesc(permission = R)
* 3 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u8 current state
* 3 : buff_size << 4 | 0xA
* 4 : u32 buff_addr
*/
void GetTaskProperty0(Kernel::HLERequestContext& ctx);
/**
* BOSS::RegisterImmediateTask service function
* Inputs:
* 0 : Header Code[0x003500C2]
* 1 : u32 size
* 2 : u8 unknown value
* 3 : u8 unknown value
* 4 : MappedBufferDesc(permission = R)
* 5 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : buff_size << 4 | 0xA
* 3 : u32 buff_addr
*/
void RegisterImmediateTask(Kernel::HLERequestContext& ctx);
/**
* BOSS::SetTaskQuery service function
* Inputs:
* 0 : Header Code[0x00360084]
* 1 : u32 buffer1 size
* 2 : u32 buffer2 size
* 3 : MappedBufferDesc1(permission = R)
* 4 : u32 buff1_addr
* 5 : MappedBufferDesc2(permission = R)
* 6 : u32 buff2_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : buff1_size << 4 | 0xA
* 3 : u32 buff1_addr
* 4 : buff2_size << 4 | 0xA
* 5 : u32 buff2_addr
*/
void SetTaskQuery(Kernel::HLERequestContext& ctx);
/**
* BOSS::GetTaskQuery service function
* Inputs:
* 0 : Header Code[0x00370084]
* 1 : u32 buffer1 size
* 2 : u32 buffer2 size
* 3 : MappedBufferDesc1(permission = R)
* 4 : u32 buff1_addr
* 5 : MappedBufferDesc2(permission = W)
* 6 : u32 buff2_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : buff1_size << 4 | 0xA
* 3 : u32 buff1_addr
* 4 : buff2_size << 4 | 0xC
* 5 : u32 buff2_addr
*/
void GetTaskQuery(Kernel::HLERequestContext& ctx);
/**
* BOSS_P::InitializeSessionPrivileged service function
* Inputs:
* 0 : Header Code[0x04010082]
* 1-2 : programID, normally zero for using the programID determined from the input PID
* 3 : 0x20, ARM11-kernel processID translate-header.
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void InitializeSessionPrivileged(Kernel::HLERequestContext& ctx);
/**
* BOSS_P::GetAppNewFlag service function
* Inputs:
* 0 : Header Code[0x04040080]
* 1-2 : u64 ProgramID
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u8 flag, 0 = nothing new, 1 = new content
*/
void GetAppNewFlag(Kernel::HLERequestContext& ctx);
/**
* BOSS_P::GetNsDataIdListPrivileged service function
* Inputs:
* 0 : Header Code[0x040D0182]
* 1-2 : u64 ProgramID
* 3 : u32 filter
* 4 : u32 Buffer size in words(max entries)
* 5 : u16, starting word-index in the internal NsDataId list
* 6 : u32, start_NsDataId
* 7 : MappedBufferDesc(permission = W)
* 8 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u16 Actual number of output entries
* 3 : u16 Last word-index copied to output in the internal NsDataId list
* 4 : buff_size << 4 | 0xC
* 5 : u32 buff_addr
*/
void GetNsDataIdListPrivileged(Kernel::HLERequestContext& ctx);
/**
* BOSS_P::GetNsDataIdListPrivileged1 service function
* Inputs:
* 0 : Header Code[0x040E0182]
* 1-2 : u64 ProgramID
* 3 : u32 filter
* 4 : u32 Buffer size in words(max entries)
* 5 : u16, starting word-index in the internal NsDataId list
* 6 : u32, start_NsDataId
* 7 : MappedBufferDesc(permission = W)
* 8 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u16 Actual number of output entries
* 3 : u16 Last word-index copied to output in the internal NsDataId list
* 4 : buff_size << 4 | 0xC
* 5 : u32 buff_addr
*/
void GetNsDataIdListPrivileged1(Kernel::HLERequestContext& ctx);
/**
* BOSS_P::SendPropertyPrivileged service function
* Inputs:
* 0 : Header Code[0x04130082]
* 1 : u16 PropertyID
* 2 : u32 size
* 3 : MappedBufferDesc(permission = R)
* 4 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : buff_size << 4 | 0xA
* 3 : u32 buff_addr
*/
void SendPropertyPrivileged(Kernel::HLERequestContext& ctx);
/**
* BOSS_P::DeleteNsDataPrivileged service function
* Inputs:
* 0 : Header Code[0x041500C0]
* 1-2 : u64 ProgramID
* 3 : u32 NsDataID
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void DeleteNsDataPrivileged(Kernel::HLERequestContext& ctx);
/**
* BOSS_P::GetNsDataHeaderInfoPrivileged service function
* Inputs:
* 0 : Header Code[0x04160142]
* 1-2 : u64 ProgramID
* 3 : u32, NsDataID
* 4 : u8, type
* 5 : u32, Size
* 6 : MappedBufferDesc(permission = W)
* 7 : u32 buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : buff_size << 4 | 0xC
* 3 : u32, buff_addr
*/
void GetNsDataHeaderInfoPrivileged(Kernel::HLERequestContext& ctx);
/**
* BOSS_P::ReadNsDataPrivileged service function
* Inputs:
* 0 : Header Code[0x04170182]
* 1-2 : u64 ProgramID
* 3 : u32, NsDataID
* 4-5 : u64, offset
* 6 : u32, Size
* 7 : MappedBufferDesc(permission = W)
* 8 : u32, buff_addr
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u32 Actual read size
* 3 : u32, unknown value
* 4 : buff_size << 4 | 0xC
* 5 : u32, buff_addr
*/
void ReadNsDataPrivileged(Kernel::HLERequestContext& ctx);
/**
* BOSS_P::SetNsDataNewFlagPrivileged service function
* Inputs:
* 0 : Header Code[0x041A0100]
* 1-2 : u64 ProgramID
* 3 : u32 unknown value
* 4 : u8 flag
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void SetNsDataNewFlagPrivileged(Kernel::HLERequestContext& ctx);
/**
* BOSS_P::GetNsDataNewFlagPrivileged service function
* Inputs:
* 0 : Header Code[0x041B00C0]
* 1-2 : u64 ProgramID
* 3 : u32 unknown value
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u8 flag
*/
void GetNsDataNewFlagPrivileged(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> boss;
std::shared_ptr<OnlineService> GetSessionService(Kernel::HLERequestContext& ctx,
IPC::RequestParser& rp);
};
private:
Core::System& system;
std::shared_ptr<Kernel::Event> task_finish_event;
u8 new_arrival_flag;
u8 ns_data_new_flag;
u8 ns_data_new_flag_privileged;
u8 output_flag;
template <class Archive>
void serialize(Archive& ar, const unsigned int);
friend class boost::serialization::access;
};
void InstallInterfaces(Core::System& system);
} // namespace Service::BOSS
SERVICE_CONSTRUCT(Service::BOSS::Module)
BOOST_CLASS_EXPORT_KEY(Service::BOSS::Module)
BOOST_CLASS_EXPORT_KEY(Service::BOSS::Module::SessionData)

View File

@@ -0,0 +1,89 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/boss/boss_p.h"
namespace Service::BOSS {
BOSS_P::BOSS_P(std::shared_ptr<Module> boss)
: Module::Interface(std::move(boss), "boss:P", DefaultMaxSessions) {
static const FunctionInfo functions[] = {
// boss:u shared commands
// clang-format off
{0x0001, &BOSS_P::InitializeSession, "InitializeSession"},
{0x0002, &BOSS_P::SetStorageInfo, "RegisterStorage"},
{0x0003, &BOSS_P::UnregisterStorage, "UnregisterStorage"},
{0x0004, &BOSS_P::GetStorageInfo, "GetStorageInfo"},
{0x0005, &BOSS_P::RegisterPrivateRootCa, "RegisterPrivateRootCa"},
{0x0006, &BOSS_P::RegisterPrivateClientCert, "RegisterPrivateClientCert"},
{0x0007, &BOSS_P::GetNewArrivalFlag, "GetNewArrivalFlag"},
{0x0008, &BOSS_P::RegisterNewArrivalEvent, "RegisterNewArrivalEvent"},
{0x0009, &BOSS_P::SetOptoutFlag, "SetOptoutFlag"},
{0x000A, &BOSS_P::GetOptoutFlag, "GetOptoutFlag"},
{0x000B, &BOSS_P::RegisterTask, "RegisterTask"},
{0x000C, &BOSS_P::UnregisterTask, "UnregisterTask"},
{0x000D, &BOSS_P::ReconfigureTask, "ReconfigureTask"},
{0x000E, &BOSS_P::GetTaskIdList, "GetTaskIdList"},
{0x000F, &BOSS_P::GetStepIdList, "GetStepIdList"},
{0x0010, &BOSS_P::GetNsDataIdList, "GetNsDataIdList"},
{0x0011, &BOSS_P::GetNsDataIdList1, "GetNsDataIdList1"},
{0x0012, &BOSS_P::GetNsDataIdList2, "GetNsDataIdList2"},
{0x0013, &BOSS_P::GetNsDataIdList3, "GetNsDataIdList3"},
{0x0014, &BOSS_P::SendProperty, "SendProperty"},
{0x0015, &BOSS_P::SendPropertyHandle, "SendPropertyHandle"},
{0x0016, &BOSS_P::ReceiveProperty, "ReceiveProperty"},
{0x0017, &BOSS_P::UpdateTaskInterval, "UpdateTaskInterval"},
{0x0018, &BOSS_P::UpdateTaskCount, "UpdateTaskCount"},
{0x0019, &BOSS_P::GetTaskInterval, "GetTaskInterval"},
{0x001A, &BOSS_P::GetTaskCount, "GetTaskCount"},
{0x001B, &BOSS_P::GetTaskServiceStatus, "GetTaskServiceStatus"},
{0x001C, &BOSS_P::StartTask, "StartTask"},
{0x001D, &BOSS_P::StartTaskImmediate, "StartTaskImmediate"},
{0x001E, &BOSS_P::CancelTask, "CancelTask"},
{0x001F, &BOSS_P::GetTaskFinishHandle, "GetTaskFinishHandle"},
{0x0020, &BOSS_P::GetTaskState, "GetTaskState"},
{0x0021, &BOSS_P::GetTaskResult, "GetTaskResult"},
{0x0022, &BOSS_P::GetTaskCommErrorCode, "GetTaskCommErrorCode"},
{0x0023, &BOSS_P::GetTaskStatus, "GetTaskStatus"},
{0x0024, &BOSS_P::GetTaskError, "GetTaskError"},
{0x0025, &BOSS_P::GetTaskInfo, "GetTaskInfo"},
{0x0026, &BOSS_P::DeleteNsData, "DeleteNsData"},
{0x0027, &BOSS_P::GetNsDataHeaderInfo, "GetNsDataHeaderInfo"},
{0x0028, &BOSS_P::ReadNsData, "ReadNsData"},
{0x0029, &BOSS_P::SetNsDataAdditionalInfo, "SetNsDataAdditionalInfo"},
{0x002A, &BOSS_P::GetNsDataAdditionalInfo, "GetNsDataAdditionalInfo"},
{0x002B, &BOSS_P::SetNsDataNewFlag, "SetNsDataNewFlag"},
{0x002C, &BOSS_P::GetNsDataNewFlag, "GetNsDataNewFlag"},
{0x002D, &BOSS_P::GetNsDataLastUpdate, "GetNsDataLastUpdate"},
{0x002E, &BOSS_P::GetErrorCode, "GetErrorCode"},
{0x002F, &BOSS_P::RegisterStorageEntry, "RegisterStorageEntry"},
{0x0030, &BOSS_P::GetStorageEntryInfo, "GetStorageEntryInfo"},
{0x0031, &BOSS_P::SetStorageOption, "SetStorageOption"},
{0x0032, &BOSS_P::GetStorageOption, "GetStorageOption"},
{0x0033, &BOSS_P::StartBgImmediate, "StartBgImmediate"},
{0x0034, &BOSS_P::GetTaskProperty0, "GetTaskProperty0"},
{0x0035, &BOSS_P::RegisterImmediateTask, "RegisterImmediateTask"},
{0x0036, &BOSS_P::SetTaskQuery, "SetTaskQuery"},
{0x0037, &BOSS_P::GetTaskQuery, "GetTaskQuery"},
// boss:p
{0x0401, &BOSS_P::InitializeSessionPrivileged, "InitializeSessionPrivileged"},
{0x0404, &BOSS_P::GetAppNewFlag, "GetAppNewFlag"},
{0x040D, &BOSS_P::GetNsDataIdListPrivileged, "GetNsDataIdListPrivileged"},
{0x040E, &BOSS_P::GetNsDataIdListPrivileged1, "GetNsDataIdListPrivileged1"},
{0x0413, &BOSS_P::SendPropertyPrivileged, "SendPropertyPrivileged"},
{0x0415, &BOSS_P::DeleteNsDataPrivileged, "DeleteNsDataPrivileged"},
{0x0416, &BOSS_P::GetNsDataHeaderInfoPrivileged, "GetNsDataHeaderInfoPrivileged"},
{0x0417, &BOSS_P::ReadNsDataPrivileged, "ReadNsDataPrivileged"},
{0x041A, &BOSS_P::SetNsDataNewFlagPrivileged, "SetNsDataNewFlagPrivileged"},
{0x041B, &BOSS_P::GetNsDataNewFlagPrivileged, "GetNsDataNewFlagPrivileged"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::BOSS
SERIALIZE_EXPORT_IMPL(Service::BOSS::BOSS_P)

View File

@@ -0,0 +1,22 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included..
#pragma once
#include "core/hle/service/boss/boss.h"
namespace Service::BOSS {
class BOSS_P final : public Module::Interface {
public:
explicit BOSS_P(std::shared_ptr<Module> boss);
private:
SERVICE_SERIALIZATION(BOSS_P, boss, Module)
};
} // namespace Service::BOSS
BOOST_CLASS_EXPORT_KEY(Service::BOSS::BOSS_P)
BOOST_SERIALIZATION_CONSTRUCT(Service::BOSS::BOSS_P)

View File

@@ -0,0 +1,77 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/boss/boss_u.h"
namespace Service::BOSS {
BOSS_U::BOSS_U(std::shared_ptr<Module> boss)
: Module::Interface(std::move(boss), "boss:U", DefaultMaxSessions) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, &BOSS_U::InitializeSession, "InitializeSession"},
{0x0002, &BOSS_U::SetStorageInfo, "SetStorageInfo"},
{0x0003, &BOSS_U::UnregisterStorage, "UnregisterStorage"},
{0x0004, &BOSS_U::GetStorageInfo, "GetStorageInfo"},
{0x0005, &BOSS_U::RegisterPrivateRootCa, "RegisterPrivateRootCa"},
{0x0006, &BOSS_U::RegisterPrivateClientCert, "RegisterPrivateClientCert"},
{0x0007, &BOSS_U::GetNewArrivalFlag, "GetNewArrivalFlag"},
{0x0008, &BOSS_U::RegisterNewArrivalEvent, "RegisterNewArrivalEvent"},
{0x0009, &BOSS_U::SetOptoutFlag, "SetOptoutFlag"},
{0x000A, &BOSS_U::GetOptoutFlag, "GetOptoutFlag"},
{0x000B, &BOSS_U::RegisterTask, "RegisterTask"},
{0x000C, &BOSS_U::UnregisterTask, "UnregisterTask"},
{0x000D, &BOSS_U::ReconfigureTask, "ReconfigureTask"},
{0x000E, &BOSS_U::GetTaskIdList, "GetTaskIdList"},
{0x000F, &BOSS_U::GetStepIdList, "GetStepIdList"},
{0x0010, &BOSS_U::GetNsDataIdList, "GetNsDataIdList"},
{0x0011, &BOSS_U::GetNsDataIdList1, "GetNsDataIdList1"},
{0x0012, &BOSS_U::GetNsDataIdList2, "GetNsDataIdList2"},
{0x0013, &BOSS_U::GetNsDataIdList3, "GetNsDataIdList3"},
{0x0014, &BOSS_U::SendProperty, "SendProperty"},
{0x0015, &BOSS_U::SendPropertyHandle, "SendPropertyHandle"},
{0x0016, &BOSS_U::ReceiveProperty, "ReceiveProperty"},
{0x0017, &BOSS_U::UpdateTaskInterval, "UpdateTaskInterval"},
{0x0018, &BOSS_U::UpdateTaskCount, "UpdateTaskCount"},
{0x0019, &BOSS_U::GetTaskInterval, "GetTaskInterval"},
{0x001A, &BOSS_U::GetTaskCount, "GetTaskCount"},
{0x001B, &BOSS_U::GetTaskServiceStatus, "GetTaskServiceStatus"},
{0x001C, &BOSS_U::StartTask, "StartTask"},
{0x001D, &BOSS_U::StartTaskImmediate, "StartTaskImmediate"},
{0x001E, &BOSS_U::CancelTask, "CancelTask"},
{0x001F, &BOSS_U::GetTaskFinishHandle, "GetTaskFinishHandle"},
{0x0020, &BOSS_U::GetTaskState, "GetTaskState"},
{0x0021, &BOSS_U::GetTaskResult, "GetTaskResult"},
{0x0022, &BOSS_U::GetTaskCommErrorCode, "GetTaskCommErrorCode"},
{0x0023, &BOSS_U::GetTaskStatus, "GetTaskStatus"},
{0x0024, &BOSS_U::GetTaskError, "GetTaskError"},
{0x0025, &BOSS_U::GetTaskInfo, "GetTaskInfo"},
{0x0026, &BOSS_U::DeleteNsData, "DeleteNsData"},
{0x0027, &BOSS_U::GetNsDataHeaderInfo, "GetNsDataHeaderInfo"},
{0x0028, &BOSS_U::ReadNsData, "ReadNsData"},
{0x0029, &BOSS_U::SetNsDataAdditionalInfo, "SetNsDataAdditionalInfo"},
{0x002A, &BOSS_U::GetNsDataAdditionalInfo, "GetNsDataAdditionalInfo"},
{0x002B, &BOSS_U::SetNsDataNewFlag, "SetNsDataNewFlag"},
{0x002C, &BOSS_U::GetNsDataNewFlag, "GetNsDataNewFlag"},
{0x002D, &BOSS_U::GetNsDataLastUpdate, "GetNsDataLastUpdate"},
{0x002E, &BOSS_U::GetErrorCode, "GetErrorCode"},
{0x002F, &BOSS_U::RegisterStorageEntry, "RegisterStorageEntry"},
{0x0030, &BOSS_U::GetStorageEntryInfo, "GetStorageEntryInfo"},
{0x0031, &BOSS_U::SetStorageOption, "SetStorageOption"},
{0x0032, &BOSS_U::GetStorageOption, "GetStorageOption"},
{0x0033, &BOSS_U::StartBgImmediate, "StartBgImmediate"},
{0x0034, &BOSS_U::GetTaskProperty0, "GetTaskProperty0"},
{0x0035, &BOSS_U::RegisterImmediateTask, "RegisterImmediateTask"},
{0x0036, &BOSS_U::SetTaskQuery, "SetTaskQuery"},
{0x0037, &BOSS_U::GetTaskQuery, "GetTaskQuery"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::BOSS
SERIALIZE_EXPORT_IMPL(Service::BOSS::BOSS_U)

View File

@@ -0,0 +1,22 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included..
#pragma once
#include "core/hle/service/boss/boss.h"
namespace Service::BOSS {
class BOSS_U final : public Module::Interface {
public:
explicit BOSS_U(std::shared_ptr<Module> boss);
private:
SERVICE_SERIALIZATION(BOSS_U, boss, Module)
};
} // namespace Service::BOSS
BOOST_CLASS_EXPORT_KEY(Service::BOSS::BOSS_U)
BOOST_SERIALIZATION_CONSTRUCT(Service::BOSS::BOSS_U)

View File

@@ -0,0 +1,542 @@
// Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <boost/serialization/map.hpp>
#include "common/archives.h"
#include "common/file_util.h"
#include "common/serialization/boost_std_variant.hpp"
#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/archive_backend.h"
#include "core/file_sys/archive_extsavedata.h"
#include "core/file_sys/archive_systemsavedata.h"
#include "core/file_sys/directory_backend.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/file_backend.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/boss/online_service.h"
namespace Service::BOSS {
namespace ErrCodes {
enum {
TaskNotFound = 51,
NsDataNotFound = 64,
UnknownPropertyID = 77,
};
}
OnlineService::OnlineService(u64 program_id_, u64 extdata_id_)
: program_id(program_id_), extdata_id(extdata_id_) {}
template <class Archive>
void OnlineService::serialize(Archive& ar, const unsigned int) {
ar& current_props;
ar& task_id_list;
ar& program_id;
ar& extdata_id;
}
SERIALIZE_IMPL(OnlineService)
template <class Archive>
void BossTaskProperties::serialize(Archive& ar, const unsigned int) {
ar& task_result;
ar& properties;
}
SERIALIZE_IMPL(BossTaskProperties)
Result OnlineService::InitializeSession(u64 init_program_id) {
// The BOSS service uses three databases:
// BOSS_A: Archive? A list of program ids and some properties that are keyed on program
// BOSS_SS: Saved Strings? Includes the url and the other string properties, and also some other
// properties?, keyed on task_id
// BOSS_SV: Saved Values? Includes task id and most properties keyed on task_id
// It saves data in its databases in the following format:
// A four byte header (always 00 80 34 12?) followed by any number of 0x800(BOSS_A) and
// 0xC00(BOSS_SS and BOSS_SV) entries.
current_props = BossTaskProperties();
if (init_program_id == 0) {
init_program_id = program_id;
}
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
FileSys::ArchiveFactory_SystemSaveData systemsavedata_factory(nand_directory);
// Open the SystemSaveData archive 0x00010034
const FileSys::Path archive_path(boss_system_savedata_id);
auto archive_result = systemsavedata_factory.Open(archive_path, 0);
std::unique_ptr<FileSys::ArchiveBackend> boss_system_save_data_archive;
if (archive_result.Succeeded()) {
boss_system_save_data_archive = std::move(archive_result).Unwrap();
} else if (archive_result.Code() == FileSys::ResultNotFound) {
// If the archive didn't exist, create the files inside
systemsavedata_factory.Format(archive_path, FileSys::ArchiveFormatInfo(), 0, 0, 0);
// Open it again to get a valid archive now that the folder exists
auto create_archive_result = systemsavedata_factory.Open(archive_path, 0);
if (!create_archive_result.Succeeded()) {
LOG_ERROR(Service_BOSS, "Could not open BOSS savedata");
// TODO: Proper error code.
return ResultUnknown;
}
boss_system_save_data_archive = std::move(create_archive_result).Unwrap();
} else {
LOG_ERROR(Service_BOSS, "Could not open BOSS savedata");
// TODO: Proper error code.
return ResultUnknown;
}
FileSys::Mode open_mode = {};
open_mode.read_flag.Assign(1);
// TODO: Read data from BOSS_A.db
auto boss_sv_result =
boss_system_save_data_archive->OpenFile(FileSys::Path("/BOSS_SV.db"), open_mode);
auto boss_ss_result =
boss_system_save_data_archive->OpenFile(FileSys::Path("/BOSS_SS.db"), open_mode);
if (!boss_sv_result.Succeeded() || !boss_ss_result.Succeeded()) {
LOG_ERROR(Service_BOSS, "Could not open BOSS database.");
return ResultSuccess;
}
auto boss_sv = std::move(boss_sv_result).Unwrap();
auto boss_ss = std::move(boss_ss_result).Unwrap();
if (!(boss_sv->GetSize() > BOSS_SAVE_HEADER_SIZE &&
((boss_sv->GetSize() - BOSS_SAVE_HEADER_SIZE) % BOSS_S_ENTRY_SIZE) == 0 &&
boss_sv->GetSize() == boss_ss->GetSize())) {
LOG_ERROR(Service_BOSS, "BOSS database has incorrect size.");
return ResultSuccess;
}
// Read the files if they already exist
const u64 num_entries = (boss_sv->GetSize() - BOSS_SAVE_HEADER_SIZE) / BOSS_S_ENTRY_SIZE;
for (u64 i = 0; i < num_entries; i++) {
const u64 entry_offset = i * BOSS_S_ENTRY_SIZE + BOSS_SAVE_HEADER_SIZE;
BossSVData sv_data;
boss_sv->Read(entry_offset, sizeof(BossSVData), reinterpret_cast<u8*>(&sv_data));
BossSSData ss_data;
boss_ss->Read(entry_offset, sizeof(BossSSData), reinterpret_cast<u8*>(&ss_data));
if (sv_data.program_id != init_program_id) {
continue;
}
std::vector<u8> url_vector(URL_SIZE);
std::memcpy(url_vector.data(), ss_data.url.data(), URL_SIZE);
current_props.properties[PropertyID::Url] = url_vector;
const auto task_id = std::string(sv_data.task_id.data());
if (task_id_list.contains(task_id)) {
LOG_WARNING(Service_BOSS, "TaskId already in list, will be replaced");
task_id_list.erase(task_id);
}
task_id_list.emplace(task_id, std::move(current_props));
current_props = BossTaskProperties();
}
return ResultSuccess;
}
void OnlineService::RegisterTask(const u32 size, Kernel::MappedBuffer& buffer) {
std::string task_id(size, 0);
buffer.Read(task_id.data(), 0, size);
if (task_id_list.contains(task_id)) {
LOG_WARNING(Service_BOSS, "TaskId already in list, will be replaced");
task_id_list.erase(task_id);
}
task_id_list.emplace(task_id, std::move(current_props));
current_props = BossTaskProperties();
}
Result OnlineService::UnregisterTask(const u32 size, Kernel::MappedBuffer& buffer) {
if (size > TASK_ID_SIZE) {
LOG_WARNING(Service_BOSS, "TaskId cannot be longer than 8");
// TODO: Proper error code.
return ResultUnknown;
}
std::string task_id(size, 0);
buffer.Read(task_id.data(), 0, size);
if (task_id_list.erase(task_id) == 0) {
LOG_WARNING(Service_BOSS, "TaskId '{}' not in list", task_id);
return {ErrCodes::TaskNotFound, ErrorModule::BOSS, ErrorSummary::InvalidState,
ErrorLevel::Status};
}
return ResultSuccess;
}
void OnlineService::GetTaskIdList() {
current_props.properties[PropertyID::TotalTasks] = static_cast<u16>(task_id_list.size());
const auto num_task_ids = TASKIDLIST_SIZE / TASK_ID_SIZE;
std::vector<std::array<u8, TASK_ID_SIZE>> task_ids(num_task_ids);
u16 num_returned_task_ids = 0;
for (const auto& iter : task_id_list) {
const std::string current_task_id = iter.first;
if (current_task_id.size() > TASK_ID_SIZE || num_returned_task_ids >= num_task_ids) {
LOG_WARNING(Service_BOSS, "TaskId {} too long or too many TaskIds", current_task_id);
continue;
}
std::memcpy(task_ids[num_returned_task_ids++].data(), current_task_id.data(), TASK_ID_SIZE);
}
auto* task_list_prop =
std::get_if<std::vector<u8>>(&current_props.properties[PropertyID::TaskIdList]);
if (task_list_prop && task_list_prop->size() == TASKIDLIST_SIZE) {
std::memcpy(task_list_prop->data(), task_ids.data(), TASKIDLIST_SIZE);
}
}
FileSys::Path OnlineService::GetBossDataDir() {
const u32 high = static_cast<u32>(extdata_id >> 32);
const u32 low = static_cast<u32>(extdata_id & 0xFFFFFFFF);
return FileSys::ConstructExtDataBinaryPath(1, high, low);
}
std::unique_ptr<FileSys::ArchiveBackend> OnlineService::OpenBossExtData() {
FileSys::ArchiveFactory_ExtSaveData boss_extdata_archive_factory(
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::ExtSaveDataType::Boss);
const FileSys::Path boss_path{GetBossDataDir()};
auto archive_result = boss_extdata_archive_factory.Open(boss_path, 0);
if (!archive_result.Succeeded()) {
LOG_WARNING(Service_BOSS, "Failed to open SpotPass ext data archive with ID '{:#010x}'.",
extdata_id);
return nullptr;
}
return std::move(archive_result).Unwrap();
}
std::vector<FileSys::Entry> OnlineService::GetBossExtDataFiles(
FileSys::ArchiveBackend* boss_archive) {
auto dir_result = boss_archive->OpenDirectory("/");
if (!dir_result.Succeeded()) {
LOG_WARNING(Service_BOSS,
"Failed to open root directory of SpotPass ext data with ID '{:#010x}'.",
extdata_id);
return {};
}
auto dir = std::move(dir_result).Unwrap();
// Keep reading the directory 32 files at a time until all files have been checked
std::vector<FileSys::Entry> boss_files;
constexpr u32 files_to_read = 32;
u32 entry_count = 0;
std::size_t i = 0;
do {
boss_files.resize(boss_files.size() + files_to_read);
entry_count = dir->Read(files_to_read, boss_files.data() + (i++ * files_to_read));
} while (entry_count > files_to_read);
// Resize to trim off unused entries from the final read.
boss_files.resize((i - 1) * files_to_read + entry_count);
return boss_files;
}
std::vector<NsDataEntry> OnlineService::GetNsDataEntries() {
auto boss_archive = OpenBossExtData();
if (!boss_archive) {
return {};
}
std::vector<NsDataEntry> ns_data;
const auto boss_files = GetBossExtDataFiles(boss_archive.get());
for (const auto& current_file : boss_files) {
constexpr u32 boss_header_length = 0x34;
if (current_file.is_directory || current_file.file_size < boss_header_length) {
LOG_WARNING(Service_BOSS,
"SpotPass extdata contains directory or file is too short: '{}'",
Common::UTF16ToUTF8(current_file.filename));
continue;
}
FileSys::Mode mode{};
mode.read_flag.Assign(1);
NsDataEntry entry{.filename = Common::UTF16ToUTF8(current_file.filename)};
auto file_result = boss_archive->OpenFile("/" + entry.filename, mode);
if (!file_result.Succeeded()) {
LOG_WARNING(Service_BOSS, "Opening SpotPass file failed.");
continue;
}
auto file = std::move(file_result).Unwrap();
file->Read(0, boss_header_length, reinterpret_cast<u8*>(&entry.header));
if (entry.header.header_length != BOSS_EXTDATA_HEADER_LENGTH) {
LOG_WARNING(
Service_BOSS,
"Incorrect header length or non-SpotPass file; expected {:#010x}, found {:#010x}",
BOSS_EXTDATA_HEADER_LENGTH, entry.header.header_length);
continue;
}
if (entry.header.program_id != program_id) {
LOG_WARNING(Service_BOSS,
"Mismatched program ID in SpotPass data. Was expecting "
"{:#018x}, found {:#018x}",
program_id, static_cast<u64>(entry.header.program_id));
continue;
}
// Check the payload size is correct, excluding header
if (entry.header.payload_size != (current_file.file_size - boss_header_length)) {
LOG_WARNING(Service_BOSS,
"Mismatched file size, was expecting {:#010x}, found {:#010x}",
static_cast<u32>(entry.header.payload_size),
current_file.file_size - boss_header_length);
continue;
}
ns_data.push_back(entry);
}
return ns_data;
}
u16 OnlineService::GetNsDataIdList(const u32 filter, const u32 max_entries,
Kernel::MappedBuffer& buffer) {
std::vector<NsDataEntry> ns_data = GetNsDataEntries();
std::vector<u32> output_entries;
for (const auto& current_entry : ns_data) {
const u32 datatype_raw = static_cast<u32>(current_entry.header.datatype);
const u16 datatype_high = static_cast<u16>(datatype_raw >> 16);
const u16 datatype_low = static_cast<u16>(datatype_raw & 0xFFFF);
const u16 filter_high = static_cast<u16>(filter >> 16);
const u16 filter_low = static_cast<u16>(filter & 0xFFFF);
if (filter != 0xFFFFFFFF &&
(filter_high != datatype_high || (filter_low & datatype_low) == 0)) {
continue;
}
if (output_entries.size() >= max_entries) {
LOG_WARNING(Service_BOSS, "Reached maximum number of entries");
break;
}
output_entries.push_back(current_entry.header.ns_data_id);
}
buffer.Write(output_entries.data(), 0, sizeof(u32) * output_entries.size());
return static_cast<u16>(output_entries.size());
}
std::optional<NsDataEntry> OnlineService::GetNsDataEntryFromId(const u32 ns_data_id) {
std::vector<NsDataEntry> ns_data = GetNsDataEntries();
const auto entry_iter = std::find_if(ns_data.begin(), ns_data.end(), [ns_data_id](auto entry) {
return entry.header.ns_data_id == ns_data_id;
});
if (entry_iter == ns_data.end()) {
LOG_WARNING(Service_BOSS, "Could not find NsData with ID {:#010X}", ns_data_id);
return std::nullopt;
}
return *entry_iter;
}
Result OnlineService::GetNsDataHeaderInfo(const u32 ns_data_id, const NsDataHeaderInfoType type,
const u32 size, Kernel::MappedBuffer& buffer) {
const auto entry = GetNsDataEntryFromId(ns_data_id);
if (!entry.has_value()) {
LOG_WARNING(Service_BOSS, "Failed to find NsData entry for ID {:#010X}", ns_data_id);
return {ErrCodes::NsDataNotFound, ErrorModule::BOSS, ErrorSummary::InvalidState,
ErrorLevel::Status};
}
static constexpr std::array EXPECTED_NS_DATA_HEADER_INFO_SIZES = {
sizeof(u64), // Program ID
sizeof(u32), // Unknown
sizeof(u32), // Data Type
sizeof(u32), // Payload Size
sizeof(u32), // NsData ID
sizeof(u32), // Version
sizeof(NsDataHeaderInfo), // Everything
};
if (size != EXPECTED_NS_DATA_HEADER_INFO_SIZES[static_cast<u8>(type)]) {
LOG_WARNING(Service_BOSS, "Invalid size {} for type {}", size, type);
// TODO: Proper error code.
return ResultUnknown;
}
switch (type) {
case NsDataHeaderInfoType::ProgramId:
buffer.Write(&entry->header.program_id, 0, size);
return ResultSuccess;
case NsDataHeaderInfoType::Unknown: {
// TODO: Figure out what this is. Stubbed to zero for now.
const u32 zero = 0;
buffer.Write(&zero, 0, size);
return ResultSuccess;
}
case NsDataHeaderInfoType::Datatype:
buffer.Write(&entry->header.datatype, 0, size);
return ResultSuccess;
case NsDataHeaderInfoType::PayloadSize:
buffer.Write(&entry->header.payload_size, 0, size);
return ResultSuccess;
case NsDataHeaderInfoType::NsDataId:
buffer.Write(&entry->header.ns_data_id, 0, size);
return ResultSuccess;
case NsDataHeaderInfoType::Version:
buffer.Write(&entry->header.version, 0, size);
return ResultSuccess;
case NsDataHeaderInfoType::Everything: {
const NsDataHeaderInfo info = {
.program_id = entry->header.program_id,
.datatype = entry->header.datatype,
.payload_size = entry->header.payload_size,
.ns_data_id = entry->header.ns_data_id,
.version = entry->header.version,
};
buffer.Write(&info, 0, size);
return ResultSuccess;
}
default:
LOG_WARNING(Service_BOSS, "Unknown header info type {}", type);
// TODO: Proper error code.
return ResultUnknown;
}
}
ResultVal<std::size_t> OnlineService::ReadNsData(const u32 ns_data_id, const u64 offset,
const u32 size, Kernel::MappedBuffer& buffer) {
std::optional<NsDataEntry> entry = GetNsDataEntryFromId(ns_data_id);
if (!entry.has_value()) {
LOG_WARNING(Service_BOSS, "Failed to find NsData entry for ID {:#010X}", ns_data_id);
return Result(ErrCodes::NsDataNotFound, ErrorModule::BOSS, ErrorSummary::InvalidState,
ErrorLevel::Status);
}
if (entry->header.payload_size < size + offset) {
LOG_WARNING(Service_BOSS,
"Invalid request to read {:#010X} bytes at offset {:#010X}, payload "
"length is {:#010X}",
size, offset, static_cast<u32>(entry->header.payload_size));
// TODO: Proper error code.
return ResultUnknown;
}
auto boss_archive = OpenBossExtData();
if (!boss_archive) {
// TODO: Proper error code.
return ResultUnknown;
}
FileSys::Path file_path = fmt::format("/{}", entry->filename);
FileSys::Mode mode{};
mode.read_flag.Assign(1);
auto file_result = boss_archive->OpenFile(file_path, mode);
if (!file_result.Succeeded()) {
LOG_WARNING(Service_BOSS, "Failed to open SpotPass extdata file '{}'.", entry->filename);
// TODO: Proper error code.
return ResultUnknown;
}
auto file = std::move(file_result).Unwrap();
std::vector<u8> ns_data_array(size);
auto read_result = file->Read(sizeof(BossHeader) + offset, size, ns_data_array.data());
if (!read_result.Succeeded()) {
LOG_WARNING(Service_BOSS, "Failed to read SpotPass extdata file '{}'.", entry->filename);
// TODO: Proper error code.
return ResultUnknown;
}
buffer.Write(ns_data_array.data(), 0, size);
return read_result;
}
template <class... Ts>
struct overload : Ts... {
using Ts::operator()...;
};
template <class... Ts>
overload(Ts...) -> overload<Ts...>;
Result OnlineService::SendProperty(const u16 id, const u32 size, Kernel::MappedBuffer& buffer) {
const auto property_id = static_cast<PropertyID>(id);
if (!current_props.properties.contains(property_id)) {
LOG_ERROR(Service_BOSS, "Unknown property with ID {:#06x} and size {}", property_id, size);
return Result(ErrCodes::UnknownPropertyID, ErrorModule::BOSS, ErrorSummary::Internal,
ErrorLevel::Status);
}
auto& prop = current_props.properties[property_id];
auto read_pod = [&]<typename T>(T& old_prop) {
static_assert(std::is_trivial<T>::value, "Only trivial types are allowed for read_pod");
if (size != sizeof(old_prop)) {
LOG_ERROR(Service_BOSS, "Unexpected size of property {:#06x}, was expecting {}, got {}",
property_id, sizeof(old_prop), size);
}
T cur_prop = 0;
buffer.Read(&cur_prop, 0, size);
prop = cur_prop;
};
auto read_vector = [&]<typename T>(std::vector<T>& old_prop) {
if (size != old_prop.size()) {
LOG_ERROR(Service_BOSS, "Unexpected size of property {:#06x}, was expecting {}, got {}",
property_id, old_prop.size(), size);
}
std::vector<T> cur_prop(size);
buffer.Read(cur_prop.data(), 0, size);
prop = cur_prop;
};
std::visit(overload{[&](u8& cur_prop) { read_pod(cur_prop); },
[&](u16& cur_prop) { read_pod(cur_prop); },
[&](u32& cur_prop) { read_pod(cur_prop); },
[&](u64& cur_prop) { read_pod(cur_prop); },
[&](std::vector<u8>& cur_prop) { read_vector(cur_prop); },
[&](std::vector<u32>& cur_prop) { read_vector(cur_prop); }},
prop);
return ResultSuccess;
}
Result OnlineService::ReceiveProperty(const u16 id, const u32 size, Kernel::MappedBuffer& buffer) {
const auto property_id = static_cast<PropertyID>(id);
if (!current_props.properties.contains(property_id)) {
LOG_ERROR(Service_BOSS, "Unknown property with ID {:#06x} and size {}", property_id, size);
return {ErrCodes::UnknownPropertyID, ErrorModule::BOSS, ErrorSummary::Internal,
ErrorLevel::Status};
}
auto write_pod = [&]<typename T>(T& cur_prop) {
static_assert(std::is_trivial<T>::value, "Only trivial types are allowed for write_pod");
if (size != sizeof(cur_prop)) {
LOG_ERROR(Service_BOSS, "Unexpected size of property {:#06x}, was expecting {}, got {}",
property_id, sizeof(cur_prop), size);
}
buffer.Write(&cur_prop, 0, size);
};
auto write_vector = [&]<typename T>(std::vector<T>& cur_prop) {
if (size != cur_prop.size()) {
LOG_ERROR(Service_BOSS, "Unexpected size of property {:#06x}, was expecting {}, got {}",
property_id, cur_prop.size(), size);
}
buffer.Write(cur_prop.data(), 0, size);
};
auto& prop = current_props.properties[property_id];
std::visit(overload{[&](u8& cur_prop) { write_pod(cur_prop); },
[&](u16& cur_prop) { write_pod(cur_prop); },
[&](u32& cur_prop) { write_pod(cur_prop); },
[&](u64& cur_prop) { write_pod(cur_prop); },
[&](std::vector<u8>& cur_prop) { write_vector(cur_prop); },
[&](std::vector<u32>& cur_prop) { write_vector(cur_prop); }},
prop);
return ResultSuccess;
}
} // namespace Service::BOSS

View File

@@ -0,0 +1,216 @@
// Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include <variant>
#include <vector>
#include "common/common_types.h"
#include "core/hle/result.h"
#include "core/loader/loader.h"
namespace Kernel {
class MappedBuffer;
}
namespace FileSys {
class ArchiveBackend;
struct Entry;
class Path;
} // namespace FileSys
namespace Service::BOSS {
constexpr u32 BOSS_PAYLOAD_HEADER_LENGTH = 0x28;
constexpr u32 BOSS_MAGIC = Loader::MakeMagic('b', 'o', 's', 's');
constexpr u32 BOSS_PAYLOAD_MAGIC = 0x10001;
constexpr u64 NEWS_PROG_ID = 0x0004013000003502;
constexpr u32 BOSS_CONTENT_HEADER_LENGTH = 0x132;
constexpr u32 BOSS_HEADER_WITH_HASH_LENGTH = 0x13C;
constexpr u32 BOSS_ENTIRE_HEADER_LENGTH = BOSS_CONTENT_HEADER_LENGTH + BOSS_HEADER_WITH_HASH_LENGTH;
constexpr u32 BOSS_EXTDATA_HEADER_LENGTH = 0x18;
constexpr u32 BOSS_S_ENTRY_SIZE = 0xC00;
constexpr u32 BOSS_SAVE_HEADER_SIZE = 4;
constexpr std::size_t TASK_ID_SIZE = 8;
constexpr std::size_t URL_SIZE = 0x200;
constexpr std::size_t HEADERS_SIZE = 0x360;
constexpr std::size_t CERTIDLIST_SIZE = 3;
constexpr std::size_t TASKIDLIST_SIZE = 0x400;
constexpr std::array<u8, 8> boss_system_savedata_id{
0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x01, 0x00,
};
#pragma pack(push, 4)
struct BossHeader {
u8 header_length;
std::array<u8, 11> zero1;
u32_be unknown;
u32_be download_date;
std::array<u8, 4> zero2;
u64_be program_id;
std::array<u8, 4> zero3;
u32_be datatype;
u32_be payload_size;
u32_be ns_data_id;
u32_be version;
};
#pragma pack(pop)
static_assert(sizeof(BossHeader) == 0x34, "BossHeader has incorrect size");
struct NsDataEntry {
std::string filename;
BossHeader header;
};
enum class NsDataHeaderInfoType : u8 {
ProgramId = 0,
Unknown = 1,
Datatype = 2,
PayloadSize = 3,
NsDataId = 4,
Version = 5,
Everything = 6,
};
struct NsDataHeaderInfo {
u64 program_id;
INSERT_PADDING_BYTES(4);
u32 datatype;
u32 payload_size;
u32 ns_data_id;
u32 version;
INSERT_PADDING_BYTES(4);
};
static_assert(sizeof(NsDataHeaderInfo) == 0x20, "NsDataHeaderInfo has incorrect size");
enum class PropertyID : u16 {
Interval = 0x03,
Duration = 0x04,
Url = 0x07,
Headers = 0x0D,
CertId = 0x0E,
CertIdList = 0x0F,
LoadCert = 0x10,
LoadRootCert = 0x11,
TotalTasks = 0x35,
TaskIdList = 0x36,
};
struct BossSVData {
INSERT_PADDING_BYTES(0x10);
u64 program_id;
std::array<char, TASK_ID_SIZE> task_id;
};
struct BossSSData {
INSERT_PADDING_BYTES(0x21C);
std::array<u8, URL_SIZE> url;
};
using BossTaskProperty = std::variant<u8, u16, u32, u64, std::vector<u8>, std::vector<u32>>;
struct BossTaskProperties {
bool task_result;
std::map<PropertyID, BossTaskProperty> properties{
{static_cast<PropertyID>(0x00), u8()},
{static_cast<PropertyID>(0x01), u8()},
{static_cast<PropertyID>(0x02), u32()},
{PropertyID::Interval, u32()},
{PropertyID::Duration, u32()},
{static_cast<PropertyID>(0x05), u8()},
{static_cast<PropertyID>(0x06), u8()},
{PropertyID::Url, std::vector<u8>(URL_SIZE)},
{static_cast<PropertyID>(0x08), u32()},
{static_cast<PropertyID>(0x09), u8()},
{static_cast<PropertyID>(0x0A), std::vector<u8>(0x100)},
{static_cast<PropertyID>(0x0B), std::vector<u8>(0x200)},
{static_cast<PropertyID>(0x0C), u32()},
{PropertyID::Headers, std::vector<u8>(HEADERS_SIZE)},
{PropertyID::CertId, u32()},
{PropertyID::CertIdList, std::vector<u32>(CERTIDLIST_SIZE)},
{PropertyID::LoadCert, u8()},
{PropertyID::LoadRootCert, u8()},
{static_cast<PropertyID>(0x12), u8()},
{static_cast<PropertyID>(0x13), u32()},
{static_cast<PropertyID>(0x14), u32()},
{static_cast<PropertyID>(0x15), std::vector<u8>(0x40)},
{static_cast<PropertyID>(0x16), u32()},
{static_cast<PropertyID>(0x18), u8()},
{static_cast<PropertyID>(0x19), u8()},
{static_cast<PropertyID>(0x1A), u8()},
{static_cast<PropertyID>(0x1B), u32()},
{static_cast<PropertyID>(0x1C), u32()},
{static_cast<PropertyID>(0x1D), u8()},
{static_cast<PropertyID>(0x1E), u8()},
{static_cast<PropertyID>(0x1F), u8()},
{static_cast<PropertyID>(0x20), u8()},
{static_cast<PropertyID>(0x21), u8()},
{static_cast<PropertyID>(0x22), u8()},
{static_cast<PropertyID>(0x23), u32()},
{static_cast<PropertyID>(0x24), u8()},
{static_cast<PropertyID>(0x25), u32()},
{static_cast<PropertyID>(0x26), u32()},
{static_cast<PropertyID>(0x27), u32()},
{static_cast<PropertyID>(0x28), u64()},
{static_cast<PropertyID>(0x29), u64()},
{static_cast<PropertyID>(0x2A), u32()},
{static_cast<PropertyID>(0x2B), u32()},
{static_cast<PropertyID>(0x2C), u8()},
{static_cast<PropertyID>(0x2D), u16()},
{static_cast<PropertyID>(0x2E), u16()},
{static_cast<PropertyID>(0x2F), std::vector<u8>(0x40)},
{PropertyID::TotalTasks, u16()},
{PropertyID::TaskIdList, std::vector<u8>(TASKIDLIST_SIZE)},
{static_cast<PropertyID>(0x3B), u32()},
{static_cast<PropertyID>(0x3E), std::vector<u8>(0x200)},
{static_cast<PropertyID>(0x3F), u8()},
};
template <class Archive>
void serialize(Archive& ar, const unsigned int);
friend class boost::serialization::access;
};
class OnlineService final {
public:
explicit OnlineService(u64 program_id_, u64 extdata_id_);
Result InitializeSession(u64 init_program_id);
void RegisterTask(const u32 size, Kernel::MappedBuffer& buffer);
Result UnregisterTask(const u32 size, Kernel::MappedBuffer& buffer);
void GetTaskIdList();
u16 GetNsDataIdList(const u32 filter, const u32 max_entries, Kernel::MappedBuffer& buffer);
std::optional<NsDataEntry> GetNsDataEntryFromId(const u32 ns_data_id);
Result GetNsDataHeaderInfo(const u32 ns_data_id, const NsDataHeaderInfoType type,
const u32 size, Kernel::MappedBuffer& buffer);
ResultVal<std::size_t> ReadNsData(const u32 ns_data_id, const u64 offset, const u32 size,
Kernel::MappedBuffer& buffer);
Result SendProperty(const u16 id, const u32 size, Kernel::MappedBuffer& buffer);
Result ReceiveProperty(const u16 id, const u32 size, Kernel::MappedBuffer& buffer);
private:
std::unique_ptr<FileSys::ArchiveBackend> OpenBossExtData();
std::vector<FileSys::Entry> GetBossExtDataFiles(FileSys::ArchiveBackend* boss_archive);
FileSys::Path GetBossDataDir();
std::vector<NsDataEntry> GetNsDataEntries();
BossTaskProperties current_props;
std::map<std::string, BossTaskProperties> task_id_list;
u64 program_id;
u64 extdata_id;
// For serialization
explicit OnlineService() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int);
friend class boost::serialization::access;
};
} // namespace Service::BOSS

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,775 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <deque>
#include <future>
#include <memory>
#include <vector>
#include <boost/serialization/array.hpp>
#include <boost/serialization/deque.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/version.hpp>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/global.h"
#include "core/hle/result.h"
#include "core/hle/service/cam/cam_params.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Camera {
class CameraInterface;
}
namespace Core {
struct TimingEventType;
}
namespace Kernel {
class Process;
}
namespace Service::CAM {
/// Stereo camera calibration data.
struct StereoCameraCalibrationData {
u8 isValidRotationXY; ///< Bool indicating whether the X and Y rotation data is valid.
INSERT_PADDING_BYTES(3);
float_le scale; ///< Scale to match the left camera image with the right.
float_le rotationZ; ///< Z axis rotation to match the left camera image with the right.
float_le translationX; ///< X axis translation to match the left camera image with the right.
float_le translationY; ///< Y axis translation to match the left camera image with the right.
float_le rotationX; ///< X axis rotation to match the left camera image with the right.
float_le rotationY; ///< Y axis rotation to match the left camera image with the right.
float_le angleOfViewRight; ///< Right camera angle of view.
float_le angleOfViewLeft; ///< Left camera angle of view.
float_le distanceToChart; ///< Distance between cameras and measurement chart.
float_le distanceCameras; ///< Distance between left and right cameras.
s16_le imageWidth; ///< Image width.
s16_le imageHeight; ///< Image height.
INSERT_PADDING_BYTES(16);
};
static_assert(sizeof(StereoCameraCalibrationData) == 64,
"StereoCameraCalibrationData structure size is wrong");
/**
* Resolution parameters for the camera.
* The native resolution of 3DS camera is 640 * 480. The captured image will be cropped in the
* region [crop_x0, crop_x1] * [crop_y0, crop_y1], and then scaled to size width * height as the
* output image. Note that all cropping coordinates are inclusive.
*/
struct Resolution {
u16 width;
u16 height;
u16 crop_x0;
u16 crop_y0;
u16 crop_x1;
u16 crop_y1;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& width;
ar& height;
ar& crop_x0;
ar& crop_y0;
ar& crop_x1;
ar& crop_y1;
}
friend class boost::serialization::access;
};
struct PackageParameterWithoutContext {
u8 camera_select;
s8 exposure;
WhiteBalance white_balance;
s8 sharpness;
bool auto_exposure;
bool auto_white_balance;
FrameRate frame_rate;
PhotoMode photo_mode;
Contrast contrast;
LensCorrection lens_correction;
bool noise_filter;
u8 padding;
s16 auto_exposure_window_x;
s16 auto_exposure_window_y;
s16 auto_exposure_window_width;
s16 auto_exposure_window_height;
s16 auto_white_balance_window_x;
s16 auto_white_balance_window_y;
s16 auto_white_balance_window_width;
s16 auto_white_balance_window_height;
INSERT_PADDING_WORDS(4);
};
static_assert(sizeof(PackageParameterWithoutContext) == 44,
"PackageParameterCameraWithoutContext structure size is wrong");
struct PackageParameterWithContext {
u8 camera_select;
u8 context_select;
Flip flip;
Effect effect;
Size size;
INSERT_PADDING_BYTES(3);
INSERT_PADDING_WORDS(3);
Resolution GetResolution() const;
};
static_assert(sizeof(PackageParameterWithContext) == 20,
"PackageParameterWithContext structure size is wrong");
struct PackageParameterWithContextDetail {
u8 camera_select;
u8 context_select;
Flip flip;
Effect effect;
Resolution resolution;
INSERT_PADDING_WORDS(3);
Resolution GetResolution() const {
return resolution;
}
};
static_assert(sizeof(PackageParameterWithContextDetail) == 28,
"PackageParameterWithContextDetail structure size is wrong");
class Module final {
public:
explicit Module(Core::System& system);
~Module();
void ReloadCameraDevices();
class Interface : public ServiceFramework<Interface> {
public:
Interface(std::shared_ptr<Module> cam, const char* name, u32 max_session);
~Interface();
std::shared_ptr<Module> GetModule() const;
protected:
/**
* Starts capturing at the selected port.
* Inputs:
* 0: 0x00010040
* 1: u8 selected port
* Outputs:
* 0: 0x00010040
* 1: Result
*/
void StartCapture(Kernel::HLERequestContext& ctx);
/**
* Stops capturing from the selected port.
* Inputs:
* 0: 0x00020040
* 1: u8 selected port
* Outputs:
* 0: 0x00020040
* 1: Result
*/
void StopCapture(Kernel::HLERequestContext& ctx);
/**
* Gets whether the selected port is currently capturing.
* Inputs:
* 0: 0x00030040
* 1: u8 selected port
* Outputs:
* 0: 0x00030080
* 1: Result
* 2: 0 if not capturing, 1 if capturing
*/
void IsBusy(Kernel::HLERequestContext& ctx);
/**
* Clears the buffer of selected ports.
* Inputs:
* 0: 0x00040040
* 1: u8 selected port
* Outputs:
* 0: 0x00040040
* 2: Result
*/
void ClearBuffer(Kernel::HLERequestContext& ctx);
/**
* Unknown
* Inputs:
* 0: 0x00050040
* 1: u8 selected port
* Outputs:
* 0: 0x00050042
* 1: Result
* 2: Descriptor: Handle
* 3: Event handle
*/
void GetVsyncInterruptEvent(Kernel::HLERequestContext& ctx);
/**
* Unknown
* Inputs:
* 0: 0x00060040
* 1: u8 selected port
* Outputs:
* 0: 0x00060042
* 1: Result
* 2: Descriptor: Handle
* 3: Event handle
*/
void GetBufferErrorInterruptEvent(Kernel::HLERequestContext& ctx);
/**
* Sets the target buffer to receive a frame of image data and starts the transfer. Each
* camera port has its own event to signal the end of the transfer.
*
* Inputs:
* 0: 0x00070102
* 1: Destination address in calling process
* 2: u8 selected port
* 3: Image size (in bytes)
* 4: u16 Transfer unit size (in bytes)
* 5: Descriptor: Handle
* 6: Handle to destination process
* Outputs:
* 0: 0x00070042
* 1: Result
* 2: Descriptor: Handle
* 3: Handle to event signalled when transfer finishes
*/
void SetReceiving(Kernel::HLERequestContext& ctx);
/**
* Gets whether the selected port finished receiving a frame.
* Inputs:
* 0: 0x00080040
* 1: u8 selected port
* Outputs:
* 0: 0x00080080
* 1: Result
* 2: 0 if not finished, 1 if finished
*/
void IsFinishedReceiving(Kernel::HLERequestContext& ctx);
/**
* Sets the number of lines the buffer contains.
* Inputs:
* 0: 0x00090100
* 1: u8 selected port
* 2: u16 Number of lines to transfer
* 3: u16 Width
* 4: u16 Height
* Outputs:
* 0: 0x00090040
* 1: Result
* @todo figure out how the "buffer" actually works.
*/
void SetTransferLines(Kernel::HLERequestContext& ctx);
/**
* Gets the maximum number of lines that fit in the buffer
* Inputs:
* 0: 0x000A0080
* 1: u16 Width
* 2: u16 Height
* Outputs:
* 0: 0x000A0080
* 1: Result
* 2: Maximum number of lines that fit in the buffer
* @todo figure out how the "buffer" actually works.
*/
void GetMaxLines(Kernel::HLERequestContext& ctx);
/**
* Sets the number of bytes the buffer contains.
* Inputs:
* 0: 0x000B0100
* 1: u8 selected port
* 2: u16 Number of bytes to transfer
* 3: u16 Width
* 4: u16 Height
* Outputs:
* 0: 0x000B0040
* 1: Result
* @todo figure out how the "buffer" actually works.
*/
void SetTransferBytes(Kernel::HLERequestContext& ctx);
/**
* Gets the number of bytes to the buffer contains.
* Inputs:
* 0: 0x000C0040
* 1: u8 selected port
* Outputs:
* 0: 0x000C0080
* 1: Result
* 2: The number of bytes the buffer contains
* @todo figure out how the "buffer" actually works.
*/
void GetTransferBytes(Kernel::HLERequestContext& ctx);
/**
* Gets the maximum number of bytes that fit in the buffer.
* Inputs:
* 0: 0x000D0080
* 1: u16 Width
* 2: u16 Height
* Outputs:
* 0: 0x000D0080
* 1: Result
* 2: Maximum number of bytes that fit in the buffer
* @todo figure out how the "buffer" actually works.
*/
void GetMaxBytes(Kernel::HLERequestContext& ctx);
/**
* Enables or disables trimming.
* Inputs:
* 0: 0x000E0080
* 1: u8 selected port
* 2: u8 bool Enable trimming if true
* Outputs:
* 0: 0x000E0040
* 1: Result
*/
void SetTrimming(Kernel::HLERequestContext& ctx);
/**
* Gets whether trimming is enabled.
* Inputs:
* 0: 0x000F0040
* 1: u8 selected port
* Outputs:
* 0: 0x000F0080
* 1: Result
* 2: u8 bool Enable trimming if true
*/
void IsTrimming(Kernel::HLERequestContext& ctx);
/**
* Sets the position to trim.
* Inputs:
* 0: 0x00100140
* 1: u8 selected port
* 2: x start
* 3: y start
* 4: x end (exclusive)
* 5: y end (exclusive)
* Outputs:
* 0: 0x00100040
* 1: Result
*/
void SetTrimmingParams(Kernel::HLERequestContext& ctx);
/**
* Gets the position to trim.
* Inputs:
* 0: 0x00110040
* 1: u8 selected port
*
* Outputs:
* 0: 0x00110140
* 1: Result
* 2: x start
* 3: y start
* 4: x end (exclusive)
* 5: y end (exclusive)
*/
void GetTrimmingParams(Kernel::HLERequestContext& ctx);
/**
* Sets the position to trim by giving the width and height. The trimming window is always
* at the center.
* Inputs:
* 0: 0x00120140
* 1: u8 selected port
* 2: s16 Trim width
* 3: s16 Trim height
* 4: s16 Camera width
* 5: s16 Camera height
* Outputs:
* 0: 0x00120040
* 1: Result
*/
void SetTrimmingParamsCenter(Kernel::HLERequestContext& ctx);
/**
* Selects up to two physical cameras to enable.
* Inputs:
* 0: 0x00130040
* 1: u8 selected camera
* Outputs:
* 0: 0x00130040
* 1: Result
*/
void Activate(Kernel::HLERequestContext& ctx);
/**
* Switches the context of camera settings.
* Inputs:
* 0: 0x00140080
* 1: u8 selected camera
* 2: u8 selected context
* Outputs:
* 0: 0x00140040
* 1: Result
*/
void SwitchContext(Kernel::HLERequestContext& ctx);
/**
* Sets flipping of images
* Inputs:
* 0: 0x001D00C0
* 1: u8 selected camera
* 2: u8 Type of flipping to perform (`Flip` enum)
* 3: u8 selected context
* Outputs:
* 0: 0x001D0040
* 1: Result
*/
void FlipImage(Kernel::HLERequestContext& ctx);
/**
* Sets camera resolution from custom parameters. For more details see the Resolution
* struct.
* Inputs:
* 0: 0x001E0200
* 1: u8 selected camera
* 2: width
* 3: height
* 4: crop x0
* 5: crop y0
* 6: crop x1
* 7: crop y1
* 8: u8 selected context
* Outputs:
* 0: 0x001E0040
* 1: Result
*/
void SetDetailSize(Kernel::HLERequestContext& ctx);
/**
* Sets camera resolution from preset resolution parameters.
* Inputs:
* 0: 0x001F00C0
* 1: u8 selected camera
* 2: u8 Camera frame resolution (`Size` enum)
* 3: u8 selected context
* Outputs:
* 0: 0x001F0040
* 1: Result
*/
void SetSize(Kernel::HLERequestContext& ctx);
/**
* Sets camera framerate.
* Inputs:
* 0: 0x00200080
* 1: u8 selected camera
* 2: u8 Camera framerate (`FrameRate` enum)
* Outputs:
* 0: 0x00200040
* 1: Result
*/
void SetFrameRate(Kernel::HLERequestContext& ctx);
/**
* Sets effect on the output image
* Inputs:
* 0: 0x002200C0
* 1: u8 selected camera
* 2: u8 image effect (`Effect` enum)
* 3: u8 selected context
* Outputs:
* 0: 0x00220040
* 1: Result
*/
void SetEffect(Kernel::HLERequestContext& ctx);
/**
* Sets format of the output image
* Inputs:
* 0: 0x002500C0
* 1: u8 selected camera
* 2: u8 image format (`OutputFormat` enum)
* 3: u8 selected context
* Outputs:
* 0: 0x00250040
* 1: Result
*/
void SetOutputFormat(Kernel::HLERequestContext& ctx);
/**
* Synchronizes the V-Sync timing of two cameras.
* Inputs:
* 0: 0x00290080
* 1: u8 selected camera 1
* 2: u8 selected camera 2
* Outputs:
* 0: 0x00280040
* 1: Result
*/
void SynchronizeVsyncTiming(Kernel::HLERequestContext& ctx);
/**
* Gets the vsync timing record of the specified camera for the specified number of signals.
* Inputs:
* 0: 0x002A0080
* 1: Port
* 2: Number of timings to get
* 64: ((PastTimings * 8) << 14) | 2
* 65: s64* TimingsOutput
* Outputs:
* 0: 0x002A0042
* 1: Result
* 2-3: Output static buffer
*/
void GetLatestVsyncTiming(Kernel::HLERequestContext& ctx);
/**
* Returns calibration data relating the outside cameras to each other, for use in AR
* applications.
*
* Inputs:
* 0: 0x002B0000
* Outputs:
* 0: 0x002B0440
* 1: Result
* 2-17: `StereoCameraCalibrationData` structure with calibration values
*/
void GetStereoCameraCalibrationData(Kernel::HLERequestContext& ctx);
/**
* Batch-configures context-free settings.
*
* Inputs:
* 0: 0x003302C0
* 1-7: struct PachageParameterWithoutContext
* 8-11: unused
* Outputs:
* 0: 0x00330040
* 1: Result
*/
void SetPackageParameterWithoutContext(Kernel::HLERequestContext& ctx);
/**
* Batch-configures context-related settings with preset resolution parameters.
*
* Inputs:
* 0: 0x00340140
* 1-2: struct PackageParameterWithContext
* 3-5: unused
* Outputs:
* 0: 0x00340040
* 1: Result
*/
void SetPackageParameterWithContext(Kernel::HLERequestContext& ctx);
/**
* Batch-configures context-related settings with custom resolution parameters
*
* Inputs:
* 0: 0x003501C0
* 1-4: struct PackageParameterWithContextDetail
* 5-7: unused
* Outputs:
* 0: 0x00350040
* 1: Result
*/
void SetPackageParameterWithContextDetail(Kernel::HLERequestContext& ctx);
/**
* Unknown
* Inputs:
* 0: 0x00360000
* Outputs:
* 0: 0x00360080
* 1: Result
* 2: ?
*/
void GetSuitableY2rStandardCoefficient(Kernel::HLERequestContext& ctx);
/**
* Unknown
* Inputs:
* 0: 0x00380040
* 1: u8 Sound ID
* Outputs:
* 0: 0x00380040
* 1: Result
*/
void PlayShutterSound(Kernel::HLERequestContext& ctx);
/**
* Initializes the camera driver. Must be called before using other functions.
* Inputs:
* 0: 0x00390000
* Outputs:
* 0: 0x00390040
* 1: Result
*/
void DriverInitialize(Kernel::HLERequestContext& ctx);
/**
* Shuts down the camera driver.
* Inputs:
* 0: 0x003A0000
* Outputs:
* 0: 0x003A0040
* 1: Result
*/
void DriverFinalize(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> cam;
};
private:
void CompletionEventCallBack(u64 port_id, s64);
void VsyncInterruptEventCallBack(u64 port_id, s64 cycles_late);
// Starts a receiving process on the specified port. This can only be called when is_busy = true
// and is_receiving = false.
void StartReceiving(int port_id);
// Cancels any ongoing receiving processes at the specified port. This is used by functions that
// stop capturing.
// TODO: what is the exact behaviour on real 3DS when stopping capture during an ongoing
// process? Will the completion event still be signaled?
void CancelReceiving(int port_id);
// Activates the specified port with the specfied camera.
void ActivatePort(int port_id, int camera_id);
template <typename PackageParameterType>
Result SetPackageParameter(const PackageParameterType& package);
struct ContextConfig {
Flip flip{Flip::None};
Effect effect{Effect::None};
OutputFormat format{OutputFormat::YUV422};
Resolution resolution = {0, 0, 0, 0, 0, 0};
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& flip;
ar& effect;
ar& format;
ar& resolution;
}
friend class boost::serialization::access;
};
struct CameraConfig {
std::unique_ptr<Camera::CameraInterface> impl;
std::array<ContextConfig, 2> contexts;
int current_context{0};
FrameRate frame_rate{FrameRate::Rate_15};
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& contexts;
ar& current_context;
ar& frame_rate;
}
friend class boost::serialization::access;
};
struct PortConfig {
int camera_id{0};
bool is_active{false}; // set when the port is activated by an Activate call.
// set if SetReceiving is called when is_busy = false. When StartCapture is called then,
// this will trigger a receiving process and reset itself.
bool is_pending_receiving{false};
// set when StartCapture is called and reset when StopCapture is called.
bool is_busy{false};
bool is_receiving{false}; // set when there is an ongoing receiving process.
bool is_trimming{false};
u16 x0{0}; // x-coordinate of starting position for trimming
u16 y0{0}; // y-coordinate of starting position for trimming
u16 x1{0}; // x-coordinate of ending position for trimming
u16 y1{0}; // y-coordinate of ending position for trimming
u16 transfer_bytes{256};
std::shared_ptr<Kernel::Event> completion_event;
std::shared_ptr<Kernel::Event> buffer_error_interrupt_event;
std::shared_ptr<Kernel::Event> vsync_interrupt_event;
std::deque<s64> vsync_timings;
std::future<std::vector<u16>> capture_result; // will hold the received frame.
Kernel::Process* dest_process{nullptr};
VAddr dest{0}; // the destination address of the receiving process
u32 dest_size{0}; // the destination size of the receiving process
void Clear();
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& camera_id;
ar& is_active;
ar& is_pending_receiving;
ar& is_busy;
ar& is_receiving;
ar& is_trimming;
ar& x0;
ar& y0;
ar& x1;
ar& y1;
ar& transfer_bytes;
ar& completion_event;
ar& buffer_error_interrupt_event;
ar& vsync_interrupt_event;
ar& vsync_timings;
// Ignore capture_result. In-progress captures might be affected but this is OK.
ar& dest_process;
ar& dest;
ar& dest_size;
}
friend class boost::serialization::access;
};
void LoadCameraImplementation(CameraConfig& camera, int camera_id);
Core::System& system;
bool initialized{};
std::array<CameraConfig, NumCameras> cameras;
std::array<PortConfig, 2> ports;
Core::TimingEventType* completion_event_callback;
Core::TimingEventType* vsync_interrupt_event_callback;
std::atomic<bool> is_camera_reload_pending{false};
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version);
friend class boost::serialization::access;
};
std::shared_ptr<Module> GetModule(Core::System& system);
void InstallInterfaces(Core::System& system);
} // namespace Service::CAM
SERVICE_CONSTRUCT(Service::CAM::Module)
BOOST_CLASS_VERSION(Service::CAM::Module, 1)
BOOST_CLASS_VERSION(Service::CAM::Module::CameraConfig, 1)

View File

@@ -0,0 +1,83 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/cam/cam.h"
#include "core/hle/service/cam/cam_c.h"
namespace Service::CAM {
CAM_C::CAM_C(std::shared_ptr<Module> cam) : Module::Interface(std::move(cam), "cam:c", 1) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, &CAM_C::StartCapture, "StartCapture"},
{0x0002, &CAM_C::StopCapture, "StopCapture"},
{0x0003, &CAM_C::IsBusy, "IsBusy"},
{0x0004, &CAM_C::ClearBuffer, "ClearBuffer"},
{0x0005, &CAM_C::GetVsyncInterruptEvent, "GetVsyncInterruptEvent"},
{0x0006, &CAM_C::GetBufferErrorInterruptEvent, "GetBufferErrorInterruptEvent"},
{0x0007, &CAM_C::SetReceiving, "SetReceiving"},
{0x0008, &CAM_C::IsFinishedReceiving, "IsFinishedReceiving"},
{0x0009, &CAM_C::SetTransferLines, "SetTransferLines"},
{0x000A, &CAM_C::GetMaxLines, "GetMaxLines"},
{0x000B, &CAM_C::SetTransferBytes, "SetTransferBytes"},
{0x000C, &CAM_C::GetTransferBytes, "GetTransferBytes"},
{0x000D, &CAM_C::GetMaxBytes, "GetMaxBytes"},
{0x000E, &CAM_C::SetTrimming, "SetTrimming"},
{0x000F, &CAM_C::IsTrimming, "IsTrimming"},
{0x0010, &CAM_C::SetTrimmingParams, "SetTrimmingParams"},
{0x0011, &CAM_C::GetTrimmingParams, "GetTrimmingParams"},
{0x0012, &CAM_C::SetTrimmingParamsCenter, "SetTrimmingParamsCenter"},
{0x0013, &CAM_C::Activate, "Activate"},
{0x0014, &CAM_C::SwitchContext, "SwitchContext"},
{0x0015, nullptr, "SetExposure"},
{0x0016, nullptr, "SetWhiteBalance"},
{0x0017, nullptr, "SetWhiteBalanceWithoutBaseUp"},
{0x0018, nullptr, "SetSharpness"},
{0x0019, nullptr, "SetAutoExposure"},
{0x001A, nullptr, "IsAutoExposure"},
{0x001B, nullptr, "SetAutoWhiteBalance"},
{0x001C, nullptr, "IsAutoWhiteBalance"},
{0x001D, &CAM_C::FlipImage, "FlipImage"},
{0x001E, &CAM_C::SetDetailSize, "SetDetailSize"},
{0x001F, &CAM_C::SetSize, "SetSize"},
{0x0020, &CAM_C::SetFrameRate, "SetFrameRate"},
{0x0021, nullptr, "SetPhotoMode"},
{0x0022, &CAM_C::SetEffect, "SetEffect"},
{0x0023, nullptr, "SetContrast"},
{0x0024, nullptr, "SetLensCorrection"},
{0x0025, &CAM_C::SetOutputFormat, "SetOutputFormat"},
{0x0026, nullptr, "SetAutoExposureWindow"},
{0x0027, nullptr, "SetAutoWhiteBalanceWindow"},
{0x0028, nullptr, "SetNoiseFilter"},
{0x0029, &CAM_C::SynchronizeVsyncTiming, "SynchronizeVsyncTiming"},
{0x002A, &CAM_C::GetLatestVsyncTiming, "GetLatestVsyncTiming"},
{0x002B, &CAM_C::GetStereoCameraCalibrationData, "GetStereoCameraCalibrationData"},
{0x002C, nullptr, "SetStereoCameraCalibrationData"},
{0x002D, nullptr, "WriteRegisterI2c"},
{0x002E, nullptr, "WriteMcuVariableI2c"},
{0x002F, nullptr, "ReadRegisterI2cExclusive"},
{0x0030, nullptr, "ReadMcuVariableI2cExclusive"},
{0x0031, nullptr, "SetImageQualityCalibrationData"},
{0x0032, nullptr, "GetImageQualityCalibrationData"},
{0x0033, &CAM_C::SetPackageParameterWithoutContext, "SetPackageParameterWithoutContext"},
{0x0034, &CAM_C::SetPackageParameterWithContext, "SetPackageParameterWithContext"},
{0x0035, &CAM_C::SetPackageParameterWithContextDetail, "SetPackageParameterWithContextDetail"},
{0x0036, &CAM_C::GetSuitableY2rStandardCoefficient, "GetSuitableY2rStandardCoefficient"},
{0x0037, nullptr, "PlayShutterSoundWithWave"},
{0x0038, &CAM_C::PlayShutterSound, "PlayShutterSound"},
{0x0039, &CAM_C::DriverInitialize, "DriverInitialize"},
{0x003A, &CAM_C::DriverFinalize, "DriverFinalize"},
{0x003B, nullptr, "GetActivatedCamera"},
{0x003C, nullptr, "GetSleepCamera"},
{0x003D, nullptr, "SetSleepCamera"},
{0x003E, nullptr, "SetBrightnessSynchronization"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::CAM
SERIALIZE_EXPORT_IMPL(Service::CAM::CAM_C)

View File

@@ -0,0 +1,22 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included..
#pragma once
#include "core/hle/service/cam/cam.h"
namespace Service::CAM {
class CAM_C final : public Module::Interface {
public:
explicit CAM_C(std::shared_ptr<Module> cam);
private:
SERVICE_SERIALIZATION(CAM_C, cam, Module)
};
} // namespace Service::CAM
BOOST_CLASS_EXPORT_KEY(Service::CAM::CAM_C)
BOOST_SERIALIZATION_CONSTRUCT(Service::CAM::CAM_C)

View File

@@ -0,0 +1,125 @@
// Copyright 2020 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
namespace Service::CAM {
enum CameraIndex {
OuterRightCamera = 0,
InnerCamera = 1,
OuterLeftCamera = 2,
NumCameras = 3,
};
enum class Effect : u8 {
None = 0,
Mono = 1,
Sepia = 2,
Negative = 3,
Negafilm = 4,
Sepia01 = 5,
};
enum class Flip : u8 {
None = 0,
Horizontal = 1,
Vertical = 2,
Reverse = 3,
};
enum class Size : u8 {
VGA = 0,
QVGA = 1,
QQVGA = 2,
CIF = 3,
QCIF = 4,
DS_LCD = 5,
DS_LCDx4 = 6,
CTR_TOP_LCD = 7,
CTR_BOTTOM_LCD = QVGA,
};
enum class FrameRate : u8 {
Rate_15 = 0,
Rate_15_To_5 = 1,
Rate_15_To_2 = 2,
Rate_10 = 3,
Rate_8_5 = 4,
Rate_5 = 5,
Rate_20 = 6,
Rate_20_To_5 = 7,
Rate_30 = 8,
Rate_30_To_5 = 9,
Rate_15_To_10 = 10,
Rate_20_To_10 = 11,
Rate_30_To_10 = 12,
};
enum class ShutterSoundType : u8 {
Normal = 0,
Movie = 1,
MovieEnd = 2,
};
enum class WhiteBalance : u8 {
BalanceAuto = 0,
Balance3200K = 1,
Balance4150K = 2,
Balance5200K = 3,
Balance6000K = 4,
Balance7000K = 5,
BalanceMax = 6,
BalanceNormal = BalanceAuto,
BalanceTungsten = Balance3200K,
BalanceWhiteFluorescentLight = Balance4150K,
BalanceDaylight = Balance5200K,
BalanceCloudy = Balance6000K,
BalanceHorizon = Balance6000K,
BalanceShade = Balance7000K,
};
enum class PhotoMode : u8 {
Normal = 0,
Portrait = 1,
Landscape = 2,
Nightview = 3,
Letter0 = 4,
};
enum class LensCorrection : u8 {
Off = 0,
On70 = 1,
On90 = 2,
Dark = Off,
Normal = On70,
Bright = On90,
};
enum class Contrast : u8 {
Pattern01 = 1,
Pattern02 = 2,
Pattern03 = 3,
Pattern04 = 4,
Pattern05 = 5,
Pattern06 = 6,
Pattern07 = 7,
Pattern08 = 8,
Pattern09 = 9,
Pattern10 = 10,
Pattern11 = 11,
Low = Pattern05,
Normal = Pattern06,
High = Pattern07,
};
enum class OutputFormat : u8 {
YUV422 = 0,
RGB565 = 1,
};
} // namespace Service::CAM

View File

@@ -0,0 +1,18 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/cam/cam_q.h"
namespace Service::CAM {
CAM_Q::CAM_Q() : ServiceFramework("cam:q", 1 /*TODO: find the true value*/) {
// Empty arrays are illegal -- commented out until an entry is added.
// static const FunctionInfo functions[] = {};
// RegisterHandlers(functions);
}
} // namespace Service::CAM
SERIALIZE_EXPORT_IMPL(Service::CAM::CAM_Q)

View File

@@ -0,0 +1,21 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included..
#pragma once
#include "core/hle/service/service.h"
namespace Service::CAM {
class CAM_Q : public ServiceFramework<CAM_Q> {
public:
CAM_Q();
private:
SERVICE_SERIALIZATION_SIMPLE
};
} // namespace Service::CAM
BOOST_CLASS_EXPORT_KEY(Service::CAM::CAM_Q)

View File

@@ -0,0 +1,83 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/cam/cam.h"
#include "core/hle/service/cam/cam_s.h"
namespace Service::CAM {
CAM_S::CAM_S(std::shared_ptr<Module> cam) : Module::Interface(std::move(cam), "cam:s", 1) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, &CAM_S::StartCapture, "StartCapture"},
{0x0002, &CAM_S::StopCapture, "StopCapture"},
{0x0003, &CAM_S::IsBusy, "IsBusy"},
{0x0004, &CAM_S::ClearBuffer, "ClearBuffer"},
{0x0005, &CAM_S::GetVsyncInterruptEvent, "GetVsyncInterruptEvent"},
{0x0006, &CAM_S::GetBufferErrorInterruptEvent, "GetBufferErrorInterruptEvent"},
{0x0007, &CAM_S::SetReceiving, "SetReceiving"},
{0x0008, &CAM_S::IsFinishedReceiving, "IsFinishedReceiving"},
{0x0009, &CAM_S::SetTransferLines, "SetTransferLines"},
{0x000A, &CAM_S::GetMaxLines, "GetMaxLines"},
{0x000B, &CAM_S::SetTransferBytes, "SetTransferBytes"},
{0x000C, &CAM_S::GetTransferBytes, "GetTransferBytes"},
{0x000D, &CAM_S::GetMaxBytes, "GetMaxBytes"},
{0x000E, &CAM_S::SetTrimming, "SetTrimming"},
{0x000F, &CAM_S::IsTrimming, "IsTrimming"},
{0x0010, &CAM_S::SetTrimmingParams, "SetTrimmingParams"},
{0x0011, &CAM_S::GetTrimmingParams, "GetTrimmingParams"},
{0x0012, &CAM_S::SetTrimmingParamsCenter, "SetTrimmingParamsCenter"},
{0x0013, &CAM_S::Activate, "Activate"},
{0x0014, &CAM_S::SwitchContext, "SwitchContext"},
{0x0015, nullptr, "SetExposure"},
{0x0016, nullptr, "SetWhiteBalance"},
{0x0017, nullptr, "SetWhiteBalanceWithoutBaseUp"},
{0x0018, nullptr, "SetSharpness"},
{0x0019, nullptr, "SetAutoExposure"},
{0x001A, nullptr, "IsAutoExposure"},
{0x001B, nullptr, "SetAutoWhiteBalance"},
{0x001C, nullptr, "IsAutoWhiteBalance"},
{0x001D, &CAM_S::FlipImage, "FlipImage"},
{0x001E, &CAM_S::SetDetailSize, "SetDetailSize"},
{0x001F, &CAM_S::SetSize, "SetSize"},
{0x0020, &CAM_S::SetFrameRate, "SetFrameRate"},
{0x0021, nullptr, "SetPhotoMode"},
{0x0022, &CAM_S::SetEffect, "SetEffect"},
{0x0023, nullptr, "SetContrast"},
{0x0024, nullptr, "SetLensCorrection"},
{0x0025, &CAM_S::SetOutputFormat, "SetOutputFormat"},
{0x0026, nullptr, "SetAutoExposureWindow"},
{0x0027, nullptr, "SetAutoWhiteBalanceWindow"},
{0x0028, nullptr, "SetNoiseFilter"},
{0x0029, &CAM_S::SynchronizeVsyncTiming, "SynchronizeVsyncTiming"},
{0x002A, &CAM_S::GetLatestVsyncTiming, "GetLatestVsyncTiming"},
{0x002B, &CAM_S::GetStereoCameraCalibrationData, "GetStereoCameraCalibrationData"},
{0x002C, nullptr, "SetStereoCameraCalibrationData"},
{0x002D, nullptr, "WriteRegisterI2c"},
{0x002E, nullptr, "WriteMcuVariableI2c"},
{0x002F, nullptr, "ReadRegisterI2cExclusive"},
{0x0030, nullptr, "ReadMcuVariableI2cExclusive"},
{0x0031, nullptr, "SetImageQualityCalibrationData"},
{0x0032, nullptr, "GetImageQualityCalibrationData"},
{0x0033, &CAM_S::SetPackageParameterWithoutContext, "SetPackageParameterWithoutContext"},
{0x0034, &CAM_S::SetPackageParameterWithContext, "SetPackageParameterWithContext"},
{0x0035, &CAM_S::SetPackageParameterWithContextDetail, "SetPackageParameterWithContextDetail"},
{0x0036, &CAM_S::GetSuitableY2rStandardCoefficient, "GetSuitableY2rStandardCoefficient"},
{0x0037, nullptr, "PlayShutterSoundWithWave"},
{0x0038, &CAM_S::PlayShutterSound, "PlayShutterSound"},
{0x0039, &CAM_S::DriverInitialize, "DriverInitialize"},
{0x003A, &CAM_S::DriverFinalize, "DriverFinalize"},
{0x003B, nullptr, "GetActivatedCamera"},
{0x003C, nullptr, "GetSleepCamera"},
{0x003D, nullptr, "SetSleepCamera"},
{0x003E, nullptr, "SetBrightnessSynchronization"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::CAM
SERIALIZE_EXPORT_IMPL(Service::CAM::CAM_S)

View File

@@ -0,0 +1,22 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included..
#pragma once
#include "core/hle/service/cam/cam.h"
namespace Service::CAM {
class CAM_S final : public Module::Interface {
public:
explicit CAM_S(std::shared_ptr<Module> cam);
private:
SERVICE_SERIALIZATION(CAM_S, cam, Module)
};
} // namespace Service::CAM
BOOST_CLASS_EXPORT_KEY(Service::CAM::CAM_S)
BOOST_SERIALIZATION_CONSTRUCT(Service::CAM::CAM_S)

View File

@@ -0,0 +1,83 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/cam/cam.h"
#include "core/hle/service/cam/cam_u.h"
namespace Service::CAM {
CAM_U::CAM_U(std::shared_ptr<Module> cam) : Module::Interface(std::move(cam), "cam:u", 1) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, &CAM_U::StartCapture, "StartCapture"},
{0x0002, &CAM_U::StopCapture, "StopCapture"},
{0x0003, &CAM_U::IsBusy, "IsBusy"},
{0x0004, &CAM_U::ClearBuffer, "ClearBuffer"},
{0x0005, &CAM_U::GetVsyncInterruptEvent, "GetVsyncInterruptEvent"},
{0x0006, &CAM_U::GetBufferErrorInterruptEvent, "GetBufferErrorInterruptEvent"},
{0x0007, &CAM_U::SetReceiving, "SetReceiving"},
{0x0008, &CAM_U::IsFinishedReceiving, "IsFinishedReceiving"},
{0x0009, &CAM_U::SetTransferLines, "SetTransferLines"},
{0x000A, &CAM_U::GetMaxLines, "GetMaxLines"},
{0x000B, &CAM_U::SetTransferBytes, "SetTransferBytes"},
{0x000C, &CAM_U::GetTransferBytes, "GetTransferBytes"},
{0x000D, &CAM_U::GetMaxBytes, "GetMaxBytes"},
{0x000E, &CAM_U::SetTrimming, "SetTrimming"},
{0x000F, &CAM_U::IsTrimming, "IsTrimming"},
{0x0010, &CAM_U::SetTrimmingParams, "SetTrimmingParams"},
{0x0011, &CAM_U::GetTrimmingParams, "GetTrimmingParams"},
{0x0012, &CAM_U::SetTrimmingParamsCenter, "SetTrimmingParamsCenter"},
{0x0013, &CAM_U::Activate, "Activate"},
{0x0014, &CAM_U::SwitchContext, "SwitchContext"},
{0x0015, nullptr, "SetExposure"},
{0x0016, nullptr, "SetWhiteBalance"},
{0x0017, nullptr, "SetWhiteBalanceWithoutBaseUp"},
{0x0018, nullptr, "SetSharpness"},
{0x0019, nullptr, "SetAutoExposure"},
{0x001A, nullptr, "IsAutoExposure"},
{0x001B, nullptr, "SetAutoWhiteBalance"},
{0x001C, nullptr, "IsAutoWhiteBalance"},
{0x001D, &CAM_U::FlipImage, "FlipImage"},
{0x001E, &CAM_U::SetDetailSize, "SetDetailSize"},
{0x001F, &CAM_U::SetSize, "SetSize"},
{0x0020, &CAM_U::SetFrameRate, "SetFrameRate"},
{0x0021, nullptr, "SetPhotoMode"},
{0x0022, &CAM_U::SetEffect, "SetEffect"},
{0x0023, nullptr, "SetContrast"},
{0x0024, nullptr, "SetLensCorrection"},
{0x0025, &CAM_U::SetOutputFormat, "SetOutputFormat"},
{0x0026, nullptr, "SetAutoExposureWindow"},
{0x0027, nullptr, "SetAutoWhiteBalanceWindow"},
{0x0028, nullptr, "SetNoiseFilter"},
{0x0029, &CAM_U::SynchronizeVsyncTiming, "SynchronizeVsyncTiming"},
{0x002A, &CAM_U::GetLatestVsyncTiming, "GetLatestVsyncTiming"},
{0x002B, &CAM_U::GetStereoCameraCalibrationData, "GetStereoCameraCalibrationData"},
{0x002C, nullptr, "SetStereoCameraCalibrationData"},
{0x002D, nullptr, "WriteRegisterI2c"},
{0x002E, nullptr, "WriteMcuVariableI2c"},
{0x002F, nullptr, "ReadRegisterI2cExclusive"},
{0x0030, nullptr, "ReadMcuVariableI2cExclusive"},
{0x0031, nullptr, "SetImageQualityCalibrationData"},
{0x0032, nullptr, "GetImageQualityCalibrationData"},
{0x0033, &CAM_U::SetPackageParameterWithoutContext, "SetPackageParameterWithoutContext"},
{0x0034, &CAM_U::SetPackageParameterWithContext, "SetPackageParameterWithContext"},
{0x0035, &CAM_U::SetPackageParameterWithContextDetail, "SetPackageParameterWithContextDetail"},
{0x0036, &CAM_U::GetSuitableY2rStandardCoefficient, "GetSuitableY2rStandardCoefficient"},
{0x0037, nullptr, "PlayShutterSoundWithWave"},
{0x0038, &CAM_U::PlayShutterSound, "PlayShutterSound"},
{0x0039, &CAM_U::DriverInitialize, "DriverInitialize"},
{0x003A, &CAM_U::DriverFinalize, "DriverFinalize"},
{0x003B, nullptr, "GetActivatedCamera"},
{0x003C, nullptr, "GetSleepCamera"},
{0x003D, nullptr, "SetSleepCamera"},
{0x003E, nullptr, "SetBrightnessSynchronization"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::CAM
SERIALIZE_EXPORT_IMPL(Service::CAM::CAM_U)

View File

@@ -0,0 +1,22 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included..
#pragma once
#include "core/hle/service/cam/cam.h"
namespace Service::CAM {
class CAM_U final : public Module::Interface {
public:
explicit CAM_U(std::shared_ptr<Module> cam);
private:
SERVICE_SERIALIZATION(CAM_U, cam, Module)
};
} // namespace Service::CAM
BOOST_CLASS_EXPORT_KEY(Service::CAM::CAM_U)
BOOST_SERIALIZATION_CONSTRUCT(Service::CAM::CAM_U)

View File

@@ -0,0 +1,733 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <cstring>
#include "common/archives.h"
#include "common/common_funcs.h"
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/cam/y2r_u.h"
#include "core/hw/y2r.h"
SERVICE_CONSTRUCT_IMPL(Service::Y2R::Y2R_U)
SERIALIZE_EXPORT_IMPL(Service::Y2R::Y2R_U)
namespace Service::Y2R {
template <class Archive>
void Y2R_U::serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<Kernel::SessionRequestHandler>(*this);
ar& completion_event;
ar& conversion;
ar& dithering_weight_params;
ar& temporal_dithering_enabled;
ar& transfer_end_interrupt_enabled;
ar& spacial_dithering_enabled;
}
constexpr std::array<CoefficientSet, 4> standard_coefficients{{
{{0x100, 0x166, 0xB6, 0x58, 0x1C5, -0x166F, 0x10EE, -0x1C5B}}, // ITU_Rec601
{{0x100, 0x193, 0x77, 0x2F, 0x1DB, -0x1933, 0xA7C, -0x1D51}}, // ITU_Rec709
{{0x12A, 0x198, 0xD0, 0x64, 0x204, -0x1BDE, 0x10F2, -0x229B}}, // ITU_Rec601_Scaling
{{0x12A, 0x1CA, 0x88, 0x36, 0x21C, -0x1F04, 0x99C, -0x2421}}, // ITU_Rec709_Scaling
}};
Result ConversionConfiguration::SetInputLineWidth(u16 width) {
if (width == 0 || width > 1024 || width % 8 != 0) {
return Result(ErrorDescription::OutOfRange, ErrorModule::CAM, ErrorSummary::InvalidArgument,
ErrorLevel::Usage); // 0xE0E053FD
}
// Note: The hardware uses the register value 0 to represent a width of 1024, so for a width of
// 1024 the `camera` module would set the value 0 here, but we don't need to emulate this
// internal detail.
this->input_line_width = width;
return ResultSuccess;
}
Result ConversionConfiguration::SetInputLines(u16 lines) {
if (lines == 0 || lines > 1024) {
return Result(ErrorDescription::OutOfRange, ErrorModule::CAM, ErrorSummary::InvalidArgument,
ErrorLevel::Usage); // 0xE0E053FD
}
// Note: In what appears to be a bug, the `camera` module does not set the hardware register at
// all if `lines` is 1024, so the conversion uses the last value that was set. The intention
// was probably to set it to 0 like in SetInputLineWidth.
if (lines != 1024) {
this->input_lines = lines;
}
return ResultSuccess;
}
Result ConversionConfiguration::SetStandardCoefficient(StandardCoefficient standard_coefficient) {
const auto index = static_cast<std::size_t>(standard_coefficient);
if (index >= standard_coefficients.size()) {
return Result(ErrorDescription::InvalidEnumValue, ErrorModule::CAM,
ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E053ED
}
std::memcpy(coefficients.data(), standard_coefficients[index].data(), sizeof(coefficients));
return ResultSuccess;
}
void Y2R_U::SetInputFormat(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
conversion.input_format = rp.PopEnum<InputFormat>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_DEBUG(Service_Y2R, "called input_format={}", conversion.input_format);
}
void Y2R_U::GetInputFormat(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.PushEnum(conversion.input_format);
LOG_DEBUG(Service_Y2R, "called input_format={}", conversion.input_format);
}
void Y2R_U::SetOutputFormat(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
conversion.output_format = rp.PopEnum<OutputFormat>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_DEBUG(Service_Y2R, "called output_format={}", conversion.output_format);
}
void Y2R_U::GetOutputFormat(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.PushEnum(conversion.output_format);
LOG_DEBUG(Service_Y2R, "called output_format={}", conversion.output_format);
}
void Y2R_U::SetRotation(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
conversion.rotation = rp.PopEnum<Rotation>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_DEBUG(Service_Y2R, "called rotation={}", conversion.rotation);
}
void Y2R_U::GetRotation(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.PushEnum(conversion.rotation);
LOG_DEBUG(Service_Y2R, "called rotation={}", conversion.rotation);
}
void Y2R_U::SetBlockAlignment(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
conversion.block_alignment = rp.PopEnum<BlockAlignment>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_DEBUG(Service_Y2R, "called block_alignment={}", conversion.block_alignment);
}
void Y2R_U::GetBlockAlignment(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.PushEnum(conversion.block_alignment);
LOG_DEBUG(Service_Y2R, "called block_alignment={}", conversion.block_alignment);
}
void Y2R_U::SetSpacialDithering(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
spacial_dithering_enabled = rp.Pop<bool>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_DEBUG(Service_Y2R, "(STUBBED) called");
}
void Y2R_U::GetSpacialDithering(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push(spacial_dithering_enabled);
LOG_DEBUG(Service_Y2R, "(STUBBED) called");
}
void Y2R_U::SetTemporalDithering(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
temporal_dithering_enabled = rp.Pop<bool>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_DEBUG(Service_Y2R, "(STUBBED) called");
}
void Y2R_U::GetTemporalDithering(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push(temporal_dithering_enabled);
LOG_DEBUG(Service_Y2R, "(STUBBED) called");
}
void Y2R_U::SetTransferEndInterrupt(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
transfer_end_interrupt_enabled = rp.Pop<bool>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_DEBUG(Service_Y2R, "(STUBBED) called");
}
void Y2R_U::GetTransferEndInterrupt(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push(transfer_end_interrupt_enabled);
LOG_DEBUG(Service_Y2R, "(STUBBED) called");
}
void Y2R_U::GetTransferEndEvent(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(ResultSuccess);
rb.PushCopyObjects(completion_event);
LOG_DEBUG(Service_Y2R, "called");
}
void Y2R_U::SetSendingY(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
conversion.src_Y.address = rp.Pop<u32>();
conversion.src_Y.image_size = rp.Pop<u32>();
conversion.src_Y.transfer_unit = rp.Pop<u32>();
conversion.src_Y.gap = rp.Pop<u32>();
auto process = rp.PopObject<Kernel::Process>();
// TODO (wwylele): pass process handle to y2r engine or convert VAddr to PAddr
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_DEBUG(Service_Y2R,
"called image_size=0x{:08X}, transfer_unit={}, transfer_stride={}, "
"src_process_id={}",
conversion.src_Y.image_size, conversion.src_Y.transfer_unit, conversion.src_Y.gap,
process->process_id);
}
void Y2R_U::SetSendingU(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
conversion.src_U.address = rp.Pop<u32>();
conversion.src_U.image_size = rp.Pop<u32>();
conversion.src_U.transfer_unit = rp.Pop<u32>();
conversion.src_U.gap = rp.Pop<u32>();
auto process = rp.PopObject<Kernel::Process>();
// TODO (wwylele): pass the process handle to y2r engine or convert VAddr to PAddr
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_DEBUG(Service_Y2R,
"called image_size=0x{:08X}, transfer_unit={}, transfer_stride={}, "
"src_process_id={}",
conversion.src_U.image_size, conversion.src_U.transfer_unit, conversion.src_U.gap,
process->process_id);
}
void Y2R_U::SetSendingV(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
conversion.src_V.address = rp.Pop<u32>();
conversion.src_V.image_size = rp.Pop<u32>();
conversion.src_V.transfer_unit = rp.Pop<u32>();
conversion.src_V.gap = rp.Pop<u32>();
auto process = rp.PopObject<Kernel::Process>();
// TODO (wwylele): pass the process handle to y2r engine or convert VAddr to PAddr
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_DEBUG(Service_Y2R,
"called image_size=0x{:08X}, transfer_unit={}, transfer_stride={}, "
"src_process_id={}",
conversion.src_V.image_size, conversion.src_V.transfer_unit, conversion.src_V.gap,
process->process_id);
}
void Y2R_U::SetSendingYUYV(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
conversion.src_YUYV.address = rp.Pop<u32>();
conversion.src_YUYV.image_size = rp.Pop<u32>();
conversion.src_YUYV.transfer_unit = rp.Pop<u32>();
conversion.src_YUYV.gap = rp.Pop<u32>();
auto process = rp.PopObject<Kernel::Process>();
// TODO (wwylele): pass the process handle to y2r engine or convert VAddr to PAddr
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_DEBUG(Service_Y2R,
"called image_size=0x{:08X}, transfer_unit={}, transfer_stride={}, "
"src_process_id={}",
conversion.src_YUYV.image_size, conversion.src_YUYV.transfer_unit,
conversion.src_YUYV.gap, process->process_id);
}
void Y2R_U::IsFinishedSendingYuv(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push<u8>(1);
LOG_DEBUG(Service_Y2R, "(STUBBED) called");
}
void Y2R_U::IsFinishedSendingY(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push<u8>(1);
LOG_DEBUG(Service_Y2R, "(STUBBED) called");
}
void Y2R_U::IsFinishedSendingU(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push<u8>(1);
LOG_DEBUG(Service_Y2R, "(STUBBED) called");
}
void Y2R_U::IsFinishedSendingV(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push<u8>(1);
LOG_DEBUG(Service_Y2R, "(STUBBED) called");
}
void Y2R_U::SetReceiving(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
conversion.dst.address = rp.Pop<u32>();
conversion.dst.image_size = rp.Pop<u32>();
conversion.dst.transfer_unit = rp.Pop<u32>();
conversion.dst.gap = rp.Pop<u32>();
auto dst_process = rp.PopObject<Kernel::Process>();
// TODO (wwylele): pass the process handle to y2r engine or convert VAddr to PAddr
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_DEBUG(Service_Y2R,
"called image_size=0x{:08X}, transfer_unit={}, transfer_stride={}, "
"dst_process_id={}",
conversion.dst.image_size, conversion.dst.transfer_unit, conversion.dst.gap,
static_cast<u32>(dst_process->process_id));
}
void Y2R_U::IsFinishedReceiving(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push<u8>(1);
LOG_DEBUG(Service_Y2R, "(STUBBED) called");
}
void Y2R_U::SetInputLineWidth(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
u32 input_line_width = rp.Pop<u32>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(conversion.SetInputLineWidth(input_line_width));
LOG_DEBUG(Service_Y2R, "called input_line_width={}", input_line_width);
}
void Y2R_U::GetInputLineWidth(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push(conversion.input_line_width);
LOG_DEBUG(Service_Y2R, "called input_line_width={}", conversion.input_line_width);
}
void Y2R_U::SetInputLines(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
u32 input_lines = rp.Pop<u32>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(conversion.SetInputLines(input_lines));
LOG_DEBUG(Service_Y2R, "called input_lines={}", input_lines);
}
void Y2R_U::GetInputLines(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push(static_cast<u32>(conversion.input_lines));
LOG_DEBUG(Service_Y2R, "called input_lines={}", conversion.input_lines);
}
void Y2R_U::SetCoefficient(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
rp.PopRaw<CoefficientSet>(conversion.coefficients);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_DEBUG(Service_Y2R, "called coefficients=[{:X}, {:X}, {:X}, {:X}, {:X}, {:X}, {:X}, {:X}]",
conversion.coefficients[0], conversion.coefficients[1], conversion.coefficients[2],
conversion.coefficients[3], conversion.coefficients[4], conversion.coefficients[5],
conversion.coefficients[6], conversion.coefficients[7]);
}
void Y2R_U::GetCoefficient(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(5, 0);
rb.Push(ResultSuccess);
rb.PushRaw(conversion.coefficients);
LOG_DEBUG(Service_Y2R, "called");
}
void Y2R_U::SetStandardCoefficient(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
u32 index = rp.Pop<u32>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(conversion.SetStandardCoefficient(static_cast<StandardCoefficient>(index)));
LOG_DEBUG(Service_Y2R, "called standard_coefficient={}", index);
}
void Y2R_U::GetStandardCoefficient(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 index = rp.Pop<u32>();
if (index < standard_coefficients.size()) {
IPC::RequestBuilder rb = rp.MakeBuilder(5, 0);
rb.Push(ResultSuccess);
rb.PushRaw(standard_coefficients[index]);
LOG_DEBUG(Service_Y2R, "called standard_coefficient={} ", index);
} else {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(Result(ErrorDescription::InvalidEnumValue, ErrorModule::CAM,
ErrorSummary::InvalidArgument, ErrorLevel::Usage));
LOG_ERROR(Service_Y2R, "called standard_coefficient={} The argument is invalid!", index);
}
}
void Y2R_U::SetAlpha(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
conversion.alpha = rp.Pop<u32>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_DEBUG(Service_Y2R, "called alpha={}", conversion.alpha);
}
void Y2R_U::GetAlpha(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push(conversion.alpha);
LOG_DEBUG(Service_Y2R, "called alpha={}", conversion.alpha);
}
void Y2R_U::SetDitheringWeightParams(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
rp.PopRaw(dithering_weight_params);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_DEBUG(Service_Y2R, "called");
}
void Y2R_U::GetDitheringWeightParams(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(9, 0);
rb.Push(ResultSuccess);
rb.PushRaw(dithering_weight_params);
LOG_DEBUG(Service_Y2R, "called");
}
void Y2R_U::StartConversion(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
// dst_image_size would seem to be perfect for this, but it doesn't include the gap :(
u32 total_output_size =
conversion.input_lines * (conversion.dst.transfer_unit + conversion.dst.gap);
system.Memory().RasterizerFlushVirtualRegion(conversion.dst.address, total_output_size,
Memory::FlushMode::FlushAndInvalidate);
HW::Y2R::PerformConversion(system.Memory(), conversion);
if (is_busy_conversion) {
system.CoreTiming().RemoveEvent(completion_signal_event);
}
// This value has been estimated as the minimum amount of cycles to resolve a race condition
// in the intro cutscene of the FIFA series of games.
// TODO: Measure the cycle count more accurately based on hardware.
static constexpr s64 MinY2RDelay = 50000;
system.CoreTiming().ScheduleEvent(MinY2RDelay, completion_signal_event);
is_busy_conversion = true;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_DEBUG(Service_Y2R, "called");
}
void Y2R_U::StopConversion(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
if (is_busy_conversion) {
is_busy_conversion = false;
system.CoreTiming().RemoveEvent(completion_signal_event);
}
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_DEBUG(Service_Y2R, "called");
}
void Y2R_U::IsBusyConversion(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push(is_busy_conversion);
LOG_DEBUG(Service_Y2R, "called");
}
void Y2R_U::SetPackageParameter(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
auto params = rp.PopRaw<ConversionParameters>();
conversion.input_format = params.input_format;
conversion.output_format = params.output_format;
conversion.rotation = params.rotation;
conversion.block_alignment = params.block_alignment;
LOG_DEBUG(Service_Y2R,
"called input_format={} output_format={} rotation={} block_alignment={} "
"input_line_width={} input_lines={} standard_coefficient={} reserved={} alpha={:X}",
params.input_format, params.output_format, params.rotation, params.block_alignment,
params.input_line_width, params.input_lines, params.standard_coefficient,
params.padding, params.alpha);
auto result = conversion.SetInputLineWidth(params.input_line_width);
SCOPE_EXIT({
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(result);
});
if (result.IsError()) {
return;
}
result = conversion.SetInputLines(params.input_lines);
if (result.IsError()) {
return;
}
result = conversion.SetStandardCoefficient(params.standard_coefficient);
if (result.IsError()) {
return;
}
conversion.padding = params.padding;
conversion.alpha = params.alpha;
}
void Y2R_U::PingProcess(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push<u8>(0);
LOG_DEBUG(Service_Y2R, "(STUBBED) called");
}
void Y2R_U::DriverInitialize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
conversion.input_format = InputFormat::YUV422_Indiv8;
conversion.output_format = OutputFormat::RGBA8;
conversion.rotation = Rotation::None;
conversion.block_alignment = BlockAlignment::Linear;
conversion.coefficients.fill(0);
conversion.SetInputLineWidth(1024);
conversion.SetInputLines(1024);
conversion.alpha = 0;
ConversionBuffer zero_buffer = {};
conversion.src_Y = zero_buffer;
conversion.src_U = zero_buffer;
conversion.src_V = zero_buffer;
conversion.dst = zero_buffer;
completion_event->Clear();
rb.Push(ResultSuccess);
LOG_DEBUG(Service_Y2R, "called");
}
void Y2R_U::DriverFinalize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_DEBUG(Service_Y2R, "called");
}
void Y2R_U::GetPackageParameter(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(4, 0);
rb.Push(ResultSuccess);
rb.PushRaw(conversion);
LOG_DEBUG(Service_Y2R, "called");
}
Y2R_U::Y2R_U(Core::System& system) : ServiceFramework("y2r:u", 1), system(system) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, &Y2R_U::SetInputFormat, "SetInputFormat"},
{0x0002, &Y2R_U::GetInputFormat, "GetInputFormat"},
{0x0003, &Y2R_U::SetOutputFormat, "SetOutputFormat"},
{0x0004, &Y2R_U::GetOutputFormat, "GetOutputFormat"},
{0x0005, &Y2R_U::SetRotation, "SetRotation"},
{0x0006, &Y2R_U::GetRotation, "GetRotation"},
{0x0007, &Y2R_U::SetBlockAlignment, "SetBlockAlignment"},
{0x0008, &Y2R_U::GetBlockAlignment, "GetBlockAlignment"},
{0x0009, &Y2R_U::SetSpacialDithering, "SetSpacialDithering"},
{0x000A, &Y2R_U::GetSpacialDithering, "GetSpacialDithering"},
{0x000B, &Y2R_U::SetTemporalDithering, "SetTemporalDithering"},
{0x000C, &Y2R_U::GetTemporalDithering, "GetTemporalDithering"},
{0x000D, &Y2R_U::SetTransferEndInterrupt, "SetTransferEndInterrupt"},
{0x000E, &Y2R_U::GetTransferEndInterrupt, "GetTransferEndInterrupt"},
{0x000F, &Y2R_U::GetTransferEndEvent, "GetTransferEndEvent"},
{0x0010, &Y2R_U::SetSendingY, "SetSendingY"},
{0x0011, &Y2R_U::SetSendingU, "SetSendingU"},
{0x0012, &Y2R_U::SetSendingV, "SetSendingV"},
{0x0013, &Y2R_U::SetSendingYUYV, "SetSendingYUYV"},
{0x0014, &Y2R_U::IsFinishedSendingYuv, "IsFinishedSendingYuv"},
{0x0015, &Y2R_U::IsFinishedSendingY, "IsFinishedSendingY"},
{0x0016, &Y2R_U::IsFinishedSendingU, "IsFinishedSendingU"},
{0x0017, &Y2R_U::IsFinishedSendingV, "IsFinishedSendingV"},
{0x0018, &Y2R_U::SetReceiving, "SetReceiving"},
{0x0019, &Y2R_U::IsFinishedReceiving, "IsFinishedReceiving"},
{0x001A, &Y2R_U::SetInputLineWidth, "SetInputLineWidth"},
{0x001B, &Y2R_U::GetInputLineWidth, "GetInputLineWidth"},
{0x001C, &Y2R_U::SetInputLines, "SetInputLines"},
{0x001D, &Y2R_U::GetInputLines, "GetInputLines"},
{0x001E, &Y2R_U::SetCoefficient, "SetCoefficient"},
{0x001F, &Y2R_U::GetCoefficient, "GetCoefficient"},
{0x0020, &Y2R_U::SetStandardCoefficient, "SetStandardCoefficient"},
{0x0021, &Y2R_U::GetStandardCoefficient, "GetStandardCoefficient"},
{0x0022, &Y2R_U::SetAlpha, "SetAlpha"},
{0x0023, &Y2R_U::GetAlpha, "GetAlpha"},
{0x0024, &Y2R_U::SetDitheringWeightParams, "SetDitheringWeightParams"},
{0x0025, &Y2R_U::GetDitheringWeightParams, "GetDitheringWeightParams"},
{0x0026, &Y2R_U::StartConversion, "StartConversion"},
{0x0027, &Y2R_U::StopConversion, "StopConversion"},
{0x0028, &Y2R_U::IsBusyConversion, "IsBusyConversion"},
{0x0029, &Y2R_U::SetPackageParameter, "SetPackageParameter"},
{0x002A, &Y2R_U::PingProcess, "PingProcess"},
{0x002B, &Y2R_U::DriverInitialize, "DriverInitialize"},
{0x002C, &Y2R_U::DriverFinalize, "DriverFinalize"},
{0x002D, &Y2R_U::GetPackageParameter, "GetPackageParameter"},
// clang-format on
};
RegisterHandlers(functions);
completion_event = system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "Y2R:Completed");
completion_signal_event =
system.CoreTiming().RegisterEvent("Y2R Completion Signal Event", [this](uintptr_t, s64) {
completion_event->Signal();
is_busy_conversion = false;
});
}
Y2R_U::~Y2R_U() = default;
void InstallInterfaces(Core::System& system) {
auto& service_manager = system.ServiceManager();
std::make_shared<Y2R_U>(system)->InstallAsService(service_manager);
}
} // namespace Service::Y2R

View File

@@ -0,0 +1,370 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <memory>
#include <string>
#include <boost/serialization/array.hpp>
#include "common/common_types.h"
#include "core/hle/result.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Kernel {
class Event;
}
namespace Service::Y2R {
enum class InputFormat : u8 {
/// 8-bit input, with YUV components in separate planes and 4:2:2 subsampling.
YUV422_Indiv8 = 0,
/// 8-bit input, with YUV components in separate planes and 4:2:0 subsampling.
YUV420_Indiv8 = 1,
/// 16-bit input (only LSB used), with YUV components in separate planes and 4:2:2 subsampling.
YUV422_Indiv16 = 2,
/// 16-bit input (only LSB used), with YUV components in separate planes and 4:2:0 subsampling.
YUV420_Indiv16 = 3,
/// 8-bit input, with a single interleaved stream in YUYV format and 4:2:2 subsampling.
YUYV422_Interleaved = 4,
};
enum class OutputFormat : u8 {
RGBA8 = 0,
RGB8 = 1,
RGB5A1 = 2,
RGB565 = 3,
};
enum class Rotation : u8 {
None = 0,
Clockwise_90 = 1,
Clockwise_180 = 2,
Clockwise_270 = 3,
};
enum class BlockAlignment : u8 {
/// Image is output in linear format suitable for use as a framebuffer.
Linear = 0,
/// Image is output in tiled PICA format, suitable for use as a texture.
Block8x8 = 1,
};
enum class StandardCoefficient : u8 {
/// ITU Rec. BT.601 primaries, with PC ranges.
ITU_Rec601 = 0,
/// ITU Rec. BT.709 primaries, with PC ranges.
ITU_Rec709 = 1,
/// ITU Rec. BT.601 primaries, with TV ranges.
ITU_Rec601_Scaling = 2,
/// ITU Rec. BT.709 primaries, with TV ranges.
ITU_Rec709_Scaling = 3,
};
/**
* A set of coefficients configuring the RGB to YUV conversion. Coefficients 0-4 are unsigned 2.8
* fixed pointer numbers representing entries on the conversion matrix, while coefficient 5-7 are
* signed 11.5 fixed point numbers added as offsets to the RGB result.
*
* The overall conversion process formula is:
* ```
* R = trunc((c_0 * Y + c_1 * V) + c_5 + 0.75)
* G = trunc((c_0 * Y - c_3 * U - c_2 * V) + c_6 + 0.75)
* B = trunc((c_0 * Y + c_4 * U ) + c_7 + 0.75)
* ```
*/
using CoefficientSet = std::array<s16, 8>;
struct ConversionBuffer {
/// Current reading/writing address of this buffer.
VAddr address;
/// Remaining amount of bytes to be DMAed, does not include the inter-trasfer gap.
u32 image_size;
/// Size of a single DMA transfer.
u16 transfer_unit;
/// Amount of bytes to be skipped between copying each `transfer_unit` bytes.
u16 gap;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& address;
ar& image_size;
ar& transfer_unit;
ar& gap;
}
friend class boost::serialization::access;
};
struct ConversionConfiguration {
InputFormat input_format;
OutputFormat output_format;
Rotation rotation;
BlockAlignment block_alignment;
u16 input_line_width;
u16 input_lines;
CoefficientSet coefficients;
u8 padding;
u16 alpha;
/// Input parameters for the Y (luma) plane
ConversionBuffer src_Y, src_U, src_V, src_YUYV;
/// Output parameters for the conversion results
ConversionBuffer dst;
Result SetInputLineWidth(u16 width);
Result SetInputLines(u16 lines);
Result SetStandardCoefficient(StandardCoefficient standard_coefficient);
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& input_format;
ar& output_format;
ar& rotation;
ar& block_alignment;
ar& input_line_width;
ar& input_lines;
ar& coefficients;
ar& padding;
ar& alpha;
ar& src_Y;
ar& src_U;
ar& src_V;
ar& src_YUYV;
ar& dst;
}
friend class boost::serialization::access;
};
struct DitheringWeightParams {
u16 w0_xEven_yEven;
u16 w0_xOdd_yEven;
u16 w0_xEven_yOdd;
u16 w0_xOdd_yOdd;
u16 w1_xEven_yEven;
u16 w1_xOdd_yEven;
u16 w1_xEven_yOdd;
u16 w1_xOdd_yOdd;
u16 w2_xEven_yEven;
u16 w2_xOdd_yEven;
u16 w2_xEven_yOdd;
u16 w2_xOdd_yOdd;
u16 w3_xEven_yEven;
u16 w3_xOdd_yEven;
u16 w3_xEven_yOdd;
u16 w3_xOdd_yOdd;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& w0_xEven_yEven;
ar& w0_xOdd_yEven;
ar& w0_xEven_yOdd;
ar& w0_xOdd_yOdd;
ar& w1_xEven_yEven;
ar& w1_xOdd_yEven;
ar& w1_xEven_yOdd;
ar& w1_xOdd_yOdd;
ar& w2_xEven_yEven;
ar& w2_xOdd_yEven;
ar& w2_xEven_yOdd;
ar& w2_xOdd_yOdd;
ar& w3_xEven_yEven;
ar& w3_xOdd_yEven;
ar& w3_xEven_yOdd;
ar& w3_xOdd_yOdd;
}
friend class boost::serialization::access;
};
struct ConversionParameters {
InputFormat input_format;
OutputFormat output_format;
Rotation rotation;
BlockAlignment block_alignment;
u16 input_line_width;
u16 input_lines;
StandardCoefficient standard_coefficient;
u8 padding;
u16 alpha;
};
static_assert(sizeof(ConversionParameters) == 12, "ConversionParameters struct has incorrect size");
class Y2R_U final : public ServiceFramework<Y2R_U> {
public:
explicit Y2R_U(Core::System& system);
~Y2R_U() override;
private:
void SetInputFormat(Kernel::HLERequestContext& ctx);
void GetInputFormat(Kernel::HLERequestContext& ctx);
void SetOutputFormat(Kernel::HLERequestContext& ctx);
void GetOutputFormat(Kernel::HLERequestContext& ctx);
void SetRotation(Kernel::HLERequestContext& ctx);
void GetRotation(Kernel::HLERequestContext& ctx);
void SetBlockAlignment(Kernel::HLERequestContext& ctx);
void GetBlockAlignment(Kernel::HLERequestContext& ctx);
/**
* Y2R_U::SetSpacialDithering service function
* Inputs:
* 1 : u8, 0 = Disabled, 1 = Enabled
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void SetSpacialDithering(Kernel::HLERequestContext& ctx);
/**
* Y2R_U::GetSpacialDithering service function
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u8, 0 = Disabled, 1 = Enabled
*/
void GetSpacialDithering(Kernel::HLERequestContext& ctx);
/**
* Y2R_U::SetTemporalDithering service function
* Inputs:
* 1 : u8, 0 = Disabled, 1 = Enabled
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void SetTemporalDithering(Kernel::HLERequestContext& ctx);
/**
* Y2R_U::GetTemporalDithering service function
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u8, 0 = Disabled, 1 = Enabled
*/
void GetTemporalDithering(Kernel::HLERequestContext& ctx);
/**
* Y2R_U::SetTransferEndInterrupt service function
* Inputs:
* 1 : u8, 0 = Disabled, 1 = Enabled
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void SetTransferEndInterrupt(Kernel::HLERequestContext& ctx);
/**
* Y2R_U::GetTransferEndInterrupt service function
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u8, 0 = Disabled, 1 = Enabled
*/
void GetTransferEndInterrupt(Kernel::HLERequestContext& ctx);
/**
* Y2R_U::GetTransferEndEvent service function
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 3 : The handle of the completion event
*/
void GetTransferEndEvent(Kernel::HLERequestContext& ctx);
void SetSendingY(Kernel::HLERequestContext& ctx);
void SetSendingU(Kernel::HLERequestContext& ctx);
void SetSendingV(Kernel::HLERequestContext& ctx);
void SetSendingYUYV(Kernel::HLERequestContext& ctx);
/**
* Y2R::IsFinishedSendingYuv service function
* Output:
* 1 : Result of the function, 0 on success, otherwise error code
* 2 : u8, 0 = Not Finished, 1 = Finished
*/
void IsFinishedSendingYuv(Kernel::HLERequestContext& ctx);
/**
* Y2R::IsFinishedSendingY service function
* Output:
* 1 : Result of the function, 0 on success, otherwise error code
* 2 : u8, 0 = Not Finished, 1 = Finished
*/
void IsFinishedSendingY(Kernel::HLERequestContext& ctx);
/**
* Y2R::IsFinishedSendingU service function
* Output:
* 1 : Result of the function, 0 on success, otherwise error code
* 2 : u8, 0 = Not Finished, 1 = Finished
*/
void IsFinishedSendingU(Kernel::HLERequestContext& ctx);
/**
* Y2R::IsFinishedSendingV service function
* Output:
* 1 : Result of the function, 0 on success, otherwise error code
* 2 : u8, 0 = Not Finished, 1 = Finished
*/
void IsFinishedSendingV(Kernel::HLERequestContext& ctx);
void SetReceiving(Kernel::HLERequestContext& ctx);
/**
* Y2R::IsFinishedReceiving service function
* Output:
* 1 : Result of the function, 0 on success, otherwise error code
* 2 : u8, 0 = Not Finished, 1 = Finished
*/
void IsFinishedReceiving(Kernel::HLERequestContext& ctx);
void SetInputLineWidth(Kernel::HLERequestContext& ctx);
void GetInputLineWidth(Kernel::HLERequestContext& ctx);
void SetInputLines(Kernel::HLERequestContext& ctx);
void GetInputLines(Kernel::HLERequestContext& ctx);
void SetCoefficient(Kernel::HLERequestContext& ctx);
void GetCoefficient(Kernel::HLERequestContext& ctx);
void SetStandardCoefficient(Kernel::HLERequestContext& ctx);
void GetStandardCoefficient(Kernel::HLERequestContext& ctx);
void SetAlpha(Kernel::HLERequestContext& ctx);
void GetAlpha(Kernel::HLERequestContext& ctx);
void SetDitheringWeightParams(Kernel::HLERequestContext& ctx);
void GetDitheringWeightParams(Kernel::HLERequestContext& ctx);
void StartConversion(Kernel::HLERequestContext& ctx);
void StopConversion(Kernel::HLERequestContext& ctx);
void IsBusyConversion(Kernel::HLERequestContext& ctx);
/**
* Y2R_U::SetPackageParameter service function
*/
void SetPackageParameter(Kernel::HLERequestContext& ctx);
void PingProcess(Kernel::HLERequestContext& ctx);
void DriverInitialize(Kernel::HLERequestContext& ctx);
void DriverFinalize(Kernel::HLERequestContext& ctx);
void GetPackageParameter(Kernel::HLERequestContext& ctx);
Core::System& system;
std::shared_ptr<Kernel::Event> completion_event;
Core::TimingEventType* completion_signal_event;
ConversionConfiguration conversion{};
DitheringWeightParams dithering_weight_params{};
bool temporal_dithering_enabled = false;
bool transfer_end_interrupt_enabled = false;
bool spacial_dithering_enabled = false;
bool is_busy_conversion = false;
template <class Archive>
void serialize(Archive& ar, const unsigned int);
friend class boost::serialization::access;
};
void InstallInterfaces(Core::System& system);
} // namespace Service::Y2R
SERVICE_CONSTRUCT(Service::Y2R::Y2R_U)
BOOST_CLASS_EXPORT_KEY(Service::Y2R::Y2R_U)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,645 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <span>
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/fs/archive.h"
#include "core/hle/service/service.h"
namespace FileSys {
class ArchiveBackend;
class FileBackend;
} // namespace FileSys
namespace Core {
class System;
}
namespace Service::CECD {
class Module final {
public:
explicit Module(Core::System& system);
~Module();
enum class CecCommand : u32 {
None = 0,
Start = 1,
ResetStart = 2,
ReadyScan = 3,
ReadyScanWait = 4,
StartScan = 5,
Rescan = 6,
NdmResume = 7,
NdmSuspend = 8,
NdmSuspendImmediate = 9,
StopWait = 0x0A,
Stop = 0x0B,
StopForce = 0x0C,
StopForceWait = 0x0D,
ResetFilter = 0x0E,
DaemonStop = 0x0F,
DaemonStart = 0x10,
Exit = 0x11,
OverBoss = 0x12,
OverBossForce = 0x13,
OverBossForceWait = 0x14,
End = 0x15,
};
/**
* CecDataPathType possible missing values; need to figure out placement
*
* data:/CEC/TMP
* data:/CEC/test
*/
enum class CecDataPathType : u32 {
Invalid = 0,
MboxList = 1, /// data:/CEC/MBoxList____
MboxInfo = 2, /// data:/CEC/<id>/MBoxInfo____
InboxInfo = 3, /// data:/CEC/<id>/InBox___/BoxInfo_____
OutboxInfo = 4, /// data:/CEC/<id>/OutBox__/BoxInfo_____
OutboxIndex = 5, /// data:/CEC/<id>/OutBox__/OBIndex_____
InboxMsg = 6, /// data:/CEC/<id>/InBox___/_<message_id>
OutboxMsg = 7, /// data:/CEC/<id>/OutBox__/_<message_id>
RootDir = 10, /// data:/CEC
MboxDir = 11, /// data:/CEC/<id>
InboxDir = 12, /// data:/CEC/<id>/InBox___
OutboxDir = 13, /// data:/CEC/<id>/OutBox__
MboxData = 100, /// data:/CEC/<id>/MBoxData.0<i-100>
MboxIcon = 101, /// data:/CEC/<id>/MBoxData.001
MboxTitle = 110, /// data:/CEC/<id>/MBoxData.010
MboxProgramId = 150, /// data:/CEC/<id>/MBoxData.050
};
enum class CecState : u32 {
None = 0,
Init = 1,
WirelessParamSetup = 2,
WirelessReady = 3,
WirelessStartConfig = 4,
Scan = 5,
Scanning = 6,
Connect = 7,
Connecting = 8,
Connected = 9,
ConnectTcp = 10,
ConnectingTcp = 11,
ConnectedTcp = 12,
Negotiation = 13,
SendRecvStart = 14,
SendRecvInit = 15,
SendReady = 16,
ReceiveReady = 17,
Receive = 18,
ConnectionFinishTcp = 19,
ConnectionFinish = 20,
SendPost = 21,
ReceivePost = 22,
Finishing = 23,
Finish = 24,
OverBoss = 25,
Idle = 26
};
enum class CecSystemInfoType : u32 { EulaVersion = 1, Eula = 2, ParentControl = 3 };
struct CecBoxInfoHeader {
u16_le magic; // 0x6262 'bb'
INSERT_PADDING_BYTES(2);
u32_le box_info_size;
u32_le max_box_size;
u32_le box_size;
u32_le max_message_num;
u32_le message_num;
u32_le max_batch_size;
u32_le max_message_size;
};
static_assert(sizeof(CecBoxInfoHeader) == 0x20, "CecBoxInfoHeader struct has incorrect size.");
struct CecMBoxInfoHeader {
u16_le magic; // 0x6363 'cc'
INSERT_PADDING_BYTES(2);
u32_le program_id;
u32_le private_id;
u8 flag;
u8 flag2;
INSERT_PADDING_BYTES(2);
std::array<u8, 32> hmac_key;
INSERT_PADDING_BYTES(4);
struct Time {
u32_le year;
u8 month;
u8 day;
u8 hour;
u8 minute;
u8 second;
u8 millisecond;
u8 microsecond;
u8 padding;
} last_accessed;
INSERT_PADDING_BYTES(4);
Time last_received;
INSERT_PADDING_BYTES(4);
Time unknown_time;
};
static_assert(sizeof(CecMBoxInfoHeader) == 0x60,
"CecMBoxInfoHeader struct has incorrect size.");
struct CecMBoxListHeader {
u16_le magic; // 0x6868 'hh'
INSERT_PADDING_BYTES(2);
u16_le version; // 0x01 00, maybe activated flag?
INSERT_PADDING_BYTES(2);
u16_le num_boxes; // 24 max
INSERT_PADDING_BYTES(2);
std::array<std::array<u8, 16>, 24> box_names; // 16 char names, 24 boxes
};
static_assert(sizeof(CecMBoxListHeader) == 0x18C,
"CecMBoxListHeader struct has incorrect size.");
struct CecMessageHeader {
u16_le magic; // 0x6060 ``
INSERT_PADDING_BYTES(2);
u32_le message_size;
u32_le header_size;
u32_le body_size;
u32_le title_id;
u32_le title_id2;
u32_le batch_id;
u32_le unknown_id;
std::array<u8, 8> message_id;
u32_le version;
std::array<u8, 8> message_id2;
u8 flag;
u8 send_method;
u8 is_unopen;
u8 is_new;
u64_le sender_id;
u64_le sender_id2;
struct Time {
u32_le year;
u8 month;
u8 day;
u8 hour;
u8 minute;
u8 second;
u8 millisecond;
u8 microsecond;
u8 padding;
} send_time, recv_time, create_time;
u8 send_count;
u8 forward_count;
u16_le user_data;
};
static_assert(sizeof(CecMessageHeader) == 0x70, "CecMessageHeader struct has incorrect size.");
struct CecOBIndexHeader {
u16_le magic; // 0x6767 'gg'
INSERT_PADDING_BYTES(2);
u32_le message_num;
// Array of messageid's 8 bytes each, same as CecMessageHeader.message_id[8]
};
static_assert(sizeof(CecOBIndexHeader) == 0x08, "CecOBIndexHeader struct has incorrect size.");
enum class CecdState : u32 {
NdmStatusWorking = 0,
NdmStatusIdle = 1,
NdmStatusSuspending = 2,
NdmStatusSuspended = 3,
};
union CecOpenMode {
u32 raw;
BitField<0, 1, u32> unknown; // 1 delete?
BitField<1, 1, u32> read; // 2
BitField<2, 1, u32> write; // 4
BitField<3, 1, u32> create; // 8
BitField<4, 1, u32> check; // 16 maybe validate sig?
BitField<30, 1, u32> unk_flag;
};
enum class CecTest : u32 {
CEC_TEST_000 = 0,
CEC_TEST_001 = 1,
CEC_TEST_002 = 2,
CEC_TEST_003 = 3,
CEC_TEST_004 = 4,
CEC_TEST_005 = 5,
CEC_TEST_006 = 6,
};
/// Opening a file and reading/writing can be handled by two different functions
/// So, we need to pass that file data around
struct SessionData : public Kernel::SessionRequestHandler::SessionDataBase {
SessionData();
~SessionData();
u32 ncch_program_id;
CecDataPathType data_path_type;
CecOpenMode open_mode;
FileSys::Path path;
std::unique_ptr<FileSys::FileBackend> file;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<Kernel::SessionRequestHandler::SessionDataBase>(
*this);
ar& ncch_program_id;
ar& data_path_type;
ar& open_mode.raw;
ar& path;
ar& file;
}
friend class boost::serialization::access;
};
class Interface : public ServiceFramework<Interface, SessionData> {
public:
Interface(std::shared_ptr<Module> cecd, const char* name, u32 max_session);
~Interface() = default;
protected:
/**
* CECD::Open service function
* Inputs:
* 0 : Header Code[0x000100C2]
* 1 : NCCH Program ID
* 2 : Path type
* 3 : File open flag
* 4 : Descriptor for process ID
* 5 : Placeholder for process ID
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : File size?
*/
void Open(Kernel::HLERequestContext& ctx);
/**
* CECD::Read service function
* Inputs:
* 0 : Header Code[0x00020042]
* 1 : Buffer size (unused)
* 2 : Descriptor for mapping a write-only buffer in the target process
* 3 : Buffer address
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Read size
* 3 : Descriptor for mapping a write-only buffer in the target process
* 4 : Buffer address
*/
void Read(Kernel::HLERequestContext& ctx);
/**
* CECD::ReadMessage service function
* Inputs:
* 0 : Header Code[0x00030104]
* 1 : NCCH Program ID
* 2 : bool is_outbox
* 3 : Message ID size (unused, always 8)
* 4 : Buffer size (unused)
* 5 : Descriptor for mapping a read-only buffer in the target process
* 6 : Message ID address
* 7 : Descriptor for mapping a write-only buffer in the target process
* 8 : Buffer address
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Read size
* 3 : Descriptor for mapping a read-only buffer in the target process
* 4 : Message ID address
* 5 : Descriptor for mapping a write-only buffer in the target process
* 6 : Buffer address
*/
void ReadMessage(Kernel::HLERequestContext& ctx);
/**
* CECD::ReadMessageWithHMAC service function
* Inputs:
* 0 : Header Code[0x00040106]
* 1 : NCCH Program ID
* 2 : bool is_outbox
* 3 : Message ID size(unused, always 8)
* 4 : Buffer size(unused)
* 5 : Descriptor for mapping a read-only buffer in the target process
* 6 : Message ID address
* 7 : Descriptor for mapping a read-only buffer in the target process
* 8 : HMAC key address
* 9 : Descriptor for mapping a write-only buffer in the target process
* 10 : Buffer address
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Read size
* 3 : Descriptor for mapping a read-only buffer in the target process
* 4 : Message ID address
* 5 : Descriptor for mapping a read-only buffer in the target process
* 6 : HMAC key address
* 7 : Descriptor for mapping a write-only buffer in the target process
* 8 : Buffer address
*/
void ReadMessageWithHMAC(Kernel::HLERequestContext& ctx);
/**
* CECD::Write service function
* Inputs:
* 0 : Header Code[0x00050042]
* 1 : Buffer size(unused)
* 2 : Descriptor for mapping a read-only buffer in the target process
* 3 : Buffer address
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Descriptor for mapping a read-only buffer in the target process
* 3 : Buffer address
*/
void Write(Kernel::HLERequestContext& ctx);
/**
* CECD::WriteMessage service function
* Inputs:
* 0 : Header Code[0x00060104]
* 1 : NCCH Program ID
* 2 : bool is_outbox
* 3 : Message ID size(unused, always 8)
* 4 : Buffer size(unused)
* 5 : Descriptor for mapping a read-only buffer in the target process
* 6 : Buffer address
* 7 : Descriptor for mapping a read/write buffer in the target process
* 8 : Message ID address
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Descriptor for mapping a read-only buffer in the target process
* 3 : Buffer address
* 4 : Descriptor for mapping a read/write buffer in the target process
* 5 : Message ID address
*/
void WriteMessage(Kernel::HLERequestContext& ctx);
/**
* CECD::WriteMessageWithHMAC service function
* Inputs:
* 0 : Header Code[0x00070106]
* 1 : NCCH Program ID
* 2 : bool is_outbox
* 3 : Message ID size(unused, always 8)
* 4 : Buffer size(unused)
* 5 : Descriptor for mapping a read-only buffer in the target process
* 6 : Buffer address
* 7 : Descriptor for mapping a read-only buffer in the target process
* 8 : HMAC key address
* 9 : Descriptor for mapping a read/write buffer in the target process
* 10 : Message ID address
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Descriptor for mapping a read-only buffer in the target process
* 3 : Buffer address
* 4 : Descriptor for mapping a read-only buffer in the target process
* 5 : HMAC key address
* 6 : Descriptor for mapping a read/write buffer in the target process
* 7 : Message ID address
*/
void WriteMessageWithHMAC(Kernel::HLERequestContext& ctx);
/**
* CECD::Delete service function
* Inputs:
* 0 : Header Code[0x00080102]
* 1 : NCCH Program ID
* 2 : Path type
* 3 : bool is_outbox
* 4 : Message ID size (unused)
* 5 : Descriptor for mapping a read-only buffer in the target process
* 6 : Message ID address
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Descriptor for mapping a read-only buffer in the target process
* 3 : Message ID address
*/
void Delete(Kernel::HLERequestContext& ctx);
/**
* CECD::SetData service function
* Inputs:
* 0 : Header Code[0x000900C2]
* 1 : NCCH Program ID
* 2 : Path type
* 3 : bool is_outbox
* 4 : Descriptor for mapping a read-only buffer in the target process
* 5 : Message ID address
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Descriptor for mapping a read-only buffer in the target process
* 3 : Message ID address
*/
void SetData(Kernel::HLERequestContext& ctx);
/**
* CECD::ReadData service function
* Inputs:
* 0 : Header Code[0x000A00C4]
* 1 : Destination buffer size (unused)
* 2 : Info type
* 3 : Param buffer size (unused)
* 4 : Descriptor for mapping a read-only buffer in the target process
* 5 : Param buffer address
* 6 : Descriptor for mapping a write-only buffer in the target process
* 7 : Destination buffer address
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Descriptor for mapping a read-only buffer in the target process
* 3 : Param buffer address
* 4 : Descriptor for mapping a write-only buffer in the target process
* 5 : Destination buffer address
*/
void ReadData(Kernel::HLERequestContext& ctx);
/**
* CECD::Start service function
* Inputs:
* 0 : Header Code[0x000B0040]
* 1 : Command
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void Start(Kernel::HLERequestContext& ctx);
/**
* CECD::Stop service function
* Inputs:
* 0 : Header Code[0x000C0040]
* 1 : Command
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void Stop(Kernel::HLERequestContext& ctx);
/**
* CECD::GetCecInfoBuffer service function
* Inputs:
* 0 : Header Code[0x000D0082]
* 1 : unknown
* 2 : unknown, buffer size?
* 3 : Descriptor for mapping a write-only buffer in the target process
* 4 : Destination buffer address
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2-3 : MappedBuffer
*/
void GetCecInfoBuffer(Kernel::HLERequestContext& ctx);
/**
* GetCecdState service function
* Inputs:
* 0: Header Code[0x000E0000]
* Outputs:
* 1: Result
* 2: CecdState
*/
void GetCecdState(Kernel::HLERequestContext& ctx);
/**
* GetCecInfoEventHandle service function
* Inputs:
* 0: Header Code[0x000F0000]
* Outputs:
* 1: Result
* 3: Event Handle
*/
void GetCecInfoEventHandle(Kernel::HLERequestContext& ctx);
/**
* GetChangeStateEventHandle service function
* Inputs:
* 0: Header Code[0x00100000]
* Outputs:
* 1: Result
* 3: Event Handle
*/
void GetChangeStateEventHandle(Kernel::HLERequestContext& ctx);
/**
* CECD::OpenAndWrite service function
* Inputs:
* 0 : Header Code[0x00110104]
* 1 : Buffer size (unused)
* 2 : NCCH Program ID
* 3 : Path type
* 4 : File open flag
* 5 : Descriptor for process ID
* 6 : Placeholder for process ID
* 7 : Descriptor for mapping a read-only buffer in the target process
* 8 : Buffer address
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Descriptor for mapping a read-only buffer in the target process
* 3 : Buffer address
*/
void OpenAndWrite(Kernel::HLERequestContext& ctx);
/**
* CECD::OpenAndRead service function
* Inputs:
* 0 : Header Code[0x00120104]
* 1 : Buffer size (unused)
* 2 : NCCH Program ID
* 3 : Path type
* 4 : File open flag
* 5 : Descriptor for process ID
* 6 : Placeholder for process ID
* 7 : Descriptor for mapping a write-only buffer in the target process
* 8 : Buffer address
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Toal bytes read
* 3 : Descriptor for mapping a write-only buffer in the target process
* 4 : Buffer address
*/
void OpenAndRead(Kernel::HLERequestContext& ctx);
/**
* CECD::GetEventLog service function
* Inputs:
* 0 : Header Code[0x001E0082]
* 1 : unknown
* 2 : unknown
* 3 : buffer descriptor
* 4 : buffer address
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : unknown
*/
void GetEventLog(Kernel::HLERequestContext& ctx);
/**
* CECD::GetEventLogStart service function
* Inputs:
* 0 : Header Code[0x001F0000]
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : unknown
*/
void GetEventLogStart(Kernel::HLERequestContext& ctx);
/**
* GetCecInfoEventHandleSys service function
* Inputs:
* 0: Header Code[0x40020002]
* Outputs:
* 1: Result
* 3: Event Handle
*/
void GetCecInfoEventHandleSys(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> cecd;
};
private:
/// String used by cecd for base64 encoding found in the sysmodule disassembly
const std::string base64_dict =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
const std::vector<u8> cecd_system_savedata_id = {0x00, 0x00, 0x00, 0x00,
0x26, 0x00, 0x01, 0x00};
/// Encoding function used for the message id
std::string EncodeBase64(std::span<const u8> in) const;
std::string GetCecDataPathTypeAsString(
const CecDataPathType type, const u32 program_id,
std::span<const u8> msg_id = std::span<const u8>{}) const;
std::string GetCecCommandAsString(const CecCommand command) const;
void CheckAndUpdateFile(const CecDataPathType path_type, const u32 ncch_program_id,
std::vector<u8>& file_buffer);
std::unique_ptr<FileSys::ArchiveBackend> cecd_system_save_data_archive;
std::shared_ptr<Kernel::Event> cecinfo_event;
std::shared_ptr<Kernel::Event> cecinfosys_event;
std::shared_ptr<Kernel::Event> change_state_event;
Core::System& system;
template <class Archive>
void serialize(Archive& ar, const unsigned int);
friend class boost::serialization::access;
};
/// Initialize CECD service(s)
void InstallInterfaces(Core::System& system);
} // namespace Service::CECD
SERVICE_CONSTRUCT(Service::CECD::Module)
BOOST_CLASS_EXPORT_KEY(Service::CECD::Module)
BOOST_CLASS_EXPORT_KEY(Service::CECD::Module::SessionData)

View File

@@ -0,0 +1,26 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/cecd/cecd_ndm.h"
SERIALIZE_EXPORT_IMPL(Service::CECD::CECD_NDM)
namespace Service::CECD {
CECD_NDM::CECD_NDM(std::shared_ptr<Module> cecd)
: Module::Interface(std::move(cecd), "cecd:ndm", DefaultMaxSessions) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, nullptr, "Initialize"},
{0x0002, nullptr, "Deinitialize"},
{0x0003, nullptr, "ResumeDaemon"},
{0x0004, nullptr, "SuspendDaemon"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::CECD

View File

@@ -0,0 +1,22 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/cecd/cecd.h"
namespace Service::CECD {
class CECD_NDM final : public Module::Interface {
public:
explicit CECD_NDM(std::shared_ptr<Module> cecd);
private:
SERVICE_SERIALIZATION(CECD_NDM, cecd, Module)
};
} // namespace Service::CECD
BOOST_CLASS_EXPORT_KEY(Service::CECD::CECD_NDM)
BOOST_SERIALIZATION_CONSTRUCT(Service::CECD::CECD_NDM)

View File

@@ -0,0 +1,45 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/cecd/cecd_s.h"
SERIALIZE_EXPORT_IMPL(Service::CECD::CECD_S)
namespace Service::CECD {
CECD_S::CECD_S(std::shared_ptr<Module> cecd)
: Module::Interface(std::move(cecd), "cecd:s", DefaultMaxSessions) {
static const FunctionInfo functions[] = {
// cecd:u shared commands
// clang-format off
{0x0001, &CECD_S::Open, "Open"},
{0x0002, &CECD_S::Read, "Read"},
{0x0003, &CECD_S::ReadMessage, "ReadMessage"},
{0x0004, &CECD_S::ReadMessageWithHMAC, "ReadMessageWithHMAC"},
{0x0005, &CECD_S::Write, "Write"},
{0x0006, &CECD_S::WriteMessage, "WriteMessage"},
{0x0007, &CECD_S::WriteMessageWithHMAC, "WriteMessageWithHMAC"},
{0x0008, &CECD_S::Delete, "Delete"},
{0x0009, &CECD_S::SetData, "SetData"},
{0x000A, &CECD_S::ReadData, "ReadData"},
{0x000B, &CECD_S::Start, "Start"},
{0x000C, &CECD_S::Stop, "Stop"},
{0x000D, &CECD_S::GetCecInfoBuffer, "GetCecInfoBuffer"},
{0x000E, &CECD_S::GetCecdState, "GetCecdState"},
{0x000F, &CECD_S::GetCecInfoEventHandle, "GetCecInfoEventHandle"},
{0x0010, &CECD_S::GetChangeStateEventHandle, "GetChangeStateEventHandle"},
{0x0011, &CECD_S::OpenAndWrite, "OpenAndWrite"},
{0x0012, &CECD_S::OpenAndRead, "OpenAndRead"},
{0x001E, nullptr, "GetEventLog"},
{0x001F, nullptr, "GetEventLogStart"},
// cecd:s commands
{0x0402, &CECD_S::GetCecInfoEventHandleSys, "GetCecInfoEventHandleSys"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::CECD

View File

@@ -0,0 +1,22 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/cecd/cecd.h"
namespace Service::CECD {
class CECD_S final : public Module::Interface {
public:
explicit CECD_S(std::shared_ptr<Module> cecd);
private:
SERVICE_SERIALIZATION(CECD_S, cecd, Module)
};
} // namespace Service::CECD
BOOST_CLASS_EXPORT_KEY(Service::CECD::CECD_S)
BOOST_SERIALIZATION_CONSTRUCT(Service::CECD::CECD_S)

View File

@@ -0,0 +1,43 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/cecd/cecd_u.h"
SERIALIZE_EXPORT_IMPL(Service::CECD::CECD_U)
namespace Service::CECD {
CECD_U::CECD_U(std::shared_ptr<Module> cecd)
: Module::Interface(std::move(cecd), "cecd:u", DefaultMaxSessions) {
static const FunctionInfo functions[] = {
// cecd:u shared commands
// clang-format off
{0x0001, &CECD_U::Open, "Open"},
{0x0002, &CECD_U::Read, "Read"},
{0x0003, &CECD_U::ReadMessage, "ReadMessage"},
{0x0004, &CECD_U::ReadMessageWithHMAC, "ReadMessageWithHMAC"},
{0x0005, &CECD_U::Write, "Write"},
{0x0006, &CECD_U::WriteMessage, "WriteMessage"},
{0x0007, &CECD_U::WriteMessageWithHMAC, "WriteMessageWithHMAC"},
{0x0008, &CECD_U::Delete, "Delete"},
{0x0009, &CECD_U::SetData, "SetData"},
{0x000A, &CECD_U::ReadData, "ReadData"},
{0x000B, &CECD_U::Start, "Start"},
{0x000C, &CECD_U::Stop, "Stop"},
{0x000D, &CECD_U::GetCecInfoBuffer, "GetCecInfoBuffer"},
{0x000E, &CECD_U::GetCecdState, "GetCecdState"},
{0x000F, &CECD_U::GetCecInfoEventHandle, "GetCecInfoEventHandle"},
{0x0010, &CECD_U::GetChangeStateEventHandle, "GetChangeStateEventHandle"},
{0x0011, &CECD_U::OpenAndWrite, "OpenAndWrite"},
{0x0012, &CECD_U::OpenAndRead, "OpenAndRead"},
{0x001E, nullptr, "GetEventLog"},
{0x001F, nullptr, "GetEventLogStart"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::CECD

View File

@@ -0,0 +1,22 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/cecd/cecd.h"
namespace Service::CECD {
class CECD_U final : public Module::Interface {
public:
explicit CECD_U(std::shared_ptr<Module> cecd);
private:
SERVICE_SERIALIZATION(CECD_U, cecd, Module)
};
} // namespace Service::CECD
BOOST_CLASS_EXPORT_KEY(Service::CECD::CECD_U)
BOOST_SERIALIZATION_CONSTRUCT(Service::CECD::CECD_U)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,705 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <memory>
#include <span>
#include <string>
#include <utility>
#include "common/common_types.h"
#include "core/hle/service/service.h"
#include "network/artic_base/artic_base_client.h"
namespace FileSys {
class ArchiveBackend;
}
namespace Core {
class System;
}
namespace Service::CFG {
enum ConfigBlockID {
ConfigSavegameVersionBlockID = 0x00000000, // Maybe?
RtcCompensationBlockID = 0x00010000,
AudioCalibrationBlockID = 0x00020000,
LeapYearCounterBlockID = 0x00030000,
UserTimeOffsetBlockID = 0x00030001,
SettingsTimeOffsetBlockID = 0x00030002,
TouchCalibrationBlockID = 0x00040000,
AnalogStickCalibrationBlockID = 0x00040001, // Maybe?
GyroscopeCalibrationBlockID = 0x00040002,
AccelerometerCalibrationBlockID = 0x00040003,
CStickCalibrationBlockID = 0x00040004,
ScreenFlickerCalibrationBlockID = 0x00050000,
BacklightControlsBlockID = 0x00050001,
BacklightPwmCalibrationBlockID = 0x00050002,
PowerSavingModeCalibrationBlockID = 0x00050003,
PowerSavingModeCalibrationLegacyBlockID = 0x00050004,
StereoCameraSettingsBlockID = 0x00050005,
_3dSwitchingDelayBlockID = 0x00050006,
Unknown_0x00050007 = 0x00050007,
PowerSavingModeExtraConfigBlockID = 0x00050008,
BacklightControlNew3dsBlockID = 0x00050009,
Unknown_0x00060000 = 0x00060000,
_3dFiltersBlockID = 0x00070000,
SoundOutputModeBlockID = 0x00070001,
MicrophoneEchoCancellationBlockID = 0x00070002,
WifiConfigurationSlot0BlockID = 0x00080000,
WifiConfigurationSlot1BlockID = 0x00080001,
WifiConfigurationSlot2BlockID = 0x00080002,
ConsoleUniqueID1BlockID = 0x00090000,
ConsoleUniqueID2BlockID = 0x00090001,
ConsoleUniqueID3BlockID = 0x00090002,
UsernameBlockID = 0x000A0000,
BirthdayBlockID = 0x000A0001,
LanguageBlockID = 0x000A0002,
CountryInfoBlockID = 0x000B0000,
CountryNameBlockID = 0x000B0001,
StateNameBlockID = 0x000B0002,
LatitudeLongitudeBlockID = 0x000B0003,
RestrictedPhotoExchangeBlockID = 0x000C0000,
CoppacsRestrictionBlockID = 0x000C0001,
ParentalRestrictionEmailBlockID = 0x000C0002,
EULAVersionBlockID = 0x000D0000,
Unknown_0x000E0000 = 0x000E0000,
DebugConfigurationBlockID = 0x000F0000,
Unknown_0x000F0001 = 0x000F0001,
Unknown_0x000F0003 = 0x000F0003,
ConsoleModelBlockID = 0x000F0004,
NetworkUpdatesEnabledBlockID = 0x000F0005,
XDeviceTokenBlockID = 0x000F0006,
TwlEulaInfoBlockID = 0x00100000,
TwlParentalRestrictionsBlockID = 0x00100001,
TwlCountryCodeBlockID = 0x00100002,
TwlMovableUniqueBlockIDBlockID = 0x00100003,
SystemSetupRequiredBlockID = 0x00110000,
LaunchMenuBlockID = 0x00110001,
VolumeSliderBoundsBlockID = 0x00120000,
DebugModeBlockID = 0x00130000,
ClockSequenceBlockID = 0x00150000,
Unknown_0x00150001 = 0x00150001,
ServerType = 0x00150002,
Unknown_0x00160000 = 0x00160000,
MiiverseAccessKeyBlockID = 0x00170000,
QtmInfraredLedRelatedBlockID = 0x00180000,
QtmCalibrationDataBlockID = 0x00180001,
Unknown_0x00190000 = 0x00190000,
};
enum SystemModel {
NINTENDO_3DS = 0,
NINTENDO_3DS_XL = 1,
NEW_NINTENDO_3DS = 2,
NINTENDO_2DS = 3,
NEW_NINTENDO_3DS_XL = 4,
NEW_NINTENDO_2DS_XL = 5
};
enum SystemLanguage {
LANGUAGE_JP = 0,
LANGUAGE_EN = 1,
LANGUAGE_FR = 2,
LANGUAGE_DE = 3,
LANGUAGE_IT = 4,
LANGUAGE_ES = 5,
LANGUAGE_ZH = 6,
LANGUAGE_KO = 7,
LANGUAGE_NL = 8,
LANGUAGE_PT = 9,
LANGUAGE_RU = 10,
LANGUAGE_TW = 11
};
enum SoundOutputMode { SOUND_MONO = 0, SOUND_STEREO = 1, SOUND_SURROUND = 2 };
struct EULAVersion {
u8 minor;
u8 major;
INSERT_PADDING_BYTES(2);
};
static_assert(sizeof(EULAVersion) == 4, "EULAVersion must be exactly 0x4 bytes");
struct UsernameBlock {
/// Exactly 20 bytes long, padded with zeros at the end if necessary
std::array<char16_t, 10> username;
u32 zero;
u32 ng_word;
};
static_assert(sizeof(UsernameBlock) == 0x1C, "UsernameBlock must be exactly 0x1C bytes");
struct BirthdayBlock {
u8 month; ///< The month of the birthday
u8 day; ///< The day of the birthday
};
static_assert(sizeof(BirthdayBlock) == 2, "BirthdayBlock must be exactly 2 bytes");
struct ConsoleModelInfo {
u8 model; ///< The console model (3DS, 2DS, etc)
std::array<u8, 3> unknown; ///< Unknown data
};
static_assert(sizeof(ConsoleModelInfo) == 4, "ConsoleModelInfo must be exactly 4 bytes");
struct ConsoleCountryInfo {
std::array<u8, 2> unknown; ///< Unknown data
u8 state_code; ///< The state or province code.
u8 country_code; ///< The country code of the console
};
static_assert(sizeof(ConsoleCountryInfo) == 4, "ConsoleCountryInfo must be exactly 4 bytes");
struct BacklightControls {
u8 power_saving_enabled; ///< Whether power saving mode is enabled.
u8 brightness_level; ///< The configured brightness level.
};
static_assert(sizeof(BacklightControls) == 2, "BacklightControls must be exactly 2 bytes");
struct New3dsBacklightControls {
std::array<u8, 4> unknown_1; ///< Unknown data
u8 auto_brightness_enabled; ///< Whether auto brightness is enabled.
std::array<u8, 3> unknown_2; ///< Unknown data
};
static_assert(sizeof(New3dsBacklightControls) == 8,
"New3dsBacklightControls must be exactly 8 bytes");
/// Access control flags for config blocks
enum class AccessFlag : u16 {
UserRead = 1 << 1, ///< Readable by user applications (cfg:u)
SystemWrite = 1 << 2, ///< Writable by system applications and services (cfg:s / cfg:i)
SystemRead = 1 << 3, ///< Readable by system applications and services (cfg:s / cfg:i)
// Commonly used combinations for convenience
System = SystemRead | SystemWrite,
Global = UserRead | SystemRead | SystemWrite,
};
DECLARE_ENUM_FLAG_OPERATORS(AccessFlag);
struct SecureInfoA {
std::array<u8, 0x100> signature;
u8 region;
u8 unknown;
std::array<u8, 0xF> serial_number;
};
static_assert(sizeof(SecureInfoA) == 0x111);
struct LocalFriendCodeSeedB {
std::array<u8, 0x100> signature;
u64 unknown;
u64 friend_code_seed;
};
static_assert(sizeof(LocalFriendCodeSeedB) == 0x110);
enum class SecureDataLoadStatus {
Loaded,
NotFound,
Invalid,
IOError,
};
class Module final {
public:
Module(Core::System& system_);
~Module();
class Interface : public ServiceFramework<Interface> {
public:
Interface(std::shared_ptr<Module> cfg, const char* name, u32 max_session);
~Interface();
std::shared_ptr<Module> GetModule() const;
void UseArticClient(std::shared_ptr<Network::ArticBase::Client>& client) {
GetModule()->artic_client = client;
}
/**
* CFG::GetCountryCodeString service function
* Inputs:
* 1 : Country Code ID
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Country's 2-char string
*/
void GetCountryCodeString(Kernel::HLERequestContext& ctx);
/**
* CFG::GetCountryCodeID service function
* Inputs:
* 1 : Country Code 2-char string
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Country Code ID
*/
void GetCountryCodeID(Kernel::HLERequestContext& ctx);
/**
* CFG::GetRegion service function
* Inputs:
* 1 : None
* Outputs:
* 0 : Result Header code
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Region value loaded from SecureInfo offset 0x100
*/
void GetRegion(Kernel::HLERequestContext& ctx);
/**
* CFG::SecureInfoGetByte101 service function
* Inputs:
* 1 : None
* Outputs:
* 0 : Result Header code
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Value loaded from SecureInfo offset 0x101
*/
void SecureInfoGetByte101(Kernel::HLERequestContext& ctx);
/**
* CFG::SecureInfoGetSerialNo service function
* Inputs:
* 1 : Buffer Size
* 2-3: Output mapped buffer
* Outputs:
* 0 : Result Header code
* 1 : Result of function, 0 on success, otherwise error code
* 2-3 : Output mapped buffer
*/
void SecureInfoGetSerialNo(Kernel::HLERequestContext& ctx);
/**
* CFG::SetUUIDClockSequence service function
* Inputs:
* 1 : UUID Clock Sequence
* Outputs:
* 0 : Result Header code
* 1 : Result of function, 0 on success, otherwise error code
*/
void SetUUIDClockSequence(Kernel::HLERequestContext& ctx);
/**
* CFG::GetUUIDClockSequence service function
* Inputs:
* 1 : None
* Outputs:
* 0 : Result Header code
* 1 : Result of function, 0 on success, otherwise error code
* 2 : UUID Clock Sequence
*/
void GetUUIDClockSequence(Kernel::HLERequestContext& ctx);
/**
* CFG::GetTransferableId service function
* Inputs:
* 1 : 20 bit application ID salt
* Outputs:
* 0 : Result Header code
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Hash/"ID" lower word
* 3 : Hash/"ID" upper word
*/
void GetTransferableId(Kernel::HLERequestContext& ctx);
/**
* CFG::IsCoppacsSupported service function
* Inputs:
* 1 : None
* Outputs:
* 0 : Result Header code
* 1 : Result of function, 0 on success, otherwise error code
* 2 : 1 if the system is a Canada or USA model, 0 otherwise
*/
void IsCoppacsSupported(Kernel::HLERequestContext& ctx);
/**
* CFG::GetSystemModel service function
* Inputs:
* 0 : 0x00050000
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Model of the console
*/
void GetSystemModel(Kernel::HLERequestContext& ctx);
/**
* CFG::GetModelNintendo2DS service function
* Inputs:
* 0 : 0x00060000
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : 0 if the system is a Nintendo 2DS, 1 otherwise
*/
void GetModelNintendo2DS(Kernel::HLERequestContext& ctx);
/**
* CFG::GetConfig service function
* Inputs:
* 0 : 0x00010082
* 1 : Size
* 2 : Block ID
* 3 : Descriptor for the output buffer
* 4 : Output buffer pointer
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void GetConfig(Kernel::HLERequestContext& ctx);
/**
* CFG::GetSystemConfig service function
* Inputs:
* 0 : 0x04010082 / 0x08010082
* 1 : Size
* 2 : Block ID
* 3 : Descriptor for the output buffer
* 4 : Output buffer pointer
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void GetSystemConfig(Kernel::HLERequestContext& ctx);
/**
* CFG::SetSystemConfig service function
* Inputs:
* 0 : 0x04020082 / 0x08020082
* 1 : Block ID
* 2 : Size
* 3 : Descriptor for the output buffer
* 4 : Output buffer pointer
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* Note:
* The parameters order is different from GetConfig/GetSystemConfig's,
* where Block ID and Size are switched.
*/
void SetSystemConfig(Kernel::HLERequestContext& ctx);
/**
* CFG::UpdateConfigNANDSavegame service function
* Inputs:
* 0 : 0x04030000 / 0x08030000
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void UpdateConfigNANDSavegame(Kernel::HLERequestContext& ctx);
/**
* CFG::GetLocalFriendCodeSeedData service function
* Inputs:
* 1 : Buffer Size
* 2-3: Output mapped buffer
* Outputs:
* 0 : Result Header code
* 1 : Result of function, 0 on success, otherwise error code
*/
void GetLocalFriendCodeSeedData(Kernel::HLERequestContext& ctx);
/**
* CFG::GetLocalFriendCodeSeed service function
* Inputs:
* Outputs:
* 0 : Result Header code
* 1 : Result of function, 0 on success, otherwise error code
* 2-3 : Friend code seed
*/
void GetLocalFriendCodeSeed(Kernel::HLERequestContext& ctx);
/**
* CFG::FormatConfig service function
* Inputs:
* 0 : 0x08060000
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void FormatConfig(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> cfg;
};
private:
// Represents save data that would normally be stored in the MCU
// on real hardware. Try to keep this struct backwards compatible
// if a new version is needed to prevent data loss.
struct MCUData {
struct Header {
static constexpr u32 MAGIC_VALUE = 0x4455434D;
static constexpr u32 VERSION_VALUE = 1;
u32 magic = MAGIC_VALUE;
u32 version = VERSION_VALUE;
u64 reserved = 0;
};
Header header;
u32 clock_sequence = 0;
[[nodiscard]] bool IsValid() const {
return header.magic == Header::MAGIC_VALUE && header.version == Header::VERSION_VALUE;
}
};
ResultVal<void*> GetConfigBlockPointer(u32 block_id, u32 size, AccessFlag accesss_flag);
/**
* Reads a block with the specified id and flag from the Config savegame buffer
* and writes the output to output. The input size must match exactly the size of the requested
* block.
*
* @param block_id The id of the block we want to read
* @param size The size of the block we want to read
* @param accesss_flag The requested block must have this access flag set
* @param output A pointer where we will write the read data
* @returns Result indicating the result of the operation, 0 on success
*/
Result GetConfigBlock(u32 block_id, u32 size, AccessFlag accesss_flag, void* output);
/**
* Reads data from input and writes to a block with the specified id and flag
* in the Config savegame buffer. The input size must match exactly the size of the target
* block.
*
* @param block_id The id of the block we want to write
* @param size The size of the block we want to write
* @param accesss_flag The target block must have this access flag set
* @param input A pointer where we will read data and write to Config savegame buffer
* @returns Result indicating the result of the operation, 0 on success
*/
Result SetConfigBlock(u32 block_id, u32 size, AccessFlag accesss_flag, const void* input);
/**
* Creates a block with the specified id and writes the input data to the cfg savegame buffer in
* memory. The config savegame file in the filesystem is not updated.
*
* @param block_id The id of the block we want to create
* @param size The size of the block we want to create
* @param accesss_flags The access flags of the new block
* @param data A pointer containing the data we will write to the new block
* @returns Result indicating the result of the operation, 0 on success
*/
Result CreateConfigBlock(u32 block_id, u16 size, AccessFlag accesss_flags, const void* data);
/**
* Deletes the config savegame file from the filesystem, the buffer in memory is not affected
* @returns Result indicating the result of the operation, 0 on success
*/
Result DeleteConfigNANDSaveFile();
/**
* Re-creates the config savegame file in memory and the filesystem with the default blocks
* @returns Result indicating the result of the operation, 0 on success
*/
Result FormatConfig();
/**
* Open the config savegame file and load it to the memory buffer
* @returns Result indicating the result of the operation, 0 on success
*/
Result LoadConfigNANDSaveFile();
/**
* Loads MCU specific data
*/
void LoadMCUConfig();
public:
u32 GetRegionValue();
// Utilities for frontend to set config data.
// Note: UpdateConfigNANDSavegame should be called after making changes to config data.
/**
* Sets the username in config savegame.
* @param name the username to set. The maximum size is 10 in char16_t.
*/
void SetUsername(const std::u16string& name);
/**
* Gets the username from config savegame.
* @returns the username
*/
std::u16string GetUsername();
/**
* Sets the profile birthday in config savegame.
* @param month the month of birthday.
* @param day the day of the birthday.
*/
void SetBirthday(u8 month, u8 day);
/**
* Gets the profile birthday from the config savegame.
* @returns a tuple of (month, day) of birthday
*/
std::tuple<u8, u8> GetBirthday();
/**
* Sets the system language in config savegame.
* @param language the system language to set.
*/
void SetSystemLanguage(SystemLanguage language);
/**
* Gets the system language from config savegame.
* @returns the system language
*/
SystemLanguage GetSystemLanguage();
/**
* Sets the sound output mode in config savegame.
* @param mode the sound output mode to set
*/
void SetSoundOutputMode(SoundOutputMode mode);
/**
* Gets the sound output mode from config savegame.
* @returns the sound output mode
*/
SoundOutputMode GetSoundOutputMode();
/**
* Sets the country code in config savegame.
* The state code is set to a default value.
* @param country_code the country code to set
*/
void SetCountryCode(u8 country_code);
/**
* Gets the country code from config savegame.
* @returns the country code
*/
u8 GetCountryCode();
/**
* Sets the country and state codes in config savegame.
* @param country_code the country code to set
* @param state_code the state code to set
*/
void SetCountryInfo(u8 country_code, u8 state_code);
/**
* Gets the state code from config savegame.
* @returns the state code
*/
u8 GetStateCode();
/**
* Generates a new random console unique id.
*
* @returns A pair containing a random number and a random console ID.
*
* @note The random number is a random generated 16bit number stored at 0x90002, used for
* generating the console ID.
*/
std::pair<u32, u64> GenerateConsoleUniqueId() const;
/**
* Sets the random_number and the console unique id in the config savegame.
* @param random_number the random_number to set
* @param console_id the console id to set
*/
Result SetConsoleUniqueId(u32 random_number, u64 console_id);
/**
* Gets the console unique id from config savegame.
* @returns the console unique id
*/
u64 GetConsoleUniqueId();
/**
* Sets the accepted EULA version in the config savegame.
* @param version the version to set
*/
void SetEULAVersion(const EULAVersion& version);
/**
* Gets the accepted EULA version from config savegame.
* @returns the EULA version
*/
EULAVersion GetEULAVersion();
/**
* Sets whether system initial setup is needed in config savegame.
* @param setup_needed Whether system initial setup is needed.
*/
void SetSystemSetupNeeded(bool setup_needed);
/**
* Gets whether system initial setup is needed from config savegame.
* @returns Whether system initial setup is needed.
*/
bool IsSystemSetupNeeded();
/**
* Writes the config savegame memory buffer to the config savegame file in the filesystem
* @returns Result indicating the result of the operation, 0 on success
*/
Result UpdateConfigNANDSavegame();
/**
* Invalidates the loaded secure data so that it is loaded again.
*/
void InvalidateSecureData();
/**
* Loads the LocalFriendCodeSeed_B file from NAND.
* @returns LocalFriendCodeSeedBLoadStatus indicating the file load status.
*/
SecureDataLoadStatus LoadSecureInfoAFile();
/**
* Loads the LocalFriendCodeSeed_B file from NAND.
* @returns LocalFriendCodeSeedBLoadStatus indicating the file load status.
*/
SecureDataLoadStatus LoadLocalFriendCodeSeedBFile();
/**
* Gets the SecureInfo_A path in the host filesystem
* @returns std::string SecureInfo_A path in the host filesystem
*/
std::string GetSecureInfoAPath();
/**
* Gets the LocalFriendCodeSeed_B path in the host filesystem
* @returns std::string LocalFriendCodeSeed_B path in the host filesystem
*/
std::string GetLocalFriendCodeSeedBPath();
/**
* Saves MCU specific data
*/
void SaveMCUConfig();
private:
void UpdatePreferredRegionCode();
SystemLanguage GetRawSystemLanguage();
Core::System& system;
static constexpr u32 CONFIG_SAVEFILE_SIZE = 0x8000;
std::array<u8, CONFIG_SAVEFILE_SIZE> cfg_config_file_buffer;
std::unique_ptr<FileSys::ArchiveBackend> cfg_system_save_data_archive;
u32 preferred_region_code = 0;
bool secure_info_a_loaded = false;
SecureInfoA secure_info_a;
bool local_friend_code_seed_b_loaded = false;
LocalFriendCodeSeedB local_friend_code_seed_b;
bool preferred_region_chosen = false;
MCUData mcu_data{};
std::shared_ptr<Network::ArticBase::Client> artic_client = nullptr;
template <class Archive>
void serialize(Archive& ar, const unsigned int);
friend class boost::serialization::access;
};
std::shared_ptr<Module> GetModule(Core::System& system);
void InstallInterfaces(Core::System& system);
/// Convenience function for getting a SHA256 hash of the Console ID
std::string GetConsoleIdHash(Core::System& system);
} // namespace Service::CFG
SERVICE_CONSTRUCT(Service::CFG::Module)
BOOST_CLASS_EXPORT_KEY(Service::CFG::Module)

View File

@@ -0,0 +1,138 @@
// Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/string_util.h"
#include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/cfg/cfg_defaults.h"
namespace Service::CFG {
constexpr std::size_t NUM_LOCATION_NAMES = 0x10;
constexpr std::size_t LOCATION_NAME_SIZE = 0x40;
constexpr auto MakeLocationNames(std::u16string_view input) {
std::array<std::array<u16_le, LOCATION_NAME_SIZE>, NUM_LOCATION_NAMES> out{};
for (std::size_t i = 0; i < NUM_LOCATION_NAMES; ++i) {
std::copy(input.cbegin(), input.cend(), out[i].data());
}
return out;
}
constexpr u8 UNITED_STATES_COUNTRY_ID = 49;
constexpr u8 WASHINGTON_DC_STATE_ID = 2;
constexpr u64_le DEFAULT_USER_TIME_OFFSET = 0;
constexpr BacklightControls DEFAULT_BACKLIGHT_CONTROLS{0, 2};
/**
* TODO(Subv): Find out what this actually is, these values fix some NaN uniforms in some games,
* for example Nintendo Zone
* Thanks Normmatt for providing this information
*/
constexpr std::array<float, 8> DEFAULT_STEREO_CAMERA_SETTINGS = {
62.0f, 289.0f, 76.80000305175781f, 46.08000183105469f,
10.0f, 5.0f, 55.58000183105469f, 21.56999969482422f,
};
constexpr New3dsBacklightControls DEFAULT_NEW_3DS_BACKLIGHT_CONTROLS{{0, 0, 0, 0}, 0, {0, 0, 0}};
constexpr u8 DEFAULT_SOUND_OUTPUT_MODE = SOUND_STEREO;
// NOTE: These two are placeholders. They are randomly generated elsewhere, rather than using fixed
// constants.
constexpr u64_le DEFAULT_CONSOLE_ID = 0;
constexpr u32_le DEFAULT_CONSOLE_RANDOM_NUMBER = 0;
constexpr UsernameBlock DEFAULT_USERNAME{{u"CITRA"}, 0, 0};
constexpr BirthdayBlock DEFAULT_BIRTHDAY{3, 25}; // March 25th, 2014
constexpr u8 DEFAULT_LANGUAGE = LANGUAGE_EN;
constexpr ConsoleCountryInfo DEFAULT_COUNTRY_INFO{
{0, 0}, WASHINGTON_DC_STATE_ID, UNITED_STATES_COUNTRY_ID};
constexpr std::array<std::array<u16_le, LOCATION_NAME_SIZE>, NUM_LOCATION_NAMES>
DEFAULT_COUNTRY_NAMES = MakeLocationNames(u"United States");
constexpr std::array<std::array<u16_le, LOCATION_NAME_SIZE>, NUM_LOCATION_NAMES>
DEFAULT_STATE_NAMES = MakeLocationNames(u"Washington DC");
constexpr u32_le DEFAULT_LATITUDE_LONGITUDE = 0;
constexpr std::array<u8, 0xC0> DEFAULT_RESTRICTED_PHOTO_EXCHANGE_SETTINGS = {};
constexpr std::array<u8, 0x14> DEFAULT_COPPACS_RESTRICTION_SETTINGS = {};
constexpr std::array<u8, 0x200> DEFAULT_PARENTAL_RESTRICTION_EMAIL = {};
constexpr EULAVersion DEFAULT_EULA_VERSION{0x7F, 0x7F};
constexpr u8 DEFAULT_0x000E0000_DATA = 0;
constexpr ConsoleModelInfo DEFAULT_CONSOLE_MODEL{NEW_NINTENDO_3DS_XL, {0, 0, 0}};
constexpr std::array<u8, 0x28> DEFAULT_X_DEVICE_TOKEN = {};
constexpr u32_le DEFAULT_SYSTEM_SETUP_REQUIRED_FLAG = 1;
constexpr u32_le DEFAULT_DEBUG_MODE_FLAG = 0;
constexpr u32_le DEFAULT_CLOCK_SEQUENCE = 0;
constexpr const char DEFAULT_SERVER_TYPE[4] = {'L', '1', '\0', '\0'};
constexpr u32_le DEFAULT_0x00160000_DATA = 0;
constexpr u32_le DEFAULT_MIIVERSE_ACCESS_KEY = 0;
static const std::unordered_map<ConfigBlockID, ConfigBlockDefaults> DEFAULT_CONFIG_BLOCKS = {
{UserTimeOffsetBlockID,
{AccessFlag::Global, &DEFAULT_USER_TIME_OFFSET, sizeof(DEFAULT_USER_TIME_OFFSET)}},
{BacklightControlsBlockID,
{AccessFlag::System, &DEFAULT_BACKLIGHT_CONTROLS, sizeof(DEFAULT_BACKLIGHT_CONTROLS)}},
{StereoCameraSettingsBlockID,
{AccessFlag::Global, &DEFAULT_STEREO_CAMERA_SETTINGS, sizeof(DEFAULT_STEREO_CAMERA_SETTINGS)}},
{BacklightControlNew3dsBlockID,
{AccessFlag::System, &DEFAULT_NEW_3DS_BACKLIGHT_CONTROLS,
sizeof(DEFAULT_NEW_3DS_BACKLIGHT_CONTROLS)}},
{SoundOutputModeBlockID,
{AccessFlag::Global, &DEFAULT_SOUND_OUTPUT_MODE, sizeof(DEFAULT_SOUND_OUTPUT_MODE)}},
{ConsoleUniqueID1BlockID,
{AccessFlag::Global, &DEFAULT_CONSOLE_ID, sizeof(DEFAULT_CONSOLE_ID)}},
{ConsoleUniqueID2BlockID,
{AccessFlag::Global, &DEFAULT_CONSOLE_ID, sizeof(DEFAULT_CONSOLE_ID)}},
{ConsoleUniqueID3BlockID,
{AccessFlag::Global, &DEFAULT_CONSOLE_RANDOM_NUMBER, sizeof(DEFAULT_CONSOLE_RANDOM_NUMBER)}},
{UsernameBlockID, {AccessFlag::Global, &DEFAULT_USERNAME, sizeof(DEFAULT_USERNAME)}},
{BirthdayBlockID, {AccessFlag::Global, &DEFAULT_BIRTHDAY, sizeof(DEFAULT_BIRTHDAY)}},
{LanguageBlockID, {AccessFlag::Global, &DEFAULT_LANGUAGE, sizeof(DEFAULT_LANGUAGE)}},
{CountryInfoBlockID, {AccessFlag::Global, &DEFAULT_COUNTRY_INFO, sizeof(DEFAULT_COUNTRY_INFO)}},
{CountryNameBlockID,
{AccessFlag::Global, &DEFAULT_COUNTRY_NAMES, sizeof(DEFAULT_COUNTRY_NAMES)}},
{StateNameBlockID, {AccessFlag::Global, &DEFAULT_STATE_NAMES, sizeof(DEFAULT_STATE_NAMES)}},
{LatitudeLongitudeBlockID,
{AccessFlag::Global, &DEFAULT_LATITUDE_LONGITUDE, sizeof(DEFAULT_LATITUDE_LONGITUDE)}},
{RestrictedPhotoExchangeBlockID,
{AccessFlag::Global, &DEFAULT_RESTRICTED_PHOTO_EXCHANGE_SETTINGS,
sizeof(DEFAULT_RESTRICTED_PHOTO_EXCHANGE_SETTINGS)}},
{CoppacsRestrictionBlockID,
{AccessFlag::Global, &DEFAULT_COPPACS_RESTRICTION_SETTINGS,
sizeof(DEFAULT_COPPACS_RESTRICTION_SETTINGS)}},
{ParentalRestrictionEmailBlockID,
{AccessFlag::Global, &DEFAULT_PARENTAL_RESTRICTION_EMAIL,
sizeof(DEFAULT_PARENTAL_RESTRICTION_EMAIL)}},
{EULAVersionBlockID, {AccessFlag::Global, &DEFAULT_EULA_VERSION, sizeof(DEFAULT_EULA_VERSION)}},
{Unknown_0x000E0000,
{AccessFlag::Global, &DEFAULT_0x000E0000_DATA, sizeof(DEFAULT_0x000E0000_DATA)}},
{ConsoleModelBlockID,
{AccessFlag::System, &DEFAULT_CONSOLE_MODEL, sizeof(DEFAULT_CONSOLE_MODEL)}},
{XDeviceTokenBlockID,
{AccessFlag::System, &DEFAULT_X_DEVICE_TOKEN, sizeof(DEFAULT_X_DEVICE_TOKEN)}},
{SystemSetupRequiredBlockID,
{AccessFlag::System, &DEFAULT_SYSTEM_SETUP_REQUIRED_FLAG,
sizeof(DEFAULT_SYSTEM_SETUP_REQUIRED_FLAG)}},
{DebugModeBlockID,
{AccessFlag::Global, &DEFAULT_DEBUG_MODE_FLAG, sizeof(DEFAULT_DEBUG_MODE_FLAG)}},
{ClockSequenceBlockID,
{AccessFlag::System, &DEFAULT_CLOCK_SEQUENCE, sizeof(DEFAULT_CLOCK_SEQUENCE)}},
{ServerType, {AccessFlag::Global, DEFAULT_SERVER_TYPE, sizeof(DEFAULT_SERVER_TYPE)}},
{Unknown_0x00160000,
{AccessFlag::Global, &DEFAULT_0x00160000_DATA, sizeof(DEFAULT_0x00160000_DATA)}},
{MiiverseAccessKeyBlockID,
{AccessFlag::Global, &DEFAULT_MIIVERSE_ACCESS_KEY, sizeof(DEFAULT_MIIVERSE_ACCESS_KEY)}},
};
const EULAVersion& GetDefaultEULAVersion() {
return DEFAULT_EULA_VERSION;
}
const std::unordered_map<ConfigBlockID, ConfigBlockDefaults>& GetDefaultConfigBlocks() {
return DEFAULT_CONFIG_BLOCKS;
}
bool HasDefaultConfigBlock(ConfigBlockID block_id) {
return DEFAULT_CONFIG_BLOCKS.contains(block_id);
}
const ConfigBlockDefaults& GetDefaultConfigBlock(ConfigBlockID block_id) {
return DEFAULT_CONFIG_BLOCKS.at(block_id);
}
} // namespace Service::CFG

View File

@@ -0,0 +1,35 @@
// Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <span>
#include <unordered_map>
#include "common/common_types.h"
#include "core/hle/service/cfg/cfg.h"
namespace Service::CFG {
/// The default parameters of a config block.
struct ConfigBlockDefaults {
AccessFlag access_flags;
std::span<const u8> data;
ConfigBlockDefaults(AccessFlag access_flags_, const void* data_, std::size_t size_)
: access_flags(access_flags_), data(reinterpret_cast<const u8*>(data_), size_) {}
};
/// Gets the default EULA version.
const EULAVersion& GetDefaultEULAVersion();
/// Retrieves a map of known config block IDs to their defaults.
const std::unordered_map<ConfigBlockID, ConfigBlockDefaults>& GetDefaultConfigBlocks();
/// Checks whether a particular config block has defaults defined.
bool HasDefaultConfigBlock(ConfigBlockID block_id);
/// Gets the defined defaults for a particular config block.
const ConfigBlockDefaults& GetDefaultConfigBlock(ConfigBlockID block_id);
} // namespace Service::CFG

View File

@@ -0,0 +1,66 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/cfg/cfg_i.h"
SERIALIZE_EXPORT_IMPL(Service::CFG::CFG_I)
namespace Service::CFG {
CFG_I::CFG_I(std::shared_ptr<Module> cfg) : Module::Interface(std::move(cfg), "cfg:i", 23) {
static const FunctionInfo functions[] = {
// cfg common
// clang-format off
{0x0001, &CFG_I::GetConfig, "GetConfig"},
{0x0002, &CFG_I::GetRegion, "GetRegion"},
{0x0003, &CFG_I::GetTransferableId, "GetTransferableId"},
{0x0004, &CFG_I::IsCoppacsSupported, "IsCoppacsSupported"},
{0x0005, &CFG_I::GetSystemModel, "GetSystemModel"},
{0x0006, &CFG_I::GetModelNintendo2DS, "GetModelNintendo2DS"},
{0x0007, nullptr, "WriteToFirstByteCfgSavegame"},
{0x0008, nullptr, "TranslateCountryInfo"},
{0x0009, &CFG_I::GetCountryCodeString, "GetCountryCodeString"},
{0x000A, &CFG_I::GetCountryCodeID, "GetCountryCodeID"},
{0x000B, nullptr, "IsFangateSupported"},
// cfg:s
{0x0401, &CFG_I::GetSystemConfig, "GetSystemConfig"},
{0x0402, &CFG_I::SetSystemConfig, "SetSystemConfig"},
{0x0403, &CFG_I::UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
{0x0404, &CFG_I::GetLocalFriendCodeSeedData, "GetLocalFriendCodeSeedData"},
{0x0405, &CFG_I::GetLocalFriendCodeSeed, "GetLocalFriendCodeSeed"},
{0x0406, &CFG_I::GetRegion, "GetRegion"},
{0x0407, &CFG_I::SecureInfoGetByte101, "SecureInfoGetByte101"},
{0x0408, &CFG_I::SecureInfoGetSerialNo, "SecureInfoGetSerialNo"},
{0x0409, nullptr, "UpdateConfigBlk00040003"},
// cfg:i
{0x0801, &CFG_I::GetSystemConfig, "GetSystemConfig"},
{0x0802, &CFG_I::SetSystemConfig, "SetSystemConfig"},
{0x0803, &CFG_I::UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
{0x0804, nullptr, "CreateConfigInfoBlk"},
{0x0805, nullptr, "DeleteConfigNANDSavefile"},
{0x0806, &CFG_I::FormatConfig, "FormatConfig"},
{0x0808, nullptr, "UpdateConfigBlk1"},
{0x0809, nullptr, "UpdateConfigBlk2"},
{0x080A, nullptr, "UpdateConfigBlk3"},
{0x080B, nullptr, "SetGetLocalFriendCodeSeedData"},
{0x080C, nullptr, "SetLocalFriendCodeSeedSignature"},
{0x080D, nullptr, "DeleteCreateNANDLocalFriendCodeSeed"},
{0x080E, nullptr, "VerifySigLocalFriendCodeSeed"},
{0x080F, nullptr, "GetLocalFriendCodeSeedData"},
{0x0810, nullptr, "GetLocalFriendCodeSeed"},
{0x0811, nullptr, "SetSecureInfo"},
{0x0812, nullptr, "DeleteCreateNANDSecureInfo"},
{0x0813, nullptr, "VerifySigSecureInfo"},
{0x0814, nullptr, "SecureInfoGetData"},
{0x0815, nullptr, "SecureInfoGetSignature"},
{0x0816, &CFG_I::GetRegion, "GetRegion"},
{0x0817, &CFG_I::SecureInfoGetByte101, "SecureInfoGetByte101"},
{0x0818, nullptr, "SecureInfoGetSerialNo"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::CFG

View File

@@ -0,0 +1,22 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/cfg/cfg.h"
namespace Service::CFG {
class CFG_I final : public Module::Interface {
public:
explicit CFG_I(std::shared_ptr<Module> cfg);
private:
SERVICE_SERIALIZATION(CFG_I, cfg, Module)
};
} // namespace Service::CFG
BOOST_CLASS_EXPORT_KEY(Service::CFG::CFG_I)
BOOST_SERIALIZATION_CONSTRUCT(Service::CFG::CFG_I)

View File

@@ -0,0 +1,24 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/cfg/cfg_nor.h"
SERIALIZE_EXPORT_IMPL(Service::CFG::CFG_NOR)
namespace Service::CFG {
CFG_NOR::CFG_NOR() : ServiceFramework("cfg:nor", 23) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, nullptr, "Initialize"},
{0x0002, nullptr, "Shutdown"},
{0x0005, nullptr, "ReadData"},
{0x0006, nullptr, "WriteData"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::CFG

View File

@@ -0,0 +1,21 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Service::CFG {
class CFG_NOR final : public ServiceFramework<CFG_NOR> {
public:
CFG_NOR();
private:
SERVICE_SERIALIZATION_SIMPLE
};
} // namespace Service::CFG
BOOST_CLASS_EXPORT_KEY(Service::CFG::CFG_NOR)

View File

@@ -0,0 +1,44 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/cfg/cfg_s.h"
SERIALIZE_EXPORT_IMPL(Service::CFG::CFG_S)
namespace Service::CFG {
CFG_S::CFG_S(std::shared_ptr<Module> cfg) : Module::Interface(std::move(cfg), "cfg:s", 23) {
static const FunctionInfo functions[] = {
// cfg common
// clang-format off
{0x0001, &CFG_S::GetConfig, "GetConfig"},
{0x0002, &CFG_S::GetRegion, "GetRegion"},
{0x0003, &CFG_S::GetTransferableId, "GetTransferableId"},
{0x0004, &CFG_S::IsCoppacsSupported, "IsCoppacsSupported"},
{0x0005, &CFG_S::GetSystemModel, "GetSystemModel"},
{0x0006, &CFG_S::GetModelNintendo2DS, "GetModelNintendo2DS"},
{0x0007, nullptr, "WriteToFirstByteCfgSavegame"},
{0x0008, nullptr, "TranslateCountryInfo"},
{0x0009, &CFG_S::GetCountryCodeString, "GetCountryCodeString"},
{0x000A, &CFG_S::GetCountryCodeID, "GetCountryCodeID"},
{0x000B, nullptr, "IsFangateSupported"},
// cfg:s
{0x0401, &CFG_S::GetSystemConfig, "GetSystemConfig"},
{0x0402, &CFG_S::SetSystemConfig, "SetSystemConfig"},
{0x0403, &CFG_S::UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
{0x0404, &CFG_S::GetLocalFriendCodeSeedData, "GetLocalFriendCodeSeedData"},
{0x0405, &CFG_S::GetLocalFriendCodeSeed, "GetLocalFriendCodeSeed"},
{0x0406, &CFG_S::GetRegion, "GetRegion"},
{0x0407, &CFG_S::SecureInfoGetByte101, "SecureInfoGetByte101"},
{0x0408, &CFG_S::SecureInfoGetSerialNo, "SecureInfoGetSerialNo"},
{0x0409, nullptr, "UpdateConfigBlk00040003"},
{0x040D, &CFG_S::SetUUIDClockSequence, "SetUUIDClockSequence"},
{0x040E, &CFG_S::GetUUIDClockSequence, "GetUUIDClockSequence"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::CFG

View File

@@ -0,0 +1,22 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/cfg/cfg.h"
namespace Service::CFG {
class CFG_S final : public Module::Interface {
public:
explicit CFG_S(std::shared_ptr<Module> cfg);
private:
SERVICE_SERIALIZATION(CFG_S, cfg, Module)
};
} // namespace Service::CFG
BOOST_CLASS_EXPORT_KEY(Service::CFG::CFG_S)
BOOST_SERIALIZATION_CONSTRUCT(Service::CFG::CFG_S)

View File

@@ -0,0 +1,32 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/cfg/cfg_u.h"
SERIALIZE_EXPORT_IMPL(Service::CFG::CFG_U)
namespace Service::CFG {
CFG_U::CFG_U(std::shared_ptr<Module> cfg) : Module::Interface(std::move(cfg), "cfg:u", 23) {
static const FunctionInfo functions[] = {
// cfg common
// clang-format off
{0x0001, &CFG_U::GetConfig, "GetConfig"},
{0x0002, &CFG_U::GetRegion, "GetRegion"},
{0x0003, &CFG_U::GetTransferableId, "GetTransferableId"},
{0x0004, &CFG_U::IsCoppacsSupported, "IsCoppacsSupported"},
{0x0005, &CFG_U::GetSystemModel, "GetSystemModel"},
{0x0006, &CFG_U::GetModelNintendo2DS, "GetModelNintendo2DS"},
{0x0007, nullptr, "WriteToFirstByteCfgSavegame"},
{0x0008, nullptr, "TranslateCountryInfo"},
{0x0009, &CFG_U::GetCountryCodeString, "GetCountryCodeString"},
{0x000A, &CFG_U::GetCountryCodeID, "GetCountryCodeID"},
{0x000B, nullptr, "IsFangateSupported"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::CFG

View File

@@ -0,0 +1,22 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/cfg/cfg.h"
namespace Service::CFG {
class CFG_U final : public Module::Interface {
public:
explicit CFG_U(std::shared_ptr<Module> cfg);
private:
SERVICE_SERIALIZATION(CFG_U, cfg, Module)
};
} // namespace Service::CFG
BOOST_CLASS_EXPORT_KEY(Service::CFG::CFG_U)
BOOST_SERIALIZATION_CONSTRUCT(Service::CFG::CFG_U)

View File

@@ -0,0 +1,535 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/alignment.h"
#include "common/archives.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/result.h"
#include "core/hle/service/csnd/csnd_snd.h"
SERVICE_CONSTRUCT_IMPL(Service::CSND::CSND_SND)
SERIALIZE_EXPORT_IMPL(Service::CSND::CSND_SND)
namespace Service::CSND {
enum class CommandId : u16 {
Start = 0x000,
Pause = 0x001,
SetEncoding = 0x002,
SetSecondBlock = 0x003,
SetLoopMode = 0x004,
// unknown = 0x005,
SetLinearInterpolation = 0x006,
SetPsgDuty = 0x007,
SetSampleRate = 0x008,
SetVolume = 0x009,
SetFirstBlock = 0x00A,
SetFirstBlockAdpcmState = 0x00B,
SetSecondBlockAdpcmState = 0x00C,
SetSecondBlockAdpcmReload = 0x00D,
ConfigureChannel = 0x00E,
ConfigurePsg = 0x00F,
ConfigurePsgNoise = 0x010,
// 0x10x commands are audio capture related
// unknown = 0x200
UpdateState = 0x300,
};
struct Type0Command {
u16_le next_command_offset;
enum_le<CommandId> command_id;
u8 finished;
INSERT_PADDING_BYTES(3);
union {
struct {
u32_le channel;
u32_le value;
INSERT_PADDING_BYTES(0x10);
} start;
struct {
u32_le channel;
u32_le value;
INSERT_PADDING_BYTES(0x10);
} pause;
struct {
u32_le channel;
Encoding value;
INSERT_PADDING_BYTES(0x13);
} set_encoding;
struct {
u32_le channel;
LoopMode value;
INSERT_PADDING_BYTES(0x13);
} set_loop_mode;
struct {
u32_le channel;
u32_le value;
INSERT_PADDING_BYTES(0x10);
} set_linear_interpolation;
struct {
u32_le channel;
u8 value;
INSERT_PADDING_BYTES(0x13);
} set_psg_duty;
struct {
u32_le channel;
u32_le value;
INSERT_PADDING_BYTES(0x10);
} set_sample_rate;
struct {
u32_le channel;
u16_le left_channel_volume;
u16_le right_channel_volume;
u16_le left_capture_volume;
u16_le right_capture_volume;
INSERT_PADDING_BYTES(0xC);
} set_volume;
struct {
u32_le channel;
u32_le address;
u32_le size;
INSERT_PADDING_BYTES(0xC);
} set_block; // for either first block or second block
struct {
u32_le channel;
s16_le predictor;
u8 step_index;
INSERT_PADDING_BYTES(0x11);
} set_adpcm_state; // for either first block or second block
struct {
u32_le channel;
u8 value;
INSERT_PADDING_BYTES(0x13);
} set_second_block_adpcm_reload;
struct {
union {
BitField<0, 6, u32> channel;
BitField<6, 1, u32> linear_interpolation;
BitField<10, 2, u32> loop_mode;
BitField<12, 2, u32> encoding;
BitField<14, 1, u32> enable_playback;
BitField<16, 16, u32> sample_rate;
};
u16_le left_channel_volume;
u16_le right_channel_volume;
u16_le left_capture_volume;
u16_le right_capture_volume;
u32_le block1_address;
u32_le block2_address;
u32_le size;
} configure_channel;
struct {
union {
BitField<0, 6, u32> channel;
BitField<14, 1, u32> enable_playback;
BitField<16, 16, u32> sample_rate;
};
u16_le left_channel_volume;
u16_le right_channel_volume;
u16_le left_capture_volume;
u16_le right_capture_volume;
u32_le duty;
INSERT_PADDING_BYTES(0x8);
} configure_psg;
struct {
union {
BitField<0, 6, u32> channel;
BitField<14, 1, u32> enable_playback;
};
u16_le left_channel_volume;
u16_le right_channel_volume;
u16_le left_capture_volume;
u16_le right_capture_volume;
INSERT_PADDING_BYTES(0xC);
} configure_psg_noise;
};
};
static_assert(sizeof(Type0Command) == 0x20, "Type0Command structure size is wrong");
struct MasterState {
u32_le unknown_channel_flag;
u32_le unknown;
};
static_assert(sizeof(MasterState) == 0x8, "MasterState structure size is wrong");
struct ChannelState {
u8 active;
INSERT_PADDING_BYTES(0x3);
s16_le adpcm_predictor;
u8 adpcm_step_index;
INSERT_PADDING_BYTES(0x1);
// 3dbrew says this is the current physical address. However the assembly of CSND module
// from 11.3 system shows this is simply assigned as 0, which is also documented on ctrulib.
u32_le zero;
};
static_assert(sizeof(ChannelState) == 0xC, "ChannelState structure size is wrong");
struct CaptureState {
u8 active;
INSERT_PADDING_BYTES(0x3);
u32_le zero;
};
static_assert(sizeof(CaptureState) == 0x8, "CaptureState structure size is wrong");
void CSND_SND::Initialize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 size = Common::AlignUp(rp.Pop<u32>(), Memory::CITRA_PAGE_SIZE);
master_state_offset = rp.Pop<u32>();
channel_state_offset = rp.Pop<u32>();
capture_state_offset = rp.Pop<u32>();
type1_command_offset = rp.Pop<u32>();
using Kernel::MemoryPermission;
mutex = system.Kernel().CreateMutex(false, "CSND:mutex");
shared_memory = system.Kernel()
.CreateSharedMemory(nullptr, size, MemoryPermission::ReadWrite,
MemoryPermission::ReadWrite, 0,
Kernel::MemoryRegion::BASE, "CSND:SharedMemory")
.Unwrap();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 3);
rb.Push(ResultSuccess);
rb.PushCopyObjects(mutex, shared_memory);
LOG_WARNING(Service_CSND,
"(STUBBED) called, size=0x{:08X} "
"master_state_offset=0x{:08X} channel_state_offset=0x{:08X} "
"capture_state_offset=0x{:08X} type1_command_offset=0x{:08X}",
size, master_state_offset, channel_state_offset, capture_state_offset,
type1_command_offset);
}
void CSND_SND::Shutdown(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
if (mutex)
mutex = nullptr;
if (shared_memory)
shared_memory = nullptr;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_WARNING(Service_CSND, "(STUBBED) called");
}
void CSND_SND::ExecuteCommands(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 addr = rp.Pop<u32>();
LOG_DEBUG(Service_CSND, "(STUBBED) called, addr=0x{:08X}", addr);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
if (!shared_memory) {
rb.Push(Result(ErrorDescription::InvalidResultValue, ErrorModule::CSND,
ErrorSummary::InvalidState, ErrorLevel::Status));
LOG_ERROR(Service_CSND, "called, shared memory not allocated");
return;
}
u32 offset = addr;
while (offset != 0xFFFF) {
Type0Command command;
u8* ptr = shared_memory->GetPointer(offset);
std::memcpy(&command, ptr, sizeof(Type0Command));
offset = command.next_command_offset;
switch (command.command_id) {
case CommandId::Start:
// TODO: start/stop the sound
break;
case CommandId::Pause:
// TODO: pause/resume the sound
break;
case CommandId::SetEncoding:
channels[command.set_encoding.channel].encoding = command.set_encoding.value;
break;
case CommandId::SetSecondBlock:
channels[command.set_block.channel].block2_address = command.set_block.address;
channels[command.set_block.channel].block2_size = command.set_block.size;
break;
case CommandId::SetLoopMode:
channels[command.set_loop_mode.channel].loop_mode = command.set_loop_mode.value;
break;
case CommandId::SetLinearInterpolation:
channels[command.set_linear_interpolation.channel].linear_interpolation =
command.set_linear_interpolation.value != 0;
break;
case CommandId::SetPsgDuty:
channels[command.set_psg_duty.channel].psg_duty = command.set_psg_duty.value;
break;
case CommandId::SetSampleRate:
channels[command.set_sample_rate.channel].sample_rate = command.set_sample_rate.value;
break;
case CommandId::SetVolume:
channels[command.set_volume.channel].left_channel_volume =
command.set_volume.left_channel_volume;
channels[command.set_volume.channel].right_channel_volume =
command.set_volume.right_channel_volume;
channels[command.set_volume.channel].left_capture_volume =
command.set_volume.left_capture_volume;
channels[command.set_volume.channel].right_capture_volume =
command.set_volume.right_capture_volume;
break;
case CommandId::SetFirstBlock:
channels[command.set_block.channel].block1_address = command.set_block.address;
channels[command.set_block.channel].block1_size = command.set_block.size;
break;
case CommandId::SetFirstBlockAdpcmState:
channels[command.set_adpcm_state.channel].block1_adpcm_state = {
command.set_adpcm_state.predictor, command.set_adpcm_state.step_index};
channels[command.set_adpcm_state.channel].block2_adpcm_state = {};
channels[command.set_adpcm_state.channel].block2_adpcm_reload = false;
break;
case CommandId::SetSecondBlockAdpcmState:
channels[command.set_adpcm_state.channel].block2_adpcm_state = {
command.set_adpcm_state.predictor, command.set_adpcm_state.step_index};
channels[command.set_adpcm_state.channel].block2_adpcm_reload = true;
break;
case CommandId::SetSecondBlockAdpcmReload:
channels[command.set_second_block_adpcm_reload.channel].block2_adpcm_reload =
command.set_second_block_adpcm_reload.value != 0;
break;
case CommandId::ConfigureChannel: {
auto& configure = command.configure_channel;
auto& channel = channels[configure.channel];
channel.linear_interpolation = configure.linear_interpolation != 0;
channel.loop_mode = static_cast<LoopMode>(configure.loop_mode.Value());
channel.encoding = static_cast<Encoding>(configure.encoding.Value());
channel.sample_rate = configure.sample_rate;
channel.left_channel_volume = configure.left_channel_volume;
channel.right_channel_volume = configure.right_channel_volume;
channel.left_capture_volume = configure.left_capture_volume;
channel.right_capture_volume = configure.right_capture_volume;
channel.block1_address = configure.block1_address;
channel.block2_address = configure.block2_address;
channel.block1_size = channel.block2_size = configure.size;
if (configure.enable_playback) {
// TODO: startthe sound
}
break;
}
case CommandId::ConfigurePsg: {
auto& configure = command.configure_psg;
auto& channel = channels[configure.channel];
channel.encoding = Encoding::Psg;
channel.psg_duty = configure.duty;
channel.sample_rate = configure.sample_rate;
channel.left_channel_volume = configure.left_channel_volume;
channel.right_channel_volume = configure.right_channel_volume;
channel.left_capture_volume = configure.left_capture_volume;
channel.right_capture_volume = configure.right_capture_volume;
if (configure.enable_playback) {
// TODO: startthe sound
}
break;
}
case CommandId::ConfigurePsgNoise: {
auto& configure = command.configure_psg_noise;
auto& channel = channels[configure.channel];
channel.encoding = Encoding::Psg;
channel.left_channel_volume = configure.left_channel_volume;
channel.right_channel_volume = configure.right_channel_volume;
channel.left_capture_volume = configure.left_capture_volume;
channel.right_capture_volume = configure.right_capture_volume;
if (configure.enable_playback) {
// TODO: startthe sound
}
break;
}
case CommandId::UpdateState: {
MasterState master{0, 0};
std::memcpy(shared_memory->GetPointer(master_state_offset), &master, sizeof(master));
u32 output_index = 0;
for (u32 i = 0; i < ChannelCount; ++i) {
if ((acquired_channel_mask & (1 << i)) == 0)
continue;
ChannelState state;
state.active = false;
state.adpcm_predictor = channels[i].block1_adpcm_state.predictor;
state.adpcm_predictor = channels[i].block1_adpcm_state.step_index;
state.zero = 0;
std::memcpy(
shared_memory->GetPointer(channel_state_offset + sizeof(state) * output_index),
&state, sizeof(state));
++output_index;
}
for (u32 i = 0; i < MaxCaptureUnits; ++i) {
if (!capture_units[i])
continue;
CaptureState state;
state.active = false;
state.zero = 0;
std::memcpy(shared_memory->GetPointer(capture_state_offset + sizeof(state) * i),
&state, sizeof(state));
}
break;
}
default:
LOG_ERROR(Service_CSND, "Unimplemented command ID 0x{:X}", command.command_id);
}
}
*shared_memory->GetPointer(addr + offsetof(Type0Command, finished)) = 1;
rb.Push(ResultSuccess);
}
void CSND_SND::AcquireSoundChannels(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
// This is "almost" hardcoded, as in CSND initializes this with some code during sysmodule
// startup, but it always compute to the same value.
acquired_channel_mask = 0xFFFFFF00;
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push(acquired_channel_mask);
LOG_WARNING(Service_CSND, "(STUBBED) called");
}
void CSND_SND::ReleaseSoundChannels(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
acquired_channel_mask = 0;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_WARNING(Service_CSND, "(STUBBED) called");
}
void CSND_SND::AcquireCapUnit(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
if (capture_units[0] && capture_units[1]) {
LOG_WARNING(Service_CSND, "No more capture units available");
rb.Push(Result(ErrorDescription::InvalidResultValue, ErrorModule::CSND,
ErrorSummary::OutOfResource, ErrorLevel::Status));
rb.Skip(1, false);
return;
}
rb.Push(ResultSuccess);
if (capture_units[0]) {
capture_units[1] = true;
rb.Push<u32>(1);
} else {
capture_units[0] = true;
rb.Push<u32>(0);
}
LOG_WARNING(Service_CSND, "(STUBBED) called");
}
void CSND_SND::ReleaseCapUnit(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 index = rp.Pop<u32>();
capture_units[index] = false;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_WARNING(Service_CSND, "(STUBBED) called, capture_unit_index={}", index);
}
void CSND_SND::FlushDataCache(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
[[maybe_unused]] const VAddr address = rp.Pop<u32>();
[[maybe_unused]] const u32 size = rp.Pop<u32>();
const auto process = rp.PopObject<Kernel::Process>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_TRACE(Service_CSND, "(STUBBED) called address=0x{:08X}, size=0x{:08X}, process={}", address,
size, process->process_id);
}
void CSND_SND::StoreDataCache(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
[[maybe_unused]] const VAddr address = rp.Pop<u32>();
[[maybe_unused]] const u32 size = rp.Pop<u32>();
const auto process = rp.PopObject<Kernel::Process>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_TRACE(Service_CSND, "(STUBBED) called address=0x{:08X}, size=0x{:08X}, process={}", address,
size, process->process_id);
}
void CSND_SND::InvalidateDataCache(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
[[maybe_unused]] const VAddr address = rp.Pop<u32>();
[[maybe_unused]] const u32 size = rp.Pop<u32>();
const auto process = rp.PopObject<Kernel::Process>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_TRACE(Service_CSND, "(STUBBED) called address=0x{:08X}, size=0x{:08X}, process={}", address,
size, process->process_id);
}
void CSND_SND::Reset(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_WARNING(Service_CSND, "(STUBBED) called");
}
CSND_SND::CSND_SND(Core::System& system) : ServiceFramework("csnd:SND", 4), system(system) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, &CSND_SND::Initialize, "Initialize"},
{0x0002, &CSND_SND::Shutdown, "Shutdown"},
{0x0003, &CSND_SND::ExecuteCommands, "ExecuteCommands"},
{0x0004, nullptr, "ExecuteType1Commands"},
{0x0005, &CSND_SND::AcquireSoundChannels, "AcquireSoundChannels"},
{0x0006, &CSND_SND::ReleaseSoundChannels, "ReleaseSoundChannels"},
{0x0007, &CSND_SND::AcquireCapUnit, "AcquireCapUnit"},
{0x0008, &CSND_SND::ReleaseCapUnit, "ReleaseCapUnit"},
{0x0009, &CSND_SND::FlushDataCache, "FlushDataCache"},
{0x000A, &CSND_SND::StoreDataCache, "StoreDataCache"},
{0x000B, &CSND_SND::InvalidateDataCache, "InvalidateDataCache"},
{0x000C, &CSND_SND::Reset, "Reset"},
// clang-format on
};
RegisterHandlers(functions);
};
void InstallInterfaces(Core::System& system) {
auto& service_manager = system.ServiceManager();
std::make_shared<CSND_SND>(system)->InstallAsService(service_manager);
}
} // namespace Service::CSND

View File

@@ -0,0 +1,280 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <boost/serialization/array.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::CSND {
enum class Encoding : u8 {
Pcm8 = 0,
Pcm16 = 1,
Adpcm = 2,
Psg = 3,
};
enum class LoopMode : u8 {
Manual = 0, // Play block 1 endlessly ignoring the size
Normal = 1, // Play block 1 once, then repeat with block 2. Block size is reloaded every time a
// new block is started
OneShot = 2, // Play block 1 once and stop
ConstantSize = 3, // Similar to Normal, but only load block size once at the beginning
};
struct AdpcmState {
s16 predictor = 0;
u8 step_index = 0;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& predictor;
ar& step_index;
}
friend class boost::serialization::access;
};
struct Channel {
PAddr block1_address = 0;
PAddr block2_address = 0;
u32 block1_size = 0;
u32 block2_size = 0;
AdpcmState block1_adpcm_state;
AdpcmState block2_adpcm_state;
bool block2_adpcm_reload = false;
u16 left_channel_volume = 0;
u16 right_channel_volume = 0;
u16 left_capture_volume = 0;
u16 right_capture_volume = 0;
u32 sample_rate = 0;
bool linear_interpolation = false;
LoopMode loop_mode = LoopMode::Manual;
Encoding encoding = Encoding::Pcm8;
u8 psg_duty = 0;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& block1_address;
ar& block2_address;
ar& block1_size;
ar& block2_size;
ar& block1_adpcm_state;
ar& block2_adpcm_state;
ar& block2_adpcm_reload;
ar& left_channel_volume;
ar& right_channel_volume;
ar& left_capture_volume;
ar& right_capture_volume;
ar& sample_rate;
ar& linear_interpolation;
ar& loop_mode;
ar& encoding;
ar& psg_duty;
}
friend class boost::serialization::access;
};
class CSND_SND final : public ServiceFramework<CSND_SND> {
public:
explicit CSND_SND(Core::System& system);
~CSND_SND() = default;
private:
/**
* CSND_SND::Initialize service function
* Inputs:
* 0 : Header Code[0x00010140]
* 1 : Shared memory block size, for mem-block creation
* 2 : offset to master state located in the shared-memory, region size=8
* 3 : offset to channel state located in the shared-memory, region size=12*num_channels
* 4 : offset to capture state located in the shared-memory, region size=8*num_captures
* 5 : offset to type 1 commands (?) located in the shared-memory.
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Handle-list header
* 3 : Mutex handle
* 4 : Shared memory block handle
*/
void Initialize(Kernel::HLERequestContext& ctx);
/**
* CSND_SND::Shutdown service function
* Inputs:
* 0 : Header Code[0x00020000]
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void Shutdown(Kernel::HLERequestContext& ctx);
/**
* CSND_SND::ExecuteCommands service function
* Inputs:
* 0 : Header Code[0x00030040]
* 1 : Command offset in shared memory.
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void ExecuteCommands(Kernel::HLERequestContext& ctx);
/**
* CSND_SND::ExecuteType1Commands service function
* Inputs:
* 0 : Header Code[0x00040080]
* 1 : unknown
* 2 : unknown
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void ExecuteType1Commands(Kernel::HLERequestContext& ctx);
/**
* CSND_SND::AcquireSoundChannels service function
* Inputs:
* 0 : Header Code[0x00050000]
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Available channel bit mask
*/
void AcquireSoundChannels(Kernel::HLERequestContext& ctx);
/**
* CSND_SND::ReleaseSoundChannels service function
* Inputs:
* 0 : Header Code[0x00060000]
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void ReleaseSoundChannels(Kernel::HLERequestContext& ctx);
/**
* CSND_SND::AcquireCapUnit service function
* This function tries to acquire one capture device (max: 2).
* Returns index of which capture device was acquired.
* Inputs:
* 0 : Header Code[0x00070000]
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Capture Unit
*/
void AcquireCapUnit(Kernel::HLERequestContext& ctx);
/**
* CSND_SND::ReleaseCapUnit service function
* Inputs:
* 0 : Header Code[0x00080040]
* 1 : Capture Unit
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void ReleaseCapUnit(Kernel::HLERequestContext& ctx);
/**
* CSND_SND::FlushDataCache service function
*
* This Function is a no-op, We aren't emulating the CPU cache any time soon.
*
* Inputs:
* 0 : Header Code[0x00090082]
* 1 : Address
* 2 : Size
* 3 : Value 0, some descriptor for the KProcess Handle
* 4 : KProcess handle
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void FlushDataCache(Kernel::HLERequestContext& ctx);
/**
* CSND_SND::StoreDataCache service function
*
* This Function is a no-op, We aren't emulating the CPU cache any time soon.
*
* Inputs:
* 0 : Header Code[0x000A0082]
* 1 : Address
* 2 : Size
* 3 : Value 0, some descriptor for the KProcess Handle
* 4 : KProcess handle
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void StoreDataCache(Kernel::HLERequestContext& ctx);
/**
* CSND_SND::InvalidateDataCache service function
*
* This Function is a no-op, We aren't emulating the CPU cache any time soon.
*
* Inputs:
* 0 : Header Code[0x000B0082]
* 1 : Address
* 2 : Size
* 3 : Value 0, some descriptor for the KProcess Handle
* 4 : KProcess handle
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void InvalidateDataCache(Kernel::HLERequestContext& ctx);
/**
* CSND_SND::Reset service function
* Inputs:
* 0 : Header Code[0x000C0000]
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void Reset(Kernel::HLERequestContext& ctx);
Core::System& system;
std::shared_ptr<Kernel::Mutex> mutex = nullptr;
std::shared_ptr<Kernel::SharedMemory> shared_memory = nullptr;
static constexpr u32 MaxCaptureUnits = 2;
std::array<bool, MaxCaptureUnits> capture_units = {false, false};
static constexpr u32 ChannelCount = 32;
std::array<Channel, ChannelCount> channels;
u32 master_state_offset = 0;
u32 channel_state_offset = 0;
u32 capture_state_offset = 0;
u32 type1_command_offset = 0;
u32 acquired_channel_mask = 0;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<Kernel::SessionRequestHandler>(*this);
ar& mutex;
ar& shared_memory;
ar& capture_units;
ar& channels;
ar& master_state_offset;
ar& channel_state_offset;
ar& capture_state_offset;
ar& type1_command_offset;
ar& acquired_channel_mask;
}
friend class boost::serialization::access;
};
/// Initializes the CSND_SND Service
void InstallInterfaces(Core::System& system);
} // namespace Service::CSND
BOOST_CLASS_EXPORT_KEY(Service::CSND::CSND_SND)
SERVICE_CONSTRUCT(Service::CSND::CSND_SND)

View File

@@ -0,0 +1,20 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/core.h"
#include "core/hle/service/dlp/dlp.h"
#include "core/hle/service/dlp/dlp_clnt.h"
#include "core/hle/service/dlp/dlp_fkcl.h"
#include "core/hle/service/dlp/dlp_srvr.h"
namespace Service::DLP {
void InstallInterfaces(Core::System& system) {
auto& service_manager = system.ServiceManager();
std::make_shared<DLP_CLNT>()->InstallAsService(service_manager);
std::make_shared<DLP_FKCL>()->InstallAsService(service_manager);
std::make_shared<DLP_SRVR>()->InstallAsService(service_manager);
}
} // namespace Service::DLP

View File

@@ -0,0 +1,18 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::DLP {
/// Initializes the DLP services.
void InstallInterfaces(Core::System& system);
} // namespace Service::DLP

View File

@@ -0,0 +1,42 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/dlp/dlp_clnt.h"
SERIALIZE_EXPORT_IMPL(Service::DLP::DLP_CLNT)
namespace Service::DLP {
DLP_CLNT::DLP_CLNT() : ServiceFramework("dlp:CLNT", 1) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, nullptr, "Initialize"},
{0x0002, nullptr, "Finalize"},
{0x0003, nullptr, "GetEventDesc"},
{0x0004, nullptr, "GetChannel"},
{0x0005, nullptr, "StartScan"},
{0x0006, nullptr, "StopScan"},
{0x0007, nullptr, "GetServerInfo"},
{0x0008, nullptr, "GetTitleInfo"},
{0x0009, nullptr, "GetTitleInfoInOrder"},
{0x000A, nullptr, "DeleteScanInfo"},
{0x000B, nullptr, "PrepareForSystemDownload"},
{0x000C, nullptr, "StartSystemDownload"},
{0x000D, nullptr, "StartTitleDownload"},
{0x000E, nullptr, "GetMyStatus"},
{0x000F, nullptr, "GetConnectingNodes"},
{0x0010, nullptr, "GetNodeInfo"},
{0x0011, nullptr, "GetWirelessRebootPassphrase"},
{0x0012, nullptr, "StopSession"},
{0x0013, nullptr, "GetCupVersion"},
{0x0014, nullptr, "GetDupAvailability"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::DLP

View File

@@ -0,0 +1,22 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Service::DLP {
class DLP_CLNT final : public ServiceFramework<DLP_CLNT> {
public:
DLP_CLNT();
~DLP_CLNT() = default;
private:
SERVICE_SERIALIZATION_SIMPLE
};
} // namespace Service::DLP
BOOST_CLASS_EXPORT_KEY(Service::DLP::DLP_CLNT)

View File

@@ -0,0 +1,39 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/dlp/dlp_fkcl.h"
SERIALIZE_EXPORT_IMPL(Service::DLP::DLP_FKCL)
namespace Service::DLP {
DLP_FKCL::DLP_FKCL() : ServiceFramework("dlp:FKCL", 1) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, nullptr, "Initialize"},
{0x0002, nullptr, "Finalize"},
{0x0003, nullptr, "GetEventDesc"},
{0x0004, nullptr, "GetChannels"},
{0x0005, nullptr, "StartScan"},
{0x0006, nullptr, "StopScan"},
{0x0007, nullptr, "GetServerInfo"},
{0x0008, nullptr, "GetTitleInfo"},
{0x0009, nullptr, "GetTitleInfoInOrder"},
{0x000A, nullptr, "DeleteScanInfo"},
{0x000B, nullptr, "StartFakeSession"},
{0x000C, nullptr, "GetMyStatus"},
{0x000D, nullptr, "GetConnectingNodes"},
{0x000E, nullptr, "GetNodeInfo"},
{0x000F, nullptr, "GetWirelessRebootPassphrase"},
{0x0010, nullptr, "StopSession"},
{0x0011, nullptr, "Initialize2"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::DLP

View File

@@ -0,0 +1,22 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Service::DLP {
class DLP_FKCL final : public ServiceFramework<DLP_FKCL> {
public:
DLP_FKCL();
~DLP_FKCL() = default;
private:
SERVICE_SERIALIZATION_SIMPLE
};
} // namespace Service::DLP
BOOST_CLASS_EXPORT_KEY(Service::DLP::DLP_FKCL)

View File

@@ -0,0 +1,52 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/result.h"
#include "core/hle/service/dlp/dlp_srvr.h"
SERIALIZE_EXPORT_IMPL(Service::DLP::DLP_SRVR)
namespace Service::DLP {
void DLP_SRVR::IsChild(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
rp.Skip(1, false);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push(false);
LOG_WARNING(Service_DLP, "(STUBBED) called");
}
DLP_SRVR::DLP_SRVR() : ServiceFramework("dlp:SRVR", 1) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, nullptr, "Initialize"},
{0x0002, nullptr, "Finalize"},
{0x0003, nullptr, "GetServerState"},
{0x0004, nullptr, "GetEventDescription"},
{0x0005, nullptr, "StartAccepting"},
{0x0006, nullptr, "EndAccepting"},
{0x0007, nullptr, "StartDistribution"},
{0x0008, nullptr, "SendWirelessRebootPassphrase"},
{0x0009, nullptr, "AcceptClient"},
{0x000A, nullptr, "DisconnectClient"},
{0x000B, nullptr, "GetConnectingClients"},
{0x000C, nullptr, "GetClientInfo"},
{0x000D, nullptr, "GetClientState"},
{0x000E, &DLP_SRVR::IsChild, "IsChild"},
{0x000F, nullptr, "InitializeWithName"},
{0x0010, nullptr, "GetDupNoticeNeed"},
// clang-format on
};
RegisterHandlers(functions);
}
} // namespace Service::DLP

View File

@@ -0,0 +1,24 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Service::DLP {
class DLP_SRVR final : public ServiceFramework<DLP_SRVR> {
public:
DLP_SRVR();
~DLP_SRVR() = default;
private:
void IsChild(Kernel::HLERequestContext& ctx);
SERVICE_SERIALIZATION_SIMPLE
};
} // namespace Service::DLP
BOOST_CLASS_EXPORT_KEY(Service::DLP::DLP_SRVR)

View File

@@ -0,0 +1,417 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "audio_core/audio_types.h"
#include "common/archives.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/dsp/dsp_dsp.h"
using DspPipe = AudioCore::DspPipe;
using InterruptType = Service::DSP::InterruptType;
SERIALIZE_EXPORT_IMPL(Service::DSP::DSP_DSP)
SERVICE_CONSTRUCT_IMPL(Service::DSP::DSP_DSP)
namespace AudioCore {
enum class DspPipe;
}
namespace Service::DSP {
void DSP_DSP::RecvData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 register_number = rp.Pop<u32>();
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push(system.DSP().RecvData(register_number));
LOG_DEBUG(Service_DSP, "register_number={}", register_number);
}
void DSP_DSP::RecvDataIsReady(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 register_number = rp.Pop<u32>();
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push(system.DSP().RecvDataIsReady(register_number));
LOG_DEBUG(Service_DSP, "register_number={}", register_number);
}
void DSP_DSP::SetSemaphore(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u16 semaphore_value = rp.Pop<u16>();
system.DSP().SetSemaphore(semaphore_value);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_INFO(Service_DSP, "called, semaphore_value={:04X}", semaphore_value);
}
void DSP_DSP::ConvertProcessAddressFromDspDram(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 address = rp.Pop<u32>();
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
// TODO(merry): There is a per-region offset missing in this calculation (that seems to be
// always zero).
rb.Push<u32>((address << 1) + (Memory::DSP_RAM_VADDR + 0x40000));
LOG_DEBUG(Service_DSP, "address=0x{:08X}", address);
}
void DSP_DSP::WriteProcessPipe(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 channel = rp.Pop<u32>();
const u32 size = rp.Pop<u32>();
auto buffer = rp.PopStaticBuffer();
const DspPipe pipe = static_cast<DspPipe>(channel);
// This behaviour was confirmed by RE.
// The likely reason for this is that games tend to pass in garbage at these bytes
// because they read random bytes off the stack.
switch (pipe) {
case DspPipe::Audio:
ASSERT(buffer.size() >= 4);
buffer[2] = 0;
buffer[3] = 0;
break;
case DspPipe::Binary:
ASSERT(buffer.size() >= 8);
buffer[4] = 1;
buffer[5] = 0;
buffer[6] = 0;
buffer[7] = 0;
break;
default:
LOG_ERROR(Service_DSP, "Unknown pipe {}", pipe);
break;
}
system.DSP().PipeWrite(pipe, buffer);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_DEBUG(Service_DSP, "channel={}, size=0x{:X}, buffer_size={:X}", channel, size,
buffer.size());
}
void DSP_DSP::ReadPipe(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 channel = rp.Pop<u32>();
const u32 peer = rp.Pop<u32>();
const u16 size = rp.Pop<u16>();
const DspPipe pipe = static_cast<DspPipe>(channel);
const u16 pipe_readable_size = static_cast<u16>(system.DSP().GetPipeReadableSize(pipe));
std::vector<u8> pipe_buffer;
if (pipe_readable_size >= size)
pipe_buffer = system.DSP().PipeRead(pipe, size);
else
UNREACHABLE(); // No more data is in pipe. Hardware hangs in this case; Should never happen.
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(ResultSuccess);
rb.PushStaticBuffer(std::move(pipe_buffer), 0);
LOG_DEBUG(Service_DSP, "channel={}, peer={}, size=0x{:04X}, pipe_readable_size=0x{:04X}",
channel, peer, size, pipe_readable_size);
}
void DSP_DSP::GetPipeReadableSize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 channel = rp.Pop<u32>();
const u32 peer = rp.Pop<u32>();
const DspPipe pipe = static_cast<DspPipe>(channel);
const u16 pipe_readable_size = static_cast<u16>(system.DSP().GetPipeReadableSize(pipe));
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push<u16>(pipe_readable_size);
LOG_DEBUG(Service_DSP, "channel={}, peer={}, return pipe_readable_size=0x{:04X}", channel, peer,
pipe_readable_size);
}
void DSP_DSP::ReadPipeIfPossible(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 channel = rp.Pop<u32>();
const u32 peer = rp.Pop<u32>();
const u16 size = rp.Pop<u16>();
const DspPipe pipe = static_cast<DspPipe>(channel);
const u16 pipe_readable_size = static_cast<u16>(system.DSP().GetPipeReadableSize(pipe));
std::vector<u8> pipe_buffer;
if (pipe_readable_size >= size) {
pipe_buffer = system.DSP().PipeRead(pipe, size);
}
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
rb.Push(ResultSuccess);
rb.Push<u16>(static_cast<u16>(pipe_buffer.size()));
rb.PushStaticBuffer(std::move(pipe_buffer), 0);
LOG_DEBUG(Service_DSP, "channel={}, peer={}, size=0x{:04X}, pipe_readable_size=0x{:04X}",
channel, peer, size, pipe_readable_size);
}
void DSP_DSP::LoadComponent(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const auto size = rp.Pop<u32>();
const auto prog_mask = rp.Pop<u16>();
const auto data_mask = rp.Pop<u16>();
auto& buffer = rp.PopMappedBuffer();
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
rb.Push(ResultSuccess);
rb.Push(true);
rb.PushMappedBuffer(buffer);
std::vector<u8> component_data(size);
buffer.Read(component_data.data(), 0, size);
system.DSP().LoadComponent(component_data);
LOG_INFO(Service_DSP, "called size=0x{:X}, prog_mask=0x{:04X}, data_mask=0x{:04X}", size,
prog_mask, data_mask);
}
void DSP_DSP::UnloadComponent(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
system.DSP().UnloadComponent();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_INFO(Service_DSP, "called");
}
void DSP_DSP::FlushDataCache(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
[[maybe_unused]] const VAddr address = rp.Pop<u32>();
[[maybe_unused]] const u32 size = rp.Pop<u32>();
const auto process = rp.PopObject<Kernel::Process>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_TRACE(Service_DSP, "called address=0x{:08X}, size=0x{:X}, process={}", address, size,
process->process_id);
}
void DSP_DSP::InvalidateDataCache(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
[[maybe_unused]] const VAddr address = rp.Pop<u32>();
[[maybe_unused]] const u32 size = rp.Pop<u32>();
const auto process = rp.PopObject<Kernel::Process>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_TRACE(Service_DSP, "called address=0x{:08X}, size=0x{:X}, process={}", address, size,
process->process_id);
}
void DSP_DSP::RegisterInterruptEvents(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 interrupt = rp.Pop<u32>();
const u32 channel = rp.Pop<u32>();
auto event = rp.PopObject<Kernel::Event>();
ASSERT_MSG(interrupt < static_cast<u32>(InterruptType::Count) &&
channel < AudioCore::num_dsp_pipe,
"Invalid type or pipe: interrupt = {}, channel = {}", interrupt, channel);
const InterruptType type = static_cast<InterruptType>(interrupt);
const DspPipe pipe = static_cast<DspPipe>(channel);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
if (event) { /// Register interrupt event
if (HasTooManyEventsRegistered()) {
LOG_INFO(Service_DSP,
"Ran out of space to register interrupts (Attempted to register "
"interrupt={}, channel={}, event={})",
interrupt, channel, event->GetName());
rb.Push(Result(ErrorDescription::InvalidResultValue, ErrorModule::DSP,
ErrorSummary::OutOfResource, ErrorLevel::Status));
return;
} else {
GetInterruptEvent(type, pipe) = event;
LOG_INFO(Service_DSP, "Registered interrupt={}, channel={}, event={}", interrupt,
channel, event->GetName());
}
} else { /// Otherwise unregister event
GetInterruptEvent(type, pipe) = nullptr;
LOG_INFO(Service_DSP, "Unregistered interrupt={}, channel={}", interrupt, channel);
}
rb.Push(ResultSuccess);
}
void DSP_DSP::GetSemaphoreEventHandle(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(ResultSuccess);
rb.PushCopyObjects(semaphore_event);
LOG_WARNING(Service_DSP, "(STUBBED) called");
}
void DSP_DSP::SetSemaphoreMask(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
preset_semaphore = rp.Pop<u16>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_WARNING(Service_DSP, "(STUBBED) called mask=0x{:04X}", preset_semaphore);
}
void DSP_DSP::GetHeadphoneStatus(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push(false); /// u8, 0 = not inserted, 1 = inserted
LOG_DEBUG(Service_DSP, "called");
}
void DSP_DSP::ForceHeadphoneOut(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u8 force = rp.Pop<u8>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_DEBUG(Service_DSP, "(STUBBED) called, force={}", force);
}
// DSP Interrupts:
// The audio-pipe interrupt occurs every frame tick. Userland programs normally have a thread
// that's waiting for an interrupt event. Immediately after this interrupt event, userland
// normally updates the state in the next region and increments the relevant frame counter by two.
void DSP_DSP::SignalInterrupt(InterruptType type, DspPipe pipe) {
LOG_TRACE(Service_DSP, "called, type={}, pipe={}", type, pipe);
const auto& event = GetInterruptEvent(type, pipe);
if (event)
event->Signal();
}
std::shared_ptr<Kernel::Event>& DSP_DSP::GetInterruptEvent(InterruptType type, DspPipe pipe) {
switch (type) {
case InterruptType::Zero:
return interrupt_zero;
case InterruptType::One:
return interrupt_one;
case InterruptType::Pipe: {
const std::size_t pipe_index = static_cast<std::size_t>(pipe);
ASSERT(pipe_index < AudioCore::num_dsp_pipe);
return pipes[pipe_index];
}
case InterruptType::Count:
default:
break;
}
UNREACHABLE_MSG("Invalid interrupt type = {}", type);
}
bool DSP_DSP::HasTooManyEventsRegistered() const {
std::size_t number =
std::count_if(pipes.begin(), pipes.end(), [](const auto& evt) { return evt != nullptr; });
if (interrupt_zero != nullptr)
number++;
if (interrupt_one != nullptr)
number++;
LOG_DEBUG(Service_DSP, "Number of events registered = {}", number);
return number >= max_number_of_interrupt_events;
}
DSP_DSP::DSP_DSP(Core::System& system)
: ServiceFramework("dsp::DSP", DefaultMaxSessions), system(system) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, &DSP_DSP::RecvData, "RecvData"},
{0x0002, &DSP_DSP::RecvDataIsReady, "RecvDataIsReady"},
{0x0003, nullptr, "SendData"},
{0x0004, nullptr, "SendDataIsEmpty"},
{0x0005, nullptr, "SendFifoEx"},
{0x0006, nullptr, "RecvFifoEx"},
{0x0007, &DSP_DSP::SetSemaphore, "SetSemaphore"},
{0x0008, nullptr, "GetSemaphore"},
{0x0009, nullptr, "ClearSemaphore"},
{0x000A, nullptr, "MaskSemaphore"},
{0x000B, nullptr, "CheckSemaphoreRequest"},
{0x000C, &DSP_DSP::ConvertProcessAddressFromDspDram, "ConvertProcessAddressFromDspDram"},
{0x000D, &DSP_DSP::WriteProcessPipe, "WriteProcessPipe"},
{0x000E, &DSP_DSP::ReadPipe, "ReadPipe"},
{0x000F, &DSP_DSP::GetPipeReadableSize, "GetPipeReadableSize"},
{0x0010, &DSP_DSP::ReadPipeIfPossible, "ReadPipeIfPossible"},
{0x0011, &DSP_DSP::LoadComponent, "LoadComponent"},
{0x0012, &DSP_DSP::UnloadComponent, "UnloadComponent"},
{0x0013, &DSP_DSP::FlushDataCache, "FlushDataCache"},
{0x0014, &DSP_DSP::InvalidateDataCache, "InvalidateDCache"},
{0x0015, &DSP_DSP::RegisterInterruptEvents, "RegisterInterruptEvents"},
{0x0016, &DSP_DSP::GetSemaphoreEventHandle, "GetSemaphoreEventHandle"},
{0x0017, &DSP_DSP::SetSemaphoreMask, "SetSemaphoreMask"},
{0x0018, nullptr, "GetPhysicalAddress"},
{0x0019, nullptr, "GetVirtualAddress"},
{0x001A, nullptr, "SetIirFilterI2S1_cmd1"},
{0x001B, nullptr, "SetIirFilterI2S1_cmd2"},
{0x001C, nullptr, "SetIirFilterEQ"},
{0x001D, nullptr, "ReadMultiEx_SPI2"},
{0x001E, nullptr, "WriteMultiEx_SPI2"},
{0x001F, &DSP_DSP::GetHeadphoneStatus, "GetHeadphoneStatus"},
{0x0020, &DSP_DSP::ForceHeadphoneOut, "ForceHeadphoneOut"},
{0x0021, nullptr, "GetIsDspOccupied"},
// clang-format on
};
RegisterHandlers(functions);
semaphore_event =
system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "DSP_DSP::semaphore_event");
semaphore_event->SetHLENotifier(
[this]() { this->system.DSP().SetSemaphore(preset_semaphore); });
system.DSP().SetInterruptHandler([dsp_ref = this, &system](InterruptType type, DspPipe pipe) {
std::scoped_lock lock{system.Kernel().GetHLELock()};
if (dsp_ref) {
dsp_ref->SignalInterrupt(type, pipe);
}
});
}
DSP_DSP::~DSP_DSP() {
semaphore_event = nullptr;
pipes = {};
}
void InstallInterfaces(Core::System& system) {
auto& service_manager = system.ServiceManager();
auto dsp = std::make_shared<DSP_DSP>(system);
dsp->InstallAsService(service_manager);
}
} // namespace Service::DSP

View File

@@ -0,0 +1,287 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <boost/serialization/array.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include "audio_core/dsp_interface.h"
#include "core/hle/kernel/event.h"
#include "core/hle/result.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::DSP {
/// There are three types of interrupts
enum class InterruptType : u32 { Zero = 0, One = 1, Pipe = 2, Count };
class DSP_DSP final : public ServiceFramework<DSP_DSP> {
public:
explicit DSP_DSP(Core::System& system);
~DSP_DSP();
/// Actual service implementation only has 6 'slots' for interrupts.
static constexpr std::size_t max_number_of_interrupt_events = 6;
/// Signal interrupt on pipe
void SignalInterrupt(InterruptType type, AudioCore::DspPipe pipe);
private:
/**
* DSP_DSP::RecvData service function
* This function reads a value out of a DSP register.
* Inputs:
* 0 : Header Code[0x00010040]
* 1 : Register Number
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u16, Value in the register
* Notes:
* This function has only been observed being called with a register number of 0.
*/
void RecvData(Kernel::HLERequestContext& ctx);
/**
* DSP_DSP::RecvDataIsReady service function
* This function checks whether a DSP register is ready to be read.
* Inputs:
* 0 : Header Code[0x00020040]
* 1 : Register Number
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Reply Register Update Flag (0 = not ready, 1 = ready)
* Note:
* This function has only been observed being called with a register number of 0.
*/
void RecvDataIsReady(Kernel::HLERequestContext& ctx);
/**
* DSP_DSP::SetSemaphore service function
* Inputs:
* 0 : Header Code[0x00070040]
* 1 : u16, Semaphore value
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void SetSemaphore(Kernel::HLERequestContext& ctx);
/**
* DSP_DSP::ConvertProcessAddressFromDspDram service function
* Inputs:
* 0 : Header Code[0x000C0040]
* 1 : Address
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Address. (inaddr << 1) + 0x1FF40000 (where 0x1FF00000 is the DSP RAM address)
*/
void ConvertProcessAddressFromDspDram(Kernel::HLERequestContext& ctx);
/**
* DSP_DSP::WriteProcessPipe service function
* Inputs:
* 0 : Header Code[0x000D0082]
* 1 : Channel (0 - 7 0:Debug from DSP 1:P-DMA 2:audio 3:binary 4-7: free ?)
* 2 : Size
* 3 : (size << 14) | 0x402
* 4 : Buffer
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
*/
void WriteProcessPipe(Kernel::HLERequestContext& ctx);
/**
* DSP_DSP::ReadPipe service function
* Inputs:
* 0 : Header Code[0x000E00C0]
* 1 : Channel (0 - 7 0:Debug from DSP 1:P-DMA 2:audio 3:binary 4-7: free ?)
* 2 : Peer (0 = from DSP, 1 = from ARM)
* 3 : u16, Size
* 0x41 : Virtual address of memory buffer to write pipe contents to
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void ReadPipe(Kernel::HLERequestContext& ctx);
/**
* DSP_DSP::GetPipeReadableSize service function
* Inputs:
* 0 : Header Code[0x000F0080]
* 1 : Channel (0 - 7 0:Debug from DSP 1:P-DMA 2:audio 3:binary 4-7: free ?)
* 2 : Peer (0 = from DSP, 1 = from ARM)
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u16, Readable size
*/
void GetPipeReadableSize(Kernel::HLERequestContext& ctx);
/**
* DSP_DSP::ReadPipeIfPossible service function
* A pipe is a means of communication between the ARM11 and DSP that occurs on
* hardware by writing to/reading from the DSP registers at 0x10203000.
* Pipes are used for initialisation. See also DspInterface::PipeRead.
* Inputs:
* 0 : Header Code[0x001000C0]
* 1 : Channel (0 - 7 0:Debug from DSP 1:P-DMA 2:audio 3:binary 4-7: free ?)
* 2 : Peer (0 = from DSP, 1 = from ARM)
* 3 : u16, Size
* 0x41 : Virtual address of memory buffer to write pipe contents to
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u16, Actual read size
*/
void ReadPipeIfPossible(Kernel::HLERequestContext& ctx);
/**
* DSP_DSP::LoadComponent service function
* Inputs:
* 0 : Header Code[0x001100C2]
* 1 : Size
* 2 : Program mask (observed only half word used)
* 3 : Data mask (observed only half word used)
* 4 : (size << 4) | 0xA
* 5 : Component Buffer
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u8, Component Loaded (0 = not loaded, 1 = loaded)
* 3 : (Size << 4) | 0xA
* 4 : Component Buffer
*/
void LoadComponent(Kernel::HLERequestContext& ctx);
/**
* DSP_DSP::UnloadComponent service function
* Inputs:
* 0 : Header Code[0x00120000]
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void UnloadComponent(Kernel::HLERequestContext& ctx);
/**
* DSP_DSP::FlushDataCache service function
*
* This Function is a no-op, We aren't emulating the CPU cache any time soon.
*
* Inputs:
* 0 : Header Code[0x00130082]
* 1 : Address
* 2 : Size
* 3 : Value 0, some descriptor for the KProcess Handle
* 4 : KProcess handle
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void FlushDataCache(Kernel::HLERequestContext& ctx);
/**
* DSP_DSP::InvalidateDataCache service function
*
* This Function is a no-op, We aren't emulating the CPU cache any time soon.
*
* Inputs:
* 0 : Header Code[0x00140082]
* 1 : Address
* 2 : Size
* 3 : Value 0, some descriptor for the KProcess Handle
* 4 : KProcess handle
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void InvalidateDataCache(Kernel::HLERequestContext& ctx);
/**
* DSP_DSP::RegisterInterruptEvents service function
* Inputs:
* 0 : Header Code[0x00150082]
* 1 : Interrupt
* 2 : Channel
* 3 : 0x0, some descriptor for the Event Handle
* 4 : Interrupt Event handle (0 = unregister the event that was previous registered)
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void RegisterInterruptEvents(Kernel::HLERequestContext& ctx);
/**
* DSP_DSP::GetSemaphoreEventHandle service function
* Inputs:
* 0 : Header Code[0x00160000]
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : 0x0, some descriptor for the Event Handle
* 3 : Semaphore Event Handle
*/
void GetSemaphoreEventHandle(Kernel::HLERequestContext& ctx);
/**
* DSP_DSP::SetSemaphoreMask service function
* Inputs:
* 0 : Header Code[0x00170040]
* 1 : u16, Mask
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void SetSemaphoreMask(Kernel::HLERequestContext& ctx);
/**
* DSP_DSP::GetHeadphoneStatus service function
* Inputs:
* 0 : Header Code[0x001F0000]
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u8, The headphone status response, 0 = Not inserted, 1 = inserted
*/
void GetHeadphoneStatus(Kernel::HLERequestContext& ctx);
/**
* DSP_DSP::ForceHeadphoneOut service function
* Inputs:
* 0 : Header Code[0x00020040]
* 1 : u8, 0 = don't force, 1 = force
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void ForceHeadphoneOut(Kernel::HLERequestContext& ctx);
/// Returns the Interrupt Event for a given pipe
std::shared_ptr<Kernel::Event>& GetInterruptEvent(InterruptType type, AudioCore::DspPipe pipe);
/// Checks if we are trying to register more than 6 events
bool HasTooManyEventsRegistered() const;
Core::System& system;
std::shared_ptr<Kernel::Event> semaphore_event;
u16 preset_semaphore = 0;
std::shared_ptr<Kernel::Event> interrupt_zero = nullptr; /// Currently unknown purpose
std::shared_ptr<Kernel::Event> interrupt_one = nullptr; /// Currently unknown purpose
/// Each DSP pipe has an associated interrupt
std::array<std::shared_ptr<Kernel::Event>, AudioCore::num_dsp_pipe> pipes = {{}};
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<Kernel::SessionRequestHandler>(*this);
ar& semaphore_event;
ar& preset_semaphore;
ar& interrupt_zero;
ar& interrupt_one;
ar& pipes;
}
friend class boost::serialization::access;
};
void InstallInterfaces(Core::System& system);
} // namespace Service::DSP
BOOST_CLASS_EXPORT_KEY(Service::DSP::DSP_DSP)
SERVICE_CONSTRUCT(Service::DSP::DSP_DSP)

View File

@@ -0,0 +1,267 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <chrono>
#include <iomanip>
#include <sstream>
#include "common/archives.h"
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/result.h"
#include "core/hle/service/err/err_f.h"
#undef exception_info // We use 'exception_info' as a plain identifier, but MSVC defines this in one
// of its many headers.
SERIALIZE_EXPORT_IMPL(Service::ERR::ERR_F)
namespace boost::serialization {
template <class Archive>
void load_construct_data(Archive& ar, Service::ERR::ERR_F* t, const unsigned int) {
::new (t) Service::ERR::ERR_F(Core::Global<Core::System>());
}
template void load_construct_data<iarchive>(iarchive& ar, Service::ERR::ERR_F* t,
const unsigned int);
} // namespace boost::serialization
namespace Service::ERR {
enum class FatalErrType : u32 {
Generic = 0,
Corrupted = 1,
CardRemoved = 2,
Exception = 3,
ResultFailure = 4,
Logged = 5,
};
enum class ExceptionType : u32 {
PrefetchAbort = 0,
DataAbort = 1,
Undefined = 2,
VectorFP = 3,
};
struct ExceptionInfo {
u8 exception_type;
INSERT_PADDING_BYTES(3);
u32 sr;
u32 ar;
u32 fpexc;
u32 fpinst;
u32 fpinst2;
};
static_assert(sizeof(ExceptionInfo) == 0x18, "ExceptionInfo struct has incorrect size");
struct ExceptionContext final {
std::array<u32, 16> arm_regs;
u32 cpsr;
};
static_assert(sizeof(ExceptionContext) == 0x44, "ExceptionContext struct has incorrect size");
struct ExceptionData {
ExceptionInfo exception_info;
ExceptionContext exception_context;
INSERT_PADDING_WORDS(1);
};
static_assert(sizeof(ExceptionData) == 0x60, "ExceptionData struct has incorrect size");
struct ErrInfo {
struct ErrInfoCommon {
u8 specifier; // 0x0
u8 rev_high; // 0x1
u16 rev_low; // 0x2
u32 result_code; // 0x4
u32 pc_address; // 0x8
u32 pid; // 0xC
u32 title_id_low; // 0x10
u32 title_id_high; // 0x14
u32 app_title_id_low; // 0x18
u32 app_title_id_high; // 0x1C
} errinfo_common;
static_assert(sizeof(ErrInfoCommon) == 0x20, "ErrInfoCommon struct has incorrect size");
union {
struct {
char data[0x60]; // 0x20
} generic;
struct {
ExceptionData exception_data; // 0x20
} exception;
struct {
char message[0x60]; // 0x20
} result_failure;
};
};
static std::string GetErrType(u8 type_code) {
switch (static_cast<FatalErrType>(type_code)) {
case FatalErrType::Generic:
return "Generic";
case FatalErrType::Corrupted:
return "Corrupted";
case FatalErrType::CardRemoved:
return "CardRemoved";
case FatalErrType::Exception:
return "Exception";
case FatalErrType::ResultFailure:
return "ResultFailure";
case FatalErrType::Logged:
return "Logged";
default:
return "Unknown Error Type";
}
}
static std::string GetExceptionType(u8 type_code) {
switch (static_cast<ExceptionType>(type_code)) {
case ExceptionType::PrefetchAbort:
return "Prefetch Abort";
case ExceptionType::DataAbort:
return "Data Abort";
case ExceptionType::Undefined:
return "Undefined Exception";
case ExceptionType::VectorFP:
return "Vector Floating Point Exception";
default:
return "Unknown Exception Type";
}
}
static std::string GetCurrentSystemTime() {
auto now = std::chrono::system_clock::now();
auto time = std::chrono::system_clock::to_time_t(now);
std::stringstream time_stream;
time_stream << std::put_time(std::localtime(&time), "%Y/%m/%d %H:%M:%S");
return time_stream.str();
}
static void LogGenericInfo(const ErrInfo::ErrInfoCommon& errinfo_common) {
LOG_CRITICAL(Service_ERR, "PID: 0x{:08X}", errinfo_common.pid);
LOG_CRITICAL(Service_ERR, "REV: 0x{:08X}_0x{:08X}", errinfo_common.rev_high,
errinfo_common.rev_low);
LOG_CRITICAL(Service_ERR, "TID: 0x{:08X}_0x{:08X}", errinfo_common.title_id_high,
errinfo_common.title_id_low);
LOG_CRITICAL(Service_ERR, "AID: 0x{:08X}_0x{:08X}", errinfo_common.app_title_id_high,
errinfo_common.app_title_id_low);
LOG_CRITICAL(Service_ERR, "ADR: 0x{:08X}", errinfo_common.pc_address);
Result result_code{errinfo_common.result_code};
LOG_CRITICAL(Service_ERR, "RSL: 0x{:08X}", result_code.raw);
LOG_CRITICAL(Service_ERR, " Level: {}", static_cast<u32>(result_code.level.Value()));
LOG_CRITICAL(Service_ERR, " Summary: {}", static_cast<u32>(result_code.summary.Value()));
LOG_CRITICAL(Service_ERR, " Module: {}", static_cast<u32>(result_code.module.Value()));
LOG_CRITICAL(Service_ERR, " Desc: {}", static_cast<u32>(result_code.description.Value()));
}
void ERR_F::ThrowFatalError(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
LOG_CRITICAL(Service_ERR, "Fatal error");
const ErrInfo errinfo = rp.PopRaw<ErrInfo>();
LOG_CRITICAL(Service_ERR, "Fatal error type: {}", GetErrType(errinfo.errinfo_common.specifier));
system.SetStatus(Core::System::ResultStatus::ErrorUnknown);
// Generic Info
LogGenericInfo(errinfo.errinfo_common);
switch (static_cast<FatalErrType>(errinfo.errinfo_common.specifier)) {
case FatalErrType::Generic:
case FatalErrType::Corrupted:
case FatalErrType::CardRemoved:
case FatalErrType::Logged: {
LOG_CRITICAL(Service_ERR, "Datetime: {}", GetCurrentSystemTime());
break;
}
case FatalErrType::Exception: {
const auto& errtype = errinfo.exception;
// Register Info
LOG_CRITICAL(Service_ERR, "ARM Registers:");
for (u32 index = 0; index < errtype.exception_data.exception_context.arm_regs.size();
++index) {
if (index < 13) {
LOG_DEBUG(Service_ERR, "r{}=0x{:08X}", index,
errtype.exception_data.exception_context.arm_regs.at(index));
} else if (index == 13) {
LOG_CRITICAL(Service_ERR, "SP=0x{:08X}",
errtype.exception_data.exception_context.arm_regs.at(index));
} else if (index == 14) {
LOG_CRITICAL(Service_ERR, "LR=0x{:08X}",
errtype.exception_data.exception_context.arm_regs.at(index));
} else if (index == 15) {
LOG_CRITICAL(Service_ERR, "PC=0x{:08X}",
errtype.exception_data.exception_context.arm_regs.at(index));
}
}
LOG_CRITICAL(Service_ERR, "CPSR=0x{:08X}", errtype.exception_data.exception_context.cpsr);
// Exception Info
LOG_CRITICAL(Service_ERR, "EXCEPTION TYPE: {}",
GetExceptionType(errtype.exception_data.exception_info.exception_type));
switch (static_cast<ExceptionType>(errtype.exception_data.exception_info.exception_type)) {
case ExceptionType::PrefetchAbort:
LOG_CRITICAL(Service_ERR, "IFSR: 0x{:08X}", errtype.exception_data.exception_info.sr);
LOG_CRITICAL(Service_ERR, "r15: 0x{:08X}", errtype.exception_data.exception_info.ar);
break;
case ExceptionType::DataAbort:
LOG_CRITICAL(Service_ERR, "DFSR: 0x{:08X}", errtype.exception_data.exception_info.sr);
LOG_CRITICAL(Service_ERR, "DFAR: 0x{:08X}", errtype.exception_data.exception_info.ar);
break;
case ExceptionType::VectorFP:
LOG_CRITICAL(Service_ERR, "FPEXC: 0x{:08X}",
errtype.exception_data.exception_info.fpinst);
LOG_CRITICAL(Service_ERR, "FINST: 0x{:08X}",
errtype.exception_data.exception_info.fpinst);
LOG_CRITICAL(Service_ERR, "FINST2: 0x{:08X}",
errtype.exception_data.exception_info.fpinst2);
break;
case ExceptionType::Undefined:
break; // Not logging exception_info for this case
}
LOG_CRITICAL(Service_ERR, "Datetime: {}", GetCurrentSystemTime());
break;
}
case FatalErrType::ResultFailure: {
const auto& errtype = errinfo.result_failure;
// Failure Message
LOG_CRITICAL(Service_ERR, "Failure Message: {}", errtype.message);
LOG_CRITICAL(Service_ERR, "Datetime: {}", GetCurrentSystemTime());
break;
}
} // switch FatalErrType
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
}
ERR_F::ERR_F(Core::System& system) : ServiceFramework("err:f", 1), system(system) {
static const FunctionInfo functions[] = {
// clang-format off
{0x0001, &ERR_F::ThrowFatalError, "ThrowFatalError"},
{0x0002, nullptr, "SetUserString"},
// clang-format on
};
RegisterHandlers(functions);
}
ERR_F::~ERR_F() = default;
void InstallInterfaces(Core::System& system) {
auto errf = std::make_shared<ERR_F>(system);
errf->InstallAsNamedPort(system.Kernel());
}
} // namespace Service::ERR

View File

@@ -0,0 +1,50 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Kernel {
class HLERequestContext;
}
namespace Service::ERR {
/// Interface to "err:f" service
class ERR_F final : public ServiceFramework<ERR_F> {
public:
explicit ERR_F(Core::System& system);
~ERR_F();
private:
/* ThrowFatalError function
* Inputs:
* 0 : Header code [0x00010800]
* 1-32 : FatalErrInfo
* Outputs:
* 0 : Header code
* 1 : Result code
*/
void ThrowFatalError(Kernel::HLERequestContext& ctx);
Core::System& system;
SERVICE_SERIALIZATION_SIMPLE
};
void InstallInterfaces(Core::System& system);
} // namespace Service::ERR
BOOST_CLASS_EXPORT_KEY(Service::ERR::ERR_F)
namespace boost::serialization {
template <class Archive>
void load_construct_data(Archive& ar, Service::ERR::ERR_F* t, const unsigned int);
}

View File

@@ -0,0 +1,294 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <cstring>
#include <vector>
#include "common/archives.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/applets/mii_selector.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/mii.h"
#include "core/hle/result.h"
#include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/frd/frd.h"
#include "core/hle/service/frd/frd_a.h"
#include "core/hle/service/frd/frd_u.h"
SERVICE_CONSTRUCT_IMPL(Service::FRD::Module)
namespace Service::FRD {
Module::Interface::Interface(std::shared_ptr<Module> frd, const char* name, u32 max_session)
: ServiceFramework(name, max_session), frd(std::move(frd)) {}
Module::Interface::~Interface() = default;
void Module::Interface::GetMyPresence(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
std::vector<u8> buffer(sizeof(MyPresence));
std::memcpy(buffer.data(), &frd->my_presence, buffer.size());
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(ResultSuccess);
rb.PushStaticBuffer(std::move(buffer), 0);
LOG_WARNING(Service_FRD, "(STUBBED) called");
}
void Module::Interface::GetFriendKeyList(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 unknown = rp.Pop<u32>();
const u32 frd_count = rp.Pop<u32>();
std::vector<u8> buffer(sizeof(FriendKey) * frd_count, 0);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
rb.Push(ResultSuccess);
rb.Push<u32>(0); // 0 friends
rb.PushStaticBuffer(std::move(buffer), 0);
LOG_WARNING(Service_FRD, "(STUBBED) called, unknown={}, frd_count={}", unknown, frd_count);
}
void Module::Interface::GetFriendProfile(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 count = rp.Pop<u32>();
const std::vector<u8> frd_keys = rp.PopStaticBuffer();
ASSERT(frd_keys.size() == count * sizeof(FriendKey));
std::vector<u8> buffer(sizeof(Profile) * count, 0);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(ResultSuccess);
rb.PushStaticBuffer(std::move(buffer), 0);
LOG_WARNING(Service_FRD, "(STUBBED) called, count={}", count);
}
void Module::Interface::GetFriendAttributeFlags(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 count = rp.Pop<u32>();
const std::vector<u8> frd_keys = rp.PopStaticBuffer();
ASSERT(frd_keys.size() == count * sizeof(FriendKey));
// TODO:(mailwl) figure out AttributeFlag size and zero all buffer. Assume 1 byte
std::vector<u8> buffer(1 * count, 0);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(ResultSuccess);
rb.PushStaticBuffer(std::move(buffer), 0);
LOG_WARNING(Service_FRD, "(STUBBED) called, count={}", count);
}
void Module::Interface::GetMyFriendKey(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(5, 0);
rb.Push(ResultSuccess);
rb.PushRaw(frd->my_friend_key);
LOG_WARNING(Service_FRD, "(STUBBED) called");
}
void Module::Interface::GetMyScreenName(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(7, 0);
auto cfg = Service::CFG::GetModule(frd->system);
auto username = cfg->GetUsername();
ASSERT_MSG(username.length() <= 10, "Username longer than expected!");
ScreenName screen_name{};
std::memcpy(screen_name.name.data(), username.data(), username.length() * sizeof(char16_t));
rb.Push(ResultSuccess);
rb.PushRaw(screen_name);
rb.Push(0);
LOG_DEBUG(Service_FRD, "called");
}
void Module::Interface::GetMyComment(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(10, 0);
constexpr Comment comment{.name = {u'H', u'e', u'y', '!'}};
rb.Push(ResultSuccess);
rb.PushRaw<Comment>(comment);
rb.Push(0);
LOG_WARNING(Service_FRD, "(STUBBED) called");
}
void Module::Interface::GetMyMii(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(0x19, 0);
const auto mii_data = HLE::Applets::MiiSelector::GetStandardMiiResult().selected_mii_data;
Mii::ChecksummedMiiData mii{};
mii.SetMiiData(mii_data);
rb.Push(ResultSuccess);
rb.PushRaw<Mii::ChecksummedMiiData>(mii);
LOG_WARNING(Service_FRD, "(STUBBED) called");
}
void Module::Interface::GetMyProfile(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(3, 0);
constexpr Profile profile{.region = 1, .country = 1, .area = 1, .language = 1, .platform = 1};
rb.Push(ResultSuccess);
rb.PushRaw<Profile>(profile);
LOG_WARNING(Service_FRD, "(STUBBED) called");
}
void Module::Interface::GetMyFavoriteGame(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(5, 0);
constexpr Game game{.title_id = 0x0004000E00030700, .version = 1};
rb.Push(ResultSuccess);
rb.PushRaw<Game>(game);
LOG_WARNING(Service_FRD, "(STUBBED) called");
}
void Module::Interface::GetMyPlayingGame(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(5, 0);
constexpr Game game{.title_id = 0x0004000E00030700, .version = 1};
rb.Push(ResultSuccess);
rb.PushRaw<Game>(game);
LOG_WARNING(Service_FRD, "(STUBBED) called");
}
void Module::Interface::GetMyPreference(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(4, 0);
constexpr u32 is_public = 1;
constexpr u32 show_game = 1;
constexpr u32 show_history = 0;
rb.Push(ResultSuccess);
rb.Push<u32>(is_public);
rb.Push<u32>(show_game);
rb.Push<u32>(show_history);
LOG_WARNING(Service_FRD, "(STUBBED) called");
}
void Module::Interface::UnscrambleLocalFriendCode(Kernel::HLERequestContext& ctx) {
const std::size_t scrambled_friend_code_size = 12;
const std::size_t friend_code_size = 8;
IPC::RequestParser rp(ctx);
const u32 friend_code_count = rp.Pop<u32>();
const std::vector<u8> scrambled_friend_codes = rp.PopStaticBuffer();
ASSERT_MSG(scrambled_friend_codes.size() == (friend_code_count * scrambled_friend_code_size),
"Wrong input buffer size");
std::vector<u8> unscrambled_friend_codes(friend_code_count * friend_code_size, 0);
// TODO(B3N30): Unscramble the codes and compare them against the friend list
// Only write 0 if the code isn't in friend list, otherwise write the
// unscrambled one
//
// Code for unscrambling (should be compared to HW):
// std::array<u16, 6> scambled_friend_code;
// Memory::ReadBlock(scrambled_friend_codes+(current*scrambled_friend_code_size),
// scambled_friend_code.data(), scrambled_friend_code_size); std::array<u16, 4>
// unscrambled_friend_code; unscrambled_friend_code[0] = scambled_friend_code[0] ^
// scambled_friend_code[5]; unscrambled_friend_code[1] = scambled_friend_code[1] ^
// scambled_friend_code[5]; unscrambled_friend_code[2] = scambled_friend_code[2] ^
// scambled_friend_code[5]; unscrambled_friend_code[3] = scambled_friend_code[3] ^
// scambled_friend_code[5];
LOG_WARNING(Service_FRD, "(STUBBED) called");
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(ResultSuccess);
rb.PushStaticBuffer(std::move(unscrambled_friend_codes), 0);
}
void Module::Interface::SetClientSdkVersion(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
u32 version = rp.Pop<u32>();
rp.PopPID();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_WARNING(Service_FRD, "(STUBBED) called, version: 0x{:08X}", version);
}
void Module::Interface::IsOnline(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push(frd->logged_in);
LOG_WARNING(Service_FRD, "(STUBBED) called");
}
void Module::Interface::HasLoggedIn(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FRD, "(STUBBED) called");
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess);
rb.Push(frd->logged_in);
}
void Module::Interface::Login(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FRD, "(STUBBED) called");
IPC::RequestParser rp(ctx);
frd->login_event = rp.PopObject<Kernel::Event>();
constexpr auto login_delay_ms = 500;
frd->login_delay_event = frd->system.CoreTiming().RegisterEvent(
"frd::login_event",
// Simulate a small login delay
[this](u64 thread_id, s64 cycle_late) {
frd->logged_in = true;
frd->login_event->Signal();
frd->system.CoreTiming().RemoveEvent(frd->login_delay_event);
});
frd->system.CoreTiming().ScheduleEvent(msToCycles(login_delay_ms), frd->login_delay_event);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
}
void Module::Interface::GetLastResponseResult(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FRD, "(STUBBED) called");
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
}
Module::Module(Core::System& system) : system(system){};
Module::~Module() = default;
void InstallInterfaces(Core::System& system) {
auto& service_manager = system.ServiceManager();
auto frd = std::make_shared<Module>(system);
std::make_shared<FRD_U>(frd)->InstallAsService(service_manager);
std::make_shared<FRD_A>(frd)->InstallAsService(service_manager);
}
} // namespace Service::FRD

View File

@@ -0,0 +1,285 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include "common/common_types.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Kernel {
class Event;
}
namespace Service::FRD {
struct FriendKey {
u32 friend_id;
u32 unknown;
u64 friend_code;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& friend_id;
ar& unknown;
ar& friend_code;
}
friend class boost::serialization::access;
};
struct MyPresence {
u8 unknown[0x12C];
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& unknown;
}
friend class boost::serialization::access;
};
struct Profile {
u8 region;
u8 country;
u8 area;
u8 language;
u8 platform;
INSERT_PADDING_BYTES(0x3);
};
static_assert(sizeof(Profile) == 0x8, "Profile has incorrect size");
struct Game {
u64 title_id;
u16 version;
INSERT_PADDING_BYTES(0x6);
};
static_assert(sizeof(Game) == 0x10, "Game has inccorect size");
struct ScreenName {
// 20 bytes according to 3dbrew
std::array<char16_t, 10> name;
};
static_assert(sizeof(ScreenName) == 0x14, "ScreenName has inccorect size");
struct Comment {
// 32 bytes according to 3dbrew
std::array<char16_t, 16> name;
};
static_assert(sizeof(Comment) == 0x20, "Comment has inccorect size");
class Module final {
public:
explicit Module(Core::System& system);
~Module();
class Interface : public ServiceFramework<Interface> {
public:
Interface(std::shared_ptr<Module> frd, const char* name, u32 max_session);
~Interface();
protected:
/**
* FRD::GetMyPresence service function
* Inputs:
* 64 : sizeof (MyPresence) << 14 | 2
* 65 : Address of MyPresence structure
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void GetMyPresence(Kernel::HLERequestContext& ctx);
/**
* FRD::GetFriendKeyList service function
* Inputs:
* 1 : Unknown
* 2 : Max friends count
* 65 : Address of FriendKey List
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : FriendKey count filled
*/
void GetFriendKeyList(Kernel::HLERequestContext& ctx);
/**
* FRD::GetFriendProfile service function
* Inputs:
* 1 : Friends count
* 2 : Friends count << 18 | 2
* 3 : Address of FriendKey List
* 64 : (count * sizeof (Profile)) << 10 | 2
* 65 : Address of Profiles List
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void GetFriendProfile(Kernel::HLERequestContext& ctx);
/**
* FRD::GetFriendAttributeFlags service function
* Inputs:
* 1 : Friends count
* 2 : Friends count << 18 | 2
* 3 : Address of FriendKey List
* 65 : Address of AttributeFlags
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void GetFriendAttributeFlags(Kernel::HLERequestContext& ctx);
/**
* FRD::GetMyFriendKey service function
* Inputs:
* none
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2-5 : FriendKey
*/
void GetMyFriendKey(Kernel::HLERequestContext& ctx);
/**
* FRD::GetMyScreenName service function
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : UTF16 encoded name (max 11 symbols)
*/
void GetMyScreenName(Kernel::HLERequestContext& ctx);
/**
* FRD::GetMyMii service function
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : MiiStoreData structure
*/
void GetMyMii(Kernel::HLERequestContext& ctx);
/**
* FRD::GetMyProfile service function
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2-3 : Profile structure
*/
void GetMyProfile(Kernel::HLERequestContext& ctx);
/**
* FRD::GetMyComment service function
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : UTF16 encoded comment (max 16 symbols)
*/
void GetMyComment(Kernel::HLERequestContext& ctx);
/**
* FRD::GetMyFavoriteGame service function
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2-3 : Game structure
*/
void GetMyFavoriteGame(Kernel::HLERequestContext& ctx);
/**
* FRD::GetMyPlayingGame service function
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2-3 : Game structure
*/
void GetMyPlayingGame(Kernel::HLERequestContext& ctx);
/**
* FRD::GetMyPreference service function
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Public mode (byte, 0 = private, non-zero = public)
* 3 : Show current game (byte, 0 = don't show, non-zero = show)
* 4 : Show game history (byte, 0 = don't show, non-zero = show)
*/
void GetMyPreference(Kernel::HLERequestContext& ctx);
/**
* FRD::UnscrambleLocalFriendCode service function
* Inputs:
* 1 : Friend code count
* 2 : ((count * 12) << 14) | 0x402
* 3 : Pointer to encoded friend codes. Each is 12 bytes large
* 64 : ((count * 8) << 14) | 2
* 65 : Pointer to write decoded local friend codes to. Each is 8 bytes large.
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void UnscrambleLocalFriendCode(Kernel::HLERequestContext& ctx);
/**
* FRD::SetClientSdkVersion service function
* Inputs:
* 1 : Used SDK Version
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void SetClientSdkVersion(Kernel::HLERequestContext& ctx);
/**
* FRD::Login service function
* Inputs:
* 65 : Address of unknown event
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void Login(Kernel::HLERequestContext& ctx);
/**
* FRD::IsOnline service function
* Inputs:
* none
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Online state (8-bit, 0 = not online, non-zero = online)
*/
void IsOnline(Kernel::HLERequestContext& ctx);
/**
* FRD::HasLoggedIn service function
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : If the user has logged in 1, otherwise 0
*/
void HasLoggedIn(Kernel::HLERequestContext& ctx);
/**
* FRD::GetLastResponseResult service function
* Inputs:
* none
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void GetLastResponseResult(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> frd;
};
private:
FriendKey my_friend_key = {0, 0, 0ull};
MyPresence my_presence = {};
bool logged_in = false;
std::shared_ptr<Kernel::Event> login_event;
Core::TimingEventType* login_delay_event;
Core::System& system;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& my_friend_key;
ar& my_presence;
ar& logged_in;
}
friend class boost::serialization::access;
};
void InstallInterfaces(Core::System& system);
} // namespace Service::FRD
SERVICE_CONSTRUCT(Service::FRD::Module)

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