From d2b83c69bb19122dbba5244c484b89ff03678c62 Mon Sep 17 00:00:00 2001 From: Silent Date: Sat, 8 Feb 2020 11:07:18 +0100 Subject: [PATCH] cellSearch updates from Brolijah Co-authored-by: Brolijah --- rpcs3/Emu/Cell/Modules/cellSearch.cpp | 679 +++++++++++++++++++++++++- 1 file changed, 658 insertions(+), 21 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellSearch.cpp b/rpcs3/Emu/Cell/Modules/cellSearch.cpp index 3fce5a8606..dfc6abf863 100644 --- a/rpcs3/Emu/Cell/Modules/cellSearch.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSearch.cpp @@ -1,4 +1,4 @@ -#include "stdafx.h" +#include "stdafx.h" #include "Emu/System.h" #include "Emu/IdManager.h" #include "Emu/Cell/PPUModule.h" @@ -6,6 +6,8 @@ #include "cellSysutil.h" #include "cellSearch.h" +#include "xxhash.h" +#include "Utilities/StrUtil.h" LOG_CHANNEL(cellSearch); @@ -42,12 +44,33 @@ void fmt_class_string::format(std::string& out, u64 arg) }); } +namespace { struct search_info { vm::ptr func; vm::ptr userData; }; +struct search_content_t +{ + CellSearchContentType type = CELL_SEARCH_CONTENTTYPE_NONE; + CellSearchTimeInfo timeInfo; + CellSearchContentInfoPath infoPath; + union + { + CellSearchMusicInfo music; + CellSearchPhotoInfo photo; + CellSearchVideoInfo video; + CellSearchMusicListInfo music_list; + CellSearchPhotoListInfo photo_list; + CellSearchVideoListInfo video_list; + CellSearchVideoSceneInfo scene; + } data; +}; + +using ContentIdType = std::pair>; +using ContentIdMap = std::unordered_map>; + struct search_object_t { // TODO: Figured out the correct values to set here @@ -55,18 +78,57 @@ struct search_object_t static const u32 id_step = 1; static const u32 id_count = 64; static const u32 invalid = 0xFFFFFFFF; + + std::vector content_ids; }; +enum class search_state +{ + not_initialized = 0, + idle, + in_progress, + initializing, + canceling, + finalizing, +}; +} + +static search_state s_search_state = search_state::not_initialized; + error_code cellSearchInitialize(CellSearchMode mode, u32 container, vm::ptr func, vm::ptr userData) { cellSearch.warning("cellSearchInitialize(mode=0x%x, container=0x%x, func=*0x%x, userData=*0x%x)", +mode, container, func, userData); + if (!func) + { + return CELL_SEARCH_ERROR_PARAM; + } + + switch (s_search_state) + { + case search_state::not_initialized: + break; + case search_state::initializing: + return CELL_SEARCH_ERROR_BUSY; + case search_state::finalizing: + return CELL_SEARCH_ERROR_FINALIZING; + default: + return CELL_SEARCH_ERROR_ALREADY_INITIALIZED; + } + + if (mode != CELL_SEARCH_MODE_NORMAL) + { + return CELL_SEARCH_ERROR_UNKNOWN_MODE; + } + const auto search = g_fxo->get(); search->func = func; search->userData = userData; + s_search_state = search_state::initializing; sysutil_register_cb([=](ppu_thread& ppu) -> s32 { + s_search_state = search_state::idle; func(ppu, CELL_SEARCH_EVENT_INITIALIZE_RESULT, CELL_OK, vm::null, userData); return CELL_OK; }); @@ -76,12 +138,29 @@ error_code cellSearchInitialize(CellSearchMode mode, u32 container, vm::ptrget(); - - sysutil_register_cb([=](ppu_thread& ppu) -> s32 + switch (s_search_state) { + case search_state::idle: + break; + case search_state::not_initialized: + return CELL_SEARCH_ERROR_NOT_INITIALIZED; + case search_state::finalizing: + return CELL_SEARCH_ERROR_FINALIZING; + case search_state::in_progress: + case search_state::initializing: + case search_state::canceling: + return CELL_SEARCH_ERROR_BUSY; + default: + return CELL_SEARCH_ERROR_GENERIC; + } + + s_search_state = search_state::finalizing; + + sysutil_register_cb([=, search = g_fxo->get()](ppu_thread& ppu) -> s32 + { + s_search_state = search_state::not_initialized; search->func(ppu, CELL_SEARCH_EVENT_FINALIZE_RESULT, CELL_OK, vm::null, search->userData); return CELL_OK; }); @@ -98,16 +177,53 @@ error_code cellSearchStartListSearch(CellSearchListSearchType type, CellSearchSo return CELL_SEARCH_ERROR_PARAM; } - const auto search = g_fxo->get(); + switch (type) + { + case CELL_SEARCH_LISTSEARCHTYPE_MUSIC_ALBUM: + case CELL_SEARCH_LISTSEARCHTYPE_MUSIC_GENRE: + case CELL_SEARCH_LISTSEARCHTYPE_MUSIC_ARTIST: + case CELL_SEARCH_LISTSEARCHTYPE_PHOTO_YEAR: + case CELL_SEARCH_LISTSEARCHTYPE_PHOTO_MONTH: + case CELL_SEARCH_LISTSEARCHTYPE_PHOTO_ALBUM: + case CELL_SEARCH_LISTSEARCHTYPE_PHOTO_PLAYLIST: + case CELL_SEARCH_LISTSEARCHTYPE_VIDEO_ALBUM: + case CELL_SEARCH_LISTSEARCHTYPE_MUSIC_PLAYLIST: + break; + default: + return CELL_SEARCH_ERROR_PARAM; + } + + if (sortOrder != CELL_SEARCH_SORTORDER_ASCENDING && sortOrder != CELL_SEARCH_SORTORDER_DESCENDING) + { + return CELL_SEARCH_ERROR_PARAM; + } + + switch (s_search_state) + { + case search_state::idle: + break; + case search_state::not_initialized: + case search_state::initializing: + return CELL_SEARCH_ERROR_NOT_INITIALIZED; + case search_state::finalizing: + return CELL_SEARCH_ERROR_FINALIZING; + case search_state::in_progress: + case search_state::canceling: + return CELL_SEARCH_ERROR_BUSY; + default: + return CELL_SEARCH_ERROR_GENERIC; + } *outSearchId = idm::make(); - sysutil_register_cb([=](ppu_thread& ppu) -> s32 + s_search_state = search_state::in_progress; + sysutil_register_cb([=, search = g_fxo->get()](ppu_thread& ppu) -> s32 { vm::var resultParam; resultParam->searchId = *outSearchId; resultParam->resultNum = 0; // TODO + s_search_state = search_state::idle; search->func(ppu, CELL_SEARCH_EVENT_LISTSEARCH_RESULT, CELL_OK, vm::cast(resultParam.addr()), search->userData); return CELL_OK; }); @@ -124,16 +240,54 @@ error_code cellSearchStartContentSearchInList(vm::cptr list return CELL_SEARCH_ERROR_PARAM; } - const auto search = g_fxo->get(); + switch (sortKey) + { + case CELL_SEARCH_SORTKEY_DEFAULT: + case CELL_SEARCH_SORTKEY_TITLE: + case CELL_SEARCH_SORTKEY_ALBUMTITLE: + case CELL_SEARCH_SORTKEY_GENRENAME: + case CELL_SEARCH_SORTKEY_ARTISTNAME: + case CELL_SEARCH_SORTKEY_IMPORTEDDATE: + case CELL_SEARCH_SORTKEY_TRACKNUMBER: + case CELL_SEARCH_SORTKEY_TAKENDATE: + case CELL_SEARCH_SORTKEY_USERDEFINED: + case CELL_SEARCH_SORTKEY_MODIFIEDDATE: + break; + default: + return CELL_SEARCH_ERROR_PARAM; + } + + if (sortOrder != CELL_SEARCH_SORTORDER_ASCENDING && sortOrder != CELL_SEARCH_SORTORDER_DESCENDING) + { + return CELL_SEARCH_ERROR_PARAM; + } + + switch (s_search_state) + { + case search_state::idle: + break; + case search_state::not_initialized: + case search_state::initializing: + return CELL_SEARCH_ERROR_NOT_INITIALIZED; + case search_state::finalizing: + return CELL_SEARCH_ERROR_FINALIZING; + case search_state::in_progress: + case search_state::canceling: + return CELL_SEARCH_ERROR_BUSY; + default: + return CELL_SEARCH_ERROR_GENERIC; + } *outSearchId = idm::make(); - sysutil_register_cb([=](ppu_thread& ppu) -> s32 + s_search_state = search_state::in_progress; + sysutil_register_cb([=, search = g_fxo->get()](ppu_thread& ppu) -> s32 { vm::var resultParam; resultParam->searchId = *outSearchId; resultParam->resultNum = 0; // TODO + s_search_state = search_state::idle; search->func(ppu, CELL_SEARCH_EVENT_CONTENTSEARCH_INLIST_RESULT, CELL_OK, vm::cast(resultParam.addr()), search->userData); return CELL_OK; }); @@ -150,16 +304,178 @@ error_code cellSearchStartContentSearch(CellSearchContentSearchType type, CellSe return CELL_SEARCH_ERROR_PARAM; } - const auto search = g_fxo->get(); + switch (sortKey) + { + case CELL_SEARCH_SORTKEY_DEFAULT: + case CELL_SEARCH_SORTKEY_TITLE: + case CELL_SEARCH_SORTKEY_ALBUMTITLE: + case CELL_SEARCH_SORTKEY_GENRENAME: + case CELL_SEARCH_SORTKEY_ARTISTNAME: + case CELL_SEARCH_SORTKEY_IMPORTEDDATE: + case CELL_SEARCH_SORTKEY_TRACKNUMBER: + case CELL_SEARCH_SORTKEY_TAKENDATE: + case CELL_SEARCH_SORTKEY_USERDEFINED: + case CELL_SEARCH_SORTKEY_MODIFIEDDATE: + break; + default: + return CELL_SEARCH_ERROR_PARAM; + } + + if (sortOrder != CELL_SEARCH_SORTORDER_ASCENDING && sortOrder != CELL_SEARCH_SORTORDER_DESCENDING) + { + return CELL_SEARCH_ERROR_PARAM; + } + + const char* media_dir; + switch (type) + { + case CELL_SEARCH_CONTENTSEARCHTYPE_MUSIC_ALL: media_dir = "music"; break; + case CELL_SEARCH_CONTENTSEARCHTYPE_PHOTO_ALL: media_dir = "photo"; break; + case CELL_SEARCH_CONTENTSEARCHTYPE_VIDEO_ALL: media_dir = "video"; break; + default: + return CELL_SEARCH_ERROR_PARAM; + } + + switch (s_search_state) + { + case search_state::idle: + break; + case search_state::not_initialized: + case search_state::initializing: + return CELL_SEARCH_ERROR_NOT_INITIALIZED; + case search_state::finalizing: + return CELL_SEARCH_ERROR_FINALIZING; + case search_state::in_progress: + case search_state::canceling: + return CELL_SEARCH_ERROR_BUSY; + default: + return CELL_SEARCH_ERROR_GENERIC; + } *outSearchId = idm::make(); - sysutil_register_cb([=](ppu_thread& ppu) -> s32 + s_search_state = search_state::in_progress; + sysutil_register_cb([=, content_map = g_fxo->get(), search = g_fxo->get()](ppu_thread& ppu) -> s32 { + auto curr_search = idm::get(*outSearchId); vm::var resultParam; resultParam->searchId = *outSearchId; - resultParam->resultNum = 0; // TODO + resultParam->resultNum = 0; // Set again later + std::function searchInFolder = [&, type](const std::string& vpath, const std::string& prev) + { + const std::string relative_vpath = (!prev.empty() ? prev + "/" : "") + vpath; + + for (auto&& item : fs::dir(vfs::get(relative_vpath))) + { + item.name = vfs::unescape(item.name); + + if (item.name == "." || item.name == "..") + { + continue; + } + + if (item.is_directory) + { + searchInFolder(item.name, relative_vpath); + continue; + } + + // TODO + // Perform first check that file is of desired type. For example, don't wanna go + // identifying "AlbumArt.jpg" as an MP3. Hrm... Postpone this thought. Do games + // perform their own checks? DIVA ignores anything without the MP3 extension. + + // TODO - Identify sorting method and insert the appropriate values where applicable + const std::string item_path(relative_vpath + "/" + item.name); + + const u64 hash = XXH64(item_path.c_str(), item_path.length(), 0); + auto found = content_map->find(hash); + if (found == content_map->end()) // content isn't yet being tracked + { + auto ext_offset = item.name.find_last_of("."); // used later if no "Title" found + + std::shared_ptr curr_find = std::make_shared(); + strcpy_trunc(curr_find->infoPath.contentPath, item_path); // TODO: This is the case where linking is required + // TODO - curr_find.infoPath.thumbnailPath + if (type == CELL_SEARCH_CONTENTSEARCHTYPE_MUSIC_ALL) + { + curr_find->type = CELL_SEARCH_CONTENTTYPE_MUSIC; + CellSearchMusicInfo& info = curr_find->data.music; + // TODO - Some kinda file music analysis and assign the values as such + info.duration = 0; + info.size = item.size; + info.importedDate = 0; + info.lastPlayedDate = 0; + info.releasedYear = 0; + info.trackNumber = 0; + info.bitrate = 0; + info.samplingRate = 0; + info.quantizationBitrate = 0; + info.playCount = 0; + info.drmEncrypted = 0; + info.codec = 0; // CellSearchCodec + info.status = 0; // CellSearchContentStatus + strcpy_trunc(info.title, item.name.substr(0, ext_offset)); // it'll do for the moment... + strcpy_trunc(info.albumTitle, "ALBUM TITLE"); + strcpy_trunc(info.artistName, "ARTIST NAME"); + strcpy_trunc(info.genreName, "GENRE NAME"); + } + else if (type == CELL_SEARCH_CONTENTSEARCHTYPE_PHOTO_ALL) + { + curr_find->type = CELL_SEARCH_CONTENTTYPE_PHOTO; + CellSearchPhotoInfo& info = curr_find->data.photo; + // TODO - Some kinda file photo analysis and assign the values as such + info.size = item.size; + info.importedDate = 0; + info.takenDate = 0; + info.width = 0; + info.height = 0; + info.orientation = 0; // CellSearchOrientation + info.codec = 0; // CellSearchCodec + info.status = 0; // CellSearchContentStatus + strcpy_trunc(info.title, item.name.substr(0, ext_offset).c_str()); + strcpy_trunc(info.albumTitle, "ALBUM TITLE"); + } + else if (type == CELL_SEARCH_CONTENTSEARCHTYPE_VIDEO_ALL) + { + curr_find->type = CELL_SEARCH_CONTENTTYPE_VIDEO; + CellSearchVideoInfo& info = curr_find->data.video; + // TODO - Some kinda file video analysis and assign the values as such + info.duration = 0; + info.size = item.size; + info.importedDate = 0; + info.takenDate = 0; + info.videoBitrate = 0; + info.audioBitrate = 0; + info.playCount = 0; + info.drmEncrypted = 0; + info.videoCodec = 0; // CellSearchCodec + info.audioCodec = 0; // CellSearchCodec + info.status = 0; // CellSearchContentStatus + strcpy_trunc(info.title, item.name.substr(0, ext_offset).c_str()); // it'll do for the moment... + strcpy_trunc(info.albumTitle, "ALBUM TITLE"); + } + + content_map->emplace(hash, curr_find); + curr_search->content_ids.emplace_back(hash, curr_find); // place this file's "ID" into the list of found types + + cellSearch.notice("cellSearchStartContentSearch(): Content ID: %08X Path: \"%s\"", hash, item_path); + } + else // file is already stored and tracked + { // TODO + // Perform checks to see if the identified file has been modified since last checked + // In which case, update the stored content's properties + // auto content_found = &content_map->at(content_id); + curr_search->content_ids.emplace_back(found->first, found->second); + } + } + }; + + searchInFolder(fmt::format("/dev_hdd0/%s", media_dir), ""); + resultParam->resultNum = ::narrow(curr_search->content_ids.size()); + + s_search_state = search_state::idle; search->func(ppu, CELL_SEARCH_EVENT_CONTENTSEARCH_RESULT, CELL_OK, vm::cast(resultParam.addr()), search->userData); return CELL_OK; }); @@ -176,16 +492,50 @@ error_code cellSearchStartSceneSearchInVideo(vm::cptr video return CELL_SEARCH_ERROR_PARAM; } - const auto search = g_fxo->get(); + switch (searchType) + { + case CELL_SEARCH_SCENESEARCHTYPE_NONE: + case CELL_SEARCH_SCENESEARCHTYPE_CHAPTER: + case CELL_SEARCH_SCENESEARCHTYPE_CLIP_HIGHLIGHT: + case CELL_SEARCH_SCENESEARCHTYPE_CLIP_USER: + case CELL_SEARCH_SCENESEARCHTYPE_CLIP: + case CELL_SEARCH_SCENESEARCHTYPE_ALL: + break; + default: + return CELL_SEARCH_ERROR_PARAM; + } + + if (sortOrder != CELL_SEARCH_SORTORDER_ASCENDING && sortOrder != CELL_SEARCH_SORTORDER_DESCENDING) + { + return CELL_SEARCH_ERROR_PARAM; + } + + switch (s_search_state) + { + case search_state::idle: + break; + case search_state::not_initialized: + case search_state::initializing: + return CELL_SEARCH_ERROR_NOT_INITIALIZED; + case search_state::finalizing: + return CELL_SEARCH_ERROR_FINALIZING; + case search_state::in_progress: + case search_state::canceling: + return CELL_SEARCH_ERROR_BUSY; + default: + return CELL_SEARCH_ERROR_GENERIC; + } *outSearchId = idm::make(); - sysutil_register_cb([=](ppu_thread& ppu) -> s32 + s_search_state = search_state::in_progress; + sysutil_register_cb([=, search = g_fxo->get()](ppu_thread& ppu) -> s32 { vm::var resultParam; resultParam->searchId = *outSearchId; resultParam->resultNum = 0; // TODO + s_search_state = search_state::idle; search->func(ppu, CELL_SEARCH_EVENT_SCENESEARCH_INVIDEO_RESULT, CELL_OK, vm::cast(resultParam.addr()), search->userData); return CELL_OK; }); @@ -202,16 +552,45 @@ error_code cellSearchStartSceneSearch(CellSearchSceneSearchType searchType, vm:: return CELL_SEARCH_ERROR_PARAM; } - const auto search = g_fxo->get(); + switch (searchType) + { + case CELL_SEARCH_SCENESEARCHTYPE_NONE: + case CELL_SEARCH_SCENESEARCHTYPE_CHAPTER: + case CELL_SEARCH_SCENESEARCHTYPE_CLIP_HIGHLIGHT: + case CELL_SEARCH_SCENESEARCHTYPE_CLIP_USER: + case CELL_SEARCH_SCENESEARCHTYPE_CLIP: + case CELL_SEARCH_SCENESEARCHTYPE_ALL: + break; + default: + return CELL_SEARCH_ERROR_PARAM; + } + + switch (s_search_state) + { + case search_state::idle: + break; + case search_state::not_initialized: + case search_state::initializing: + return CELL_SEARCH_ERROR_NOT_INITIALIZED; + case search_state::finalizing: + return CELL_SEARCH_ERROR_FINALIZING; + case search_state::in_progress: + case search_state::canceling: + return CELL_SEARCH_ERROR_BUSY; + default: + return CELL_SEARCH_ERROR_GENERIC; + } *outSearchId = idm::make(); - sysutil_register_cb([=](ppu_thread& ppu) -> s32 + s_search_state = search_state::in_progress; + sysutil_register_cb([=, search = g_fxo->get()](ppu_thread& ppu) -> s32 { vm::var resultParam; resultParam->searchId = *outSearchId; resultParam->resultNum = 0; // TODO + s_search_state = search_state::idle; search->func(ppu, CELL_SEARCH_EVENT_SCENESEARCH_RESULT, CELL_OK, vm::cast(resultParam.addr()), search->userData); return CELL_OK; }); @@ -221,7 +600,7 @@ error_code cellSearchStartSceneSearch(CellSearchSceneSearchType searchType, vm:: error_code cellSearchGetContentInfoByOffset(CellSearchId searchId, s32 offset, vm::ptr infoBuffer, vm::ptr outContentType, vm::ptr outContentId) { - cellSearch.todo("cellSearchGetContentInfoByOffset(searchId=0x%x, offset=0x%x, infoBuffer=*0x%x, outContentType=*0x%x, outContentId=*0x%x)", searchId, offset, infoBuffer, outContentType, outContentId); + cellSearch.warning("cellSearchGetContentInfoByOffset(searchId=0x%x, offset=0x%x, infoBuffer=*0x%x, outContentType=*0x%x, outContentId=*0x%x)", searchId, offset, infoBuffer, outContentType, outContentId); if (!outContentType) { @@ -235,30 +614,157 @@ error_code cellSearchGetContentInfoByOffset(CellSearchId searchId, s32 offset, v return CELL_SEARCH_ERROR_INVALID_SEARCHID; } + switch (s_search_state) + { + case search_state::idle: + break; + case search_state::not_initialized: + case search_state::initializing: + return CELL_SEARCH_ERROR_NOT_INITIALIZED; + case search_state::finalizing: + return CELL_SEARCH_ERROR_FINALIZING; + case search_state::in_progress: + case search_state::canceling: + return CELL_SEARCH_ERROR_BUSY; + default: + return CELL_SEARCH_ERROR_GENERIC; + } + + if (offset >= 0 && offset < searchObject->content_ids.size()) + { + const auto& content_id = searchObject->content_ids[offset]; + const auto& content_info = content_id.second; + switch (content_info->type) + { + case CELL_SEARCH_CONTENTTYPE_MUSIC: + std::memcpy(infoBuffer.get_ptr(), &content_info->data.music, sizeof(content_info->data.music)); + break; + case CELL_SEARCH_CONTENTTYPE_PHOTO: + std::memcpy(infoBuffer.get_ptr(), &content_info->data.photo, sizeof(content_info->data.photo)); + break; + case CELL_SEARCH_CONTENTTYPE_VIDEO: + std::memcpy(infoBuffer.get_ptr(), &content_info->data.video, sizeof(content_info->data.photo)); + break; + case CELL_SEARCH_CONTENTTYPE_MUSICLIST: + std::memcpy(infoBuffer.get_ptr(), &content_info->data.music_list, sizeof(content_info->data.music_list)); + break; + case CELL_SEARCH_CONTENTTYPE_PHOTOLIST: + std::memcpy(infoBuffer.get_ptr(), &content_info->data.photo_list, sizeof(content_info->data.photo_list)); + break; + case CELL_SEARCH_CONTENTTYPE_VIDEOLIST: + std::memcpy(infoBuffer.get_ptr(), &content_info->data.video_list, sizeof(content_info->data.video_list)); + break; + case CELL_SEARCH_CONTENTTYPE_SCENE: + std::memcpy(infoBuffer.get_ptr(), &content_info->data.scene, sizeof(content_info->data.scene)); + break; + default: + return CELL_SEARCH_ERROR_GENERIC; + } + + const u128 content_id_128 = content_id.first; + *outContentType = content_info->type; + std::memcpy(outContentId->data, &content_id_128, CELL_SEARCH_CONTENT_ID_SIZE); + } + else // content ID not found, perform a search first + { + return CELL_SEARCH_ERROR_OUT_OF_RANGE; + } + return CELL_OK; } error_code cellSearchGetContentInfoByContentId(vm::cptr contentId, vm::ptr infoBuffer, vm::ptr outContentType) { - cellSearch.todo("cellSearchGetContentInfoByContentId(contentId=*0x%x, infoBuffer=*0x%x, outContentType=*0x%x)", contentId, infoBuffer, outContentType); + cellSearch.warning("cellSearchGetContentInfoByContentId(contentId=*0x%x, infoBuffer=*0x%x, outContentType=*0x%x)", contentId, infoBuffer, outContentType); if (!outContentType) { return CELL_SEARCH_ERROR_PARAM; } + switch (s_search_state) + { + case search_state::idle: + break; + case search_state::not_initialized: + case search_state::initializing: + return CELL_SEARCH_ERROR_NOT_INITIALIZED; + case search_state::finalizing: + return CELL_SEARCH_ERROR_FINALIZING; + case search_state::in_progress: + case search_state::canceling: + return CELL_SEARCH_ERROR_BUSY; + default: + return CELL_SEARCH_ERROR_GENERIC; + } + + const auto content_map = g_fxo->get(); + auto found = content_map->find(*reinterpret_cast(contentId->data)); + if (found != content_map->end()) + { + const auto& content_info = found->second; + switch (content_info->type) + { + case CELL_SEARCH_CONTENTTYPE_MUSIC: + std::memcpy(infoBuffer.get_ptr(), &content_info->data.music, sizeof(content_info->data.music)); + break; + case CELL_SEARCH_CONTENTTYPE_PHOTO: + std::memcpy(infoBuffer.get_ptr(), &content_info->data.photo, sizeof(content_info->data.photo)); + break; + case CELL_SEARCH_CONTENTTYPE_VIDEO: + std::memcpy(infoBuffer.get_ptr(), &content_info->data.video, sizeof(content_info->data.photo)); + break; + case CELL_SEARCH_CONTENTTYPE_MUSICLIST: + std::memcpy(infoBuffer.get_ptr(), &content_info->data.music_list, sizeof(content_info->data.music_list)); + break; + case CELL_SEARCH_CONTENTTYPE_PHOTOLIST: + std::memcpy(infoBuffer.get_ptr(), &content_info->data.photo_list, sizeof(content_info->data.photo_list)); + break; + case CELL_SEARCH_CONTENTTYPE_VIDEOLIST: + std::memcpy(infoBuffer.get_ptr(), &content_info->data.video_list, sizeof(content_info->data.video_list)); + break; + case CELL_SEARCH_CONTENTTYPE_SCENE: + std::memcpy(infoBuffer.get_ptr(), &content_info->data.scene, sizeof(content_info->data.scene)); + break; + default: + return CELL_SEARCH_ERROR_GENERIC; + } + + *outContentType = content_info->type; + } + else // content ID not found, perform a search first + { + return CELL_SEARCH_ERROR_CONTENT_NOT_FOUND; + } + return CELL_OK; } error_code cellSearchGetOffsetByContentId(CellSearchId searchId, vm::cptr contentId, vm::ptr outOffset) { - cellSearch.todo("cellSearchGetOffsetByContentId(searchId=0x%x, contentId=*0x%x, outOffset=*0x%x)", searchId, contentId, outOffset); + cellSearch.warning("cellSearchGetOffsetByContentId(searchId=0x%x, contentId=*0x%x, outOffset=*0x%x)", searchId, contentId, outOffset); if (!outOffset) { return CELL_SEARCH_ERROR_PARAM; } + switch (s_search_state) + { + case search_state::idle: + break; + case search_state::not_initialized: + case search_state::initializing: + return CELL_SEARCH_ERROR_NOT_INITIALIZED; + case search_state::finalizing: + return CELL_SEARCH_ERROR_FINALIZING; + case search_state::in_progress: + case search_state::canceling: + return CELL_SEARCH_ERROR_BUSY; + default: + return CELL_SEARCH_ERROR_GENERIC; + } + const auto searchObject = idm::get(searchId); if (!searchObject) @@ -266,7 +772,19 @@ error_code cellSearchGetOffsetByContentId(CellSearchId searchId, vm::cptr(contentId->data); + for (auto& content_id : searchObject->content_ids) + { + if (content_id.first == content_hash) + { + *outOffset = i; + return CELL_OK; + } + ++i; + } + + return CELL_SEARCH_ERROR_CONTENT_NOT_FOUND; } error_code cellSearchGetContentIdByOffset(CellSearchId searchId, s32 offset, vm::ptr outContentType, vm::ptr outContentId, vm::ptr outTimeInfo) @@ -285,6 +803,40 @@ error_code cellSearchGetContentIdByOffset(CellSearchId searchId, s32 offset, vm: return CELL_SEARCH_ERROR_INVALID_SEARCHID; } + switch (s_search_state) + { + case search_state::idle: + break; + case search_state::not_initialized: + case search_state::initializing: + return CELL_SEARCH_ERROR_NOT_INITIALIZED; + case search_state::finalizing: + return CELL_SEARCH_ERROR_FINALIZING; + case search_state::in_progress: + case search_state::canceling: + return CELL_SEARCH_ERROR_BUSY; + default: + return CELL_SEARCH_ERROR_GENERIC; + } + + if (offset > -1 && offset < searchObject->content_ids.size()) + { + auto& content_id = searchObject->content_ids.at(offset); + const u128 content_id_128 = content_id.first; + *outContentType = content_id.second->type; + std::memcpy(outContentId->data, &content_id_128, CELL_SEARCH_CONTENT_ID_SIZE); + + if (outTimeInfo) + { + std::memcpy(outTimeInfo.get_ptr(), &content_id.second->timeInfo, sizeof(content_id.second->timeInfo)); + } + + } + else // content ID not found, perform a search first + { + return CELL_SEARCH_ERROR_OUT_OF_RANGE; + } + return CELL_OK; } @@ -340,6 +892,37 @@ error_code cellSearchGetContentInfoPath(vm::cptr contentId, return CELL_SEARCH_ERROR_PARAM; } + switch (s_search_state) + { + case search_state::idle: + break; + case search_state::not_initialized: + case search_state::initializing: + return CELL_SEARCH_ERROR_NOT_INITIALIZED; + case search_state::finalizing: + return CELL_SEARCH_ERROR_FINALIZING; + case search_state::in_progress: + case search_state::canceling: + return CELL_SEARCH_ERROR_BUSY; + default: + return CELL_SEARCH_ERROR_GENERIC; + } + + const u64 id = *reinterpret_cast(contentId->data); + const auto content_map = g_fxo->get(); + auto found = content_map->find(id); + if(found != content_map->end()) + { + std::memcpy(infoPath.get_ptr(), &found->second->infoPath, sizeof(found->second->infoPath)); + } + else + { + cellSearch.error("cellSearchGetContentInfoPath(): ID not found : 0x%08X", id); + return CELL_SEARCH_ERROR_CONTENT_NOT_FOUND; + } + + cellSearch.success("contentId = %08X contentPath = \"%s\"", id, infoPath->contentPath); + return CELL_OK; } @@ -364,6 +947,25 @@ error_code cellSearchPrepareFile(vm::cptr path) return CELL_SEARCH_ERROR_PARAM; } + switch (s_search_state) + { + case search_state::idle: + break; + case search_state::not_initialized: + case search_state::initializing: + return CELL_SEARCH_ERROR_NOT_INITIALIZED; + case search_state::finalizing: + return CELL_SEARCH_ERROR_FINALIZING; + case search_state::in_progress: + case search_state::canceling: + return CELL_SEARCH_ERROR_BUSY; + default: + return CELL_SEARCH_ERROR_GENERIC; + } + + // TODO: Create a hard link if file path is too long + // Store mappings in cellSearch internally and resolve them here if needed + return CELL_OK; } @@ -402,12 +1004,47 @@ error_code cellSearchCancel(CellSearchId searchId) return CELL_SEARCH_ERROR_INVALID_SEARCHID; } + switch (s_search_state) + { + case search_state::in_progress: + break; + case search_state::not_initialized: + case search_state::initializing: + return CELL_SEARCH_ERROR_NOT_INITIALIZED; + case search_state::finalizing: + return CELL_SEARCH_ERROR_FINALIZING; + case search_state::canceling: + return CELL_SEARCH_ERROR_BUSY; + case search_state::idle: + return CELL_SEARCH_ERROR_ALREADY_GOT_RESULT; + default: + return CELL_SEARCH_ERROR_GENERIC; + } + + // TODO + return CELL_OK; } error_code cellSearchEnd(CellSearchId searchId) { - cellSearch.warning("cellSearchEnd(searchId=0x%x)", searchId); + cellSearch.todo("cellSearchEnd(searchId=0x%x)", searchId); + + switch (s_search_state) + { + case search_state::idle: + break; + case search_state::not_initialized: + return CELL_SEARCH_ERROR_NOT_INITIALIZED; + case search_state::finalizing: + return CELL_SEARCH_ERROR_FINALIZING; + case search_state::in_progress: + case search_state::initializing: + case search_state::canceling: + return CELL_SEARCH_ERROR_BUSY; + default: + return CELL_SEARCH_ERROR_GENERIC; + } const auto searchObject = idm::get(searchId);