#include #include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" #include #include #include namespace { std::atomic g_initialized{false}; constexpr int ErrorNotInitialized = static_cast(0x809D1001u); constexpr int ErrorLimitTooBig = static_cast(0x809D100Au); constexpr std::uint32_t MaxSearchLimit = 92; constexpr std::int64_t LibraryUpdateId = 0; bool IsContentColumn(std::int32_t column) { switch (column) { case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 9: case 10: case 11: case 12: case 14: return true; default: return false; } } bool IsSortColumn(std::int32_t column) { switch (column) { case 1: case 4: case 5: case 6: case 9: case 10: return true; default: return false; } } bool IsConnector(std::int32_t connector) { return connector >= 0 && connector <= 2; } void ValidateColumnSets(const char* funcName, const ContentSearchContentColumnSet* columnSet, std::uint32_t columnSetLength) { if (columnSet == nullptr && columnSetLength != 0) throw std::invalid_argument(std::string(funcName) + ": null column set"); for (std::uint32_t i = 0; i < columnSetLength; ++i) { const auto& set = columnSet[i]; if (set.columns == nullptr || set.columns_length <= 0) throw std::invalid_argument(std::string(funcName) + ": empty column set"); if (!IsConnector(set.logical_connector)) throw std::invalid_argument(std::string(funcName) + ": unknown logical connector " + std::to_string(set.logical_connector)); for (std::int32_t j = 0; j < set.columns_length; ++j) { const auto& column = set.columns[j]; if (!IsContentColumn(column.column_type)) throw std::invalid_argument(std::string(funcName) + ": unknown column " + std::to_string(column.column_type)); if (column.column_operator < 0 || column.column_operator > 5) throw std::invalid_argument(std::string(funcName) + ": unknown column operator " + std::to_string(column.column_operator)); if (column.value == nullptr) throw std::invalid_argument(std::string(funcName) + ": null column value"); if (!IsConnector(column.logical_connector)) throw std::invalid_argument(std::string(funcName) + ": unknown logical connector " + std::to_string(column.logical_connector)); } } } void ValidateOrderBy(const char* funcName, const ContentSearchContentOrderByCondition* orderByConditions, std::uint32_t orderByConditionsLength) { if (orderByConditions == nullptr && orderByConditionsLength != 0) throw std::invalid_argument(std::string(funcName) + ": null order-by conditions"); for (std::uint32_t i = 0; i < orderByConditionsLength; ++i) { const auto& condition = orderByConditions[i]; if (!IsSortColumn(condition.column_type)) throw std::invalid_argument(std::string(funcName) + ": unknown sort column " + std::to_string(condition.column_type)); if (condition.sort_type != 0 && condition.sort_type != 1) throw std::invalid_argument(std::string(funcName) + ": unknown sort type " + std::to_string(condition.sort_type)); } } [[noreturn]] void ThrowUnknownMetadataId(const char* funcName, std::int32_t metadataId) { throw std::logic_error(std::string(funcName) + ": metadata id " + std::to_string(metadataId) + " was never opened, as the host content library is empty, and the console's error for it is unknown"); } } extern "C" { int APS5_VABI sceContentSearchInit(const ContentSearchInitParam* init_param) { if (init_param == nullptr) APS5_INVALID_ARG_EX; bool expected = false; if (!g_initialized.compare_exchange_strong(expected, true)) throw std::logic_error(std::string(__func__) + ": already initialized"); return 0; } int APS5_VABI sceContentSearchCloseMetadata(std::int32_t metadataId) { if (!g_initialized.load()) return ErrorNotInitialized; ThrowUnknownMetadataId(__func__, metadataId); } int APS5_VABI sceContentSearchGetMetadataFieldInfo(std::int32_t metadataId, const char* field, std::int32_t* metadataType, std::int32_t* size) { if (!g_initialized.load()) return ErrorNotInitialized; if (field == nullptr || metadataType == nullptr || size == nullptr) APS5_INVALID_ARG_EX; ThrowUnknownMetadataId(__func__, metadataId); } int APS5_VABI sceContentSearchGetMetadataValue(std::int32_t metadataId, const char* field, void* value) { if (!g_initialized.load()) return ErrorNotInitialized; if (field == nullptr || value == nullptr) APS5_INVALID_ARG_EX; ThrowUnknownMetadataId(__func__, metadataId); } int APS5_VABI sceContentSearchOpenMetadata(const char* filePath, std::int32_t* metadataId) { if (!g_initialized.load()) return ErrorNotInitialized; if (filePath == nullptr || metadataId == nullptr) APS5_INVALID_ARG_EX; throw std::logic_error(std::string(__func__) + ": " + filePath + " is not in the host content library, which is empty, and the console's error for it is unknown"); } int APS5_VABI sceContentSearchSearchContent(const ContentSearchContentColumnSet* columnSet, std::uint32_t columnSetLength, const ContentSearchContentOrderByCondition* orderByConditions, std::uint32_t orderByConditionsLength, std::uint32_t offset, std::uint32_t limit, std::int64_t* numOfContent, void* infos, std::int64_t* lastUpdateId) { (void)offset; if (!g_initialized.load()) return ErrorNotInitialized; if (numOfContent == nullptr || lastUpdateId == nullptr || (infos == nullptr && limit != 0)) APS5_INVALID_ARG_EX; ValidateColumnSets(__func__, columnSet, columnSetLength); ValidateOrderBy(__func__, orderByConditions, orderByConditionsLength); if (limit > MaxSearchLimit) return ErrorLimitTooBig; *numOfContent = 0; *lastUpdateId = LibraryUpdateId; return 0; } int APS5_VABI sceContentSearchGetContentLastUpdateId(std::int64_t* lastUpdateId) { if (!g_initialized.load()) return ErrorNotInitialized; if (lastUpdateId == nullptr) APS5_INVALID_ARG_EX; *lastUpdateId = LibraryUpdateId; return 0; } int APS5_VABI sceContentSearchOpenMetadataByContentId(std::int64_t contentId, std::int32_t* metadataId) { if (!g_initialized.load()) return ErrorNotInitialized; if (metadataId == nullptr) APS5_INVALID_ARG_EX; throw std::logic_error(std::string(__func__) + ": content id " + std::to_string(contentId) + " is not in the host content library, which is empty, and the console's error for it is unknown"); } int APS5_VABI sceContentSearchGetTotalContentSize(const ContentSearchContentColumnSet* columnSet, std::uint32_t columnSetLength, std::int64_t* size, std::int64_t* lastUpdateId) { if (!g_initialized.load()) return ErrorNotInitialized; if (size == nullptr || lastUpdateId == nullptr) APS5_INVALID_ARG_EX; ValidateColumnSets(__func__, columnSet, columnSetLength); *size = 0; *lastUpdateId = LibraryUpdateId; return 0; } int APS5_VABI sceContentSearchGetNumOfContent(const ContentSearchContentColumnSet* columnSet, std::uint32_t columnSetLength, std::int64_t* numOfContent, std::int64_t* lastUpdateId) { if (!g_initialized.load()) return ErrorNotInitialized; if (numOfContent == nullptr || lastUpdateId == nullptr) APS5_INVALID_ARG_EX; ValidateColumnSets(__func__, columnSet, columnSetLength); *numOfContent = 0; *lastUpdateId = LibraryUpdateId; return 0; } int APS5_VABI sceContentSearchTerm(void) { bool expected = true; if (!g_initialized.compare_exchange_strong(expected, false)) throw std::logic_error(std::string(__func__) + ": not initialized"); return 0; } }