rpcs3/rpcs3/Emu/Cell/Modules/cellCrossController.cpp
2022-09-17 16:35:02 +03:00

196 lines
6.3 KiB
C++

#include "stdafx.h"
#include "Emu/System.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/IdManager.h"
#include "Emu/localized_string.h"
#include "cellSysutil.h"
#include "cellCrossController.h"
#include "cellMsgDialog.h"
LOG_CHANNEL(cellCrossController);
template <>
void fmt_class_string<CellCrossControllerError>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](CellCrossControllerError value)
{
switch (value)
{
STR_CASE(CELL_CROSS_CONTROLLER_ERROR_CANCEL);
STR_CASE(CELL_CROSS_CONTROLLER_ERROR_NETWORK);
STR_CASE(CELL_CROSS_CONTROLLER_ERROR_OUT_OF_MEMORY);
STR_CASE(CELL_CROSS_CONTROLLER_ERROR_FATAL);
STR_CASE(CELL_CROSS_CONTROLLER_ERROR_INVALID_PKG_FILENAME);
STR_CASE(CELL_CROSS_CONTROLLER_ERROR_INVALID_SIG_FILENAME);
STR_CASE(CELL_CROSS_CONTROLLER_ERROR_INVALID_ICON_FILENAME);
STR_CASE(CELL_CROSS_CONTROLLER_ERROR_INVALID_VALUE);
STR_CASE(CELL_CROSS_CONTROLLER_ERROR_PKG_FILE_OPEN);
STR_CASE(CELL_CROSS_CONTROLLER_ERROR_SIG_FILE_OPEN);
STR_CASE(CELL_CROSS_CONTROLLER_ERROR_ICON_FILE_OPEN);
STR_CASE(CELL_CROSS_CONTROLLER_ERROR_INVALID_STATE);
STR_CASE(CELL_CROSS_CONTROLLER_ERROR_INVALID_PKG_FILE);
STR_CASE(CELL_CROSS_CONTROLLER_ERROR_INTERNAL);
}
return unknown;
});
}
void finish_callback(ppu_thread& ppu, s32 button_type, vm::ptr<void> userdata); // Forward declaration
struct cross_controller
{
atomic_t<s32> status{0};
std::unique_ptr<named_thread<std::function<void()>>> connection_thread;
vm::ptr<CellCrossControllerCallback> callback = vm::null;
vm::ptr<void> userdata = vm::null;
void on_connection_established(s32 status)
{
ensure(!!callback);
close_msg_dialog();
sysutil_register_cb([this, status](ppu_thread& ppu) -> s32
{
callback(ppu, CELL_CROSS_CONTROLLER_STATUS_FINALIZED, status, vm::null, userdata);
return CELL_OK;
});
}
void run_thread(vm::cptr<CellCrossControllerPackageInfo> pPkgInfo)
{
ensure(!!pPkgInfo);
ensure(!!callback);
const std::string msg = fmt::format("%s\n\n%s",
get_localized_string(localized_string_id::CELL_CROSS_CONTROLLER_MSG, pPkgInfo->pTitle.get_ptr()),
get_localized_string(localized_string_id::CELL_CROSS_CONTROLLER_FW_MSG));
vm::bptr<CellMsgDialogCallback> msg_dialog_callback = vm::null;
msg_dialog_callback.set(g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(finish_callback)));
// TODO: Show icons from comboplay_plugin.rco in dialog. Maybe use a new dialog or add an optional icon to this one.
error_code res = open_msg_dialog(false, CELL_MSGDIALOG_TYPE_DISABLE_CANCEL_OFF, vm::make_str(msg), msg_dialog_callback, userdata);
sysutil_register_cb([this, res](ppu_thread& ppu) -> s32
{
callback(ppu, CELL_CROSS_CONTROLLER_STATUS_INITIALIZED, res == CELL_OK ? +CELL_OK : +CELL_CROSS_CONTROLLER_ERROR_INTERNAL, vm::null, userdata);
return CELL_OK;
});
status = CELL_CROSS_CONTROLLER_STATUS_INITIALIZED;
connection_thread = std::make_unique<named_thread<std::function<void()>>>(fmt::format("Cross-Controller Thread"), [this]()
{
while (thread_ctrl::state() != thread_state::aborting)
{
if (Emu.IsPaused())
{
thread_ctrl::wait_for(10'000);
continue;
}
// TODO: establish connection to PS Vita
if (false)
{
on_connection_established(CELL_OK);
}
thread_ctrl::wait_for(1000);
}
status = CELL_CROSS_CONTROLLER_STATUS_FINALIZED;
});
}
void stop_thread()
{
if (connection_thread)
{
auto& thread = *connection_thread;
thread = thread_state::aborting;
thread();
connection_thread.reset();
}
};
};
void finish_callback(ppu_thread& ppu, s32 button_type, vm::ptr<void> userdata)
{
cross_controller& cc = g_fxo->get<cross_controller>();
// This function should only be called when the user canceled the dialog
ensure(cc.callback && button_type == CELL_MSGDIALOG_BUTTON_ESCAPE);
cc.callback(ppu, CELL_CROSS_CONTROLLER_STATUS_FINALIZED, CELL_CROSS_CONTROLLER_ERROR_CANCEL, vm::null, userdata);
cc.stop_thread();
}
error_code cellCrossControllerInitialize(vm::cptr<CellCrossControllerParam> pParam, vm::cptr<CellCrossControllerPackageInfo> pPkgInfo, vm::ptr<CellCrossControllerCallback> cb, vm::ptr<void> userdata) // LittleBigPlanet 2 and 3
{
cellCrossController.todo("cellCrossControllerInitialize(pParam=*0x%x, pPkgInfo=*0x%x, cb=*0x%x, userdata=*0x%x)", pParam, pPkgInfo, cb, userdata);
if (pParam)
{
cellCrossController.notice("cellCrossControllerInitialize: pParam: pPackageFileName=%s, pSignatureFileName=%s, pIconFileName=%s", pParam->pPackageFileName, pParam->pSignatureFileName, pParam->pIconFileName);
}
if (pPkgInfo)
{
cellCrossController.notice("cellCrossControllerInitialize: pPkgInfo: pTitle=%s, pTitleId=%s, pAppVer=%s", pPkgInfo->pTitle, pPkgInfo->pTitleId, pPkgInfo->pAppVer);
}
cross_controller& cc = g_fxo->get<cross_controller>();
if (cc.status == CELL_CROSS_CONTROLLER_STATUS_INITIALIZED) // TODO: confirm this logic
{
return CELL_CROSS_CONTROLLER_ERROR_INVALID_STATE;
}
if (!pParam || !pPkgInfo)
{
return CELL_CROSS_CONTROLLER_ERROR_INVALID_VALUE;
}
// Check if the strings exceed the allowed size (not counting null terminators)
if (!pParam->pPackageFileName || !memchr(pParam->pPackageFileName.get_ptr(), '\0', CELL_CROSS_CONTROLLER_PARAM_FILE_NAME_LEN + 1))
{
return CELL_CROSS_CONTROLLER_ERROR_INVALID_PKG_FILENAME;
}
if (!pParam->pSignatureFileName || !memchr(pParam->pSignatureFileName.get_ptr(), '\0', CELL_CROSS_CONTROLLER_PARAM_FILE_NAME_LEN + 1))
{
return CELL_CROSS_CONTROLLER_ERROR_INVALID_SIG_FILENAME;
}
if (!pParam->pIconFileName || !memchr(pParam->pIconFileName.get_ptr(), '\0', CELL_CROSS_CONTROLLER_PARAM_FILE_NAME_LEN + 1))
{
return CELL_CROSS_CONTROLLER_ERROR_INVALID_ICON_FILENAME;
}
if (!pPkgInfo->pAppVer || !memchr(pPkgInfo->pAppVer.get_ptr(), '\0', CELL_CROSS_CONTROLLER_PKG_APP_VER_LEN + 1) ||
!pPkgInfo->pTitleId || !memchr(pPkgInfo->pTitleId.get_ptr(), '\0', CELL_CROSS_CONTROLLER_PKG_TITLE_ID_LEN + 1) ||
!pPkgInfo->pTitle || !memchr(pPkgInfo->pTitle.get_ptr(), '\0', CELL_CROSS_CONTROLLER_PKG_TITLE_LEN + 1) ||
!cb)
{
return CELL_CROSS_CONTROLLER_ERROR_INVALID_VALUE;
}
cc.callback = cb;
cc.userdata = userdata;
cc.run_thread(pPkgInfo);
return CELL_OK;
}
DECLARE(ppu_module_manager::cellCrossController)("cellCrossController", []()
{
REG_FUNC(cellCrossController, cellCrossControllerInitialize);
// Helper Function
REG_HIDDEN_FUNC(finish_callback);
});