Write ApplicationErrorArg ErrorApplets to log

These applets are used by applications to display a custom error message to the user. Both the error message and the detailed error message are printed to the error log.


Co-authored-by: lynxnb <niccolo.betto@gmail.com>
This commit is contained in:
MCredstoner2004 2022-07-01 13:41:36 -05:00 committed by ◱ Mark
parent f9a0394577
commit 942e22f275
11 changed files with 200 additions and 30 deletions

View File

@ -262,6 +262,7 @@ add_library(skyline SHARED
${source_DIR}/skyline/services/btm/IBtmUserCore.cpp
${source_DIR}/skyline/applet/applet_creator.cpp
${source_DIR}/skyline/applet/controller_applet.cpp
${source_DIR}/skyline/applet/error_applet.cpp
${source_DIR}/skyline/applet/player_select_applet.cpp
${source_DIR}/skyline/applet/swkbd/software_keyboard_applet.cpp
${source_DIR}/skyline/applet/swkbd/software_keyboard_config.cpp

View File

@ -1,9 +1,10 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include "controller_applet.h"
#include "player_select_applet.h"
#include "applet_creator.h"
#include "controller_applet.h"
#include "error_applet.h"
#include "player_select_applet.h"
#include "swkbd/software_keyboard_applet.h"
namespace skyline::applet {
@ -20,8 +21,10 @@ namespace skyline::applet {
return std::make_shared<PlayerSelectApplet>(state, manager, std::move(onAppletStateChanged), std::move(onNormalDataPushFromApplet), std::move(onInteractiveDataPushFromApplet), appletMode);
case AppletId::LibraryAppletSwkbd:
return std::make_shared<swkbd::SoftwareKeyboardApplet>(state, manager, std::move(onAppletStateChanged), std::move(onNormalDataPushFromApplet), std::move(onInteractiveDataPushFromApplet), appletMode);
case AppletId::LibraryAppletError:
return std::make_shared<ErrorApplet>(state, manager, std::move(onAppletStateChanged), std::move(onNormalDataPushFromApplet), std::move(onInteractiveDataPushFromApplet), appletMode);
default:
throw exception("Unimplemented Applet: 0x{:X} ({})", static_cast<u32>(appletId), ToString(appletId));
throw exception{"Unimplemented Applet: 0x{:X} ({})", static_cast<u32>(appletId), ToString(appletId)};
}
}
}

View File

@ -15,7 +15,7 @@ namespace skyline::applet {
std::shared_ptr<kernel::type::KEvent> onNormalDataPushFromApplet,
std::shared_ptr<kernel::type::KEvent> onInteractiveDataPushFromApplet,
service::applet::LibraryAppletMode appletMode)
: IApplet(state, manager, std::move(onAppletStateChanged), std::move(onNormalDataPushFromApplet), std::move(onInteractiveDataPushFromApplet), appletMode) {}
: IApplet{state, manager, std::move(onAppletStateChanged), std::move(onNormalDataPushFromApplet), std::move(onInteractiveDataPushFromApplet), appletMode} {}
void ControllerApplet::HandleShowControllerSupport(input::NpadStyleSet styleSet, ControllerAppletVersion version, span<u8> arg) {
// Generic macro due to both versions of arguments sharing the same fields but having different layouts
@ -82,7 +82,7 @@ namespace skyline::applet {
}
}
std::scoped_lock lock{inputDataMutex};
std::scoped_lock lock{normalInputDataMutex};
switch (argPrivate.mode) {
case ControllerSupportMode::ShowControllerSupport:
HandleShowControllerSupport(argPrivate.styleSet, appletVersion, normalInputData.front()->GetSpan());
@ -100,15 +100,14 @@ namespace skyline::applet {
// Notify the guest that we've finished running
onAppletStateChanged->Signal();
return {};
};
}
Result ControllerApplet::GetResult() {
return {};
}
void ControllerApplet::PushNormalDataToApplet(std::shared_ptr<service::am::IStorage> data) {
std::scoped_lock lock{inputDataMutex};
normalInputData.emplace(std::move(data));
PushNormalInput(data);
}
void ControllerApplet::PushInteractiveDataToApplet(std::shared_ptr<service::am::IStorage> data) {}

View File

@ -2,6 +2,7 @@
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <services/am/applet/IApplet.h>
#include <services/applet/common_arguments.h>
#include <input/npad_device.h>
@ -10,7 +11,7 @@ namespace skyline::applet {
/**
* @brief The Controller applet is responsible for notifiying the user of a games controller requirements and for allowing user management og controllers
*/
class ControllerApplet : public service::am::IApplet {
class ControllerApplet : public service::am::IApplet, service::am::EnableNormalQueue {
private:
/**
* @brief The version of the controller applet interface that an application supports
@ -98,17 +99,6 @@ namespace skyline::applet {
};
static_assert(sizeof(ControllerSupportResultInfo) == 0xC);
std::mutex inputDataMutex;
std::queue<std::shared_ptr<service::am::IStorage>> normalInputData;
template<typename T>
T PopNormalInput() {
std::scoped_lock lock{inputDataMutex};
auto data{normalInputData.front()->GetSpan().as<T>()};
normalInputData.pop();
return static_cast<T>(data);
}
/**
* @brief Handles the 'ShowControllerSupport' mode of the controller applet
*/

View File

@ -0,0 +1,55 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include "error_applet.h"
#include "services/am/storage/ObjIStorage.h"
namespace skyline::applet {
ErrorApplet::ErrorApplet(const DeviceState &state,
service::ServiceManager &manager,
std::shared_ptr<kernel::type::KEvent> onAppletStateChanged,
std::shared_ptr<kernel::type::KEvent> onNormalDataPushFromApplet,
std::shared_ptr<kernel::type::KEvent> onInteractiveDataPushFromApplet,
service::applet::LibraryAppletMode appletMode)
: IApplet{state, manager, std::move(onAppletStateChanged), std::move(onNormalDataPushFromApplet), std::move(onInteractiveDataPushFromApplet), appletMode} {}
Result ErrorApplet::Start() {
auto commonArg{PopNormalInput<service::applet::CommonArguments>()};
errorStorage = PopNormalInput();
auto errorCommonHeader{errorStorage->GetSpan().as<ErrorCommonHeader>()};
Logger::Debug("ErrorApplet: version: 0x{:X}, type: 0x{:X}", commonArg.apiVersion, errorCommonHeader.type);
switch (errorCommonHeader.type) {
case ErrorType::ApplicationErrorArg:
HandleApplicationErrorArg();
break;
default:
throw exception("ErrorApplet type 0x{:X} is not implemented", errorCommonHeader.type);
}
// Notify the guest that we've finished running
onAppletStateChanged->Signal();
return {};
}
void ErrorApplet::HandleApplicationErrorArg() {
auto applicationErrorStorage{errorStorage->GetSpan().as<ApplicationErrorArg>()};
if (applicationErrorStorage.fullscreenMessage[0] == '\0')
Logger::ErrorNoPrefix("Application Error: {}", applicationErrorStorage.dialogMessage.data());
else
Logger::ErrorNoPrefix("Application Error: {}\nFull message: {}", applicationErrorStorage.dialogMessage.data(), applicationErrorStorage.fullscreenMessage.data());
}
Result ErrorApplet::GetResult() {
return {};
}
void ErrorApplet::PushNormalDataToApplet(std::shared_ptr<service::am::IStorage> data) {
PushNormalInput(data);
}
void ErrorApplet::PushInteractiveDataToApplet(std::shared_ptr<service::am::IStorage> data) {}
}

View File

@ -0,0 +1,70 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
// Copyright © 2020 Ryujinx Team and Contributors (https://github.com/ryujinx/)
#pragma once
#include <services/am/applet/IApplet.h>
#include "common/language.h"
namespace skyline::applet {
/**
* @brief The Error Applet is utilized by the guest to display an error to the user, this class prints the supplied error to the logger
* @url https://switchbrew.org/wiki/Error_Applet
* @url https://switchbrew.org/wiki/Error_applet
*/
class ErrorApplet : public service::am::IApplet, service::am::EnableNormalQueue {
private:
#pragma pack(push, 1)
enum class ErrorType : u8 {
ErrorCommonArg = 0,
SystemErrorArg = 1,
ApplicationErrorArg = 2,
ErrorEulaArg = 3,
ErrorPctlArg = 4,
ErrorRecordArg = 5,
SystemUpdateEulaArg = 8
};
/**
* @url https://switchbrew.org/wiki/Error_Applet#ErrorCommonHeader
*/
struct ErrorCommonHeader {
ErrorType type;
u8 jump;
u8 _pad_[0x3];
u8 contextFlag;
u8 resultFlag;
u8 contextFlag2;
};
static_assert(sizeof(ErrorCommonHeader) == 0x8);
struct ApplicationErrorArg {
ErrorCommonHeader commonHeader;
u32 errorNumber;
LanguageCode languageCode;
std::array<char, 0x800> dialogMessage;
std::array<char, 0x800> fullscreenMessage; //!< The message displayed when the user clicks on "Details", when not set this disables displaying Details
};
static_assert(sizeof(ApplicationErrorArg) == 0x1014);
#pragma pack(pop)
std::shared_ptr<service::am::IStorage> errorStorage;
void HandleApplicationErrorArg();
public:
ErrorApplet(const DeviceState &state, service::ServiceManager &manager, std::shared_ptr<kernel::type::KEvent> onAppletStateChanged, std::shared_ptr<kernel::type::KEvent> onNormalDataPushFromApplet, std::shared_ptr<kernel::type::KEvent> onInteractiveDataPushFromApplet, service::applet::LibraryAppletMode appletMode);
Result Start() override;
Result GetResult() override;
void PushNormalDataToApplet(std::shared_ptr<service::am::IStorage> data) override;
void PushInteractiveDataToApplet(std::shared_ptr<service::am::IStorage> data) override;
};
}

View File

@ -11,7 +11,7 @@ namespace skyline::applet {
std::shared_ptr<kernel::type::KEvent> onNormalDataPushFromApplet,
std::shared_ptr<kernel::type::KEvent> onInteractiveDataPushFromApplet,
service::applet::LibraryAppletMode appletMode)
: IApplet(state, manager, std::move(onAppletStateChanged), std::move(onNormalDataPushFromApplet), std::move(onInteractiveDataPushFromApplet), appletMode) {}
: IApplet{state, manager, std::move(onAppletStateChanged), std::move(onNormalDataPushFromApplet), std::move(onInteractiveDataPushFromApplet), appletMode} {}
Result PlayerSelectApplet::Start() {
// Return default user

View File

@ -74,7 +74,7 @@ namespace skyline::applet::swkbd {
}
Result SoftwareKeyboardApplet::Start() {
std::scoped_lock lock{inputDataMutex};
std::scoped_lock lock{normalInputDataMutex};
auto commonArgs{normalInputData.front()->GetSpan().as<service::applet::CommonArguments>()};
normalInputData.pop();
@ -135,8 +135,7 @@ namespace skyline::applet::swkbd {
}
void SoftwareKeyboardApplet::PushNormalDataToApplet(std::shared_ptr<service::am::IStorage> data) {
std::scoped_lock lock{inputDataMutex};
normalInputData.emplace(data);
PushNormalInput(data);
}
void SoftwareKeyboardApplet::PushInteractiveDataToApplet(std::shared_ptr<service::am::IStorage> data) {

View File

@ -15,7 +15,7 @@ namespace skyline::applet::swkbd {
* @url https://switchbrew.org/wiki/Software_Keyboard
* @brief An implementation for the Software Keyboard (swkbd) Applet which handles translating guest applet transactions to the appropriate host behavior
*/
class SoftwareKeyboardApplet : public service::am::IApplet {
class SoftwareKeyboardApplet : public service::am::IApplet, service::am::EnableNormalQueue {
private:
/**
* @url https://switchbrew.org/wiki/Software_Keyboard#CloseResult
@ -73,8 +73,6 @@ namespace skyline::applet::swkbd {
#pragma pack(pop)
std::mutex inputDataMutex;
std::queue<std::shared_ptr<service::am::IStorage>> normalInputData;
KeyboardConfigVB config{};
bool validationPending{};
std::u16string currentText{};

View File

@ -7,7 +7,7 @@
namespace skyline::applet::swkbd {
KeyboardConfigVB::KeyboardConfigVB() = default;
KeyboardConfigVB::KeyboardConfigVB(const KeyboardConfigV7 &v7config) : commonConfig(v7config.commonConfig), separateTextPos(v7config.separateTextPos) {}
KeyboardConfigVB::KeyboardConfigVB(const KeyboardConfigV7 &v7config) : commonConfig{v7config.commonConfig}, separateTextPos{v7config.separateTextPos} {}
KeyboardConfigVB::KeyboardConfigVB(const KeyboardConfigV0 &v0config) : commonConfig(v0config.commonConfig) {}
KeyboardConfigVB::KeyboardConfigVB(const KeyboardConfigV0 &v0config) : commonConfig{v0config.commonConfig} {}
}

View File

@ -8,7 +8,6 @@
#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
@ -70,4 +69,60 @@ namespace skyline::service::am {
*/
std::shared_ptr<IStorage> PopInteractiveAndClear();
};
/**
* @brief Utility class for applets that need to queue the normal data sent to them
*/
class EnableNormalQueue {
protected:
std::mutex normalInputDataMutex;
std::queue<std::shared_ptr<service::am::IStorage>> normalInputData;
std::shared_ptr<service::am::IStorage> PopNormalInput() {
std::scoped_lock lock{normalInputDataMutex};
auto data{normalInputData.front()};
normalInputData.pop();
return data;
}
template<class T>
T PopNormalInput() {
std::scoped_lock lock{normalInputDataMutex};
auto data{normalInputData.front()->GetSpan().as<T>()};
normalInputData.pop();
return data;
}
void PushNormalInput(std::shared_ptr<service::am::IStorage> data) {
normalInputData.emplace(data);
}
};
/**
* @brief Utility class for applets that need to queue the interactive data sent to them
*/
class EnableInteractiveQueue {
protected:
std::mutex interactiveInputDataMutex;
std::queue<std::shared_ptr<service::am::IStorage>> interactiveInputData;
std::shared_ptr<service::am::IStorage> PopInteractiveInput() {
std::scoped_lock lock{interactiveInputDataMutex};
auto data{interactiveInputData.front()};
interactiveInputData.pop();
return data;
}
template<class T>
T PopInteractiveInput() {
std::scoped_lock lock{interactiveInputDataMutex};
auto data{interactiveInputData.front()->GetSpan().as<T>()};
interactiveInputData.pop();
return data;
}
void PushInteractiveInput(std::shared_ptr<service::am::IStorage> data) {
interactiveInputData.emplace(data);
}
};
}