erreula: Rework implementation and fix bugs

- ErrEula doesn't disappear on its own anymore. The expected behavior is for the game to call Disappear once a button has been selected. This fixes issues where the dialog would softlock in some games
- Modernized code a bit
- Added a subtle fade in/out effect
This commit is contained in:
Exzap 2024-11-10 10:10:46 +01:00
parent a5717e1b11
commit 66658351c1
5 changed files with 311 additions and 180 deletions

View File

@ -637,40 +637,40 @@ namespace CafeSystem
fsc_unmount("/cemuBossStorage/", FSC_PRIORITY_BASE); fsc_unmount("/cemuBossStorage/", FSC_PRIORITY_BASE);
} }
STATUS_CODE LoadAndMountForegroundTitle(TitleId titleId) PREPARE_STATUS_CODE LoadAndMountForegroundTitle(TitleId titleId)
{ {
cemuLog_log(LogType::Force, "Mounting title {:016x}", (uint64)titleId); cemuLog_log(LogType::Force, "Mounting title {:016x}", (uint64)titleId);
sGameInfo_ForegroundTitle = CafeTitleList::GetGameInfo(titleId); sGameInfo_ForegroundTitle = CafeTitleList::GetGameInfo(titleId);
if (!sGameInfo_ForegroundTitle.IsValid()) if (!sGameInfo_ForegroundTitle.IsValid())
{ {
cemuLog_log(LogType::Force, "Mounting failed: Game meta information is either missing, inaccessible or not valid (missing or invalid .xml files in code and meta folder)"); cemuLog_log(LogType::Force, "Mounting failed: Game meta information is either missing, inaccessible or not valid (missing or invalid .xml files in code and meta folder)");
return STATUS_CODE::UNABLE_TO_MOUNT; return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
} }
// check base // check base
TitleInfo& titleBase = sGameInfo_ForegroundTitle.GetBase(); TitleInfo& titleBase = sGameInfo_ForegroundTitle.GetBase();
if (!titleBase.IsValid()) if (!titleBase.IsValid())
return STATUS_CODE::UNABLE_TO_MOUNT; return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
if(!titleBase.ParseXmlInfo()) if(!titleBase.ParseXmlInfo())
return STATUS_CODE::UNABLE_TO_MOUNT; return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
cemuLog_log(LogType::Force, "Base: {}", titleBase.GetPrintPath()); cemuLog_log(LogType::Force, "Base: {}", titleBase.GetPrintPath());
// mount base // mount base
if (!titleBase.Mount("/vol/content", "content", FSC_PRIORITY_BASE) || !titleBase.Mount(GetInternalVirtualCodeFolder(), "code", FSC_PRIORITY_BASE)) if (!titleBase.Mount("/vol/content", "content", FSC_PRIORITY_BASE) || !titleBase.Mount(GetInternalVirtualCodeFolder(), "code", FSC_PRIORITY_BASE))
{ {
cemuLog_log(LogType::Force, "Mounting failed"); cemuLog_log(LogType::Force, "Mounting failed");
return STATUS_CODE::UNABLE_TO_MOUNT; return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
} }
// check update // check update
TitleInfo& titleUpdate = sGameInfo_ForegroundTitle.GetUpdate(); TitleInfo& titleUpdate = sGameInfo_ForegroundTitle.GetUpdate();
if (titleUpdate.IsValid()) if (titleUpdate.IsValid())
{ {
if (!titleUpdate.ParseXmlInfo()) if (!titleUpdate.ParseXmlInfo())
return STATUS_CODE::UNABLE_TO_MOUNT; return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
cemuLog_log(LogType::Force, "Update: {}", titleUpdate.GetPrintPath()); cemuLog_log(LogType::Force, "Update: {}", titleUpdate.GetPrintPath());
// mount update // mount update
if (!titleUpdate.Mount("/vol/content", "content", FSC_PRIORITY_PATCH) || !titleUpdate.Mount(GetInternalVirtualCodeFolder(), "code", FSC_PRIORITY_PATCH)) if (!titleUpdate.Mount("/vol/content", "content", FSC_PRIORITY_PATCH) || !titleUpdate.Mount(GetInternalVirtualCodeFolder(), "code", FSC_PRIORITY_PATCH))
{ {
cemuLog_log(LogType::Force, "Mounting failed"); cemuLog_log(LogType::Force, "Mounting failed");
return STATUS_CODE::UNABLE_TO_MOUNT; return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
} }
} }
else else
@ -682,20 +682,20 @@ namespace CafeSystem
// todo - support for multi-title AOC // todo - support for multi-title AOC
TitleInfo& titleAOC = aocList[0]; TitleInfo& titleAOC = aocList[0];
if (!titleAOC.ParseXmlInfo()) if (!titleAOC.ParseXmlInfo())
return STATUS_CODE::UNABLE_TO_MOUNT; return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
cemu_assert_debug(titleAOC.IsValid()); cemu_assert_debug(titleAOC.IsValid());
cemuLog_log(LogType::Force, "DLC: {}", titleAOC.GetPrintPath()); cemuLog_log(LogType::Force, "DLC: {}", titleAOC.GetPrintPath());
// mount AOC // mount AOC
if (!titleAOC.Mount(fmt::format("/vol/aoc{:016x}", titleAOC.GetAppTitleId()), "content", FSC_PRIORITY_PATCH)) if (!titleAOC.Mount(fmt::format("/vol/aoc{:016x}", titleAOC.GetAppTitleId()), "content", FSC_PRIORITY_PATCH))
{ {
cemuLog_log(LogType::Force, "Mounting failed"); cemuLog_log(LogType::Force, "Mounting failed");
return STATUS_CODE::UNABLE_TO_MOUNT; return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
} }
} }
else else
cemuLog_log(LogType::Force, "DLC: Not present"); cemuLog_log(LogType::Force, "DLC: Not present");
sForegroundTitleId = titleId; sForegroundTitleId = titleId;
return STATUS_CODE::SUCCESS; return PREPARE_STATUS_CODE::SUCCESS;
} }
void UnmountForegroundTitle() void UnmountForegroundTitle()
@ -723,7 +723,7 @@ namespace CafeSystem
} }
} }
STATUS_CODE SetupExecutable() PREPARE_STATUS_CODE SetupExecutable()
{ {
// set rpx path from cos.xml if available // set rpx path from cos.xml if available
_pathToBaseExecutable = _pathToExecutable; _pathToBaseExecutable = _pathToExecutable;
@ -755,7 +755,7 @@ namespace CafeSystem
} }
} }
LoadMainExecutable(); LoadMainExecutable();
return STATUS_CODE::SUCCESS; return PREPARE_STATUS_CODE::SUCCESS;
} }
void SetupMemorySpace() void SetupMemorySpace()
@ -769,7 +769,7 @@ namespace CafeSystem
memory_unmapForCurrentTitle(); memory_unmapForCurrentTitle();
} }
STATUS_CODE PrepareForegroundTitle(TitleId titleId) PREPARE_STATUS_CODE PrepareForegroundTitle(TitleId titleId)
{ {
CafeTitleList::WaitForMandatoryScan(); CafeTitleList::WaitForMandatoryScan();
sLaunchModeIsStandalone = false; sLaunchModeIsStandalone = false;
@ -780,21 +780,21 @@ namespace CafeSystem
// mount mlc storage // mount mlc storage
MountBaseDirectories(); MountBaseDirectories();
// mount title folders // mount title folders
STATUS_CODE r = LoadAndMountForegroundTitle(titleId); PREPARE_STATUS_CODE r = LoadAndMountForegroundTitle(titleId);
if (r != STATUS_CODE::SUCCESS) if (r != PREPARE_STATUS_CODE::SUCCESS)
return r; return r;
gameProfile_load(); gameProfile_load();
// setup memory space and PPC recompiler // setup memory space and PPC recompiler
SetupMemorySpace(); SetupMemorySpace();
PPCRecompiler_init(); PPCRecompiler_init();
r = SetupExecutable(); // load RPX r = SetupExecutable(); // load RPX
if (r != STATUS_CODE::SUCCESS) if (r != PREPARE_STATUS_CODE::SUCCESS)
return r; return r;
InitVirtualMlcStorage(); InitVirtualMlcStorage();
return STATUS_CODE::SUCCESS; return PREPARE_STATUS_CODE::SUCCESS;
} }
STATUS_CODE PrepareForegroundTitleFromStandaloneRPX(const fs::path& path) PREPARE_STATUS_CODE PrepareForegroundTitleFromStandaloneRPX(const fs::path& path)
{ {
sLaunchModeIsStandalone = true; sLaunchModeIsStandalone = true;
cemuLog_log(LogType::Force, "Launching executable in standalone mode due to incorrect layout or missing meta files"); cemuLog_log(LogType::Force, "Launching executable in standalone mode due to incorrect layout or missing meta files");
@ -812,7 +812,7 @@ namespace CafeSystem
if (!r) if (!r)
{ {
cemuLog_log(LogType::Force, "Failed to mount {}", _pathToUtf8(contentPath)); cemuLog_log(LogType::Force, "Failed to mount {}", _pathToUtf8(contentPath));
return STATUS_CODE::UNABLE_TO_MOUNT; return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
} }
} }
} }
@ -824,7 +824,7 @@ namespace CafeSystem
// since a lot of systems (including save folder location) rely on a TitleId, we derive a placeholder id from the executable hash // since a lot of systems (including save folder location) rely on a TitleId, we derive a placeholder id from the executable hash
auto execData = fsc_extractFile(_pathToExecutable.c_str()); auto execData = fsc_extractFile(_pathToExecutable.c_str());
if (!execData) if (!execData)
return STATUS_CODE::INVALID_RPX; return PREPARE_STATUS_CODE::INVALID_RPX;
uint32 h = generateHashFromRawRPXData(execData->data(), execData->size()); uint32 h = generateHashFromRawRPXData(execData->data(), execData->size());
sForegroundTitleId = 0xFFFFFFFF00000000ULL | (uint64)h; sForegroundTitleId = 0xFFFFFFFF00000000ULL | (uint64)h;
cemuLog_log(LogType::Force, "Generated placeholder TitleId: {:016x}", sForegroundTitleId); cemuLog_log(LogType::Force, "Generated placeholder TitleId: {:016x}", sForegroundTitleId);
@ -834,7 +834,7 @@ namespace CafeSystem
// load executable // load executable
SetupExecutable(); SetupExecutable();
InitVirtualMlcStorage(); InitVirtualMlcStorage();
return STATUS_CODE::SUCCESS; return PREPARE_STATUS_CODE::SUCCESS;
} }
void _LaunchTitleThread() void _LaunchTitleThread()

View File

@ -15,20 +15,19 @@ namespace CafeSystem
virtual void CafeRecreateCanvas() = 0; virtual void CafeRecreateCanvas() = 0;
}; };
enum class STATUS_CODE enum class PREPARE_STATUS_CODE
{ {
SUCCESS, SUCCESS,
INVALID_RPX, INVALID_RPX,
UNABLE_TO_MOUNT, // failed to mount through TitleInfo (most likely caused by an invalid or outdated path) UNABLE_TO_MOUNT, // failed to mount through TitleInfo (most likely caused by an invalid or outdated path)
//BAD_META_DATA, - the title list only stores titles with valid meta, so this error code is impossible
}; };
void Initialize(); void Initialize();
void SetImplementation(SystemImplementation* impl); void SetImplementation(SystemImplementation* impl);
void Shutdown(); void Shutdown();
STATUS_CODE PrepareForegroundTitle(TitleId titleId); PREPARE_STATUS_CODE PrepareForegroundTitle(TitleId titleId);
STATUS_CODE PrepareForegroundTitleFromStandaloneRPX(const fs::path& path); PREPARE_STATUS_CODE PrepareForegroundTitleFromStandaloneRPX(const fs::path& path);
void LaunchForegroundTitle(); void LaunchForegroundTitle();
bool IsTitleRunning(); bool IsTitleRunning();

View File

@ -40,7 +40,12 @@ namespace coreinit
inline TimerTicks ConvertNsToTimerTicks(uint64 ns) inline TimerTicks ConvertNsToTimerTicks(uint64 ns)
{ {
return ((GetTimerClock() / 31250LL) * ((ns)) / 32000LL); return ((GetTimerClock() / 31250LL) * ((TimerTicks)ns) / 32000LL);
}
inline TimerTicks ConvertMsToTimerTicks(uint64 ms)
{
return (TimerTicks)ms * GetTimerClock() / 1000LL;
} }
}; };

View File

@ -9,32 +9,45 @@
#include <wx/msgdlg.h> #include <wx/msgdlg.h>
#include "Cafe/OS/libs/coreinit/coreinit_FS.h" #include "Cafe/OS/libs/coreinit/coreinit_FS.h"
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
#include "Cafe/OS/libs/vpad/vpad.h" #include "Cafe/OS/libs/vpad/vpad.h"
namespace nn namespace nn
{ {
namespace erreula namespace erreula
{ {
#define RESULTTYPE_NONE 0
#define RESULTTYPE_FINISH 1
#define RESULTTYPE_NEXT 2
#define RESULTTYPE_JUMP 3
#define RESULTTYPE_PASSWORD 4
#define ERRORTYPE_CODE 0 enum class ErrorDialogType : uint32
#define ERRORTYPE_TEXT 1
#define ERRORTYPE_TEXT_ONE_BUTTON 2
#define ERRORTYPE_TEXT_TWO_BUTTON 3
#define ERREULA_STATE_HIDDEN 0
#define ERREULA_STATE_APPEARING 1
#define ERREULA_STATE_VISIBLE 2
#define ERREULA_STATE_DISAPPEARING 3
struct AppearArg_t
{ {
AppearArg_t() = default; Code = 0,
AppearArg_t(const AppearArg_t& o) Text = 1,
TextOneButton = 2,
TextTwoButton = 3
};
static const sint32 FADE_TIME = 80;
enum class ErrEulaState : uint32
{
Hidden = 0,
Appearing = 1,
Visible = 2,
Disappearing = 3
};
enum class ResultType : uint32
{
None = 0,
Finish = 1,
Next = 2,
Jump = 3,
Password = 4
};
struct AppearError
{
AppearError() = default;
AppearError(const AppearError& o)
{ {
errorType = o.errorType; errorType = o.errorType;
screenType = o.screenType; screenType = o.screenType;
@ -49,7 +62,7 @@ namespace erreula
drawCursor = o.drawCursor; drawCursor = o.drawCursor;
} }
uint32be errorType; betype<ErrorDialogType> errorType;
uint32be screenType; uint32be screenType;
uint32be controllerType; uint32be controllerType;
uint32be holdType; uint32be holdType;
@ -63,7 +76,9 @@ namespace erreula
bool drawCursor{}; bool drawCursor{};
}; };
static_assert(sizeof(AppearArg_t) == 0x2C); // maybe larger using AppearArg = AppearError;
static_assert(sizeof(AppearError) == 0x2C); // maybe larger
struct HomeNixSignArg_t struct HomeNixSignArg_t
{ {
@ -80,6 +95,132 @@ namespace erreula
static_assert(sizeof(ControllerInfo_t) == 0x14); // maybe larger static_assert(sizeof(ControllerInfo_t) == 0x14); // maybe larger
class ErrEulaInstance
{
public:
enum class BUTTON_SELECTION : uint32
{
NONE = 0xFFFFFFFF,
LEFT = 0,
RIGHT = 1,
};
void Init()
{
m_buttonSelection = BUTTON_SELECTION::NONE;
m_resultCode = -1;
m_resultCodeForLeftButton = 0;
m_resultCodeForRightButton = 0;
SetState(ErrEulaState::Hidden);
}
void DoAppearError(AppearArg* arg)
{
m_buttonSelection = BUTTON_SELECTION::NONE;
m_resultCode = -1;
m_resultCodeForLeftButton = -1;
m_resultCodeForRightButton = -1;
// for standard dialog its 0 and 1?
m_resultCodeForLeftButton = 0;
m_resultCodeForRightButton = 1;
SetState(ErrEulaState::Appearing);
}
void DoDisappearError()
{
if(m_state != ErrEulaState::Visible)
return;
SetState(ErrEulaState::Disappearing);
}
void DoCalc()
{
// appearing and disappearing state will automatically advance after some time
if (m_state == ErrEulaState::Appearing || m_state == ErrEulaState::Disappearing)
{
uint32 elapsedTick = coreinit::OSGetTime() - m_lastStateChange;
if (elapsedTick > coreinit::EspressoTime::ConvertMsToTimerTicks(FADE_TIME))
{
SetState(m_state == ErrEulaState::Appearing ? ErrEulaState::Visible : ErrEulaState::Hidden);
}
}
}
bool IsDecideSelectButtonError() const
{
return m_buttonSelection != BUTTON_SELECTION::NONE;
}
bool IsDecideSelectLeftButtonError() const
{
return m_buttonSelection != BUTTON_SELECTION::LEFT;
}
bool IsDecideSelectRightButtonError() const
{
return m_buttonSelection != BUTTON_SELECTION::RIGHT;
}
void SetButtonSelection(BUTTON_SELECTION selection)
{
cemu_assert_debug(m_buttonSelection == BUTTON_SELECTION::NONE);
m_buttonSelection = selection;
cemu_assert_debug(selection == BUTTON_SELECTION::LEFT || selection == BUTTON_SELECTION::RIGHT);
m_resultCode = selection == BUTTON_SELECTION::LEFT ? m_resultCodeForLeftButton : m_resultCodeForRightButton;
}
ErrEulaState GetState() const
{
return m_state;
}
sint32 GetResultCode() const
{
return m_resultCode;
}
ResultType GetResultType() const
{
if(m_resultCode == -1)
return ResultType::None;
if(m_resultCode < 10)
return ResultType::Finish;
if(m_resultCode >= 9999)
return ResultType::Next;
if(m_resultCode == 40)
return ResultType::Password;
return ResultType::Jump;
}
float GetFadeTransparency() const
{
if(m_state == ErrEulaState::Appearing || m_state == ErrEulaState::Disappearing)
{
uint32 elapsedTick = coreinit::OSGetTime() - m_lastStateChange;
if(m_state == ErrEulaState::Appearing)
return std::min<float>(1.0f, (float)elapsedTick / (float)coreinit::EspressoTime::ConvertMsToTimerTicks(FADE_TIME));
else
return std::max<float>(0.0f, 1.0f - (float)elapsedTick / (float)coreinit::EspressoTime::ConvertMsToTimerTicks(FADE_TIME));
}
return 1.0f;
}
private:
void SetState(ErrEulaState state)
{
m_state = state;
m_lastStateChange = coreinit::OSGetTime();
}
ErrEulaState m_state;
uint32 m_lastStateChange;
/* +0x30 */ betype<sint32> m_resultCode;
/* +0x239C */ betype<BUTTON_SELECTION> m_buttonSelection;
/* +0x23A0 */ betype<sint32> m_resultCodeForLeftButton;
/* +0x23A4 */ betype<sint32> m_resultCodeForRightButton;
};
struct ErrEula_t struct ErrEula_t
{ {
SysAllocator<coreinit::OSMutex> mutex; SysAllocator<coreinit::OSMutex> mutex;
@ -87,17 +228,11 @@ namespace erreula
uint32 langType; uint32 langType;
MEMPTR<coreinit::FSClient_t> fsClient; MEMPTR<coreinit::FSClient_t> fsClient;
AppearArg_t currentDialog; std::unique_ptr<ErrEulaInstance> errEulaInstance;
uint32 state;
bool buttonPressed;
bool rightButtonPressed;
AppearError currentDialog;
bool homeNixSignVisible; bool homeNixSignVisible;
std::chrono::steady_clock::time_point stateTimer{};
} g_errEula = {}; } g_errEula = {};
std::wstring GetText(uint16be* text) std::wstring GetText(uint16be* text)
{ {
@ -113,22 +248,61 @@ namespace erreula
} }
void export_ErrEulaCreate(PPCInterpreter_t* hCPU) void ErrEulaCreate(void* workmem, uint32 regionType, uint32 langType, coreinit::FSClient_t* fsClient)
{ {
ppcDefineParamMEMPTR(thisptr, uint8, 0);
ppcDefineParamU32(regionType, 1);
ppcDefineParamU32(langType, 2);
ppcDefineParamMEMPTR(fsClient, coreinit::FSClient_t, 3);
coreinit::OSLockMutex(&g_errEula.mutex); coreinit::OSLockMutex(&g_errEula.mutex);
g_errEula.regionType = regionType; g_errEula.regionType = regionType;
g_errEula.langType = langType; g_errEula.langType = langType;
g_errEula.fsClient = fsClient; g_errEula.fsClient = fsClient;
cemu_assert_debug(!g_errEula.errEulaInstance);
g_errEula.errEulaInstance = std::make_unique<ErrEulaInstance>();
g_errEula.errEulaInstance->Init();
coreinit::OSUnlockMutex(&g_errEula.mutex); coreinit::OSUnlockMutex(&g_errEula.mutex);
}
osLib_returnFromFunction(hCPU, 0); void ErrEulaDestroy()
{
g_errEula.errEulaInstance.reset();
}
// check if any dialog button was selected
bool IsDecideSelectButtonError()
{
if(!g_errEula.errEulaInstance)
return false;
return g_errEula.errEulaInstance->IsDecideSelectButtonError();
}
// check if left dialog button was selected
bool IsDecideSelectLeftButtonError()
{
if(!g_errEula.errEulaInstance)
return false;
return g_errEula.errEulaInstance->IsDecideSelectLeftButtonError();
}
// check if right dialog button was selected
bool IsDecideSelectRightButtonError()
{
if(!g_errEula.errEulaInstance)
return false;
return g_errEula.errEulaInstance->IsDecideSelectRightButtonError();
}
sint32 GetResultCode()
{
if(!g_errEula.errEulaInstance)
return -1;
return g_errEula.errEulaInstance->GetResultCode();
}
ResultType GetResultType()
{
if(!g_errEula.errEulaInstance)
return ResultType::None;
return g_errEula.errEulaInstance->GetResultType();
} }
void export_AppearHomeNixSign(PPCInterpreter_t* hCPU) void export_AppearHomeNixSign(PPCInterpreter_t* hCPU)
@ -137,28 +311,24 @@ namespace erreula
osLib_returnFromFunction(hCPU, 0); osLib_returnFromFunction(hCPU, 0);
} }
void export_AppearError(PPCInterpreter_t* hCPU) void ErrEulaAppearError(AppearArg* arg)
{ {
ppcDefineParamMEMPTR(arg, AppearArg_t, 0); g_errEula.currentDialog = *arg;
if(g_errEula.errEulaInstance)
g_errEula.currentDialog = *arg.GetPtr(); g_errEula.errEulaInstance->DoAppearError(arg);
g_errEula.state = ERREULA_STATE_APPEARING;
g_errEula.buttonPressed = false;
g_errEula.rightButtonPressed = false;
g_errEula.stateTimer = tick_cached();
osLib_returnFromFunction(hCPU, 0);
} }
void export_GetStateErrorViewer(PPCInterpreter_t* hCPU) void ErrEulaDisappearError()
{ {
osLib_returnFromFunction(hCPU, g_errEula.state); if(g_errEula.errEulaInstance)
g_errEula.errEulaInstance->DoDisappearError();
} }
void export_DisappearError(PPCInterpreter_t* hCPU)
ErrEulaState ErrEulaGetStateErrorViewer()
{ {
g_errEula.state = ERREULA_STATE_HIDDEN; if(!g_errEula.errEulaInstance)
osLib_returnFromFunction(hCPU, 0); return ErrEulaState::Hidden;
return g_errEula.errEulaInstance->GetState();
} }
void export_ChangeLang(PPCInterpreter_t* hCPU) void export_ChangeLang(PPCInterpreter_t* hCPU)
@ -168,27 +338,6 @@ namespace erreula
osLib_returnFromFunction(hCPU, 0); osLib_returnFromFunction(hCPU, 0);
} }
void export_IsDecideSelectButtonError(PPCInterpreter_t* hCPU)
{
if (g_errEula.buttonPressed)
cemuLog_logDebug(LogType::Force, "IsDecideSelectButtonError: TRUE");
osLib_returnFromFunction(hCPU, g_errEula.buttonPressed);
}
void export_IsDecideSelectLeftButtonError(PPCInterpreter_t* hCPU)
{
if (g_errEula.buttonPressed)
cemuLog_logDebug(LogType::Force, "IsDecideSelectLeftButtonError: TRUE");
osLib_returnFromFunction(hCPU, g_errEula.buttonPressed);
}
void export_IsDecideSelectRightButtonError(PPCInterpreter_t* hCPU)
{
if (g_errEula.rightButtonPressed)
cemuLog_logDebug(LogType::Force, "IsDecideSelectRightButtonError: TRUE");
osLib_returnFromFunction(hCPU, g_errEula.rightButtonPressed);
}
void export_IsAppearHomeNixSign(PPCInterpreter_t* hCPU) void export_IsAppearHomeNixSign(PPCInterpreter_t* hCPU)
{ {
osLib_returnFromFunction(hCPU, g_errEula.homeNixSignVisible); osLib_returnFromFunction(hCPU, g_errEula.homeNixSignVisible);
@ -200,61 +349,19 @@ namespace erreula
osLib_returnFromFunction(hCPU, 0); osLib_returnFromFunction(hCPU, 0);
} }
void export_GetResultType(PPCInterpreter_t* hCPU) void ErrEulaCalc(ControllerInfo_t* controllerInfo)
{ {
uint32 result = RESULTTYPE_NONE; if(g_errEula.errEulaInstance)
if (g_errEula.buttonPressed || g_errEula.rightButtonPressed) g_errEula.errEulaInstance->DoCalc();
{
cemuLog_logDebug(LogType::Force, "GetResultType: FINISH");
result = RESULTTYPE_FINISH;
}
osLib_returnFromFunction(hCPU, result);
}
void export_Calc(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(controllerInfo, ControllerInfo_t, 0);
// TODO: check controller buttons bla to accept dialog?
osLib_returnFromFunction(hCPU, 0);
} }
void render(bool mainWindow) void render(bool mainWindow)
{ {
if(g_errEula.state == ERREULA_STATE_HIDDEN) if(!g_errEula.errEulaInstance)
return; return;
if(g_errEula.errEulaInstance->GetState() != ErrEulaState::Visible && g_errEula.errEulaInstance->GetState() != ErrEulaState::Appearing && g_errEula.errEulaInstance->GetState() != ErrEulaState::Disappearing)
if(g_errEula.state == ERREULA_STATE_APPEARING)
{
if(std::chrono::duration_cast<std::chrono::milliseconds>(tick_cached() - g_errEula.stateTimer).count() <= 1000)
{
return;
}
g_errEula.state = ERREULA_STATE_VISIBLE;
g_errEula.stateTimer = tick_cached();
}
/*else if(g_errEula.state == STATE_VISIBLE)
{
if (std::chrono::duration_cast<std::chrono::milliseconds>(tick_cached() - g_errEula.stateTimer).count() >= 1000)
{
g_errEula.state = STATE_DISAPPEARING;
g_errEula.stateTimer = tick_cached();
return;
}
}*/
else if(g_errEula.state == ERREULA_STATE_DISAPPEARING)
{
if (std::chrono::duration_cast<std::chrono::milliseconds>(tick_cached() - g_errEula.stateTimer).count() >= 2000)
{
g_errEula.state = ERREULA_STATE_HIDDEN;
g_errEula.stateTimer = tick_cached();
}
return; return;
} const AppearError& appearArg = g_errEula.currentDialog;
const AppearArg_t& appearArg = g_errEula.currentDialog;
std::string text; std::string text;
const uint32 errorCode = (uint32)appearArg.errorCode; const uint32 errorCode = (uint32)appearArg.errorCode;
if (errorCode != 0) if (errorCode != 0)
@ -276,17 +383,28 @@ namespace erreula
ImGui::SetNextWindowPos(position, ImGuiCond_Always, pivot); ImGui::SetNextWindowPos(position, ImGuiCond_Always, pivot);
ImGui::SetNextWindowBgAlpha(0.9f); ImGui::SetNextWindowBgAlpha(0.9f);
ImGui::PushFont(font); ImGui::PushFont(font);
std::string title; std::string title;
if (appearArg.title) if (appearArg.title)
title = boost::nowide::narrow(GetText(appearArg.title.GetPtr())); title = boost::nowide::narrow(GetText(appearArg.title.GetPtr()));
if(title.empty()) // ImGui doesn't allow empty titles, so set one if appearArg.title is not set or empty if (title.empty()) // ImGui doesn't allow empty titles, so set one if appearArg.title is not set or empty
title = "ErrEula"; title = "ErrEula";
float fadeTransparency = 1.0f;
if (g_errEula.errEulaInstance->GetState() == ErrEulaState::Appearing || g_errEula.errEulaInstance->GetState() == ErrEulaState::Disappearing)
{
fadeTransparency = g_errEula.errEulaInstance->GetFadeTransparency();
}
float originalAlpha = ImGui::GetStyle().Alpha;
ImGui::GetStyle().Alpha = fadeTransparency;
ImGui::SetNextWindowBgAlpha(0.9f * fadeTransparency);
if (ImGui::Begin(title.c_str(), nullptr, kPopupFlags)) if (ImGui::Begin(title.c_str(), nullptr, kPopupFlags))
{ {
const float startx = ImGui::GetWindowSize().x / 2.0f; const float startx = ImGui::GetWindowSize().x / 2.0f;
bool hasLeftButtonPressed = false, hasRightButtonPressed = false;
switch ((uint32)appearArg.errorType) switch (appearArg.errorType)
{ {
default: default:
{ {
@ -294,11 +412,10 @@ namespace erreula
ImGui::TextUnformatted(text.c_str(), text.c_str() + text.size()); ImGui::TextUnformatted(text.c_str(), text.c_str() + text.size());
ImGui::Spacing(); ImGui::Spacing();
ImGui::SetCursorPosX(startx - 50); ImGui::SetCursorPosX(startx - 50);
g_errEula.buttonPressed |= ImGui::Button("OK", {100, 0}); hasLeftButtonPressed = ImGui::Button("OK", {100, 0});
break; break;
} }
case ERRORTYPE_TEXT: case ErrorDialogType::Text:
{ {
std::string txtTmp = "Unknown Error"; std::string txtTmp = "Unknown Error";
if (appearArg.text) if (appearArg.text)
@ -309,10 +426,10 @@ namespace erreula
ImGui::Spacing(); ImGui::Spacing();
ImGui::SetCursorPosX(startx - 50); ImGui::SetCursorPosX(startx - 50);
g_errEula.buttonPressed |= ImGui::Button("OK", { 100, 0 }); hasLeftButtonPressed = ImGui::Button("OK", { 100, 0 });
break; break;
} }
case ERRORTYPE_TEXT_ONE_BUTTON: case ErrorDialogType::TextOneButton:
{ {
std::string txtTmp = "Unknown Error"; std::string txtTmp = "Unknown Error";
if (appearArg.text) if (appearArg.text)
@ -328,10 +445,10 @@ namespace erreula
float width = std::max(100.0f, ImGui::CalcTextSize(button1.c_str()).x + 10.0f); float width = std::max(100.0f, ImGui::CalcTextSize(button1.c_str()).x + 10.0f);
ImGui::SetCursorPosX(startx - (width / 2.0f)); ImGui::SetCursorPosX(startx - (width / 2.0f));
g_errEula.buttonPressed |= ImGui::Button(button1.c_str(), { width, 0 }); hasLeftButtonPressed = ImGui::Button(button1.c_str(), { width, 0 });
break; break;
} }
case ERRORTYPE_TEXT_TWO_BUTTON: case ErrorDialogType::TextTwoButton:
{ {
std::string txtTmp = "Unknown Error"; std::string txtTmp = "Unknown Error";
if (appearArg.text) if (appearArg.text)
@ -352,42 +469,52 @@ namespace erreula
float width2 = std::max(100.0f, ImGui::CalcTextSize(button2.c_str()).x + 10.0f); float width2 = std::max(100.0f, ImGui::CalcTextSize(button2.c_str()).x + 10.0f);
ImGui::SetCursorPosX(startx - (width1 / 2.0f) - (width2 / 2.0f) - 10); ImGui::SetCursorPosX(startx - (width1 / 2.0f) - (width2 / 2.0f) - 10);
g_errEula.buttonPressed |= ImGui::Button(button1.c_str(), { width1, 0 }); hasLeftButtonPressed = ImGui::Button(button1.c_str(), { width1, 0 });
ImGui::SameLine(); ImGui::SameLine();
g_errEula.rightButtonPressed |= ImGui::Button(button2.c_str(), { width2, 0 }); hasRightButtonPressed = ImGui::Button(button2.c_str(), { width2, 0 });
break; break;
} }
} }
if (!g_errEula.errEulaInstance->IsDecideSelectButtonError())
{
if (hasLeftButtonPressed)
g_errEula.errEulaInstance->SetButtonSelection(ErrEulaInstance::BUTTON_SELECTION::LEFT);
if (hasRightButtonPressed)
g_errEula.errEulaInstance->SetButtonSelection(ErrEulaInstance::BUTTON_SELECTION::RIGHT);
}
} }
ImGui::End(); ImGui::End();
ImGui::PopFont(); ImGui::PopFont();
ImGui::GetStyle().Alpha = originalAlpha;
if(g_errEula.buttonPressed || g_errEula.rightButtonPressed)
{
g_errEula.state = ERREULA_STATE_DISAPPEARING;
g_errEula.stateTimer = tick_cached();
}
} }
void load() void load()
{ {
g_errEula.errEulaInstance.reset();
OSInitMutexEx(&g_errEula.mutex, nullptr); OSInitMutexEx(&g_errEula.mutex, nullptr);
//osLib_addFunction("erreula", "ErrEulaCreate__3RplFPUcQ3_2nn7erreula10", export_ErrEulaCreate); // copy ctor? cafeExportRegisterFunc(ErrEulaCreate, "erreula", "ErrEulaCreate__3RplFPUcQ3_2nn7erreula10RegionTypeQ3_2nn7erreula8LangTypeP8FSClient", LogType::Placeholder);
osLib_addFunction("erreula", "ErrEulaCreate__3RplFPUcQ3_2nn7erreula10RegionTypeQ3_2nn7erreula8LangTypeP8FSClient", export_ErrEulaCreate); cafeExportRegisterFunc(ErrEulaDestroy, "erreula", "ErrEulaDestroy__3RplFv", LogType::Placeholder);
cafeExportRegisterFunc(IsDecideSelectButtonError, "erreula", "ErrEulaIsDecideSelectButtonError__3RplFv", LogType::Placeholder);
cafeExportRegisterFunc(IsDecideSelectLeftButtonError, "erreula", "ErrEulaIsDecideSelectLeftButtonError__3RplFv", LogType::Placeholder);
cafeExportRegisterFunc(IsDecideSelectRightButtonError, "erreula", "ErrEulaIsDecideSelectRightButtonError__3RplFv", LogType::Placeholder);
cafeExportRegisterFunc(GetResultCode, "erreula", "ErrEulaGetResultCode__3RplFv", LogType::Placeholder);
cafeExportRegisterFunc(GetResultType, "erreula", "ErrEulaGetResultType__3RplFv", LogType::Placeholder);
cafeExportRegisterFunc(ErrEulaAppearError, "erreula", "ErrEulaAppearError__3RplFRCQ3_2nn7erreula9AppearArg", LogType::Placeholder);
cafeExportRegisterFunc(ErrEulaDisappearError, "erreula", "ErrEulaDisappearError__3RplFv", LogType::Placeholder);
cafeExportRegisterFunc(ErrEulaGetStateErrorViewer, "erreula", "ErrEulaGetStateErrorViewer__3RplFv", LogType::Placeholder);
cafeExportRegisterFunc(ErrEulaCalc, "erreula", "ErrEulaCalc__3RplFRCQ3_2nn7erreula14ControllerInfo", LogType::Placeholder);
osLib_addFunction("erreula", "ErrEulaAppearHomeNixSign__3RplFRCQ3_2nn7erreula14HomeNixSignArg", export_AppearHomeNixSign); osLib_addFunction("erreula", "ErrEulaAppearHomeNixSign__3RplFRCQ3_2nn7erreula14HomeNixSignArg", export_AppearHomeNixSign);
osLib_addFunction("erreula", "ErrEulaAppearError__3RplFRCQ3_2nn7erreula9AppearArg", export_AppearError);
osLib_addFunction("erreula", "ErrEulaGetStateErrorViewer__3RplFv", export_GetStateErrorViewer);
osLib_addFunction("erreula", "ErrEulaChangeLang__3RplFQ3_2nn7erreula8LangType", export_ChangeLang); osLib_addFunction("erreula", "ErrEulaChangeLang__3RplFQ3_2nn7erreula8LangType", export_ChangeLang);
osLib_addFunction("erreula", "ErrEulaIsDecideSelectButtonError__3RplFv", export_IsDecideSelectButtonError);
osLib_addFunction("erreula", "ErrEulaCalc__3RplFRCQ3_2nn7erreula14ControllerInfo", export_Calc);
osLib_addFunction("erreula", "ErrEulaIsDecideSelectLeftButtonError__3RplFv", export_IsDecideSelectLeftButtonError);
osLib_addFunction("erreula", "ErrEulaIsDecideSelectRightButtonError__3RplFv", export_IsDecideSelectRightButtonError);
osLib_addFunction("erreula", "ErrEulaIsAppearHomeNixSign__3RplFv", export_IsAppearHomeNixSign); osLib_addFunction("erreula", "ErrEulaIsAppearHomeNixSign__3RplFv", export_IsAppearHomeNixSign);
osLib_addFunction("erreula", "ErrEulaDisappearHomeNixSign__3RplFv", export_DisappearHomeNixSign); osLib_addFunction("erreula", "ErrEulaDisappearHomeNixSign__3RplFv", export_DisappearHomeNixSign);
osLib_addFunction("erreula", "ErrEulaGetResultType__3RplFv", export_GetResultType);
osLib_addFunction("erreula", "ErrEulaDisappearError__3RplFv", export_DisappearError);
} }
} }
} }

View File

@ -483,20 +483,20 @@ bool MainWindow::FileLoad(const fs::path launchPath, wxLaunchGameEvent::INITIATE
wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
return false; return false;
} }
CafeSystem::STATUS_CODE r = CafeSystem::PrepareForegroundTitle(baseTitleId); CafeSystem::PREPARE_STATUS_CODE r = CafeSystem::PrepareForegroundTitle(baseTitleId);
if (r == CafeSystem::STATUS_CODE::INVALID_RPX) if (r == CafeSystem::PREPARE_STATUS_CODE::INVALID_RPX)
{ {
cemu_assert_debug(false); cemu_assert_debug(false);
return false; return false;
} }
else if (r == CafeSystem::STATUS_CODE::UNABLE_TO_MOUNT) else if (r == CafeSystem::PREPARE_STATUS_CODE::UNABLE_TO_MOUNT)
{ {
wxString t = _("Unable to mount title.\nMake sure the configured game paths are still valid and refresh the game list.\n\nFile which failed to load:\n"); wxString t = _("Unable to mount title.\nMake sure the configured game paths are still valid and refresh the game list.\n\nFile which failed to load:\n");
t.append(_pathToUtf8(launchPath)); t.append(_pathToUtf8(launchPath));
wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
return false; return false;
} }
else if (r != CafeSystem::STATUS_CODE::SUCCESS) else if (r != CafeSystem::PREPARE_STATUS_CODE::SUCCESS)
{ {
wxString t = _("Failed to launch game."); wxString t = _("Failed to launch game.");
t.append(_pathToUtf8(launchPath)); t.append(_pathToUtf8(launchPath));
@ -511,8 +511,8 @@ bool MainWindow::FileLoad(const fs::path launchPath, wxLaunchGameEvent::INITIATE
CafeTitleFileType fileType = DetermineCafeSystemFileType(launchPath); CafeTitleFileType fileType = DetermineCafeSystemFileType(launchPath);
if (fileType == CafeTitleFileType::RPX || fileType == CafeTitleFileType::ELF) if (fileType == CafeTitleFileType::RPX || fileType == CafeTitleFileType::ELF)
{ {
CafeSystem::STATUS_CODE r = CafeSystem::PrepareForegroundTitleFromStandaloneRPX(launchPath); CafeSystem::PREPARE_STATUS_CODE r = CafeSystem::PrepareForegroundTitleFromStandaloneRPX(launchPath);
if (r != CafeSystem::STATUS_CODE::SUCCESS) if (r != CafeSystem::PREPARE_STATUS_CODE::SUCCESS)
{ {
cemu_assert_debug(false); // todo cemu_assert_debug(false); // todo
wxString t = _("Failed to launch executable. Path: "); wxString t = _("Failed to launch executable. Path: ");