From bcd77ac65ed27031ff738bcccfebdac78833c252 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 26 Apr 2022 20:35:28 +0200 Subject: [PATCH] halfplement cellVideoExport --- rpcs3/Emu/Cell/Modules/cellVideoExport.cpp | 256 +++++++++++++++++++-- 1 file changed, 243 insertions(+), 13 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellVideoExport.cpp b/rpcs3/Emu/Cell/Modules/cellVideoExport.cpp index d3a8cdd83b..cfbaae8d8b 100644 --- a/rpcs3/Emu/Cell/Modules/cellVideoExport.cpp +++ b/rpcs3/Emu/Cell/Modules/cellVideoExport.cpp @@ -1,12 +1,64 @@ #include "stdafx.h" #include "Emu/Cell/PPUModule.h" +#include "Emu/IdManager.h" +#include "Emu/VFS.h" #include "cellSysutil.h" - - LOG_CHANNEL(cellVideoExport); +enum CellVideoExportUtilError : u32 +{ + CELL_VIDEO_EXPORT_UTIL_ERROR_BUSY = 0x8002ca01, + CELL_VIDEO_EXPORT_UTIL_ERROR_INTERNAL = 0x8002ca02, + CELL_VIDEO_EXPORT_UTIL_ERROR_PARAM = 0x8002ca03, + CELL_VIDEO_EXPORT_UTIL_ERROR_ACCESS_ERROR = 0x8002ca04, + CELL_VIDEO_EXPORT_UTIL_ERROR_DB_INTERNAL = 0x8002ca05, + CELL_VIDEO_EXPORT_UTIL_ERROR_DB_REGIST = 0x8002ca06, + CELL_VIDEO_EXPORT_UTIL_ERROR_SET_META = 0x8002ca07, + CELL_VIDEO_EXPORT_UTIL_ERROR_FLUSH_META = 0x8002ca08, + CELL_VIDEO_EXPORT_UTIL_ERROR_MOVE = 0x8002ca09, + CELL_VIDEO_EXPORT_UTIL_ERROR_INITIALIZE = 0x8002ca0a, +}; + +enum +{ + CELL_VIDEO_EXPORT_UTIL_RET_OK = 0, + CELL_VIDEO_EXPORT_UTIL_RET_CANCEL = 1, +}; + +enum +{ + CELL_VIDEO_EXPORT_UTIL_VERSION_CURRENT = 0, + CELL_VIDEO_EXPORT_UTIL_HDD_PATH_MAX = 1055, + CELL_VIDEO_EXPORT_UTIL_VIDEO_TITLE_MAX_LENGTH = 64, + CELL_VIDEO_EXPORT_UTIL_GAME_TITLE_MAX_LENGTH = 64, + CELL_VIDEO_EXPORT_UTIL_GAME_COMMENT_MAX_SIZE = 1024, +}; + +template<> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](auto error) + { + switch (error) + { + STR_CASE(CELL_VIDEO_EXPORT_UTIL_ERROR_BUSY); + STR_CASE(CELL_VIDEO_EXPORT_UTIL_ERROR_INTERNAL); + STR_CASE(CELL_VIDEO_EXPORT_UTIL_ERROR_PARAM); + STR_CASE(CELL_VIDEO_EXPORT_UTIL_ERROR_ACCESS_ERROR); + STR_CASE(CELL_VIDEO_EXPORT_UTIL_ERROR_DB_INTERNAL); + STR_CASE(CELL_VIDEO_EXPORT_UTIL_ERROR_DB_REGIST); + STR_CASE(CELL_VIDEO_EXPORT_UTIL_ERROR_SET_META); + STR_CASE(CELL_VIDEO_EXPORT_UTIL_ERROR_FLUSH_META); + STR_CASE(CELL_VIDEO_EXPORT_UTIL_ERROR_MOVE); + STR_CASE(CELL_VIDEO_EXPORT_UTIL_ERROR_INITIALIZE); + } + + return unknown; + }); +} + struct CellVideoExportSetParam { vm::bptr title; @@ -18,22 +70,61 @@ struct CellVideoExportSetParam using CellVideoExportUtilFinishCallback = void(s32 result, vm::ptr userdata); -error_code cellVideoExportProgress(vm::ptr funcFinish, vm::ptr userdata) + +struct video_export { - cellVideoExport.todo("cellVideoExportProgress(funcFinish=*0x%x, userdata=*0x%x)", funcFinish, userdata); + atomic_t progress = 0; // 0x0-0xFFFF for 0-100% +}; - sysutil_register_cb([=](ppu_thread& ppu) -> s32 + +bool check_file_path(const std::string& file_path) +{ + if (file_path.size() >= CELL_VIDEO_EXPORT_UTIL_HDD_PATH_MAX) { - funcFinish(ppu, 0xFFFF, userdata); // 0-0xFFFF where 0xFFFF = 100% - return CELL_OK; - }); + return false; + } - return CELL_OK; + for (char c : file_path) + { + if (!((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + c == '-' || c == '_' || + c == '/' || c == '.')) + { + return false; + } + } + + if (!file_path.starts_with("/dev_hdd0"sv) && + !file_path.starts_with("/dev_bdvd"sv) && + !file_path.starts_with("/dev_hdd1"sv)) + { + return false; + } + + if (file_path.find(".."sv) != umax) + { + return false; + } + + return true; } + error_code cellVideoExportInitialize2(u32 version, vm::ptr funcFinish, vm::ptr userdata) { - cellVideoExport.todo("cellVideoExportInitialize2(version=0x%x, funcFinish=*0x%x, userdata=*0x%x)", version, funcFinish, userdata); + cellVideoExport.notice("cellVideoExportInitialize2(version=0x%x, funcFinish=*0x%x, userdata=*0x%x)", version, funcFinish, userdata); + + if (version != CELL_VIDEO_EXPORT_UTIL_VERSION_CURRENT) + { + return CELL_VIDEO_EXPORT_UTIL_ERROR_PARAM; + } + + if (!funcFinish) + { + return CELL_VIDEO_EXPORT_UTIL_ERROR_PARAM; + } sysutil_register_cb([=](ppu_thread& ppu) -> s32 { @@ -46,7 +137,26 @@ error_code cellVideoExportInitialize2(u32 version, vm::ptr funcFinish, vm::ptr userdata) { - cellVideoExport.todo("cellVideoExportInitialize(version=0x%x, container=0x%x, funcFinish=*0x%x, userdata=*0x%x)", version, container, funcFinish, userdata); + cellVideoExport.notice("cellVideoExportInitialize(version=0x%x, container=0x%x, funcFinish=*0x%x, userdata=*0x%x)", version, container, funcFinish, userdata); + + if (version != CELL_VIDEO_EXPORT_UTIL_VERSION_CURRENT) + { + return CELL_VIDEO_EXPORT_UTIL_ERROR_PARAM; + } + + // Check container (same sub-function as cellVideoExportInitialize2, so we have to check this parameter anyway) + if (container != 0xfffffffe) + { + if (false) // invalid container or container size < 0x500000 + { + return CELL_VIDEO_EXPORT_UTIL_ERROR_PARAM; + } + } + + if (!funcFinish) + { + return CELL_VIDEO_EXPORT_UTIL_ERROR_PARAM; + } sysutil_register_cb([=](ppu_thread& ppu) -> s32 { @@ -57,12 +167,71 @@ error_code cellVideoExportInitialize(u32 version, u32 container, vm::ptr funcFinish, vm::ptr userdata) +{ + cellVideoExport.todo("cellVideoExportProgress(funcFinish=*0x%x, userdata=*0x%x)", funcFinish, userdata); + + if (!funcFinish) + { + return CELL_VIDEO_EXPORT_UTIL_ERROR_PARAM; + } + + sysutil_register_cb([=](ppu_thread& ppu) -> s32 + { + funcFinish(ppu, 0xFFFF, userdata); // 0-0xFFFF where 0xFFFF = 100% + return CELL_OK; + }); + + return CELL_OK; +} + error_code cellVideoExportFromFileWithCopy(vm::cptr srcHddDir, vm::cptr srcHddFile, vm::ptr param, vm::ptr funcFinish, vm::ptr userdata) { cellVideoExport.todo("cellVideoExportFromFileWithCopy(srcHddDir=%s, srcHddFile=%s, param=*0x%x, funcFinish=*0x%x, userdata=*0x%x)", srcHddDir, srcHddFile, param, funcFinish, userdata); + if (!param || !srcHddDir || !srcHddDir[0] || !srcHddFile || !srcHddFile[0]) + { + return CELL_VIDEO_EXPORT_UTIL_ERROR_PARAM; + } + + const std::string file_path = fmt::format("%s/%s", srcHddDir, srcHddFile); + + if (!check_file_path(file_path)) + { + return { CELL_VIDEO_EXPORT_UTIL_ERROR_PARAM, file_path }; + } + + if (!funcFinish) + { + return CELL_VIDEO_EXPORT_UTIL_ERROR_PARAM; + } + sysutil_register_cb([=](ppu_thread& ppu) -> s32 { + auto& vexp = g_fxo->get(); + vexp.progress = 0; // 0% + + const std::string filename = file_path.substr(file_path.find_last_of('/') + 1); + const std::string src_path = vfs::get(file_path); + const std::string dst_path = vfs::get("/dev_hdd0/video/" + filename); + + cellVideoExport.notice("Copying file from '%s' to '%s'", file_path, dst_path); + + if (!fs::copy_file(src_path, dst_path, true)) + { + // TODO: find out which error is used + cellVideoExport.error("Failed to copy file from '%s' to '%s' (%s)", src_path, dst_path, fs::g_tls_error); + funcFinish(ppu, CELL_VIDEO_EXPORT_UTIL_ERROR_MOVE, userdata); + return CELL_OK; + } + + // TODO: write metadata file sometime in the far future + // const std::string metadata_file = dst_path + ".vmd"; + + // TODO: track progress during file copy + + vexp.progress = 0xFFFF; // 100% + funcFinish(ppu, CELL_OK, userdata); return CELL_OK; }); @@ -73,9 +242,61 @@ error_code cellVideoExportFromFileWithCopy(vm::cptr srcHddDir, vm::cptr srcHddDir, vm::cptr srcHddFile, vm::ptr param, vm::ptr funcFinish, vm::ptr userdata) { cellVideoExport.todo("cellVideoExportFromFile(srcHddDir=%s, srcHddFile=%s, param=*0x%x, funcFinish=*0x%x, userdata=*0x%x)", srcHddDir, srcHddFile, param, funcFinish, userdata); + + if (!param || !srcHddDir || !srcHddDir[0] || !srcHddFile || !srcHddFile[0]) + { + return CELL_VIDEO_EXPORT_UTIL_ERROR_PARAM; + } + + const std::string file_path = fmt::format("%s/%s", srcHddDir, srcHddFile); + + if (!check_file_path(file_path)) + { + return { CELL_VIDEO_EXPORT_UTIL_ERROR_PARAM, file_path }; + } + + if (!funcFinish) + { + return CELL_VIDEO_EXPORT_UTIL_ERROR_PARAM; + } sysutil_register_cb([=](ppu_thread& ppu) -> s32 { + auto& vexp = g_fxo->get(); + vexp.progress = 0; // 0% + + const std::string filename = file_path.substr(file_path.find_last_of('/') + 1); + const std::string src_path = vfs::get(file_path); + const std::string dst_path = vfs::get("/dev_hdd0/movie/" + filename); + + cellVideoExport.notice("Copying file from '%s' to '%s'", file_path, dst_path); + + if (!fs::create_path(fs::get_parent_dir(dst_path)) || !fs::copy_file(src_path, dst_path, true)) + { + // TODO: find out which error is used + cellVideoExport.error("Failed to copy file from '%s' to '%s' (%s)", src_path, dst_path, fs::g_tls_error); + funcFinish(ppu, CELL_VIDEO_EXPORT_UTIL_ERROR_MOVE, userdata); + return CELL_OK; + } + + if (!file_path.starts_with("/dev_bdvd"sv)) + { + cellVideoExport.notice("Removing file '%s'", src_path); + + if (!fs::remove_file(src_path)) + { + // TODO: find out if an error is used here + cellVideoExport.error("Failed to remove file '%s' (%s)", src_path, fs::g_tls_error); + } + } + + // TODO: write metadata file sometime in the far future + // const std::string metadata_file = dst_path + ".vmd"; + + // TODO: track progress during file copy + + vexp.progress = 0xFFFF; // 100% + funcFinish(ppu, CELL_OK, userdata); return CELL_OK; }); @@ -85,11 +306,20 @@ error_code cellVideoExportFromFile(vm::cptr srcHddDir, vm::cptr srcH error_code cellVideoExportFinalize(vm::ptr funcFinish, vm::ptr userdata) { - cellVideoExport.todo("cellVideoExportFinalize(funcFinish=*0x%x, userdata=*0x%x)", funcFinish, userdata); + cellVideoExport.notice("cellVideoExportFinalize(funcFinish=*0x%x, userdata=*0x%x)", funcFinish, userdata); + + if (!funcFinish) + { + return CELL_VIDEO_EXPORT_UTIL_ERROR_PARAM; + } sysutil_register_cb([=](ppu_thread& ppu) -> s32 { - funcFinish(ppu, CELL_OK, userdata); + // Set the status as 0x0-0xFFFF (0-100%) depending on the copy status. + // Only the copy or move of the movie and metadata files is considered for the progress. + const auto& vexp = g_fxo->get(); + + funcFinish(ppu, vexp.progress, userdata); return CELL_OK; });