From 416b14de72b2aba16a753112eafcbb570b8d9ba0 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sun, 31 Jul 2022 22:10:10 +0200 Subject: [PATCH] cellPhotoExport: first implementation --- rpcs3/Emu/Cell/Modules/cellPhotoExport.cpp | 306 ++++++++++++++++++++- 1 file changed, 294 insertions(+), 12 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellPhotoExport.cpp b/rpcs3/Emu/Cell/Modules/cellPhotoExport.cpp index 45481684cf..4072fc8a7f 100644 --- a/rpcs3/Emu/Cell/Modules/cellPhotoExport.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPhotoExport.cpp @@ -1,9 +1,9 @@ #include "stdafx.h" #include "Emu/Cell/PPUModule.h" +#include "Emu/IdManager.h" +#include "Emu/VFS.h" #include "cellSysutil.h" - - LOG_CHANNEL(cellPhotoExport); // Return Codes @@ -44,6 +44,19 @@ void fmt_class_string::format(std::string& out, u64 arg) }); } +enum +{ + CELL_PHOTO_EXPORT_UTIL_VERSION_CURRENT = 0 +}; + +enum +{ + CELL_PHOTO_EXPORT_UTIL_HDD_PATH_MAX = 1055, + CELL_PHOTO_EXPORT_UTIL_PHOTO_TITLE_MAX_LENGTH = 64, + CELL_PHOTO_EXPORT_UTIL_GAME_TITLE_MAX_LENGTH = 64, + CELL_PHOTO_EXPORT_UTIL_GAME_COMMENT_MAX_SIZE = 1024, +}; + struct CellPhotoExportSetParam { vm::bptr photo_title; @@ -54,27 +67,196 @@ struct CellPhotoExportSetParam using CellPhotoExportUtilFinishCallback = void(s32 result, vm::ptr userdata); -error_code cellPhotoInitialize() +struct photo_export { - UNIMPLEMENTED_FUNC(cellPhotoExport); + atomic_t progress = 0; // 0x0-0xFFFF for 0-100% +}; + + +bool check_photo_path(const std::string& file_path) +{ + if (file_path.size() >= CELL_PHOTO_EXPORT_UTIL_HDD_PATH_MAX) + { + return false; + } + + 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; +} + +std::string get_available_photo_path(const std::string& filename) +{ + const std::string photo_dir = "/dev_hdd0/photo/"; + std::string dst_path = vfs::get(photo_dir + filename); + + // Do not overwrite existing files. Add a suffix instead. + for (u32 i = 0; fs::exists(dst_path); i++) + { + const std::string suffix = std::to_string(i); + std::string new_filename = filename; + + if (const usz pos = new_filename.find_last_of('.'); pos != std::string::npos) + { + new_filename.insert(pos, suffix); + } + else + { + new_filename.append(suffix); + } + + dst_path = vfs::get(photo_dir + new_filename); + } + + return dst_path; +} + + +error_code cellPhotoInitialize(s32 version, u32 container, vm::ptr funcFinish, vm::ptr userdata) +{ + cellPhotoExport.notice("cellPhotoInitialize(version=%d, container=%d, funcFinish=*0x%x, userdata=*0x%x)", version, container, funcFinish, userdata); + + if (version != CELL_PHOTO_EXPORT_UTIL_VERSION_CURRENT) + { + return CELL_PHOTO_EXPORT_UTIL_ERROR_PARAM; + } + + if (container != 0xfffffffe) + { + // TODO + } + + if (!funcFinish) + { + return CELL_PHOTO_EXPORT_UTIL_ERROR_PARAM; + } + + sysutil_register_cb([=](ppu_thread& ppu) -> s32 + { + funcFinish(ppu, CELL_OK, userdata); + return CELL_OK; + }); + return CELL_OK; } -error_code cellPhotoFinalize() +error_code cellPhotoFinalize(vm::ptr funcFinish, vm::ptr userdata) { - UNIMPLEMENTED_FUNC(cellPhotoExport); + cellPhotoExport.notice("cellPhotoFinalize(funcFinish=*0x%x, userdata=*0x%x)", funcFinish, userdata); + + if (!funcFinish) + { + return CELL_PHOTO_EXPORT_UTIL_ERROR_PARAM; + } + + sysutil_register_cb([=](ppu_thread& ppu) -> s32 + { + funcFinish(ppu, CELL_OK, userdata); + return CELL_OK; + }); + return CELL_OK; } -error_code cellPhotoRegistFromFile() +error_code cellPhotoRegistFromFile(vm::cptr path, vm::cptr photo_title, vm::cptr game_title, vm::cptr game_comment, vm::ptr funcFinish, vm::ptr userdata) { - UNIMPLEMENTED_FUNC(cellPhotoExport); + cellPhotoExport.todo("cellPhotoRegistFromFile(path=%s, photo_title=%s, game_title=%s, game_comment=%s, funcFinish=*0x%x, userdata=*0x%x)", path, photo_title, game_title, game_comment, funcFinish, userdata); + + if (!path || !funcFinish) + { + return CELL_PHOTO_EXPORT_UTIL_ERROR_PARAM; + } + + const std::string file_path = path.get_ptr(); + + if (!check_photo_path(file_path)) + { + return { CELL_PHOTO_EXPORT_UTIL_ERROR_PARAM, file_path }; + } + + sysutil_register_cb([=](ppu_thread& ppu) -> s32 + { + auto& pexp = g_fxo->get(); + pexp.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 = get_available_photo_path(filename); + + cellPhotoExport.notice("Copying file from '%s' to '%s'", file_path, dst_path); + + // TODO: We ignore metadata for now + + if (!fs::create_path(fs::get_parent_dir(dst_path)) || !fs::copy_file(src_path, dst_path, false)) + { + // TODO: find out which error is used + cellPhotoExport.error("Failed to copy file from '%s' to '%s' (%s)", src_path, dst_path, fs::g_tls_error); + funcFinish(ppu, CELL_PHOTO_EXPORT_UTIL_ERROR_MOVE, userdata); + return CELL_OK; + } + + if (file_path.starts_with("/dev_hdd0"sv)) + { + cellPhotoExport.notice("Removing file '%s'", src_path); + + if (!fs::remove_file(src_path)) + { + // TODO: find out if an error is used here + cellPhotoExport.error("Failed to remove file '%s' (%s)", src_path, fs::g_tls_error); + } + } + + // TODO: track progress during file copy + + pexp.progress = 0xFFFF; // 100% + + funcFinish(ppu, CELL_OK, userdata); + return CELL_OK; + }); + return CELL_OK; } error_code cellPhotoExportInitialize(u32 version, u32 container, vm::ptr funcFinish, vm::ptr userdata) { - cellPhotoExport.todo("cellPhotoExportInitialize(version=0x%x, container=0x%x, funcFinish=*0x%x, userdata=*0x%x)", version, container, funcFinish, userdata); + cellPhotoExport.notice("cellPhotoExportInitialize(version=0x%x, container=0x%x, funcFinish=*0x%x, userdata=*0x%x)", version, container, funcFinish, userdata); + + if (version != CELL_PHOTO_EXPORT_UTIL_VERSION_CURRENT) + { + return CELL_PHOTO_EXPORT_UTIL_ERROR_PARAM; + } + + if (container != 0xfffffffe) + { + // TODO + } + + if (!funcFinish) + { + return CELL_PHOTO_EXPORT_UTIL_ERROR_PARAM; + } sysutil_register_cb([=](ppu_thread& ppu) -> s32 { @@ -87,7 +269,12 @@ error_code cellPhotoExportInitialize(u32 version, u32 container, vm::ptr funcFinish, vm::ptr userdata) { - cellPhotoExport.todo("cellPhotoExportInitialize2(version=0x%x, funcFinish=*0x%x, userdata=*0x%x)", version, funcFinish, userdata); + cellPhotoExport.notice("cellPhotoExportInitialize2(version=0x%x, funcFinish=*0x%x, userdata=*0x%x)", version, funcFinish, userdata); + + if (version != CELL_PHOTO_EXPORT_UTIL_VERSION_CURRENT) + { + return CELL_PHOTO_EXPORT_UTIL_ERROR_PARAM; + } sysutil_register_cb([=](ppu_thread& ppu) -> s32 { @@ -100,7 +287,12 @@ error_code cellPhotoExportInitialize2(u32 version, vm::ptr funcFinish, vm::ptr userdata) { - cellPhotoExport.todo("cellPhotoExportFinalize(funcFinish=*0x%x, userdata=*0x%x)", funcFinish, userdata); + cellPhotoExport.notice("cellPhotoExportFinalize(funcFinish=*0x%x, userdata=*0x%x)", funcFinish, userdata); + + if (!funcFinish) + { + return CELL_PHOTO_EXPORT_UTIL_ERROR_PARAM; + } sysutil_register_cb([=](ppu_thread& ppu) -> s32 { @@ -115,8 +307,54 @@ error_code cellPhotoExportFromFile(vm::cptr srcHddDir, vm::cptr srcH { cellPhotoExport.todo("cellPhotoExportFromFile(srcHddDir=%s, srcHddFile=%s, param=*0x%x, funcFinish=*0x%x, userdata=*0x%x)", srcHddDir, srcHddFile, param, funcFinish, userdata); + if (!param || !funcFinish) + { + return CELL_PHOTO_EXPORT_UTIL_ERROR_PARAM; + } + + const std::string file_path = fmt::format("%s/%s", srcHddDir.get_ptr(), srcHddFile.get_ptr()); + + if (!check_photo_path(file_path)) + { + return { CELL_PHOTO_EXPORT_UTIL_ERROR_PARAM, file_path }; + } + sysutil_register_cb([=](ppu_thread& ppu) -> s32 { + auto& pexp = g_fxo->get(); + pexp.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 = get_available_photo_path(filename); + + cellPhotoExport.notice("Copying file from '%s' to '%s'", file_path, dst_path); + + // TODO: We ignore metadata for now + + if (!fs::create_path(fs::get_parent_dir(dst_path)) || !fs::copy_file(src_path, dst_path, false)) + { + // TODO: find out which error is used + cellPhotoExport.error("Failed to copy file from '%s' to '%s' (%s)", src_path, dst_path, fs::g_tls_error); + funcFinish(ppu, CELL_PHOTO_EXPORT_UTIL_ERROR_MOVE, userdata); + return CELL_OK; + } + + if (file_path.starts_with("/dev_hdd0"sv)) + { + cellPhotoExport.notice("Removing file '%s'", src_path); + + if (!fs::remove_file(src_path)) + { + // TODO: find out if an error is used here + cellPhotoExport.error("Failed to remove file '%s' (%s)", src_path, fs::g_tls_error); + } + } + + // TODO: track progress during file copy + + pexp.progress = 0xFFFF; // 100% + funcFinish(ppu, CELL_OK, userdata); return CELL_OK; }); @@ -128,8 +366,43 @@ error_code cellPhotoExportFromFileWithCopy(vm::cptr srcHddDir, vm::cptr s32 { + auto& pexp = g_fxo->get(); + pexp.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 = get_available_photo_path(filename); + + cellPhotoExport.notice("Copying file from '%s' to '%s'", file_path, dst_path); + + // TODO: We ignore metadata for now + + if (!fs::create_path(fs::get_parent_dir(dst_path)) || !fs::copy_file(src_path, dst_path, false)) + { + // TODO: find out which error is used + cellPhotoExport.error("Failed to copy file from '%s' to '%s' (%s)", src_path, dst_path, fs::g_tls_error); + funcFinish(ppu, CELL_PHOTO_EXPORT_UTIL_ERROR_MOVE, userdata); + return CELL_OK; + } + + // TODO: track progress during file copy + + pexp.progress = 0xFFFF; // 100% + funcFinish(ppu, CELL_OK, userdata); return CELL_OK; }); @@ -141,9 +414,18 @@ error_code cellPhotoExportProgress(vm::ptr fu { cellPhotoExport.todo("cellPhotoExportProgress(funcFinish=*0x%x, userdata=*0x%x)", funcFinish, userdata); + if (!funcFinish) + { + return CELL_PHOTO_EXPORT_UTIL_ERROR_PARAM; + } + sysutil_register_cb([=](ppu_thread& ppu) -> s32 { - funcFinish(ppu, 0xFFFF, userdata); // 0-0xFFFF where 0xFFFF = 100% + // 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& pexp = g_fxo->get(); + + funcFinish(ppu, pexp.progress, userdata); return CELL_OK; });