Infrastructure for applets to be implemented

This removes a stub for an applet and implements several applet related service calls.
This commit is contained in:
MCredstoner2004 2022-04-09 22:16:11 -05:00 committed by PixelyIon
parent 164d4852fa
commit dec0571eee
21 changed files with 500 additions and 44 deletions

View File

@ -241,8 +241,12 @@ add_library(skyline SHARED
${source_DIR}/skyline/services/am/controller/ISelfController.cpp ${source_DIR}/skyline/services/am/controller/ISelfController.cpp
${source_DIR}/skyline/services/am/controller/IWindowController.cpp ${source_DIR}/skyline/services/am/controller/IWindowController.cpp
${source_DIR}/skyline/services/am/storage/IStorage.cpp ${source_DIR}/skyline/services/am/storage/IStorage.cpp
${source_DIR}/skyline/services/am/storage/VectorIStorage.cpp
${source_DIR}/skyline/services/am/storage/TransferMemoryIStorage.cpp
${source_DIR}/skyline/services/am/storage/IStorageAccessor.cpp ${source_DIR}/skyline/services/am/storage/IStorageAccessor.cpp
${source_DIR}/skyline/services/am/applet/ILibraryAppletAccessor.cpp ${source_DIR}/skyline/services/am/applet/ILibraryAppletAccessor.cpp
${source_DIR}/skyline/services/am/applet/IApplet.cpp
${source_DIR}/skyline/applet/applet_creator.cpp
${source_DIR}/skyline/services/codec/IHardwareOpusDecoder.cpp ${source_DIR}/skyline/services/codec/IHardwareOpusDecoder.cpp
${source_DIR}/skyline/services/codec/IHardwareOpusDecoderManager.cpp ${source_DIR}/skyline/services/codec/IHardwareOpusDecoderManager.cpp
${source_DIR}/skyline/services/hid/IHidServer.cpp ${source_DIR}/skyline/services/hid/IHidServer.cpp

View File

@ -0,0 +1,18 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include "applet_creator.h"
namespace skyline::applet {
std::shared_ptr<service::am::IApplet> CreateApplet(
const DeviceState &state, service::ServiceManager &manager,
applet::AppletId appletId, const std::shared_ptr<kernel::type::KEvent> &onAppletStateChanged,
const std::shared_ptr<kernel::type::KEvent> &onNormalDataPushFromApplet,
const std::shared_ptr<kernel::type::KEvent> &onInteractiveDataPushFromApplet,
service::applet::LibraryAppletMode appletMode) {
switch (appletId) {
default:
throw exception("Unimplemented Applet: 0x{:X} ({})", static_cast<u32>(appletId), ToString(appletId));
}
}
}

View File

@ -0,0 +1,81 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <common/macros.h>
#include <services/am/applet/IApplet.h>
#include <services/applet/common_arguments.h>
namespace skyline::applet {
#define APPLETS \
APPLET_ENTRY(None, 0x000) \
APPLET_ENTRY(Application, 0x001) \
APPLET_ENTRY(OverlayApplet, 0x002) \
APPLET_ENTRY(SystemAppletMenu, 0x003) \
APPLET_ENTRY(SystemApplication, 0x004) \
APPLET_ENTRY(LibraryAppletAuth, 0x00A) \
APPLET_ENTRY(LibraryAppletCabinet, 0x00B) \
APPLET_ENTRY(LibraryAppletController, 0x00C) \
APPLET_ENTRY(LibraryAppletDataErase, 0x00D) \
APPLET_ENTRY(LibraryAppletError, 0x00E) \
APPLET_ENTRY(LibraryAppletNetConnect, 0x00F) \
APPLET_ENTRY(LibraryAppletPlayerSelect, 0x010) \
APPLET_ENTRY(LibraryAppletSwkbd, 0x011) \
APPLET_ENTRY(LibraryAppletMiiEdit, 0x012) \
APPLET_ENTRY(LibraryAppletWeb, 0x013) \
APPLET_ENTRY(LibraryAppletShop, 0x014) \
APPLET_ENTRY(LibraryAppletPhotoViewer, 0x015) \
APPLET_ENTRY(LibraryAppletSet, 0x016) \
APPLET_ENTRY(LibraryAppletOfflineWeb, 0x017) \
APPLET_ENTRY(LibraryAppletLoginShare, 0x018) \
APPLET_ENTRY(LibraryAppletWifiWebAuth, 0x019) \
APPLET_ENTRY(LibraryAppletMyPage, 0x01A) \
APPLET_ENTRY(LibraryAppletGift, 0x01B) \
APPLET_ENTRY(LibraryAppletUserMigration, 0x01C) \
APPLET_ENTRY(LibraryAppletPreomiaSys, 0x01D) \
APPLET_ENTRY(LibraryAppletStory, 0x01E) \
APPLET_ENTRY(LibraryAppletPreomiaUsr, 0x01F) \
APPLET_ENTRY(LibraryAppletPreomiaUsrDummy, 0x020) \
APPLET_ENTRY(LibraryAppletSample, 0x021) \
APPLET_ENTRY(DevlopmentTool, 0x3E8) \
APPLET_ENTRY(CombinationLA, 0x3F1) \
APPLET_ENTRY(AeSystemApplet, 0x3F2) \
APPLET_ENTRY(AeOverlayApplet, 0x3F3) \
APPLET_ENTRY(AeStarter, 0x3F4) \
APPLET_ENTRY(AeLibraryAppletAlone, 0x3F5) \
APPLET_ENTRY(AeLibraryApplet1, 0x3F6) \
APPLET_ENTRY(AeLibraryApplet2, 0x3F7) \
APPLET_ENTRY(AeLibraryApplet3, 0x3F8) \
APPLET_ENTRY(AeLibraryApplet4, 0x3F9) \
APPLET_ENTRY(AppletISA, 0x3FA) \
APPLET_ENTRY(AppletIOA, 0x3FB) \
APPLET_ENTRY(AppletISTA, 0x3FC) \
APPLET_ENTRY(AppletILA1, 0x3FD) \
APPLET_ENTRY(AppletILA2, 0x3FE)
/**
* @url https://switchbrew.org/wiki/Applet_Manager_services#AppletId
*/
enum class AppletId : u32 {
#define APPLET_ENTRY(name, id) name = id,
APPLETS
#undef APPLET_ENTRY
};
#define APPLET_ENTRY(name, id) ENUM_CASE(name);
ENUM_STRING(AppletId, APPLETS)
#undef APPLET_ENTRY
/**
* @brief Creates an Applet of the appropiate class depending on the AppletId
*/
std::shared_ptr<service::am::IApplet> CreateApplet(
const DeviceState &state, service::ServiceManager &manager,
applet::AppletId appletId, const std::shared_ptr<kernel::type::KEvent> &onAppletStateChanged,
const std::shared_ptr<kernel::type::KEvent> &onNormalDataPushFromApplet,
const std::shared_ptr<kernel::type::KEvent> &onInteractiveDataPushFromApplet,
service::applet::LibraryAppletMode appletMode);
}

View File

@ -4,6 +4,8 @@
#pragma once #pragma once
#include <common.h> #include <common.h>
#include "types/KSession.h"
#include "types/KProcess.h"
namespace skyline { namespace skyline {
namespace constant { namespace constant {
@ -231,6 +233,20 @@ namespace skyline {
return view; return view;
} }
/**
* @brief Pops a Service object from the response as a domain or kernel handle
*/
template<typename ServiceType>
std::shared_ptr<ServiceType> PopService(u32 id, type::KSession &session) {
std::shared_ptr<service::BaseService> serviceObject;
if (session.isDomain)
serviceObject = session.domains.at(domainObjects.at(id));
else
serviceObject = session.state.process->GetHandle<kernel::type::KSession>(moveHandles.at(id))->serviceObject;
return std::static_pointer_cast<ServiceType>(serviceObject);
}
/** /**
* @brief Skips an object to pop off the top * @brief Skips an object to pop off the top
*/ */

View File

@ -0,0 +1,43 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include "IApplet.h"
namespace skyline::service::am {
IApplet::IApplet(const DeviceState &state, ServiceManager &manager, std::shared_ptr<kernel::type::KEvent> onAppletStateChanged, std::shared_ptr<kernel::type::KEvent> onNormalDataPushFromApplet, std::shared_ptr<kernel::type::KEvent> onInteractiveDataPushFromApplet, applet::LibraryAppletMode appletMode)
: BaseService(state, manager), onAppletStateChanged(std::move(onAppletStateChanged)),
onNormalDataPushFromApplet(std::move(onNormalDataPushFromApplet)),
onInteractiveDataPushFromApplet(std::move(onInteractiveDataPushFromApplet)) {}
IApplet::~IApplet() = default;
void IApplet::PushNormalDataAndSignal(const std::shared_ptr<IStorage> &data) {
normalOutputData.emplace(data);
onNormalDataPushFromApplet->Signal();
}
void IApplet::PushInteractiveDataAndSignal(const std::shared_ptr<IStorage> &data) {
interactiveOutputData.emplace(data);
onInteractiveDataPushFromApplet->Signal();
}
std::shared_ptr<IStorage> IApplet::PopNormalAndClear() {
if (normalOutputData.empty())
return {};
std::shared_ptr<IStorage> data(normalOutputData.front());
normalOutputData.pop();
onNormalDataPushFromApplet->ResetSignal();
return data;
}
std::shared_ptr<IStorage> IApplet::PopInteractiveAndClear() {
if (interactiveOutputData.empty())
return {};
std::shared_ptr<IStorage> data(interactiveOutputData.front());
interactiveOutputData.pop();
onInteractiveDataPushFromApplet->ResetSignal();
return data;
}
}

View File

@ -0,0 +1,71 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <queue>
#include <services/am/storage/IStorage.h>
#include <kernel/types/KEvent.h>
#include <services/applet/common_arguments.h>
namespace skyline::service::am {
/**
* @brief The base class all Applets have to inherit from
*/
class IApplet : public BaseService {
private:
std::shared_ptr<kernel::type::KEvent> onNormalDataPushFromApplet;
std::shared_ptr<kernel::type::KEvent> onInteractiveDataPushFromApplet;
protected:
std::shared_ptr<kernel::type::KEvent> onAppletStateChanged;
std::queue<std::shared_ptr<IStorage>> normalOutputData; //!< Stores data sent by the applet so the guest can read it when it needs to
std::queue<std::shared_ptr<IStorage>> interactiveOutputData; //!< Stores interactive data sent by the applet so the guest can read it when it needs to
/**
* @brief Utility to send data to the guest and trigger the onNormalDataPushFromApplet event
*/
void PushNormalDataAndSignal(const std::shared_ptr<IStorage> &data);
/**
* @brief Utility to send data to the guest and trigger the onInteractiveDataPushFromApplet event
*/
void PushInteractiveDataAndSignal(const std::shared_ptr<IStorage> &data);
public:
IApplet(const DeviceState &state, ServiceManager &manager, std::shared_ptr<kernel::type::KEvent> onAppletStateChanged, std::shared_ptr<kernel::type::KEvent> onNormalDataPushFromApplet, std::shared_ptr<kernel::type::KEvent> onInteractiveDataPushFromApplet, applet::LibraryAppletMode appletMode);
virtual ~IApplet();
/**
* @brief Called when the applet is started
*/
virtual Result Start() = 0;
/**
* @brief Called when the applet is stopped
*/
virtual Result GetResult() = 0;
/**
* @brief Called when data is pushed to the applet by the guest through the normal queue
*/
virtual void PushNormalDataToApplet(std::shared_ptr<IStorage> data) = 0;
/**
* @brief Called when data is pushed to the applet by the guest through the interactive queue
*/
virtual void PushInteractiveDataToApplet(std::shared_ptr<IStorage> data) = 0;
/**
* @brief Used by ILibraryAppletAccessor to pop data from the normal queue and reset the corresponding event
*/
std::shared_ptr<IStorage> PopNormalAndClear();
/**
* @brief Used by ILibraryAppletAccessor to pop data from the interactive queue and reset the corresponding event
*/
std::shared_ptr<IStorage> PopInteractiveAndClear();
};
}

View File

@ -1,47 +1,73 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
// Copyright © 2020 Ryujinx Team and Contributors
#include <kernel/types/KProcess.h> #include <kernel/types/KProcess.h>
#include <services/account/IAccountServiceForApplication.h> #include <services/account/IAccountServiceForApplication.h>
#include <services/am/storage/IStorage.h> #include <services/am/storage/VectorIStorage.h>
#include <applet/applet_creator.h>
#include "ILibraryAppletAccessor.h" #include "ILibraryAppletAccessor.h"
namespace skyline::service::am { namespace skyline::service::am {
ILibraryAppletAccessor::ILibraryAppletAccessor(const DeviceState &state, ServiceManager &manager) : stateChangeEvent(std::make_shared<type::KEvent>(state, false)), BaseService(state, manager) {} ILibraryAppletAccessor::ILibraryAppletAccessor(const DeviceState &state, ServiceManager &manager, skyline::applet::AppletId appletId, applet::LibraryAppletMode appletMode)
: BaseService(state, manager),
stateChangeEvent(std::make_shared<type::KEvent>(state, false)),
popNormalOutDataEvent((std::make_shared<type::KEvent>(state, false))),
popInteractiveOutDataEvent((std::make_shared<type::KEvent>(state, false))),
applet(skyline::applet::CreateApplet(state, manager, appletId, stateChangeEvent, popNormalOutDataEvent, popInteractiveOutDataEvent, appletMode)) {
stateChangeEventHandle = state.process->InsertItem(stateChangeEvent);
popNormalOutDataEventHandle = state.process->InsertItem(popNormalOutDataEvent);
popInteractiveOutDataEventHandle = state.process->InsertItem(popInteractiveOutDataEvent);
Logger::Debug("Applet accessor for {} ID created with appletMode 0x{:X}", ToString(appletId), appletMode);
}
Result ILibraryAppletAccessor::GetAppletStateChangedEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result ILibraryAppletAccessor::GetAppletStateChangedEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
stateChangeEvent->Signal(); Logger::Debug("Applet State Change Event Handle: 0x{:X}", stateChangeEventHandle);
response.copyHandles.push_back(stateChangeEventHandle);
KHandle handle{state.process->InsertItem(stateChangeEvent)};
Logger::Debug("Applet State Change Event Handle: 0x{:X}", handle);
response.copyHandles.push_back(handle);
return {}; return {};
} }
Result ILibraryAppletAccessor::Start(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result ILibraryAppletAccessor::Start(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
return {}; return applet->Start();
} }
Result ILibraryAppletAccessor::GetResult(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result ILibraryAppletAccessor::GetResult(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
return {}; return applet->GetResult();
} }
Result ILibraryAppletAccessor::PushInData(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result ILibraryAppletAccessor::PushInData(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
applet->PushNormalDataToApplet(request.PopService<IStorage>(0, session));
return {};
}
Result ILibraryAppletAccessor::PushInteractiveInData(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
applet->PushInteractiveDataToApplet(request.PopService<IStorage>(0, session));
return {}; return {};
} }
Result ILibraryAppletAccessor::PopOutData(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result ILibraryAppletAccessor::PopOutData(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
constexpr u32 LaunchParameterMagic{0xC79497CA}; //!< The magic of the application launch parameters if (auto outIStorage{applet->PopNormalAndClear()}) {
constexpr size_t LaunchParameterSize{0x88}; //!< The size of the launch parameter IStorage manager.RegisterService(outIStorage, session, response);
return {};
}
return result::NotAvailable;
}
auto storageService{std::make_shared<IStorage>(state, manager, LaunchParameterSize)}; Result ILibraryAppletAccessor::PopInteractiveOutData(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
if (auto outIStorage = applet->PopInteractiveAndClear()) {
manager.RegisterService(outIStorage, session, response);
return {};
}
return result::NotAvailable;
}
storageService->Push<u32>(LaunchParameterMagic); Result ILibraryAppletAccessor::GetPopOutDataEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
storageService->Push<u32>(1); response.copyHandles.push_back(popNormalOutDataEventHandle);
storageService->Push(constant::DefaultUserId); return {};
}
manager.RegisterService(storageService, session, response); Result ILibraryAppletAccessor::GetPopInteractiveOutDataEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
response.copyHandles.push_back(popInteractiveOutDataEventHandle);
return {}; return {};
} }
} }

View File

@ -5,21 +5,38 @@
#include <kernel/types/KEvent.h> #include <kernel/types/KEvent.h>
#include <services/serviceman.h> #include <services/serviceman.h>
#include "IApplet.h"
#include <applet/applet_creator.h>
namespace skyline::service::am { namespace skyline::service::am {
namespace result {
constexpr Result ObjectInvalid(128, 500);
constexpr Result OutOfBounds(128, 503);
constexpr Result NotAvailable(128, 2);
}
/** /**
* @brief ILibraryAppletAccessor is used to communicate with the library applet * @brief ILibraryAppletAccessor is used to communicate with the library applet
* @url https://switchbrew.org/wiki/Applet_Manager_services#ILibraryAppletAccessor * @url https://switchbrew.org/wiki/Applet_Manager_services#ILibraryAppletAccessor
*/ */
class ILibraryAppletAccessor : public BaseService { class ILibraryAppletAccessor : public BaseService {
private: private:
std::shared_ptr<kernel::type::KEvent> stateChangeEvent; //!< This KEvent is triggered when the applet's state changes std::shared_ptr<kernel::type::KEvent> stateChangeEvent;
std::shared_ptr<kernel::type::KEvent> popNormalOutDataEvent;
std::shared_ptr<kernel::type::KEvent> popInteractiveOutDataEvent;
KHandle stateChangeEventHandle{};
KHandle popNormalOutDataEventHandle{};
KHandle popInteractiveOutDataEventHandle{};
std::shared_ptr<IApplet> applet;
public: public:
ILibraryAppletAccessor(const DeviceState &state, ServiceManager &manager); ILibraryAppletAccessor(const DeviceState &state, ServiceManager &manager, skyline::applet::AppletId appletId, applet::LibraryAppletMode appletMode);
/** /**
* @brief Returns a handle to the library applet state change event * @brief Returns a handle to the library applet state change event
* @url https://switchbrew.org/wiki/Applet_Manager_services#GetAppletStateChangedEvent
*/ */
Result GetAppletStateChangedEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); Result GetAppletStateChangedEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
@ -47,12 +64,40 @@ namespace skyline::service::am {
*/ */
Result PopOutData(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); Result PopOutData(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Pushes in data to the library applet, through the interactive queue
* @url https://switchbrew.org/wiki/Applet_Manager_services#PushInteractiveInData
*/
Result PushInteractiveInData(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Receives data from the library applet, from the interactive queue
* @url https://switchbrew.org/wiki/Applet_Manager_services#PopInteractiveOutData
*/
Result PopInteractiveOutData(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Gets te KEvent for when there's data to be popped by the guest on the normal queue
* @url https://switchbrew.org/wiki/Applet_Manager_services#GetPopOutDataEvent
*/
Result GetPopOutDataEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Gets the KEvent for when there's data to be popped by the guest on the interactive queue
* @url https://switchbrew.org/wiki/Applet_Manager_services#GetPopInteractiveOutDataEvent
*/
Result GetPopInteractiveOutDataEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
SERVICE_DECL( SERVICE_DECL(
SFUNC(0x0, ILibraryAppletAccessor, GetAppletStateChangedEvent), SFUNC(0x0, ILibraryAppletAccessor, GetAppletStateChangedEvent),
SFUNC(0xA, ILibraryAppletAccessor, Start), SFUNC(0xA, ILibraryAppletAccessor, Start),
SFUNC(0x1E, ILibraryAppletAccessor, GetResult), SFUNC(0x1E, ILibraryAppletAccessor, GetResult),
SFUNC(0x64, ILibraryAppletAccessor, PushInData), SFUNC(0x64, ILibraryAppletAccessor, PushInData),
SFUNC(0x65, ILibraryAppletAccessor, PopOutData) SFUNC(0x65, ILibraryAppletAccessor, PopOutData),
SFUNC(0x67, ILibraryAppletAccessor, PushInteractiveInData),
SFUNC(0x68, ILibraryAppletAccessor, PopInteractiveOutData),
SFUNC(0x69, ILibraryAppletAccessor, GetPopOutDataEvent),
SFUNC(0x6A, ILibraryAppletAccessor, GetPopInteractiveOutDataEvent)
) )
}; };
} }

View File

@ -7,7 +7,7 @@
#include <os.h> #include <os.h>
#include <kernel/types/KProcess.h> #include <kernel/types/KProcess.h>
#include <services/account/IAccountServiceForApplication.h> #include <services/account/IAccountServiceForApplication.h>
#include <services/am/storage/IStorage.h> #include <services/am/storage/VectorIStorage.h>
#include "IApplicationFunctions.h" #include "IApplicationFunctions.h"
namespace skyline::service::am { namespace skyline::service::am {
@ -29,7 +29,7 @@ namespace skyline::service::am {
return result::NotAvailable; return result::NotAvailable;
case LaunchParameterKind::PreselectedUser: { case LaunchParameterKind::PreselectedUser: {
storageService = std::make_shared<IStorage>(state, manager, LaunchParameterSize); storageService = std::make_shared<VectorIStorage>(state, manager, LaunchParameterSize);
storageService->Push<u32>(LaunchParameterMagic); storageService->Push<u32>(LaunchParameterMagic);
storageService->Push<u32>(1); storageService->Push<u32>(1);

View File

@ -1,25 +1,38 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <services/am/storage/IStorage.h> #include <services/am/storage/VectorIStorage.h>
#include <services/am/storage/TransferMemoryIStorage.h>
#include <services/am/applet/ILibraryAppletAccessor.h> #include <services/am/applet/ILibraryAppletAccessor.h>
#include <kernel/types/KTransferMemory.h>
#include <kernel/types/KProcess.h>
#include "ILibraryAppletCreator.h" #include "ILibraryAppletCreator.h"
namespace skyline::service::am { namespace skyline::service::am {
ILibraryAppletCreator::ILibraryAppletCreator(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {} ILibraryAppletCreator::ILibraryAppletCreator(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {}
Result ILibraryAppletCreator::CreateLibraryApplet(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result ILibraryAppletCreator::CreateLibraryApplet(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
manager.RegisterService(SRVREG(ILibraryAppletAccessor), session, response); auto appletId{request.Pop<skyline::applet::AppletId>()};
auto appletMode{request.Pop<applet::LibraryAppletMode>()};
manager.RegisterService(SRVREG(ILibraryAppletAccessor, appletId, appletMode), session, response);
return {}; return {};
} }
Result ILibraryAppletCreator::CreateStorage(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result ILibraryAppletCreator::CreateStorage(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto size{request.Pop<i64>()}; auto size{request.Pop<i64>()};
if (size < 0) if (size < 0)
throw exception("Cannot create an IStorage with a negative size"); throw exception("Cannot create an IStorage with a negative size");
manager.RegisterService(SRVREG(VectorIStorage, size), session, response);
return {};
}
manager.RegisterService(std::make_shared<IStorage>(state, manager, size), session, response); Result ILibraryAppletCreator::CreateTransferMemoryStorage(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
bool writable{request.Pop<u64>() != 0};
i64 size{request.Pop<i64>()};
if (size < 0)
throw exception("Cannot create an IStorage with a negative size");
manager.RegisterService(SRVREG(TransferMemoryIStorage, state.process->GetHandle<kernel::type::KTransferMemory>(request.copyHandles.at(0)), writable), session, response);
return {}; return {};
} }
} }

View File

@ -20,14 +20,21 @@ namespace skyline::service::am {
Result CreateLibraryApplet(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); Result CreateLibraryApplet(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/** /**
* @brief Creates an IStorage that can be used by the application * @brief Creates an IStorage that can be used by the application, backed by service-allocated memory
* @url https://switchbrew.org/wiki/Applet_Manager_services#CreateStorage * @url https://switchbrew.org/wiki/Applet_Manager_services#CreateStorage
*/ */
Result CreateStorage(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); Result CreateStorage(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Creates an IStorage that can be used by the application, backed by the supplied transfer memory
* @url https://switchbrew.org/wiki/Applet_Manager_services#CreateTransferMemoryStorage
*/
Result CreateTransferMemoryStorage(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
SERVICE_DECL( SERVICE_DECL(
SFUNC(0x0, ILibraryAppletCreator, CreateLibraryApplet), SFUNC(0x0, ILibraryAppletCreator, CreateLibraryApplet),
SFUNC(0xA, ILibraryAppletCreator, CreateStorage) SFUNC(0xA, ILibraryAppletCreator, CreateStorage),
SFUNC(0xB, ILibraryAppletCreator, CreateTransferMemoryStorage)
) )
}; };
} }

View File

@ -5,10 +5,12 @@
#include "IStorage.h" #include "IStorage.h"
namespace skyline::service::am { namespace skyline::service::am {
IStorage::IStorage(const DeviceState &state, ServiceManager &manager, size_t size) : content(size, 0), BaseService(state, manager) {} IStorage::IStorage(const DeviceState &state, ServiceManager &manager, bool writable) : writable(writable), BaseService(state, manager) {}
Result IStorage::Open(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IStorage::Open(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
manager.RegisterService(std::make_shared<IStorageAccessor>(state, manager, shared_from_this()), session, response); manager.RegisterService(std::make_shared<IStorageAccessor>(state, manager, std::static_pointer_cast<IStorage>(shared_from_this())), session, response);
return {}; return {};
} }
IStorage::~IStorage() = default;
} }

View File

@ -10,14 +10,23 @@ namespace skyline::service::am {
* @brief IStorage is used to open an IStorageAccessor to access a region of memory * @brief IStorage is used to open an IStorageAccessor to access a region of memory
* @url https://switchbrew.org/wiki/Applet_Manager_services#IStorage * @url https://switchbrew.org/wiki/Applet_Manager_services#IStorage
*/ */
class IStorage : public BaseService, public std::enable_shared_from_this<IStorage> { class IStorage : public BaseService {
public:
bool writable; //!< Whether the storage is writable by the guest
private: private:
size_t offset{}; //!< The current offset within the content for pushing data size_t offset{}; //!< The current offset within the content for pushing data
public: protected:
std::vector<u8> content; //!< The container for this IStorage's contents IStorage(const DeviceState &state, ServiceManager &manager, bool writable);
IStorage(const DeviceState &state, ServiceManager &manager, size_t size); public:
virtual ~IStorage();
/**
* @brief A span of the backing storage for this IStorage
*/
virtual span <u8> GetSpan() = 0;
/** /**
* @brief Returns an IStorageAccessor that can read and write data to an IStorage * @brief Returns an IStorageAccessor that can read and write data to an IStorage
@ -29,10 +38,10 @@ namespace skyline::service::am {
*/ */
template<typename ValueType> template<typename ValueType>
void Push(const ValueType &value) { void Push(const ValueType &value) {
if (offset + sizeof(ValueType) > content.size()) if (offset + sizeof(ValueType) > this->GetSpan().size())
throw exception("The supplied value cannot fit into the IStorage"); throw exception("The supplied value cannot fit into the IStorage");
std::memcpy(content.data() + offset, reinterpret_cast<const u8 *>(&value), sizeof(ValueType)); std::memcpy(this->GetSpan().data() + offset, reinterpret_cast<const u8 *>(&value), sizeof(ValueType));
offset += sizeof(ValueType); offset += sizeof(ValueType);
} }

View File

@ -8,20 +8,23 @@ namespace skyline::service::am {
IStorageAccessor::IStorageAccessor(const DeviceState &state, ServiceManager &manager, std::shared_ptr<IStorage> parent) : parent(std::move(parent)), BaseService(state, manager) {} IStorageAccessor::IStorageAccessor(const DeviceState &state, ServiceManager &manager, std::shared_ptr<IStorage> parent) : parent(std::move(parent)), BaseService(state, manager) {}
Result IStorageAccessor::GetSize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IStorageAccessor::GetSize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
response.Push<i64>(static_cast<i64>(parent->content.size())); response.Push<i64>(static_cast<i64>(parent->GetSpan().size()));
return {}; return {};
} }
Result IStorageAccessor::Write(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IStorageAccessor::Write(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto offset{request.Pop<i64>()}; auto offset{request.Pop<i64>()};
if (offset < 0 || offset > parent->content.size()) if (!parent->writable)
return result::ObjectInvalid;
auto storageSpan{parent->GetSpan()};
if (offset < 0 || offset > storageSpan.size())
return result::OutOfBounds; return result::OutOfBounds;
size_t size{std::min(request.inputBuf.at(0).size(), parent->content.size() - static_cast<size_t>(offset))}; size_t size{std::min(request.inputBuf.at(0).size(), storageSpan.size() - static_cast<size_t>(offset))};
if (size) if (size)
span(parent->content).copy_from(request.inputBuf.at(0), size); storageSpan.subspan(static_cast<size_t>(offset)).copy_from(request.inputBuf.at(0), size);
return {}; return {};
} }
@ -29,13 +32,14 @@ namespace skyline::service::am {
Result IStorageAccessor::Read(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IStorageAccessor::Read(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto offset{request.Pop<i64>()}; auto offset{request.Pop<i64>()};
if (offset < 0 || offset > parent->content.size()) auto storageSpan{parent->GetSpan()};
if (offset < 0 || offset > storageSpan.size())
return result::OutOfBounds; return result::OutOfBounds;
size_t size{std::min(request.outputBuf.at(0).size(), parent->content.size() - static_cast<size_t>(offset))}; size_t size{std::min(request.outputBuf.at(0).size(), storageSpan.size() - static_cast<size_t>(offset))};
if (size) if (size)
request.outputBuf.at(0).copy_from(span(parent->content.data() + offset, size)); request.outputBuf.at(0).copy_from(span(storageSpan.data() + offset, size));
return {}; return {};
} }

View File

@ -7,6 +7,7 @@
namespace skyline::service::am { namespace skyline::service::am {
namespace result { namespace result {
constexpr Result ObjectInvalid(128, 500);
constexpr Result OutOfBounds(128, 503); constexpr Result OutOfBounds(128, 503);
} }

View File

@ -0,0 +1,16 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include "TransferMemoryIStorage.h"
#include <utility>
namespace skyline::service::am {
TransferMemoryIStorage::TransferMemoryIStorage(const DeviceState &state, ServiceManager &manager, std::shared_ptr<kernel::type::KTransferMemory> transferMemory, bool writable) : transferMemory(std::move(transferMemory)), IStorage(state, manager, writable) {}
span<u8> TransferMemoryIStorage::GetSpan() {
return transferMemory->Get();
}
TransferMemoryIStorage::~TransferMemoryIStorage() = default;
}

View File

@ -0,0 +1,26 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <services/serviceman.h>
#include <kernel/types/KTransferMemory.h>
#include "IStorage.h"
namespace skyline::service::am {
/**
* @brief An IStorage backed by a transfer memory supplied by the guest
*/
class TransferMemoryIStorage : public IStorage {
private:
std::shared_ptr<kernel::type::KTransferMemory> transferMemory;
public:
TransferMemoryIStorage(const DeviceState &state, ServiceManager &manager, std::shared_ptr<kernel::type::KTransferMemory> transferMemory, bool writable);
~TransferMemoryIStorage() override;
virtual span<u8> GetSpan() override;
};
}

View File

@ -0,0 +1,18 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include "VectorIStorage.h"
#include <utility>
namespace skyline::service::am {
VectorIStorage::VectorIStorage(const DeviceState &state, ServiceManager &manager, size_t size) : content(size, 0), IStorage(state, manager, true) {}
VectorIStorage::VectorIStorage(const DeviceState &state, ServiceManager &manager, std::vector<u8> data) : content(std::move(data)), IStorage(state, manager, true) {}
span<u8> VectorIStorage::GetSpan() {
return content;
}
VectorIStorage::~VectorIStorage() = default;
}

View File

@ -0,0 +1,26 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <services/serviceman.h>
#include "IStorage.h"
namespace skyline::service::am {
/**
* @brief VectorIStorage is an IStorage backed by a vector
*/
class VectorIStorage : public IStorage {
private:
std::vector<u8> content;
public:
VectorIStorage(const DeviceState &state, ServiceManager &manager, size_t size);
VectorIStorage(const DeviceState &state, ServiceManager &manager, std::vector<u8> data);
~VectorIStorage() override;
virtual span<u8> GetSpan() override;
};
}

View File

@ -0,0 +1,30 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
namespace skyline::service::applet {
/**
* @brief Specifies how the applet should run
* @url https://switchbrew.org/wiki/Applet_Manager_services#LibraryAppletMode
*/
enum class LibraryAppletMode : u32 {
AllForeground = 0x0,
PartialForeground = 0x1,
NoUi = 0x2,
PartialForegroundWithIndirectDisplay = 0x3,
AllForegroundInitiallyHidden = 0x4,
};
/**
* @brief Common arguments to all LibraryApplets
* @url https://switchbrew.org/wiki/Applet_Manager_services#CommonArguments
*/
struct CommonArguments {
u32 version;
u32 size;
u32 apiVersion;
u32 themeColor;
u64 playStartupSound;
u64 systemTick;
};
}

View File

@ -40,7 +40,7 @@ namespace skyline::service {
/** /**
* @brief The base class for the HOS service interfaces hosted by sysmodules * @brief The base class for the HOS service interfaces hosted by sysmodules
*/ */
class BaseService { class BaseService : public std::enable_shared_from_this<BaseService> {
private: private:
std::string name; //!< The name of the service, it's only assigned after GetName is called and shouldn't be used directly std::string name; //!< The name of the service, it's only assigned after GetName is called and shouldn't be used directly