mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 00:22:57 -05:00
sceContentExportStart hands out positive session ids, at most 10 live (SharpProspero MaxExecution), and sceContentExportFinish closes them. sceContentExportGetProgress, sceContentExportCancel and sceContentExportFromDataWithThumbnail take their Orbital signatures and check the session. The host has no media library to export into, so FromDataWithThumbnail throws before it calls either data callback, and GetProgress and Cancel throw for a live session since no export ever runs and their result for an idle session is unknown. An eleventh Start and Term with live sessions throw. Titles: Cult of the Lamb PPSA06465 imports GetProgress, Cancel and FromDataWithThumbnail (#2686); PPSA21564 (#271) and Cyberpunk 2077 PPSA04029 (#266) import Start. Sources: Orbital tools/ida/db_types.json (cc408367) for the signatures, SharpProspero Interop/Content/ContentExport.cs (5dfdb4b0) for the session model and MaxExecution, shadPS4 content_export.h (443f0224) for the parameter and callback layouts. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> (cherry picked from commit 473d6dd355d2c0ee22f9792927e0e465846569cf)
89 lines
3.7 KiB
C++
89 lines
3.7 KiB
C++
#include "SceTypes.hpp"
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <set>
|
|
#include <stdexcept>
|
|
|
|
extern "C" int APS5_VABI sceContentExportInit2(const ContentExportInitParam2* init_param);
|
|
extern "C" int APS5_VABI sceContentExportTerm(void);
|
|
extern "C" int APS5_VABI sceContentExportStart(void);
|
|
extern "C" int APS5_VABI sceContentExportFinish(std::int32_t expId);
|
|
extern "C" int APS5_VABI sceContentExportGetProgress(std::int32_t expId);
|
|
extern "C" int APS5_VABI sceContentExportCancel(std::int32_t expId);
|
|
extern "C" int APS5_VABI sceContentExportFromDataWithThumbnail(std::int32_t expId, const ContentExportParam* param, const ContentExportCallbackParam* contentCallbackParam,
|
|
const char* thumbnailType, const ContentExportCallbackParam* thumbnailCallbackParam, char* path, std::size_t pathBufferLength);
|
|
|
|
namespace {
|
|
|
|
void Require(bool value) { if (!value) std::abort(); }
|
|
|
|
template <typename TException = std::logic_error, typename TAction>
|
|
bool Throws(TAction action) {
|
|
try {
|
|
action();
|
|
} catch (const TException&) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int allocator = 0;
|
|
char path[1024]{};
|
|
|
|
void Export(std::int32_t expId) {
|
|
ContentExportParam param{};
|
|
ContentExportCallbackParam content{};
|
|
ContentExportCallbackParam thumbnail{};
|
|
sceContentExportFromDataWithThumbnail(expId, ¶m, &content, "image/png", &thumbnail, path, sizeof(path));
|
|
}
|
|
|
|
}
|
|
|
|
int main() {
|
|
ContentExportInitParam2 param{&allocator, &allocator, nullptr, 0x10000, 0, 0};
|
|
Require(Throws([] { sceContentExportInit2(nullptr); }));
|
|
Require(Throws([] { sceContentExportTerm(); }));
|
|
Require(Throws([] { sceContentExportStart(); }));
|
|
Require(Throws([] { sceContentExportFinish(1); }));
|
|
ContentExportInitParam2 missing = param;
|
|
missing.free_func = nullptr;
|
|
Require(Throws([&] { sceContentExportInit2(&missing); }));
|
|
ContentExportInitParam2 reserved = param;
|
|
reserved.reserved1 = 1;
|
|
Require(Throws([&] { sceContentExportInit2(&reserved); }));
|
|
Require(sceContentExportInit2(¶m) == 0);
|
|
Require(Throws([&] { sceContentExportInit2(¶m); }));
|
|
Require(sceContentExportTerm() == 0);
|
|
Require(Throws([] { sceContentExportTerm(); }));
|
|
Require(sceContentExportInit2(¶m) == 0);
|
|
|
|
std::set<int> sessions;
|
|
for (int i = 0; i < 10; ++i) {
|
|
int session = sceContentExportStart();
|
|
Require(session > 0);
|
|
Require(sessions.insert(session).second);
|
|
}
|
|
Require(Throws<std::runtime_error>([] { sceContentExportStart(); }));
|
|
Require(Throws<std::runtime_error>([] { sceContentExportTerm(); }));
|
|
int first = *sessions.begin();
|
|
Require(Throws<std::runtime_error>([&] { sceContentExportGetProgress(first); }));
|
|
Require(Throws<std::runtime_error>([&] { sceContentExportCancel(first); }));
|
|
Require(Throws<std::runtime_error>([&] { Export(first); }));
|
|
Require(path[0] == 0);
|
|
Require(sceContentExportFinish(first) == 0);
|
|
Require(Throws<std::invalid_argument>([&] { sceContentExportFinish(first); }));
|
|
Require(Throws<std::invalid_argument>([&] { sceContentExportGetProgress(first); }));
|
|
Require(Throws<std::invalid_argument>([&] { sceContentExportCancel(first); }));
|
|
Require(Throws<std::invalid_argument>([&] { Export(first); }));
|
|
Require(Throws<std::invalid_argument>([] { sceContentExportFinish(0); }));
|
|
Require(Throws<std::invalid_argument>([] { sceContentExportFinish(-1); }));
|
|
sessions.erase(first);
|
|
int reopened = sceContentExportStart();
|
|
Require(reopened > 0 && reopened != first);
|
|
Require(sessions.insert(reopened).second);
|
|
for (int session : sessions) Require(sceContentExportFinish(session) == 0);
|
|
Require(sceContentExportTerm() == 0);
|
|
Require(Throws([] { sceContentExportStart(); }));
|
|
Require(Throws([&] { sceContentExportFinish(reopened); }));
|
|
}
|