Files
AnyPS5/core/libs/tests/GuestContentExport.cpp
T
David 6ce1240aba feat(libSceContentExport): implement the export session calls
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)
2026-10-12 00:01:17 +02:00

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, &param, &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(&param) == 0);
Require(Throws([&] { sceContentExportInit2(&param); }));
Require(sceContentExportTerm() == 0);
Require(Throws([] { sceContentExportTerm(); }));
Require(sceContentExportInit2(&param) == 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); }));
}