First Commit
This commit is contained in:
217
src/core/hle/service/ac/ac.cpp
Normal file
217
src/core/hle/service/ac/ac.cpp
Normal 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
|
||||
187
src/core/hle/service/ac/ac.h
Normal file
187
src/core/hle/service/ac/ac.h
Normal 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)
|
||||
41
src/core/hle/service/ac/ac_i.cpp
Normal file
41
src/core/hle/service/ac/ac_i.cpp
Normal 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)
|
||||
23
src/core/hle/service/ac/ac_i.h
Normal file
23
src/core/hle/service/ac/ac_i.h
Normal 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)
|
||||
41
src/core/hle/service/ac/ac_u.cpp
Normal file
41
src/core/hle/service/ac/ac_u.cpp
Normal 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)
|
||||
23
src/core/hle/service/ac/ac_u.h
Normal file
23
src/core/hle/service/ac/ac_u.h
Normal 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)
|
||||
69
src/core/hle/service/act/act.cpp
Normal file
69
src/core/hle/service/act/act.cpp
Normal 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
|
||||
72
src/core/hle/service/act/act.h
Normal file
72
src/core/hle/service/act/act.h
Normal 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
|
||||
77
src/core/hle/service/act/act_a.cpp
Normal file
77
src/core/hle/service/act/act_a.cpp
Normal 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)
|
||||
22
src/core/hle/service/act/act_a.h
Normal file
22
src/core/hle/service/act/act_a.h
Normal 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)
|
||||
757
src/core/hle/service/act/act_errors.cpp
Normal file
757
src/core/hle/service/act/act_errors.cpp
Normal 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
|
||||
613
src/core/hle/service/act/act_errors.h
Normal file
613
src/core/hle/service/act/act_errors.h
Normal 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
|
||||
46
src/core/hle/service/act/act_u.cpp
Normal file
46
src/core/hle/service/act/act_u.cpp
Normal 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)
|
||||
22
src/core/hle/service/act/act_u.h
Normal file
22
src/core/hle/service/act/act_u.h
Normal 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)
|
||||
2480
src/core/hle/service/am/am.cpp
Normal file
2480
src/core/hle/service/am/am.cpp
Normal file
File diff suppressed because it is too large
Load Diff
810
src/core/hle/service/am/am.h
Normal file
810
src/core/hle/service/am/am.h
Normal 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)
|
||||
33
src/core/hle/service/am/am_app.cpp
Normal file
33
src/core/hle/service/am/am_app.cpp
Normal 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)
|
||||
22
src/core/hle/service/am/am_app.h
Normal file
22
src/core/hle/service/am/am_app.h
Normal 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)
|
||||
130
src/core/hle/service/am/am_net.cpp
Normal file
130
src/core/hle/service/am/am_net.cpp
Normal 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)
|
||||
22
src/core/hle/service/am/am_net.h
Normal file
22
src/core/hle/service/am/am_net.h
Normal 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)
|
||||
78
src/core/hle/service/am/am_sys.cpp
Normal file
78
src/core/hle/service/am/am_sys.cpp
Normal 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)
|
||||
22
src/core/hle/service/am/am_sys.h
Normal file
22
src/core/hle/service/am/am_sys.h
Normal 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)
|
||||
90
src/core/hle/service/am/am_u.cpp
Normal file
90
src/core/hle/service/am/am_u.cpp
Normal 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)
|
||||
22
src/core/hle/service/am/am_u.h
Normal file
22
src/core/hle/service/am/am_u.h
Normal 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)
|
||||
1600
src/core/hle/service/apt/applet_manager.cpp
Normal file
1600
src/core/hle/service/apt/applet_manager.cpp
Normal file
File diff suppressed because it is too large
Load Diff
564
src/core/hle/service/apt/applet_manager.h
Normal file
564
src/core/hle/service/apt/applet_manager.h
Normal 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)
|
||||
1483
src/core/hle/service/apt/apt.cpp
Normal file
1483
src/core/hle/service/apt/apt.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1097
src/core/hle/service/apt/apt.h
Normal file
1097
src/core/hle/service/apt/apt.h
Normal file
File diff suppressed because it is too large
Load Diff
114
src/core/hle/service/apt/apt_a.cpp
Normal file
114
src/core/hle/service/apt/apt_a.cpp
Normal 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)
|
||||
22
src/core/hle/service/apt/apt_a.h
Normal file
22
src/core/hle/service/apt/apt_a.h
Normal 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)
|
||||
114
src/core/hle/service/apt/apt_s.cpp
Normal file
114
src/core/hle/service/apt/apt_s.cpp
Normal 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)
|
||||
29
src/core/hle/service/apt/apt_s.h
Normal file
29
src/core/hle/service/apt/apt_s.h
Normal 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)
|
||||
114
src/core/hle/service/apt/apt_u.cpp
Normal file
114
src/core/hle/service/apt/apt_u.cpp
Normal 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)
|
||||
29
src/core/hle/service/apt/apt_u.h
Normal file
29
src/core/hle/service/apt/apt_u.h
Normal 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)
|
||||
106
src/core/hle/service/apt/bcfnt/bcfnt.cpp
Normal file
106
src/core/hle/service/apt/bcfnt/bcfnt.cpp
Normal 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(§ion_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(§ion_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
|
||||
89
src/core/hle/service/apt/bcfnt/bcfnt.h
Normal file
89
src/core/hle/service/apt/bcfnt/bcfnt.h
Normal 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
|
||||
13
src/core/hle/service/apt/errors.h
Normal file
13
src/core/hle/service/apt/errors.h
Normal 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
|
||||
57
src/core/hle/service/apt/ns.cpp
Normal file
57
src/core/hle/service/apt/ns.cpp
Normal 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
|
||||
25
src/core/hle/service/apt/ns.h
Normal file
25
src/core/hle/service/apt/ns.h
Normal 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
|
||||
23
src/core/hle/service/apt/ns_c.cpp
Normal file
23
src/core/hle/service/apt/ns_c.cpp
Normal 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)
|
||||
25
src/core/hle/service/apt/ns_c.h
Normal file
25
src/core/hle/service/apt/ns_c.h
Normal 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)
|
||||
36
src/core/hle/service/apt/ns_s.cpp
Normal file
36
src/core/hle/service/apt/ns_s.cpp
Normal 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)
|
||||
25
src/core/hle/service/apt/ns_s.h
Normal file
25
src/core/hle/service/apt/ns_s.h
Normal 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)
|
||||
1064
src/core/hle/service/boss/boss.cpp
Normal file
1064
src/core/hle/service/boss/boss.cpp
Normal file
File diff suppressed because it is too large
Load Diff
999
src/core/hle/service/boss/boss.h
Normal file
999
src/core/hle/service/boss/boss.h
Normal 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)
|
||||
89
src/core/hle/service/boss/boss_p.cpp
Normal file
89
src/core/hle/service/boss/boss_p.cpp
Normal 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)
|
||||
22
src/core/hle/service/boss/boss_p.h
Normal file
22
src/core/hle/service/boss/boss_p.h
Normal 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)
|
||||
77
src/core/hle/service/boss/boss_u.cpp
Normal file
77
src/core/hle/service/boss/boss_u.cpp
Normal 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)
|
||||
22
src/core/hle/service/boss/boss_u.h
Normal file
22
src/core/hle/service/boss/boss_u.h
Normal 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)
|
||||
542
src/core/hle/service/boss/online_service.cpp
Normal file
542
src/core/hle/service/boss/online_service.cpp
Normal 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>>(¤t_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
|
||||
216
src/core/hle/service/boss/online_service.h
Normal file
216
src/core/hle/service/boss/online_service.h
Normal 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
|
||||
1196
src/core/hle/service/cam/cam.cpp
Normal file
1196
src/core/hle/service/cam/cam.cpp
Normal file
File diff suppressed because it is too large
Load Diff
775
src/core/hle/service/cam/cam.h
Normal file
775
src/core/hle/service/cam/cam.h
Normal 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)
|
||||
83
src/core/hle/service/cam/cam_c.cpp
Normal file
83
src/core/hle/service/cam/cam_c.cpp
Normal 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)
|
||||
22
src/core/hle/service/cam/cam_c.h
Normal file
22
src/core/hle/service/cam/cam_c.h
Normal 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)
|
||||
125
src/core/hle/service/cam/cam_params.h
Normal file
125
src/core/hle/service/cam/cam_params.h
Normal 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
|
||||
18
src/core/hle/service/cam/cam_q.cpp
Normal file
18
src/core/hle/service/cam/cam_q.cpp
Normal 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)
|
||||
21
src/core/hle/service/cam/cam_q.h
Normal file
21
src/core/hle/service/cam/cam_q.h
Normal 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)
|
||||
83
src/core/hle/service/cam/cam_s.cpp
Normal file
83
src/core/hle/service/cam/cam_s.cpp
Normal 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)
|
||||
22
src/core/hle/service/cam/cam_s.h
Normal file
22
src/core/hle/service/cam/cam_s.h
Normal 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)
|
||||
83
src/core/hle/service/cam/cam_u.cpp
Normal file
83
src/core/hle/service/cam/cam_u.cpp
Normal 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)
|
||||
22
src/core/hle/service/cam/cam_u.h
Normal file
22
src/core/hle/service/cam/cam_u.h
Normal 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)
|
||||
733
src/core/hle/service/cam/y2r_u.cpp
Normal file
733
src/core/hle/service/cam/y2r_u.cpp
Normal 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
|
||||
370
src/core/hle/service/cam/y2r_u.h
Normal file
370
src/core/hle/service/cam/y2r_u.h
Normal 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)
|
||||
1482
src/core/hle/service/cecd/cecd.cpp
Normal file
1482
src/core/hle/service/cecd/cecd.cpp
Normal file
File diff suppressed because it is too large
Load Diff
645
src/core/hle/service/cecd/cecd.h
Normal file
645
src/core/hle/service/cecd/cecd.h
Normal 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)
|
||||
26
src/core/hle/service/cecd/cecd_ndm.cpp
Normal file
26
src/core/hle/service/cecd/cecd_ndm.cpp
Normal 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
|
||||
22
src/core/hle/service/cecd/cecd_ndm.h
Normal file
22
src/core/hle/service/cecd/cecd_ndm.h
Normal 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)
|
||||
45
src/core/hle/service/cecd/cecd_s.cpp
Normal file
45
src/core/hle/service/cecd/cecd_s.cpp
Normal 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
|
||||
22
src/core/hle/service/cecd/cecd_s.h
Normal file
22
src/core/hle/service/cecd/cecd_s.h
Normal 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)
|
||||
43
src/core/hle/service/cecd/cecd_u.cpp
Normal file
43
src/core/hle/service/cecd/cecd_u.cpp
Normal 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
|
||||
22
src/core/hle/service/cecd/cecd_u.h
Normal file
22
src/core/hle/service/cecd/cecd_u.h
Normal 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)
|
||||
1017
src/core/hle/service/cfg/cfg.cpp
Normal file
1017
src/core/hle/service/cfg/cfg.cpp
Normal file
File diff suppressed because it is too large
Load Diff
705
src/core/hle/service/cfg/cfg.h
Normal file
705
src/core/hle/service/cfg/cfg.h
Normal 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)
|
||||
138
src/core/hle/service/cfg/cfg_defaults.cpp
Normal file
138
src/core/hle/service/cfg/cfg_defaults.cpp
Normal 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
|
||||
35
src/core/hle/service/cfg/cfg_defaults.h
Normal file
35
src/core/hle/service/cfg/cfg_defaults.h
Normal 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
|
||||
66
src/core/hle/service/cfg/cfg_i.cpp
Normal file
66
src/core/hle/service/cfg/cfg_i.cpp
Normal 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
|
||||
22
src/core/hle/service/cfg/cfg_i.h
Normal file
22
src/core/hle/service/cfg/cfg_i.h
Normal 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)
|
||||
24
src/core/hle/service/cfg/cfg_nor.cpp
Normal file
24
src/core/hle/service/cfg/cfg_nor.cpp
Normal 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
|
||||
21
src/core/hle/service/cfg/cfg_nor.h
Normal file
21
src/core/hle/service/cfg/cfg_nor.h
Normal 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)
|
||||
44
src/core/hle/service/cfg/cfg_s.cpp
Normal file
44
src/core/hle/service/cfg/cfg_s.cpp
Normal 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
|
||||
22
src/core/hle/service/cfg/cfg_s.h
Normal file
22
src/core/hle/service/cfg/cfg_s.h
Normal 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)
|
||||
32
src/core/hle/service/cfg/cfg_u.cpp
Normal file
32
src/core/hle/service/cfg/cfg_u.cpp
Normal 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
|
||||
22
src/core/hle/service/cfg/cfg_u.h
Normal file
22
src/core/hle/service/cfg/cfg_u.h
Normal 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)
|
||||
535
src/core/hle/service/csnd/csnd_snd.cpp
Normal file
535
src/core/hle/service/csnd/csnd_snd.cpp
Normal 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
|
||||
280
src/core/hle/service/csnd/csnd_snd.h
Normal file
280
src/core/hle/service/csnd/csnd_snd.h
Normal 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)
|
||||
20
src/core/hle/service/dlp/dlp.cpp
Normal file
20
src/core/hle/service/dlp/dlp.cpp
Normal 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
|
||||
18
src/core/hle/service/dlp/dlp.h
Normal file
18
src/core/hle/service/dlp/dlp.h
Normal 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
|
||||
42
src/core/hle/service/dlp/dlp_clnt.cpp
Normal file
42
src/core/hle/service/dlp/dlp_clnt.cpp
Normal 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
|
||||
22
src/core/hle/service/dlp/dlp_clnt.h
Normal file
22
src/core/hle/service/dlp/dlp_clnt.h
Normal 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)
|
||||
39
src/core/hle/service/dlp/dlp_fkcl.cpp
Normal file
39
src/core/hle/service/dlp/dlp_fkcl.cpp
Normal 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
|
||||
22
src/core/hle/service/dlp/dlp_fkcl.h
Normal file
22
src/core/hle/service/dlp/dlp_fkcl.h
Normal 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)
|
||||
52
src/core/hle/service/dlp/dlp_srvr.cpp
Normal file
52
src/core/hle/service/dlp/dlp_srvr.cpp
Normal 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
|
||||
24
src/core/hle/service/dlp/dlp_srvr.h
Normal file
24
src/core/hle/service/dlp/dlp_srvr.h
Normal 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)
|
||||
417
src/core/hle/service/dsp/dsp_dsp.cpp
Normal file
417
src/core/hle/service/dsp/dsp_dsp.cpp
Normal 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
|
||||
287
src/core/hle/service/dsp/dsp_dsp.h
Normal file
287
src/core/hle/service/dsp/dsp_dsp.h
Normal 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)
|
||||
267
src/core/hle/service/err/err_f.cpp
Normal file
267
src/core/hle/service/err/err_f.cpp
Normal 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
|
||||
50
src/core/hle/service/err/err_f.h
Normal file
50
src/core/hle/service/err/err_f.h
Normal 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);
|
||||
}
|
||||
294
src/core/hle/service/frd/frd.cpp
Normal file
294
src/core/hle/service/frd/frd.cpp
Normal 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
|
||||
285
src/core/hle/service/frd/frd.h
Normal file
285
src/core/hle/service/frd/frd.h
Normal 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
Reference in New Issue
Block a user