#include #include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" #include #include #include #include #include #include // The game is fully installed on the host, so every chunk is local and nothing is pending. static constexpr int SCE_PLAYGO_ERROR_BAD_POINTER = static_cast(0x80B2000A); static constexpr int SCE_PLAYGO_ERROR_BAD_HANDLE = static_cast(0x80B20009); static constexpr int PLAYGO_HANDLE = 1; static constexpr int8_t PLAYGO_LOCUS_NOT_DOWNLOADED = 0; static constexpr int8_t PLAYGO_LOCUS_LOCAL_SLOW = 2; static constexpr int8_t PLAYGO_LOCUS_LOCAL_FAST = 3; static constexpr uint64_t PLAYGO_LANGUAGE_MASK_ALL = ~0ull; static constexpr int32_t PLAYGO_INSTALL_SPEED_FULL = 2; static int32_t g_installSpeed = PLAYGO_INSTALL_SPEED_FULL; static constexpr int SCE_PLAYGO_ERROR_BAD_CHUNK_ID = static_cast(0x80B2000C); static constexpr int SCE_PLAYGO_ERROR_BAD_SIZE = static_cast(0x80B2000B); static constexpr int SCE_PLAYGO_ERROR_BAD_LOCUS = static_cast(0x80B20010); struct ChunkTable { std::set ids{0}; bool defined = false; }; static bool ReadPackageChunks(ChunkTable& table) { constexpr std::size_t CountOffset = 0x0A; std::ifstream file(ResolvePath_nid_no_patch("/app0/sce_sys/playgo-chunk.dat"), std::ios::binary); char header[CountOffset + sizeof(uint16_t)]{}; if (!file.read(header, sizeof(header)) || std::memcmp(header, "plgx", 4) != 0) return false; uint16_t count = 0; std::memcpy(&count, header + CountOffset, sizeof(count)); if (count == 0) return false; for (uint32_t id = 0; id < count; ++id) table.ids.insert(static_cast(id)); table.defined = true; return true; } static const ChunkTable& Chunks() { static const ChunkTable chunks = [] { ChunkTable table; std::ifstream file(ResolvePath_nid_no_patch("/app0/playgo-chunkdefs.xml")); if (!file) { ReadPackageChunks(table); return table; } table.defined = true; auto& result = table.ids; const std::string text((std::istreambuf_iterator(file)), std::istreambuf_iterator()); static const std::regex chunk(R"re((std::stoul((*it)[1].str()))); static const std::regex defaultChunk(R"re(default_chunk="(\d+)")re"); std::smatch match; if (std::regex_search(text, match, defaultChunk)) { const auto last = std::stoul(match[1].str()); for (unsigned long id = 0; id <= last && id <= 0xFFFF; ++id) result.insert(static_cast(id)); } return table; }(); return chunks; } static bool IsKnownChunk(uint16_t id) { const auto& chunks = Chunks(); return !chunks.defined || chunks.ids.contains(id); } static int ValidateChunks(const uint16_t* chunk_ids, uint32_t number_of_entries) { if (!chunk_ids) return SCE_PLAYGO_ERROR_BAD_POINTER; if (number_of_entries == 0) return SCE_PLAYGO_ERROR_BAD_SIZE; for (uint32_t index = 0; index < number_of_entries; ++index) if (!IsKnownChunk(chunk_ids[index])) return SCE_PLAYGO_ERROR_BAD_CHUNK_ID; return 0; } extern "C" { int APS5_VABI scePlayGoClose(int handle) { if (handle != PLAYGO_HANDLE) return SCE_PLAYGO_ERROR_BAD_HANDLE; return 0; } int APS5_VABI scePlayGoGetChunkId(int handle, uint16_t* out_chunk_id_list, uint32_t number_of_entries, uint32_t* out_entries) { if (handle != PLAYGO_HANDLE) return SCE_PLAYGO_ERROR_BAD_HANDLE; if (!out_entries) return SCE_PLAYGO_ERROR_BAD_POINTER; const auto& valid = Chunks().ids; if (!out_chunk_id_list) { *out_entries = static_cast(valid.size()); return 0; } uint32_t written = 0; for (const auto id : valid) { if (written == number_of_entries) break; out_chunk_id_list[written++] = id; } *out_entries = written; return 0; } int APS5_VABI scePlayGoGetEta(int handle, const uint16_t* chunk_ids, uint32_t number_of_entries, int64_t* out_eta) { if (handle != PLAYGO_HANDLE) return SCE_PLAYGO_ERROR_BAD_HANDLE; if (!out_eta) return SCE_PLAYGO_ERROR_BAD_POINTER; if (const int error = ValidateChunks(chunk_ids, number_of_entries)) return error; *out_eta = 0; return 0; } int APS5_VABI scePlayGoGetInstallChunkId(int handle, uint16_t* out_chunk_id_list, uint32_t number_of_entries, uint32_t* out_entries) { return scePlayGoGetChunkId(handle, out_chunk_id_list, number_of_entries, out_entries); } int APS5_VABI scePlayGoGetInstallSpeed(int handle, int32_t* out_speed) { if (handle != PLAYGO_HANDLE) return SCE_PLAYGO_ERROR_BAD_HANDLE; if (!out_speed) return SCE_PLAYGO_ERROR_BAD_POINTER; *out_speed = g_installSpeed; return 0; } int APS5_VABI scePlayGoGetLanguageMask(int handle, uint64_t* out_language_mask) { if (handle != PLAYGO_HANDLE) return SCE_PLAYGO_ERROR_BAD_HANDLE; if (!out_language_mask) return SCE_PLAYGO_ERROR_BAD_POINTER; *out_language_mask = PLAYGO_LANGUAGE_MASK_ALL; return 0; } int APS5_VABI scePlayGoGetLocus(int handle, const uint16_t* chunk_ids, uint32_t number_of_entries, int8_t* out_loci) { if (handle != PLAYGO_HANDLE) return SCE_PLAYGO_ERROR_BAD_HANDLE; if (!out_loci) return SCE_PLAYGO_ERROR_BAD_POINTER; if (const int error = ValidateChunks(chunk_ids, number_of_entries)) return error; for (uint32_t index = 0; index < number_of_entries; ++index) out_loci[index] = PLAYGO_LOCUS_LOCAL_FAST; return 0; } int APS5_VABI scePlayGoGetOptionalChunk(int handle, int32_t type, PlayGoOptionalChunk* option) { if (handle != PLAYGO_HANDLE) return SCE_PLAYGO_ERROR_BAD_HANDLE; (void)type; if (!option) return SCE_PLAYGO_ERROR_BAD_POINTER; option->bitmask = 0; return 0; } int APS5_VABI scePlayGoGetProgress(int handle, const uint16_t* chunk_ids, uint32_t number_of_entries, PlayGoProgress* out_progress) { if (handle != PLAYGO_HANDLE) return SCE_PLAYGO_ERROR_BAD_HANDLE; if (!out_progress) return SCE_PLAYGO_ERROR_BAD_POINTER; if (const int error = ValidateChunks(chunk_ids, number_of_entries)) return error; out_progress->progress_size = 1; out_progress->total_size = 1; return 0; } int APS5_VABI scePlayGoGetSupportedOptionalChunk(int handle, int32_t type, PlayGoOptionalChunk* option) { if (handle != PLAYGO_HANDLE) return SCE_PLAYGO_ERROR_BAD_HANDLE; (void)type; if (!option) return SCE_PLAYGO_ERROR_BAD_POINTER; option->bitmask = 0; return 0; } int APS5_VABI scePlayGoGetToDoList(int handle, PlayGoToDo* out_todo_list, uint32_t number_of_entries, uint32_t* out_entries) { if (handle != PLAYGO_HANDLE) return SCE_PLAYGO_ERROR_BAD_HANDLE; (void)out_todo_list; (void)number_of_entries; if (!out_entries) return SCE_PLAYGO_ERROR_BAD_POINTER; *out_entries = 0; return 0; } int APS5_VABI scePlayGoInitialize(const PlayGoInitParams* init) { if (!init) return SCE_PLAYGO_ERROR_BAD_POINTER; return 0; } int APS5_VABI scePlayGoOpen(int* out_handle, const void* param) { (void)param; if (!out_handle) return SCE_PLAYGO_ERROR_BAD_POINTER; *out_handle = PLAYGO_HANDLE; return 0; } int APS5_VABI scePlayGoPrefetch(int handle, const uint16_t* chunk_ids, uint32_t number_of_entries, int8_t minimum_locus) { if (handle != PLAYGO_HANDLE) return SCE_PLAYGO_ERROR_BAD_HANDLE; (void)minimum_locus; return ValidateChunks(chunk_ids, number_of_entries); } int APS5_VABI scePlayGoPrefetchOptionalChunk(int handle, int32_t type, const PlayGoOptionalChunk* option) { if (handle != PLAYGO_HANDLE) return SCE_PLAYGO_ERROR_BAD_HANDLE; (void)type; (void)option; return 0; } int APS5_VABI scePlayGoSetInstallSpeed(int handle, int32_t speed) { if (handle != PLAYGO_HANDLE) return SCE_PLAYGO_ERROR_BAD_HANDLE; g_installSpeed = speed; return 0; } int APS5_VABI scePlayGoSetToDoList(int handle, const PlayGoToDo* todo_list, uint32_t number_of_entries) { if (handle != PLAYGO_HANDLE) return SCE_PLAYGO_ERROR_BAD_HANDLE; if (!todo_list) return SCE_PLAYGO_ERROR_BAD_POINTER; if (number_of_entries == 0) return SCE_PLAYGO_ERROR_BAD_SIZE; for (uint32_t index = 0; index < number_of_entries; ++index) { if (!IsKnownChunk(todo_list[index].chunk_id)) return SCE_PLAYGO_ERROR_BAD_CHUNK_ID; const int8_t locus = todo_list[index].locus; if (locus != PLAYGO_LOCUS_NOT_DOWNLOADED && locus != PLAYGO_LOCUS_LOCAL_SLOW && locus != PLAYGO_LOCUS_LOCAL_FAST) return SCE_PLAYGO_ERROR_BAD_LOCUS; } return 0; } int APS5_VABI scePlayGoTerminate(void) { return 0; } }