Add nn::erreula library.

This commit is contained in:
James Benton 2022-01-10 14:10:48 +00:00 committed by fincs
parent e11ded4992
commit 432c895f75
9 changed files with 807 additions and 0 deletions

View File

@ -26,6 +26,7 @@ SOURCES := cafe \
libraries/wutsocket \
libraries/libwhb/src \
libraries/libgfd/src \
libraries/nn_erreula \
libraries/nn_swkbd
DATA := data
INCLUDES := include \

View File

@ -0,0 +1,6 @@
/**
* \defgroup erreula erreula
*
* C++ linkage for the error viewer, see \link nn::erreula \endlink for
* general use.
*/

View File

@ -0,0 +1,49 @@
#pragma once
#include <wut.h>
#include <coreinit/filesystem.h>
#include <nn/erreula.h>
/**
* \defgroup erreula_rpl RPL Interface
* \ingroup erreula
* C++ linkage for erreula, see \link nn::erreula \endlink for general use.
* @{
*/
#ifdef __cplusplus
struct FSClient;
namespace Rpl
{
void ErrEulaSetVersion(int version);
bool ErrEulaJump(const char *buffer, uint32_t bufferSize);
void ErrEulaPlayAppearSE(bool playAppearSoundEffect);
bool ErrEulaIsSelectCursorActive();
void ErrEulaChangeLang(nn::erreula::LangType language);
void ErrEulaDisappearHomeNixSign();
bool ErrEulaIsAppearHomeNixSign();
void ErrEulaAppearHomeNixSign(const nn::erreula::HomeNixSignArg &arg);
void ErrEulaSetControllerRemo(nn::erreula::ControllerType controller);
int32_t ErrEulaGetSelectButtonNumError();
int32_t ErrEulaGetResultCode();
nn::erreula::ResultType ErrEulaGetResultType();
nn::erreula::State ErrEulaGetStateErrorViewer();
bool ErrEulaIsDecideSelectRightButtonError();
bool ErrEulaIsDecideSelectLeftButtonError();
bool ErrEulaIsDecideSelectButtonError();
void ErrEulaDisappearError();
void ErrEulaAppearError(const nn::erreula::AppearArg &arg);
void ErrEulaCalc(const nn::erreula::ControllerInfo &controllerInfo);
void ErrEulaDrawDRC();
void ErrEulaDrawTV();
void ErrEulaDestroy();
void ErrEulaCreate(void *workMemory, nn::erreula::RegionType region,
nn::erreula::LangType language, FSClient *fsClient);
} // namespace Rpl
#endif // ifdef __cplusplus
/** @} */

8
include/nn/erreula.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
/**
* \defgroup nn_erreula nn_erreula
* Graphical error viewer, supporting several languages and configurations.
*/
#include <nn/erreula/erreula_cpp.h>

View File

@ -0,0 +1,270 @@
#pragma once
#include <wut.h>
#include <coreinit/filesystem.h>
#include <nn/result.h>
#include <padscore/kpad.h>
#include <vpad/input.h>
#include <string.h>
/**
* \defgroup nn_erreula_erreula Error Viewer
* \ingroup nn_erreula
* See \link nn::erreula \endlink.
*
* @{
*/
#ifdef __cplusplus
namespace nn
{
namespace erreula
{
enum class ControllerType
{
WiiRemote0 = 0,
WiiRemote1 = 1,
WiiRemote2 = 2,
WiiRemote3 = 3,
DrcGamepad = 4,
};
enum class ErrorType
{
Code = 0,
Message = 1,
Message1Button = 2,
Message2Button = 3,
};
enum class LangType
{
Japanese = 0,
English = 1,
// TODO: More languages
};
enum class RegionType
{
Japan = 0,
USA = 1,
Europe = 2,
China = 3,
Korea = 4,
Taiwan = 5,
};
enum class ResultType
{
None = 0,
//! The user exited the error viewer
Exited = 1,
// TODO: More result types
};
enum class RenderTarget
{
//! Render only to TV
Tv = 0,
//! Render only to DRC gamepad
Drc = 1,
//! Render to both TV and Gamepad
Both = 2,
};
enum class State
{
//! The error viewer is completely hidden.
Hidden = 0,
//! The error viewer is drawing the fade-in animation.
FadeIn = 1,
//! The error viewer is done drawing the fade in animation and completely visible.
Visible = 2,
//! The error viewer is drawing the fade-out animation.
FadeOut = 3,
};
struct ErrorArg
{
ErrorArg() :
errorType(ErrorType::Code),
renderTarget(RenderTarget::Both),
controllerType(ControllerType::DrcGamepad),
unknown0x0C(0),
errorCode(0),
unknown0x14(1),
errorMessage(nullptr),
button1Label(nullptr),
button2Label(nullptr),
errorTitle(nullptr),
unknown0x28(true)
{
}
ErrorType errorType;
RenderTarget renderTarget;
ControllerType controllerType;
uint32_t unknown0x0C;
int32_t errorCode;
uint32_t unknown0x14;
const char16_t *errorMessage;
const char16_t *button1Label;
const char16_t *button2Label;
const char16_t *errorTitle;
bool unknown0x28;
WUT_PADDING_BYTES(3);
};
WUT_CHECK_OFFSET(ErrorArg, 0x00, errorType);
WUT_CHECK_OFFSET(ErrorArg, 0x04, renderTarget);
WUT_CHECK_OFFSET(ErrorArg, 0x08, controllerType);
WUT_CHECK_OFFSET(ErrorArg, 0x0C, unknown0x0C);
WUT_CHECK_OFFSET(ErrorArg, 0x10, errorCode);
WUT_CHECK_OFFSET(ErrorArg, 0x14, unknown0x14);
WUT_CHECK_OFFSET(ErrorArg, 0x18, errorMessage);
WUT_CHECK_OFFSET(ErrorArg, 0x1C, button1Label);
WUT_CHECK_OFFSET(ErrorArg, 0x20, button2Label);
WUT_CHECK_OFFSET(ErrorArg, 0x24, errorTitle);
WUT_CHECK_OFFSET(ErrorArg, 0x28, unknown0x28);
WUT_CHECK_SIZE(ErrorArg, 0x2C);
struct AppearArg
{
ErrorArg errorArg;
};
WUT_CHECK_OFFSET(AppearArg, 0x00, errorArg);
WUT_CHECK_SIZE(AppearArg, 0x2C);
//! Configuration options for the error
struct CreateArg
{
CreateArg() :
workMemory(nullptr),
region(RegionType::Europe),
language(LangType::English),
fsClient(nullptr)
{
}
void *workMemory;
RegionType region;
LangType language;
FSClient *fsClient;
};
WUT_CHECK_OFFSET(CreateArg, 0x00, workMemory);
WUT_CHECK_OFFSET(CreateArg, 0x04, region);
WUT_CHECK_OFFSET(CreateArg, 0x08, language);
WUT_CHECK_OFFSET(CreateArg, 0x0C, fsClient);
WUT_CHECK_SIZE(CreateArg, 0x10);
struct ControllerInfo
{
ControllerInfo()
{
memset(this, 0, sizeof(*this));
}
const VPADStatus *vpad;
const KPADStatus *kpad[4];
};
WUT_CHECK_OFFSET(ControllerInfo, 0x00, vpad);
WUT_CHECK_OFFSET(ControllerInfo, 0x04, kpad);
WUT_CHECK_SIZE(ControllerInfo, 0x14);
struct HomeNixSignArg
{
HomeNixSignArg() :
unknown0x00(1)
{
}
uint32_t unknown0x00;
};
WUT_CHECK_OFFSET(HomeNixSignArg, 0x00, unknown0x00);
WUT_CHECK_SIZE(HomeNixSignArg, 0x04);
void
AppearErrorViewer(const AppearArg &arg);
void
AppearHomeNixSign(const HomeNixSignArg &arg);
void
Calc(const ControllerInfo &controllerInfo);
void
ChangeLangError(LangType languageType);
bool
Create(const CreateArg &args);
void
Destroy();
void
DisappearErrorViewer();
void
DisappearHomeNixSign();
void
DrawDRC();
void
DrawTV();
int32_t
GetResultCode();
ResultType
GetResultType();
int32_t
GetSelectButtonNumError();
State
GetStateErrorViewer();
uint32_t
GetWorkMemorySize();
bool
IsAppearHomeNixSign();
bool
IsDecideSelectButtonError();
bool
IsDecideSelectLeftButtonError();
bool
IsDecideSelectRightButtonError();
bool
IsSelectCursorActive();
bool
Jump(const char *buffer, uint32_t bufferSize);
void
PlayAppearSE(bool playAppearSoundEffect);
void
SetControllerRemo(ControllerType controller);
} // namespace erreula
} // namespace nn
#endif // ifdef __cplusplus
/** @} */

View File

@ -0,0 +1,359 @@
#include <nn/erreula.h>
#include <erreula/rpl_interface.h>
#include <coreinit/debug.h>
#include <coreinit/dynload.h>
#include <coreinit/memexpheap.h>
namespace nn
{
namespace erreula
{
static const uint32_t kWorkMemorySize = 0x1F00000;
static const uint32_t kRplAcquireBufferSize = 0xC80000;
static MEMHeapHandle sHeapHandle = NULL;
static OSDynLoad_Module sModuleHandle = NULL;
static void *sAppearError = NULL;
static void *sAppearHomeNixSign = NULL;
static void *sCalc = NULL;
static void *sChangeLang = NULL;
static void *sCreate = NULL;
static void *sDestroy = NULL;
static void *sDisappearError = NULL;
static void *sDisappearHomeNixSign = NULL;
static void *sDrawDRC = NULL;
static void *sDrawTV = NULL;
static void *sGetResultCode = NULL;
static void *sGetResultType = NULL;
static void *sGetSelectButtonNumError = NULL;
static void *sGetStateErrorViewer = NULL;
static void *sIsAppearHomeNixSign = NULL;
static void *sIsDecideSelectButtonError = NULL;
static void *sIsDecideSelectLeftButtonError = NULL;
static void *sIsDecideSelectRightButtonError = NULL;
static void *sIsSelectCursorActive = NULL;
static void *sJump = NULL;
static void *sPlayAppearSE = NULL;
static void *sSetControllerRemo = NULL;
static void *sSetVersion = NULL;
static OSDynLoad_Error
allocForDynLoad(int32_t size,
int32_t align,
void **outAddr)
{
void *addr = MEMAllocFromExpHeapEx(sHeapHandle, size, align);
if (!addr) {
return OS_DYNLOAD_OUT_OF_MEMORY;
}
*outAddr = addr;
return OS_DYNLOAD_OK;
}
static void
freeForDynLoad(void *addr)
{
MEMFreeToExpHeap(sHeapHandle, addr);
}
static void
Create(void *workMemory,
RegionType region,
LangType language,
FSClient *fsClient)
{
return reinterpret_cast<decltype(&Rpl::ErrEulaCreate)>(sCreate)(
workMemory, region, language, fsClient);
}
static void
SetVersion(int version)
{
return reinterpret_cast<decltype(&Rpl::ErrEulaSetVersion)>(sSetVersion)(
version);
}
bool
Create(const CreateArg &args)
{
OSDynLoadAllocFn prevDynLoadAlloc = NULL;
OSDynLoadFreeFn prevDynLoadFree = NULL;
uint32_t dynloadAcquireUseSize = 0;
void *workMemory = NULL;
bool result = true;
if (!args.workMemory) {
OSReport("ERREULA: Create failed. CreateArg.workMemory is NULL.");
return false;
}
if (!args.fsClient) {
OSReport("ERREULA: Create failed. CreateArg.fsClient is NULL.");
return false;
}
sHeapHandle = MEMCreateExpHeapEx(args.workMemory, kWorkMemorySize, 0);
OSDynLoad_GetAllocator(&prevDynLoadAlloc, &prevDynLoadFree);
OSDynLoad_SetAllocator(allocForDynLoad, freeForDynLoad);
if (OSDynLoad_Error error = OSDynLoad_Acquire("erreula.rpl", &sModuleHandle)) {
OSReport("ERREULA: Create failed. OSDynLoad_Acquire() return error(%d).\n",
error);
result = false;
goto out;
}
dynloadAcquireUseSize = kWorkMemorySize -
MEMGetAllocatableSizeForExpHeapEx(sHeapHandle, 4);
OSReport("ERREULA: OSDynLoad_Acquire() use [%d/%d]\n",
dynloadAcquireUseSize, kRplAcquireBufferSize);
if (dynloadAcquireUseSize > kRplAcquireBufferSize) {
OSReport("ERREULA: Create failed. dynload_acquire_use_size > kRplAcquireBufferSize.");
result = false;
goto out;
}
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaAppearError__3RplFRCQ3_2nn7erreula9AppearArg", &sAppearError);
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaCalc__3RplFRCQ3_2nn7erreula14ControllerInfo", &sCalc);
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaCreate__3RplFPUcQ3_2nn7erreula10RegionTypeQ3_2nn7erreula8LangTypeP8FSClient", &sCreate);
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaDestroy__3RplFv", &sDestroy);
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaDisappearError__3RplFv", &sDisappearError);
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaDrawDRC__3RplFv", &sDrawDRC);
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaDrawTV__3RplFv", &sDrawTV);
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaGetStateErrorViewer__3RplFv", &sGetStateErrorViewer);
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaIsDecideSelectButtonError__3RplFv", &sIsDecideSelectButtonError);
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaIsDecideSelectLeftButtonError__3RplFv", &sIsDecideSelectLeftButtonError);
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaIsDecideSelectRightButtonError__3RplFv", &sIsDecideSelectRightButtonError);
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaSetControllerRemo__3RplFQ3_2nn7erreula14ControllerType", &sSetControllerRemo);
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaAppearHomeNixSign__3RplFRCQ3_2nn7erreula14HomeNixSignArg", &sAppearHomeNixSign);
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaIsAppearHomeNixSign__3RplFv", &sIsAppearHomeNixSign);
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaDisappearHomeNixSign__3RplFv", &sDisappearHomeNixSign);
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaChangeLang__3RplFQ3_2nn7erreula8LangType", &sChangeLang);
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaIsSelectCursorActive__3RplFv", &sIsSelectCursorActive);
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaGetResultType__3RplFv", &sGetResultType);
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaGetResultCode__3RplFv", &sGetResultCode);
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaGetSelectButtonNumError__3RplFv", &sGetSelectButtonNumError);
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaSetVersion__3RplFi", &sSetVersion);
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaPlayAppearSE__3RplFb", &sPlayAppearSE);
OSDynLoad_FindExport(sModuleHandle, FALSE, "ErrEulaJump__3RplFPCcUi", &sJump);
workMemory = MEMAllocFromExpHeapEx(sHeapHandle,
kWorkMemorySize - kRplAcquireBufferSize,
4);
if (!workMemory) {
OSReport("ERREULA: Create failed. framework_buffer == NULL.");
result = false;
goto out;
} else {
SetVersion(3);
Create(workMemory, args.region, args.language, args.fsClient);
result = true;
}
out:
if (!result) {
if (sModuleHandle) {
OSDynLoad_Release(sModuleHandle);
sModuleHandle = NULL;
}
if (sHeapHandle) {
MEMDestroyExpHeap(sHeapHandle);
sHeapHandle = NULL;
}
}
OSDynLoad_SetAllocator(prevDynLoadAlloc, prevDynLoadFree);
return result;
}
void
AppearErrorViewer(const AppearArg &arg)
{
return reinterpret_cast<decltype(&Rpl::ErrEulaAppearError)>
(sAppearError)
(arg);
}
void
AppearHomeNixSign(const HomeNixSignArg &arg)
{
return reinterpret_cast<decltype(&Rpl::ErrEulaAppearHomeNixSign)>
(sAppearHomeNixSign)
(arg);
}
void
Calc(const ControllerInfo &controllerInfo)
{
return reinterpret_cast<decltype(&Rpl::ErrEulaCalc)>
(sCalc)
(controllerInfo);
}
void
ChangeLangError(LangType language)
{
return reinterpret_cast<decltype(&Rpl::ErrEulaChangeLang)>
(sChangeLang)
(language);
}
void
Destroy()
{
reinterpret_cast<decltype(&Rpl::ErrEulaDestroy)>(sDestroy)();
if (sModuleHandle) {
OSDynLoad_Release(sModuleHandle);
sModuleHandle = NULL;
}
if (sHeapHandle) {
MEMDestroyExpHeap(sHeapHandle);
sHeapHandle = NULL;
}
}
void
DisappearErrorViewer()
{
return reinterpret_cast<decltype(&Rpl::ErrEulaDisappearError)>
(sDisappearError)
();
}
void
DisappearHomeNixSign()
{
return reinterpret_cast<decltype(&Rpl::ErrEulaDisappearHomeNixSign)>
(sDisappearHomeNixSign)
();
}
void
DrawDRC()
{
return reinterpret_cast<decltype(&Rpl::ErrEulaDrawDRC)>
(sDrawDRC)
();
}
void
DrawTV()
{
return reinterpret_cast<decltype(&Rpl::ErrEulaDrawTV)>
(sDrawTV)
();
}
int32_t
GetResultCode()
{
return reinterpret_cast<decltype(&Rpl::ErrEulaGetResultCode)>
(sGetResultCode)
();
}
ResultType
GetResultType()
{
return reinterpret_cast<decltype(&Rpl::ErrEulaGetResultType)>
(sGetResultType)
();
}
int32_t
GetSelectButtonNumError()
{
return reinterpret_cast<decltype(&Rpl::ErrEulaGetSelectButtonNumError)>
(sGetSelectButtonNumError)
();
}
State
GetStateErrorViewer()
{
return reinterpret_cast<decltype(&Rpl::ErrEulaGetStateErrorViewer)>
(sGetStateErrorViewer)
();
}
uint32_t
GetWorkMemorySize()
{
return kWorkMemorySize;
}
bool
IsAppearHomeNixSign()
{
return reinterpret_cast<decltype(&Rpl::ErrEulaIsAppearHomeNixSign)>
(sIsAppearHomeNixSign)
();
}
bool
IsDecideSelectButtonError()
{
return reinterpret_cast<decltype(&Rpl::ErrEulaIsDecideSelectButtonError)>
(sIsDecideSelectButtonError)
();
}
bool
IsDecideSelectLeftButtonError()
{
return reinterpret_cast<decltype(&Rpl::ErrEulaIsDecideSelectLeftButtonError)>
(sIsDecideSelectLeftButtonError)
();
}
bool
IsDecideSelectRightButtonError()
{
return reinterpret_cast<decltype(&Rpl::ErrEulaIsDecideSelectRightButtonError)>
(sIsDecideSelectRightButtonError)
();
}
bool
IsSelectCursorActive()
{
return reinterpret_cast<decltype(&Rpl::ErrEulaIsSelectCursorActive)>
(sIsSelectCursorActive)
();
}
bool
Jump(const char *buffer, uint32_t bufferSize)
{
return reinterpret_cast<decltype(&Rpl::ErrEulaJump)>
(sJump)
(buffer, bufferSize);
}
void
PlayAppearSE(bool playAppearSoundEffect)
{
return reinterpret_cast<decltype(&Rpl::ErrEulaPlayAppearSE)>
(sPlayAppearSE)
(playAppearSoundEffect);
}
void
SetControllerRemo(ControllerType controller)
{
return reinterpret_cast<decltype(&Rpl::ErrEulaSetControllerRemo)>
(sSetControllerRemo)
(controller);
}
} // namespace erreula
} // namespace nn

View File

@ -4,6 +4,7 @@ project(samples)
include("${DEVKITPRO}/wut/share/wut.cmake" REQUIRED)
add_subdirectory(custom_default_heap)
add_subdirectory(erreula)
add_subdirectory(gx2_triangle)
add_subdirectory(helloworld)
add_subdirectory(helloworld_cpp)

View File

@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.2)
project(erreula CXX)
include("${DEVKITPRO}/wut/share/wut.cmake" REQUIRED)
add_executable(erreula
main.cpp)
wut_create_rpx(erreula)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/erreula.rpx"
DESTINATION "${CMAKE_INSTALL_PREFIX}")

View File

@ -0,0 +1,102 @@
#include <coreinit/filesystem.h>
#include <coreinit/memdefaultheap.h>
#include <nn/erreula.h>
#include <vpad/input.h>
#include <whb/proc.h>
#include <whb/gfx.h>
#include <whb/log.h>
#include <whb/log_udp.h>
int main(int argc, char **argv)
{
WHBLogUdpInit();
WHBProcInit();
WHBGfxInit();
FSInit();
VPADInit();
// Create FSClient for erreula
FSClient *fsClient = (FSClient *)MEMAllocFromDefaultHeap(sizeof(FSClient));
FSAddClient(fsClient, FS_ERROR_FLAG_NONE);
// Create erreula
nn::erreula::CreateArg createArg;
createArg.region = nn::erreula::RegionType::Europe;
createArg.language = nn::erreula::LangType::English;
createArg.workMemory = MEMAllocFromDefaultHeap(nn::erreula::GetWorkMemorySize());
createArg.fsClient = fsClient;
if (!nn::erreula::Create(createArg)) {
WHBLogPrintf("nn::erreula::Create failed");
WHBProcShutdown();
return -1;
}
// Show the error viewer
nn::erreula::AppearArg appearArg;
appearArg.errorArg.errorType = nn::erreula::ErrorType::Message2Button;
appearArg.errorArg.renderTarget = nn::erreula::RenderTarget::Both;
appearArg.errorArg.controllerType = nn::erreula::ControllerType::DrcGamepad;
appearArg.errorArg.errorMessage = u"This is my error message";
appearArg.errorArg.button1Label = u"Left Button";
appearArg.errorArg.button2Label = u"Right Button";
appearArg.errorArg.errorTitle = u"Title";
nn::erreula::AppearErrorViewer(appearArg);
WHBLogPrintf("Begin rendering...");
while (WHBProcIsRunning()) {
// Read vpad for erreula::Calc
VPADStatus vpadStatus;
VPADRead(VPAD_CHAN_0, &vpadStatus, 1, nullptr);
VPADGetTPCalibratedPoint(VPAD_CHAN_0, &vpadStatus.tpNormal, &vpadStatus.tpNormal);
// Update erreula
nn::erreula::ControllerInfo controllerInfo;
controllerInfo.vpad = &vpadStatus;
controllerInfo.kpad[0] = nullptr;
controllerInfo.kpad[1] = nullptr;
controllerInfo.kpad[2] = nullptr;
controllerInfo.kpad[3] = nullptr;
nn::erreula::Calc(controllerInfo);
if (nn::erreula::IsDecideSelectButtonError()) {
nn::erreula::DisappearErrorViewer();
break;
}
WHBGfxBeginRender();
WHBGfxBeginRenderTV();
WHBGfxClearColor(0.0f, 0.0f, 1.0f, 1.0f);
nn::erreula::DrawTV();
WHBGfxFinishRenderTV();
WHBGfxBeginRenderDRC();
WHBGfxClearColor(1.0f, 0.0f, 1.0f, 1.0f);
nn::erreula::DrawDRC();
WHBGfxFinishRenderDRC();
WHBGfxFinishRender();
}
nn::erreula::ResultType resultType = nn::erreula::GetResultType();
int32_t resultCode = nn::erreula::GetResultCode();
WHBLogPrintf("Result type: %d, result code: %d",
static_cast<int>(resultType), resultCode);
// Cleanup
WHBLogPrintf("Exiting...");
nn::erreula::Destroy();
MEMFreeToDefaultHeap(createArg.workMemory);
FSDelClient(fsClient, FS_ERROR_FLAG_NONE);
MEMFreeToDefaultHeap(fsClient);
FSShutdown();
VPADShutdown();
WHBGfxShutdown();
WHBProcShutdown();
WHBLogUdpDeinit();
return 0;
}