From 577f379a1223ec536077774b56583bc4cdd7c34b Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 23 Jul 2022 21:16:43 +0200 Subject: [PATCH] implement cellPhotoImport --- rpcs3/Emu/Cell/Modules/cellPhotoImport.cpp | 208 ++++++++++++++++-- .../Overlays/overlay_media_list_dialog.cpp | 38 +++- rpcs3/Emu/localized_string_id.h | 1 + rpcs3/rpcs3qt/localized_emu.h | 1 + 4 files changed, 217 insertions(+), 31 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellPhotoImport.cpp b/rpcs3/Emu/Cell/Modules/cellPhotoImport.cpp index 7be26e6bc4..1586601720 100644 --- a/rpcs3/Emu/Cell/Modules/cellPhotoImport.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPhotoImport.cpp @@ -1,11 +1,13 @@ #include "stdafx.h" #include "Emu/Cell/PPUModule.h" #include "Emu/Cell/lv2/sys_fs.h" +#include "Emu/RSX/Overlays/overlay_media_list_dialog.h" +#include "Emu/VFS.h" +#include "Emu/System.h" +#include "Utilities/StrUtil.h" #include "cellSysutil.h" - - -LOG_CHANNEL(cellPhotoImportUtil); +LOG_CHANNEL(cellPhotoImportUtil, "PhotoImport"); // Return Codes enum CellPhotoImportError : u32 @@ -37,6 +39,11 @@ void fmt_class_string::format(std::string& out, u64 arg) }); } +enum CellPhotoImportVersion : u32 +{ + CELL_PHOTO_IMPORT_VERSION_CURRENT = 0, +}; + enum { CELL_PHOTO_IMPORT_HDD_PATH_MAX = 1055, @@ -92,36 +99,199 @@ struct CellPhotoImportSetParam using CellPhotoImportFinishCallback = void(s32 result, vm::ptr filedata, vm::ptr userdata); +struct photo_import +{ + shared_mutex mutex; + bool is_busy = false; + CellPhotoImportSetParam param{}; + vm::ptr func_finish{}; + vm::ptr userdata{}; +}; + +error_code select_photo(std::string dst_dir) +{ + auto& pi_manager = g_fxo->get(); + + if (!pi_manager.func_finish) + return CELL_PHOTO_IMPORT_ERROR_PARAM; + + if (!fs::is_dir(dst_dir)) + { + // TODO: check if the dir is user accessible and can be written to + return CELL_PHOTO_IMPORT_ERROR_PARAM; // TODO: is this correct? + } + + pi_manager.is_busy = true; + + const std::string vfs_dir_path = vfs::get("/dev_hdd0/photo"); + const std::string title = get_localized_string(localized_string_id::RSX_OVERLAYS_MEDIA_DIALOG_TITLE_PHOTO_IMPORT); + + error_code error = rsx::overlays::show_media_list_dialog(rsx::overlays::media_list_dialog::media_type::photo, vfs_dir_path, title, + [&pi_manager, dst_dir](s32 status, utils::media_info info) + { + sysutil_register_cb([&pi_manager, dst_dir, info, status](ppu_thread& ppu) -> s32 + { + vm::var filedata = vm::make_var(CellPhotoImportFileData{}); + vm::var sub = vm::make_var(CellPhotoImportFileDataSub{}); + + u32 result = status >= 0 ? u32{CELL_OK} : u32{CELL_CANCEL}; + + if (result == CELL_OK) + { + fs::stat_t f_info{}; + + if (!fs::stat(info.path, f_info) || f_info.is_directory) + { + result = CELL_PHOTO_IMPORT_ERROR_ACCESS_ERROR; // TODO: is this correct ? + pi_manager.func_finish(ppu, result, filedata, pi_manager.userdata); + return CELL_OK; + } + + if (f_info.size > pi_manager.param.fileSizeMax) + { + result = CELL_PHOTO_IMPORT_ERROR_COPY; // TODO: is this correct ? + pi_manager.func_finish(ppu, result, filedata, pi_manager.userdata); + return CELL_OK; + } + + const std::string filename = info.path.substr(info.path.find_last_of(fs::delim) + 1); + const std::string title = info.get_metadata("title", filename); + const std::string sub_type = fmt::to_lower(info.sub_type); + const std::string dst_path = dst_dir + "/" + filename; + + strcpy_trunc(filedata->dstFileName, filename); + strcpy_trunc(filedata->photo_title, title); + strcpy_trunc(filedata->game_title, Emu.GetTitle()); + // strcpy_trunc(filedata->game_comment, ...); // TODO + + filedata->data_sub = sub; + filedata->data_sub->width = info.width; + filedata->data_sub->height = info.height; + + if (sub_type == "jpg" || sub_type == "jpeg") + { + filedata->data_sub->format = CELL_PHOTO_IMPORT_FT_JPEG; + } + else if (sub_type == "png") + { + filedata->data_sub->format = CELL_PHOTO_IMPORT_FT_PNG; + } + else if (sub_type == "tif" || sub_type == "tiff") + { + filedata->data_sub->format = CELL_PHOTO_IMPORT_FT_TIFF; + } + else if (sub_type == "bmp") + { + filedata->data_sub->format = CELL_PHOTO_IMPORT_FT_BMP; + } + else if (sub_type == "gif") + { + filedata->data_sub->format = CELL_PHOTO_IMPORT_FT_GIF; + } + else if (sub_type == "mpo") + { + filedata->data_sub->format = CELL_PHOTO_IMPORT_FT_MPO; + } + else + { + filedata->data_sub->format = CELL_PHOTO_IMPORT_FT_UNKNOWN; + } + + switch (info.orientation) + { + default: + case CELL_SEARCH_ORIENTATION_UNKNOWN: + case CELL_SEARCH_ORIENTATION_TOP_LEFT: + filedata->data_sub->rotate = CELL_PHOTO_IMPORT_TEX_ROT_0; + break; + case CELL_SEARCH_ORIENTATION_TOP_RIGHT: + filedata->data_sub->rotate = CELL_PHOTO_IMPORT_TEX_ROT_90; + break; + case CELL_SEARCH_ORIENTATION_BOTTOM_RIGHT: + filedata->data_sub->rotate = CELL_PHOTO_IMPORT_TEX_ROT_180; + break; + case CELL_SEARCH_ORIENTATION_BOTTOM_LEFT: + filedata->data_sub->rotate = CELL_PHOTO_IMPORT_TEX_ROT_270; + break; + } + + cellPhotoImportUtil.success("Media list dialog: selected entry '%s'. Copying to '%s'...", info.path, dst_path); + if (!fs::copy_file(info.path, dst_path, false)) + { + cellPhotoImportUtil.error("Failed to copy '%s' to '%s'. Error = '%s'", info.path, dst_path, fs::g_tls_error); + result = CELL_PHOTO_IMPORT_ERROR_COPY; + } + } + else + { + cellPhotoImportUtil.notice("Media list dialog was canceled"); + } + + pi_manager.is_busy = false; + pi_manager.func_finish(ppu, result, filedata, pi_manager.userdata); + return CELL_OK; + }); + }); + + if (error != CELL_OK) + { + pi_manager.is_busy = false; + } + + return error; +} + error_code cellPhotoImport(u32 version, vm::cptr dstHddPath, vm::ptr param, u32 container, vm::ptr funcFinish, vm::ptr userdata) { cellPhotoImportUtil.todo("cellPhotoImport(version=0x%x, dstHddPath=%s, param=*0x%x, container=0x%x, funcFinish=*0x%x, userdata=*0x%x)", version, dstHddPath, param, container, funcFinish, userdata); - sysutil_register_cb([=](ppu_thread& ppu) -> s32 + if (version != CELL_PHOTO_IMPORT_VERSION_CURRENT || !funcFinish || !param || !dstHddPath) { - vm::var filedata; - vm::var sub; - filedata->data_sub = sub; - funcFinish(ppu, CELL_OK, filedata, userdata); - return CELL_OK; - }); + return CELL_PHOTO_IMPORT_ERROR_PARAM; + } - return CELL_OK; + if (container != 0xffffffff && false) // TODO + { + return CELL_PHOTO_IMPORT_ERROR_PARAM; + } + + auto& pi_manager = g_fxo->get(); + std::lock_guard lock(pi_manager.mutex); + + if (pi_manager.is_busy) + { + return CELL_PHOTO_IMPORT_ERROR_BUSY; + } + + pi_manager.param = *param; + pi_manager.func_finish = funcFinish; + pi_manager.userdata = userdata; + + return select_photo(dstHddPath.get_ptr()); } error_code cellPhotoImport2(u32 version, vm::cptr dstHddPath, vm::ptr param, vm::ptr funcFinish, vm::ptr userdata) { cellPhotoImportUtil.todo("cellPhotoImport2(version=0x%x, dstHddPath=%s, param=*0x%x, funcFinish=*0x%x, userdata=*0x%x)", version, dstHddPath, param, funcFinish, userdata); - sysutil_register_cb([=](ppu_thread& ppu) -> s32 + if (version != CELL_PHOTO_IMPORT_VERSION_CURRENT || !funcFinish || !param || !dstHddPath) { - vm::var filedata; - vm::var sub; - filedata->data_sub = sub; - funcFinish(ppu, CELL_OK, filedata, userdata); - return CELL_OK; - }); + return CELL_PHOTO_IMPORT_ERROR_PARAM; + } - return CELL_OK; + auto& pi_manager = g_fxo->get(); + std::lock_guard lock(pi_manager.mutex); + + if (pi_manager.is_busy) + { + return CELL_PHOTO_IMPORT_ERROR_BUSY; + } + + pi_manager.param = *param; + pi_manager.func_finish = funcFinish; + pi_manager.userdata = userdata; + + return select_photo(dstHddPath.get_ptr()); } DECLARE(ppu_module_manager::cellPhotoImportUtil)("cellPhotoImportUtil", []() diff --git a/rpcs3/Emu/RSX/Overlays/overlay_media_list_dialog.cpp b/rpcs3/Emu/RSX/Overlays/overlay_media_list_dialog.cpp index edfe3b9b02..7af72640c2 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_media_list_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_media_list_dialog.cpp @@ -34,6 +34,27 @@ namespace rsx { if (fs::exists(entry.info.path)) { + // Fit the new image into the available space + if (entry.info.width > 0 && entry.info.height > 0) + { + const u16 target_width = image->w - (image->padding_left + image->padding_right); + const u16 target_height = image->h - (image->padding_top + image->padding_bottom); + const f32 target_ratio = target_width / static_cast(target_height); + const f32 image_ratio = entry.info.width / static_cast(entry.info.height); + const f32 convert_ratio = image_ratio / target_ratio; + + if (convert_ratio > 1.0f) + { + const u16 new_padding = (target_height - target_height / convert_ratio) / 2; + image->set_padding(image->padding_left, image->padding_right, new_padding + image->padding_top, new_padding + image->padding_bottom); + } + else if (convert_ratio < 1.0f) + { + const u16 new_padding = (target_width - target_width * convert_ratio) / 2; + image->set_padding(image->padding_left + new_padding, image->padding_right + new_padding, image->padding_top, image->padding_bottom); + } + } + icon_data = std::make_unique(entry.info.path.c_str()); static_cast(image.get())->set_raw_image(icon_data.get()); } @@ -263,7 +284,7 @@ namespace rsx continue; } - media_list_dialog::media_entry new_entry; + media_list_dialog::media_entry new_entry{}; parse_media_recursive(depth, media_path + "/" + dir_entry.name, dir_entry.name, type, new_entry); if (new_entry.type != media_list_dialog::media_type::invalid) { @@ -285,19 +306,12 @@ namespace rsx current_entry.info.path = media_path; } } - else if (type == media_list_dialog::media_type::photo) - { - // Let's restrict this to png and jpg for now - if (name.ends_with(".png") || name.ends_with(".jpg")) - { - current_entry.type = type; - rsx_log.notice("parse_media_recursive: Found photo '%s'", media_path); - } - } else { - // Try to peek into the audio or video file - const s32 av_media_type = type == media_list_dialog::media_type::video ? 0 /*AVMEDIA_TYPE_VIDEO*/ : 1 /*AVMEDIA_TYPE_AUDIO*/; + // Try to peek into the file + const s32 av_media_type = type == media_list_dialog::media_type::photo ? -1 + : type == media_list_dialog::media_type::video ? 0 /*AVMEDIA_TYPE_VIDEO*/ + : 1 /*AVMEDIA_TYPE_AUDIO*/; auto [success, info] = utils::get_media_info(media_path, av_media_type); if (success) { diff --git a/rpcs3/Emu/localized_string_id.h b/rpcs3/Emu/localized_string_id.h index 6542f1542d..7bc2c33446 100644 --- a/rpcs3/Emu/localized_string_id.h +++ b/rpcs3/Emu/localized_string_id.h @@ -25,6 +25,7 @@ enum class localized_string_id RSX_OVERLAYS_OSK_DIALOG_ENTER_TEXT, RSX_OVERLAYS_OSK_DIALOG_ENTER_PASSWORD, RSX_OVERLAYS_MEDIA_DIALOG_TITLE, + RSX_OVERLAYS_MEDIA_DIALOG_TITLE_PHOTO_IMPORT, RSX_OVERLAYS_MEDIA_DIALOG_EMPTY, RSX_OVERLAYS_LIST_SELECT, RSX_OVERLAYS_LIST_CANCEL, diff --git a/rpcs3/rpcs3qt/localized_emu.h b/rpcs3/rpcs3qt/localized_emu.h index d66c4880c0..f2fc286f71 100644 --- a/rpcs3/rpcs3qt/localized_emu.h +++ b/rpcs3/rpcs3qt/localized_emu.h @@ -55,6 +55,7 @@ private: case localized_string_id::RSX_OVERLAYS_OSK_DIALOG_ENTER_TEXT: return tr("[Enter Text]", "OSK Dialog"); case localized_string_id::RSX_OVERLAYS_OSK_DIALOG_ENTER_PASSWORD: return tr("[Enter Password]", "OSK Dialog"); case localized_string_id::RSX_OVERLAYS_MEDIA_DIALOG_TITLE: return tr("Select media", "Media dialog"); + case localized_string_id::RSX_OVERLAYS_MEDIA_DIALOG_TITLE_PHOTO_IMPORT: return tr("Select photo to import", "Media dialog"); case localized_string_id::RSX_OVERLAYS_MEDIA_DIALOG_EMPTY: return tr("No media found.", "Media dialog"); case localized_string_id::RSX_OVERLAYS_LIST_SELECT: return tr("Enter", "Enter Dialog List"); case localized_string_id::RSX_OVERLAYS_LIST_CANCEL: return tr("Back", "Cancel Dialog List");