#include "stdafx.h" #include "Emu/Cell/PPUModule.h" #include "Emu/IdManager.h" #include "cellSubDisplay.h" LOG_CHANNEL(cellSubDisplay); template <> void fmt_class_string::format(std::string& out, u64 arg) { format_enum(out, arg, [](auto error) { switch (error) { STR_CASE(CELL_SUBDISPLAY_ERROR_OUT_OF_MEMORY); STR_CASE(CELL_SUBDISPLAY_ERROR_FATAL); STR_CASE(CELL_SUBDISPLAY_ERROR_NOT_FOUND); STR_CASE(CELL_SUBDISPLAY_ERROR_INVALID_VALUE); STR_CASE(CELL_SUBDISPLAY_ERROR_NOT_INITIALIZED); STR_CASE(CELL_SUBDISPLAY_ERROR_NOT_SUPPORTED); STR_CASE(CELL_SUBDISPLAY_ERROR_SET_SAMPLE); STR_CASE(CELL_SUBDISPLAY_ERROR_AUDIOOUT_IS_BUSY); STR_CASE(CELL_SUBDISPLAY_ERROR_ZERO_REGISTERED); } return unknown; }); } enum class sub_display_status : u32 { uninitialized = 0, stopped = 1, started = 2 }; struct sub_display_manager { shared_mutex mutex; sub_display_status status = sub_display_status::uninitialized; CellSubDisplayParam param{}; vm::ptr func = vm::null; vm::ptr userdata = vm::null; // Video data vm::ptr video_buffer = vm::null; u32 buf_size = 0; // Audio data bool audio_is_busy = false; // Touch data std::array touch_info{}; }; error_code check_param(CellSubDisplayParam* param) { if (!param || param->version < CELL_SUBDISPLAY_VERSION_0001 || param->version > CELL_SUBDISPLAY_VERSION_0003 || param->mode != CELL_SUBDISPLAY_MODE_REMOTEPLAY || param->nGroup != 1 || param->nPeer != 1 || param->audioParam.ch != 2 || param->audioParam.audioMode < CELL_SUBDISPLAY_AUDIO_MODE_SETDATA || param->audioParam.audioMode > CELL_SUBDISPLAY_AUDIO_MODE_CAPTURE) { return CELL_SUBDISPLAY_ERROR_INVALID_VALUE; } switch (param->videoParam.format) { case CELL_SUBDISPLAY_VIDEO_FORMAT_A8R8G8B8: case CELL_SUBDISPLAY_VIDEO_FORMAT_R8G8B8A8: { if (param->version == CELL_SUBDISPLAY_VERSION_0003 || param->videoParam.width != 480 || param->videoParam.height != 272 || (param->videoParam.pitch != 1920 && param->videoParam.pitch != 2048) || param->videoParam.aspectRatio < CELL_SUBDISPLAY_VIDEO_ASPECT_RATIO_16_9 || param->videoParam.aspectRatio > CELL_SUBDISPLAY_VIDEO_ASPECT_RATIO_4_3 || param->videoParam.videoMode < CELL_SUBDISPLAY_VIDEO_MODE_SETDATA || param->videoParam.videoMode > CELL_SUBDISPLAY_VIDEO_MODE_CAPTURE) { return CELL_SUBDISPLAY_ERROR_INVALID_VALUE; } break; } case CELL_SUBDISPLAY_VIDEO_FORMAT_YUV420: { if (param->version != CELL_SUBDISPLAY_VERSION_0003 || param->videoParam.width != CELL_SUBDISPLAY_0003_WIDTH || param->videoParam.height != CELL_SUBDISPLAY_0003_HEIGHT || param->videoParam.pitch != CELL_SUBDISPLAY_0003_PITCH || param->videoParam.aspectRatio != CELL_SUBDISPLAY_VIDEO_ASPECT_RATIO_16_9 || param->videoParam.videoMode != CELL_SUBDISPLAY_VIDEO_MODE_SETDATA) { return CELL_SUBDISPLAY_ERROR_INVALID_VALUE; } break; } default: { return CELL_SUBDISPLAY_ERROR_INVALID_VALUE; } } return CELL_OK; } error_code cellSubDisplayInit(vm::ptr pParam, vm::ptr func, vm::ptr userdata, u32 container) { cellSubDisplay.todo("cellSubDisplayInit(pParam=*0x%x, func=*0x%x, userdata=*0x%x, container=0x%x)", pParam, func, userdata, container); auto& manager = g_fxo->get(); std::lock_guard lock(manager.mutex); if (manager.func) { return CELL_SUBDISPLAY_ERROR_NOT_INITIALIZED; } if (error_code error = check_param(pParam.get_ptr())) { return error; } if (!func) { return CELL_SUBDISPLAY_ERROR_INVALID_VALUE; } manager.param = *pParam; manager.func = func; manager.userdata = userdata; if (true) // TODO { return CELL_SUBDISPLAY_ERROR_ZERO_REGISTERED; } return CELL_OK; } error_code cellSubDisplayEnd() { cellSubDisplay.todo("cellSubDisplayEnd()"); auto& manager = g_fxo->get(); std::lock_guard lock(manager.mutex); if (!manager.func) { return CELL_SUBDISPLAY_ERROR_NOT_INITIALIZED; } if (manager.status == sub_display_status::started) { // TODO: // cellSubDisplayStop(); } manager.param = {}; manager.func = vm::null; manager.userdata = vm::null; manager.status = sub_display_status::uninitialized; return CELL_OK; } error_code cellSubDisplayGetRequiredMemory(vm::ptr pParam) { cellSubDisplay.warning("cellSubDisplayGetRequiredMemory(pParam=*0x%x)", pParam); if (error_code error = check_param(pParam.get_ptr())) { return error; } switch (pParam->version) { case CELL_SUBDISPLAY_VERSION_0001: return not_an_error(CELL_SUBDISPLAY_0001_MEMORY_CONTAINER_SIZE); case CELL_SUBDISPLAY_VERSION_0002: return not_an_error(CELL_SUBDISPLAY_0002_MEMORY_CONTAINER_SIZE); case CELL_SUBDISPLAY_VERSION_0003: return not_an_error(CELL_SUBDISPLAY_0003_MEMORY_CONTAINER_SIZE); default: break; } return CELL_SUBDISPLAY_ERROR_INVALID_VALUE; } error_code cellSubDisplayStart() { cellSubDisplay.todo("cellSubDisplayStart()"); auto& manager = g_fxo->get(); std::lock_guard lock(manager.mutex); if (manager.status == sub_display_status::uninitialized) { return CELL_SUBDISPLAY_ERROR_NOT_INITIALIZED; } manager.status = sub_display_status::started; // TODO return CELL_OK; } error_code cellSubDisplayStop() { cellSubDisplay.todo("cellSubDisplayStop()"); auto& manager = g_fxo->get(); std::lock_guard lock(manager.mutex); if (manager.status == sub_display_status::uninitialized) { return CELL_SUBDISPLAY_ERROR_NOT_INITIALIZED; } manager.status = sub_display_status::stopped; // TODO return CELL_OK; } error_code cellSubDisplayGetVideoBuffer(s32 groupId, vm::pptr ppVideoBuf, vm::ptr pSize) { cellSubDisplay.todo("cellSubDisplayGetVideoBuffer(groupId=%d, ppVideoBuf=**0x%x, pSize=*0x%x)", groupId, ppVideoBuf, pSize); auto& manager = g_fxo->get(); std::lock_guard lock(manager.mutex); if (manager.status == sub_display_status::uninitialized) { return CELL_SUBDISPLAY_ERROR_NOT_INITIALIZED; } if (groupId != 0 || !ppVideoBuf || !pSize) { return CELL_SUBDISPLAY_ERROR_INVALID_VALUE; } *pSize = manager.buf_size; *ppVideoBuf = manager.video_buffer; return CELL_OK; } error_code cellSubDisplayAudioOutBlocking(s32 groupId, vm::ptr pvData, s32 samples) { cellSubDisplay.todo("cellSubDisplayAudioOutBlocking(groupId=%d, pvData=*0x%x, samples=%d)", groupId, pvData, samples); auto& manager = g_fxo->get(); std::lock_guard lock(manager.mutex); if (manager.status == sub_display_status::uninitialized) { return CELL_SUBDISPLAY_ERROR_NOT_INITIALIZED; } if (groupId != 0 || samples < 0) { return CELL_SUBDISPLAY_ERROR_INVALID_VALUE; } if (samples % 1024) { return CELL_SUBDISPLAY_ERROR_SET_SAMPLE; } // TODO return CELL_OK; } error_code cellSubDisplayAudioOutNonBlocking(s32 groupId, vm::ptr pvData, s32 samples) { cellSubDisplay.todo("cellSubDisplayAudioOutNonBlocking(groupId=%d, pvData=*0x%x, samples=%d)", groupId, pvData, samples); auto& manager = g_fxo->get(); std::lock_guard lock(manager.mutex); if (manager.status == sub_display_status::uninitialized) { return CELL_SUBDISPLAY_ERROR_NOT_INITIALIZED; } if (groupId != 0 || samples < 0) { return CELL_SUBDISPLAY_ERROR_INVALID_VALUE; } if (samples % 1024) { return CELL_SUBDISPLAY_ERROR_SET_SAMPLE; } if (manager.audio_is_busy) { return CELL_SUBDISPLAY_ERROR_AUDIOOUT_IS_BUSY; } // TODO: fetch audio async // manager.audio_is_busy = true; return CELL_OK; } error_code cellSubDisplayGetPeerNum(s32 groupId) { cellSubDisplay.todo("cellSubDisplayGetPeerNum(groupId=%d)", groupId); auto& manager = g_fxo->get(); std::lock_guard lock(manager.mutex); if (manager.status == sub_display_status::uninitialized) { return CELL_SUBDISPLAY_ERROR_NOT_INITIALIZED; } if (groupId != 0) { return CELL_SUBDISPLAY_ERROR_INVALID_VALUE; } s32 peer_num = 0; // TODO return not_an_error(peer_num); } error_code cellSubDisplayGetPeerList(s32 groupId, vm::ptr pInfo, vm::ptr pNum) { cellSubDisplay.todo("cellSubDisplayGetPeerList(groupId=%d, pInfo=*0x%x, pNum=*0x%x)", groupId, pInfo, pNum); auto& manager = g_fxo->get(); std::lock_guard lock(manager.mutex); if (manager.status == sub_display_status::uninitialized) { return CELL_SUBDISPLAY_ERROR_NOT_INITIALIZED; } if (groupId != 0) { return CELL_OK; } if (!pInfo || !pNum || *pNum < 1) { return CELL_SUBDISPLAY_ERROR_INVALID_VALUE; } *pNum = 0; // TODO return CELL_OK; } error_code cellSubDisplayGetTouchInfo(s32 groupId, vm::ptr pTouchInfo, vm::ptr pNumTouchInfo) { cellSubDisplay.todo("cellSubDisplayGetTouchInfo(groupId=%d, pTouchInfo=*0x%x, pNumTouchInfo=*0x%x)", groupId, pTouchInfo, pNumTouchInfo); if (groupId != 0 || !pNumTouchInfo || !pTouchInfo) { return CELL_SUBDISPLAY_ERROR_INVALID_VALUE; } auto& manager = g_fxo->get(); std::lock_guard lock(manager.mutex); if (manager.param.version != CELL_SUBDISPLAY_VERSION_0003) { return CELL_SUBDISPLAY_ERROR_NOT_SUPPORTED; } if (*pNumTouchInfo > CELL_SUBDISPLAY_TOUCH_MAX_TOUCH_INFO) { *pNumTouchInfo = CELL_SUBDISPLAY_TOUCH_MAX_TOUCH_INFO; } std::memcpy(pTouchInfo.get_ptr(), manager.touch_info.data(), *pNumTouchInfo * sizeof(CellSubDisplayTouchInfo)); return CELL_OK; } DECLARE(ppu_module_manager::cellSubDisplay)("cellSubDisplay", []() { // Initialization / Termination Functions REG_FUNC(cellSubDisplay, cellSubDisplayInit); REG_FUNC(cellSubDisplay, cellSubDisplayEnd); REG_FUNC(cellSubDisplay, cellSubDisplayGetRequiredMemory); REG_FUNC(cellSubDisplay, cellSubDisplayStart); REG_FUNC(cellSubDisplay, cellSubDisplayStop); // Data Setting Functions REG_FUNC(cellSubDisplay, cellSubDisplayGetVideoBuffer); REG_FUNC(cellSubDisplay, cellSubDisplayAudioOutBlocking); REG_FUNC(cellSubDisplay, cellSubDisplayAudioOutNonBlocking); // Peer Status Acquisition Functions REG_FUNC(cellSubDisplay, cellSubDisplayGetPeerNum); REG_FUNC(cellSubDisplay, cellSubDisplayGetPeerList); // REG_FUNC(cellSubDisplay, cellSubDisplayGetTouchInfo); });