Files
AnyPS5/core/libs/tests/GuestFios2.cpp
T
MarcZX10 0db3e4c00a feat(libSceFios2): implement file handles
Implement sceFiosFHOpen, sceFiosFHOpenWithMode, sceFiosFHClose,
sceFiosFHStat, sceFiosFHTruncate, sceFiosFHSync, the read, write,
pread and pwrite families including the vectored forms with their Sync
variants, and sceFiosFHSeek, sceFiosFHTell, sceFiosFHGetSize,
sceFiosFHGetPath and sceFiosFHGetOpenParams.

A handle owns a kernel descriptor and serializes its transfers.
Transfers the open flags exclude, bad vector counts, negative sizes and
offsets and closed handles return SCE_FIOS_ERROR_* codes.
2026-10-28 06:05:27 +00:00

424 lines
22 KiB
C++

#include "prx/libSceFios2/include/Api.hpp"
#include "prx/libkernel/include/Api.hpp"
#include "prx/libc/include/General.hpp"
#include <atomic>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <string>
#include <thread>
#include <vector>
static void Require(bool condition, int line) {
if (!condition) {
std::fprintf(stderr, "guest_fios2 check failed at line %d\n", line);
std::abort();
}
}
#define REQUIRE(condition) Require((condition), __LINE__)
static std::atomic<int> g_completed{0};
static std::atomic<int> g_deleted{0};
static std::atomic<int> g_lastError{0};
static std::atomic<int> g_lastOp{0};
static int APS5_VABI CountEvents(void* context, SceFiosOp op, SceFiosOpEvent event, int error) {
REQUIRE(context == reinterpret_cast<void*>(0x1234));
g_lastOp = op;
if (event == SCE_FIOS_OPEVENT_COMPLETE) {
g_lastError = error;
++g_completed;
} else if (event == SCE_FIOS_OPEVENT_DELETE) {
++g_deleted;
}
return 0;
}
static SceFiosOpAttr CountingAttr() {
SceFiosOpAttr attr{};
attr.pCallback = CountEvents;
attr.pCallbackContext = reinterpret_cast<void*>(0x1234);
return attr;
}
static void CheckInitialization() {
REQUIRE(!sceFiosIsInitialized(nullptr));
std::uint32_t params[16] = {};
params[0] = (sizeof(params) << 1) | 0u;
params[3] = 0xfeedbeef;
REQUIRE(sceFiosInitialize(reinterpret_cast<const SceFiosParams*>(params)) == SCE_FIOS_OK);
REQUIRE(sceFiosIsInitialized(nullptr));
std::uint32_t copy[16] = {};
REQUIRE(sceFiosIsInitialized(reinterpret_cast<SceFiosParams*>(copy)));
REQUIRE(copy[3] == 0xfeedbeef);
params[3] = 0x1;
sceFiosUpdateParameters(reinterpret_cast<const SceFiosParams*>(params));
REQUIRE(sceFiosIsInitialized(reinterpret_cast<SceFiosParams*>(copy)) && copy[3] == 0x1);
}
static void CheckQueries(const std::string& directory, const std::string& file) {
REQUIRE(sceFiosExistsSync(nullptr, file.c_str()));
REQUIRE(sceFiosExistsSync(nullptr, directory.c_str()));
REQUIRE(!sceFiosExistsSync(nullptr, (directory + "/missing").c_str()));
REQUIRE(sceFiosFileExistsSync(nullptr, file.c_str()));
REQUIRE(!sceFiosFileExistsSync(nullptr, directory.c_str()));
REQUIRE(sceFiosDirectoryExistsSync(nullptr, directory.c_str()));
REQUIRE(!sceFiosDirectoryExistsSync(nullptr, file.c_str()));
REQUIRE(!sceFiosExistsSync(nullptr, nullptr));
REQUIRE(!sceFiosExistsSync(nullptr, ""));
REQUIRE(sceFiosFileGetSizeSync(nullptr, file.c_str()) == 10);
REQUIRE(sceFiosFileGetSizeSync(nullptr, directory.c_str()) == SCE_FIOS_ERROR_NOT_A_FILE);
REQUIRE(sceFiosFileGetSizeSync(nullptr, (directory + "/missing").c_str()) == SCE_FIOS_ERROR_BAD_PATH);
REQUIRE(sceFiosFileGetSizeSync(nullptr, nullptr) == SCE_FIOS_ERROR_BAD_PTR);
REQUIRE(sceFiosFileGetSizeSync(nullptr, "") == SCE_FIOS_ERROR_BAD_PATH);
const std::string tooLong(2000, 'a');
REQUIRE(sceFiosFileGetSizeSync(nullptr, tooLong.c_str()) == SCE_FIOS_ERROR_PATH_TOO_LONG);
SceFiosStat stat{};
std::memset(&stat, 0xaa, sizeof(stat));
REQUIRE(sceFiosStatSync(nullptr, file.c_str(), &stat) == SCE_FIOS_OK);
REQUIRE(stat.fileSize == 10 && (stat.statFlags & SCE_FIOS_STAT_DIRECTORY) == 0);
REQUIRE(stat.modificationDate > 1000000000ull * 1000000000ull);
REQUIRE((stat.mode & 0170000) == 0100000);
REQUIRE(sceFiosStatSync(nullptr, directory.c_str(), &stat) == SCE_FIOS_OK);
REQUIRE((stat.statFlags & SCE_FIOS_STAT_DIRECTORY) != 0);
REQUIRE(sceFiosStatSync(nullptr, (directory + "/missing").c_str(), &stat) == SCE_FIOS_ERROR_BAD_PATH);
REQUIRE(sceFiosStatSync(nullptr, file.c_str(), nullptr) == SCE_FIOS_ERROR_BAD_PTR);
}
static void CheckOperationLifecycle(const std::string& file) {
const SceFiosOp op = sceFiosFileGetSize(nullptr, file.c_str());
REQUIRE(op > 0);
REQUIRE(sceFiosIsValidHandle(op));
REQUIRE(sceFiosOpIsDone(op));
REQUIRE(sceFiosOpGetError(op) == SCE_FIOS_OK);
REQUIRE(sceFiosOpWait(op) == SCE_FIOS_OK);
REQUIRE(sceFiosOpGetActualCount(op) == 10);
REQUIRE(sceFiosOpGetPath(op) != nullptr && file == sceFiosOpGetPath(op));
REQUIRE(sceFiosOpGetAttr(op) != nullptr && !sceFiosOpIsCancelled(op));
REQUIRE(sceFiosOpGetOffset(op) == 0);
std::vector<SceFiosOp> listed(8);
REQUIRE(sceFiosGetAllOps(listed.data(), static_cast<std::uint32_t>(listed.size())) >= 1);
REQUIRE(listed[0] == op);
REQUIRE(sceFiosOpSyncWaitForIO(op) == 10);
REQUIRE(!sceFiosIsValidHandle(op));
REQUIRE(sceFiosOpGetError(op) == SCE_FIOS_ERROR_BAD_OP);
REQUIRE(sceFiosOpWait(op) == SCE_FIOS_ERROR_BAD_OP);
REQUIRE(sceFiosOpSyncWait(op) == SCE_FIOS_ERROR_BAD_OP);
REQUIRE(sceFiosOpGetActualCount(op) == SCE_FIOS_ERROR_BAD_OP);
REQUIRE(sceFiosOpGetPath(op) == nullptr);
sceFiosOpDelete(op);
bool exists = false;
const SceFiosOp existsOp = sceFiosExists(nullptr, file.c_str(), &exists);
REQUIRE(exists && sceFiosOpGetActualCount(existsOp) == 1);
REQUIRE(sceFiosOpSyncWait(existsOp) == 1);
const SceFiosOp missingOp = sceFiosExists(nullptr, "fios2_probe/none", &exists);
REQUIRE(!exists && sceFiosOpSyncWait(missingOp) == 0);
const SceFiosOp failed = sceFiosFileGetSize(nullptr, "fios2_probe/none");
REQUIRE(sceFiosOpGetError(failed) == SCE_FIOS_ERROR_BAD_PATH);
REQUIRE(sceFiosOpSyncWait(failed) == SCE_FIOS_ERROR_BAD_PATH);
}
static void CheckCallbacks(const std::string& file) {
g_completed = 0;
g_deleted = 0;
const SceFiosOpAttr attr = CountingAttr();
const SceFiosOp op = sceFiosFileExists(&attr, file.c_str(), nullptr);
REQUIRE(g_completed == 1 && g_lastOp == op && g_lastError == SCE_FIOS_OK);
REQUIRE(g_deleted == 0);
sceFiosOpDelete(op);
REQUIRE(g_deleted == 1);
REQUIRE(sceFiosFileExistsSync(&attr, file.c_str()));
REQUIRE(g_completed == 2 && g_deleted == 2);
const SceFiosOp failed = sceFiosFileGetSize(&attr, "fios2_probe/none");
REQUIRE(g_completed == 3 && g_lastError == SCE_FIOS_ERROR_BAD_PATH);
sceFiosOpDelete(failed);
SceFiosOpAttr stored{};
REQUIRE(!sceFiosGetGlobalDefaultOpAttr(&stored));
SceFiosOpAttr global = CountingAttr();
global.userTag = 77;
sceFiosSetGlobalDefaultOpAttr(&global);
REQUIRE(sceFiosGetGlobalDefaultOpAttr(&stored) && stored.userTag == 77);
SceFiosOpAttr effective{};
sceFiosGetDefaultOpAttr(&effective);
REQUIRE(effective.userTag == 77);
const SceFiosOp usesGlobal = sceFiosFileExists(nullptr, file.c_str(), nullptr);
REQUIRE(sceFiosOpGetAttr(usesGlobal)->userTag == 77 && g_completed == 4);
sceFiosOpDelete(usesGlobal);
SceFiosOpAttr thread = CountingAttr();
thread.userTag = 88;
sceFiosSetThreadDefaultOpAttr(&thread);
REQUIRE(sceFiosGetThreadDefaultOpAttr(&stored) && stored.userTag == 88);
std::thread other([&] {
SceFiosOpAttr seen{};
REQUIRE(!sceFiosGetThreadDefaultOpAttr(&seen));
sceFiosGetDefaultOpAttr(&seen);
REQUIRE(seen.userTag == 77);
});
other.join();
const SceFiosOp usesThread = sceFiosFileExists(nullptr, file.c_str(), nullptr);
REQUIRE(sceFiosOpGetAttr(usesThread)->userTag == 88);
sceFiosOpDelete(usesThread);
sceFiosSetThreadDefaultOpAttr(nullptr);
REQUIRE(!sceFiosGetThreadDefaultOpAttr(&stored));
sceFiosSetGlobalDefaultOpAttr(nullptr);
REQUIRE(!sceFiosGetGlobalDefaultOpAttr(&stored));
}
static void CheckSuspension(const std::string& file) {
REQUIRE(!sceFiosIsSuspended() && sceFiosGetSuspendCount() == 0 && sceFiosIsIdle());
g_completed = 0;
const SceFiosOpAttr attr = CountingAttr();
sceFiosSuspend();
sceFiosSuspend();
REQUIRE(sceFiosIsSuspended() && sceFiosGetSuspendCount() == 2);
const SceFiosOp first = sceFiosFileGetSize(&attr, file.c_str());
const SceFiosOp second = sceFiosFileGetSize(&attr, file.c_str());
REQUIRE(!sceFiosOpIsDone(first) && !sceFiosOpIsDone(second) && !sceFiosIsIdle());
REQUIRE(sceFiosOpGetError(first) == 1 && g_completed == 0);
sceFiosResume();
REQUIRE(!sceFiosOpIsDone(first) && sceFiosGetSuspendCount() == 1);
std::thread resumer([] {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
sceFiosResume();
});
REQUIRE(sceFiosOpWait(first) == SCE_FIOS_OK);
resumer.join();
REQUIRE(sceFiosOpIsDone(second) && sceFiosIsIdle() && g_completed == 2);
REQUIRE(sceFiosOpGetActualCount(first) == 10 && sceFiosOpGetActualCount(second) == 10);
sceFiosOpDelete(first);
sceFiosOpDelete(second);
sceFiosResume();
REQUIRE(sceFiosGetSuspendCount() == 0);
sceFiosSuspend();
const SceFiosOp cancelled = sceFiosFileGetSize(&attr, file.c_str());
const SceFiosOp alsoCancelled = sceFiosFileGetSize(&attr, file.c_str());
sceFiosOpCancel(cancelled);
REQUIRE(sceFiosOpIsDone(cancelled) && sceFiosOpIsCancelled(cancelled));
REQUIRE(sceFiosOpGetError(cancelled) == SCE_FIOS_ERROR_CANCELLED);
REQUIRE(g_lastError == SCE_FIOS_ERROR_CANCELLED);
sceFiosCancelAllOps();
REQUIRE(sceFiosOpGetError(alsoCancelled) == SCE_FIOS_ERROR_CANCELLED && sceFiosIsIdle());
sceFiosResume();
REQUIRE(sceFiosOpSyncWaitForIO(cancelled) == SCE_FIOS_ERROR_CANCELLED);
sceFiosOpDelete(alsoCancelled);
sceFiosSuspend();
const SceFiosOp abandoned = sceFiosFileGetSize(nullptr, file.c_str());
sceFiosOpDelete(abandoned);
REQUIRE(!sceFiosIsValidHandle(abandoned));
sceFiosResume();
sceFiosResume();
REQUIRE(sceFiosGetSuspendCount() == 0);
}
static void CheckMutations(const std::string& directory) {
const std::string sub = directory + "/sub";
const std::string target = sub + "/a.bin";
REQUIRE(sceFiosDirectoryCreateSync(nullptr, sub.c_str()) == SCE_FIOS_OK);
REQUIRE(sceFiosDirectoryCreateSync(nullptr, sub.c_str()) == SCE_FIOS_ERROR_ALREADY_EXISTS);
REQUIRE(sceFiosDirectoryCreateSync(nullptr, (directory + "/none/deeper").c_str()) == SCE_FIOS_ERROR_BAD_PATH);
REQUIRE(sceFiosDirectoryCreateSync(nullptr, nullptr) == SCE_FIOS_ERROR_BAD_PTR);
REQUIRE(sceFiosDirectoryExistsSync(nullptr, sub.c_str()));
REQUIRE(sceFiosFileWriteSync(nullptr, target.c_str(), "hello world", 11, 0) == 11);
char buffer[32] = {};
REQUIRE(sceFiosFileReadSync(nullptr, target.c_str(), buffer, 11, 0) == 11 && std::memcmp(buffer, "hello world", 11) == 0);
std::memset(buffer, 0, sizeof(buffer));
REQUIRE(sceFiosFileReadSync(nullptr, target.c_str(), buffer, 5, 6) == 5 && std::memcmp(buffer, "world", 5) == 0);
REQUIRE(sceFiosFileReadSync(nullptr, target.c_str(), buffer, 100, 6) == 5);
REQUIRE(sceFiosFileReadSync(nullptr, target.c_str(), buffer, 4, 500) == 0);
REQUIRE(sceFiosFileReadSync(nullptr, target.c_str(), buffer, 0, 0) == 0);
REQUIRE(sceFiosFileReadSync(nullptr, (sub + "/none").c_str(), buffer, 4, 0) == SCE_FIOS_ERROR_BAD_PATH);
REQUIRE(sceFiosFileReadSync(nullptr, target.c_str(), buffer, -1, 0) == SCE_FIOS_ERROR_BAD_SIZE);
REQUIRE(sceFiosFileReadSync(nullptr, target.c_str(), buffer, 4, -1) == SCE_FIOS_ERROR_BAD_OFFSET);
REQUIRE(sceFiosFileReadSync(nullptr, target.c_str(), nullptr, 4, 0) == SCE_FIOS_ERROR_BAD_PTR);
REQUIRE(sceFiosFileReadSync(nullptr, sub.c_str(), buffer, 4, 0) < 0);
REQUIRE(sceFiosFileWriteSync(nullptr, target.c_str(), "xyzzy", 5, 20) == 5);
REQUIRE(sceFiosFileGetSizeSync(nullptr, target.c_str()) == 25);
REQUIRE(sceFiosFileWriteSync(nullptr, target.c_str(), nullptr, 5, 0) == SCE_FIOS_ERROR_BAD_PTR);
REQUIRE(sceFiosFileWriteSync(nullptr, (sub + "/none/x").c_str(), "a", 1, 0) == SCE_FIOS_ERROR_BAD_PATH);
REQUIRE(sceFiosFileWriteSync(nullptr, (sub + "/created.bin").c_str(), "a", 1, 0) == 1);
REQUIRE(sceFiosFileDeleteSync(nullptr, (sub + "/created.bin").c_str()) == SCE_FIOS_OK);
std::memset(buffer, 0, sizeof(buffer));
const SceFiosOp read = sceFiosFileRead(nullptr, target.c_str(), buffer, 5, 6);
REQUIRE(sceFiosOpGetRequestCount(read) == 5 && sceFiosOpGetOffset(read) == 6 && sceFiosOpGetBuffer(read) == buffer);
REQUIRE(sceFiosOpGetActualCount(read) == 5 && sceFiosOpSyncWaitForIO(read) == 5 && std::memcmp(buffer, "world", 5) == 0);
REQUIRE(sceFiosFileTruncateSync(nullptr, target.c_str(), 4) == SCE_FIOS_OK);
REQUIRE(sceFiosFileGetSizeSync(nullptr, target.c_str()) == 4);
REQUIRE(sceFiosFileTruncateSync(nullptr, target.c_str(), -1) == SCE_FIOS_ERROR_BAD_SIZE);
REQUIRE(sceFiosFileTruncateSync(nullptr, (sub + "/none").c_str(), 4) == SCE_FIOS_ERROR_BAD_PATH);
const std::string renamed = sub + "/b.bin";
REQUIRE(sceFiosRenameSync(nullptr, target.c_str(), renamed.c_str()) == SCE_FIOS_OK);
REQUIRE(!sceFiosExistsSync(nullptr, target.c_str()) && sceFiosFileExistsSync(nullptr, renamed.c_str()));
REQUIRE(sceFiosRenameSync(nullptr, target.c_str(), renamed.c_str()) == SCE_FIOS_ERROR_BAD_PATH);
REQUIRE(sceFiosRenameSync(nullptr, renamed.c_str(), nullptr) == SCE_FIOS_ERROR_BAD_PTR);
REQUIRE(sceFiosRenameSync(nullptr, nullptr, renamed.c_str()) == SCE_FIOS_ERROR_BAD_PTR);
REQUIRE(sceFiosFileDeleteSync(nullptr, sub.c_str()) == SCE_FIOS_ERROR_NOT_A_FILE);
REQUIRE(sceFiosDirectoryDeleteSync(nullptr, renamed.c_str()) == SCE_FIOS_ERROR_NOT_A_DIRECTORY);
REQUIRE(sceFiosDirectoryDeleteSync(nullptr, sub.c_str()) == SCE_FIOS_ERROR_ACCESS);
REQUIRE(sceFiosFileDeleteSync(nullptr, renamed.c_str()) == SCE_FIOS_OK);
REQUIRE(sceFiosFileDeleteSync(nullptr, renamed.c_str()) == SCE_FIOS_ERROR_BAD_PATH);
REQUIRE(sceFiosDirectoryDeleteSync(nullptr, sub.c_str()) == SCE_FIOS_OK);
REQUIRE(sceFiosDirectoryCreateSync(nullptr, sub.c_str()) == SCE_FIOS_OK);
REQUIRE(sceFiosDeleteSync(nullptr, sub.c_str()) == SCE_FIOS_OK);
REQUIRE(sceFiosFileWriteSync(nullptr, renamed.c_str(), "a", 1, 0) == SCE_FIOS_ERROR_BAD_PATH);
REQUIRE(sceFiosFileWriteSync(nullptr, (directory + "/c.bin").c_str(), "a", 1, 0) == 1);
REQUIRE(sceFiosDeleteSync(nullptr, (directory + "/c.bin").c_str()) == SCE_FIOS_OK);
REQUIRE(sceFiosDeleteSync(nullptr, (directory + "/c.bin").c_str()) == SCE_FIOS_ERROR_BAD_PATH);
g_completed = 0;
const SceFiosOpAttr attr = CountingAttr();
const SceFiosOp created = sceFiosDirectoryCreate(&attr, sub.c_str());
REQUIRE(g_completed == 1 && sceFiosOpGetError(created) == SCE_FIOS_OK);
sceFiosOpDelete(created);
const SceFiosOp removed = sceFiosDelete(&attr, sub.c_str());
REQUIRE(g_completed == 2 && sceFiosOpSyncWait(removed) == SCE_FIOS_OK);
}
static void CheckFileHandles(const std::string& directory) {
const std::string path = directory + "/handle.bin";
SceFiosOpenParams writer{};
writer.openFlags = (SCE_FIOS_O_READ | SCE_FIOS_O_WRITE | SCE_FIOS_O_CREAT);
SceFiosFH fh = 0;
REQUIRE(sceFiosFHOpenSync(nullptr, &fh, path.c_str(), nullptr) == SCE_FIOS_ERROR_BAD_PATH);
REQUIRE(sceFiosFHOpenSync(nullptr, nullptr, path.c_str(), &writer) == SCE_FIOS_ERROR_BAD_PTR);
REQUIRE(sceFiosFHOpenSync(nullptr, &fh, nullptr, &writer) == SCE_FIOS_ERROR_BAD_PTR);
REQUIRE(sceFiosFHOpenSync(nullptr, &fh, directory.c_str(), nullptr) == SCE_FIOS_ERROR_NOT_A_FILE);
REQUIRE(sceFiosFHOpenSync(nullptr, &fh, path.c_str(), &writer) == SCE_FIOS_OK);
REQUIRE(fh > 0 && sceFiosIsValidHandle(fh));
REQUIRE(path == sceFiosFHGetPath(fh) && sceFiosFHGetOpenParams(fh)->openFlags == writer.openFlags);
REQUIRE(sceFiosFHGetSize(fh) == 0);
std::uint32_t listed[4] = {};
REQUIRE(sceFiosGetAllFHs(reinterpret_cast<SceFiosFH*>(listed), 4) == 1 && listed[0] == static_cast<std::uint32_t>(fh));
REQUIRE(sceFiosFHWriteSync(nullptr, fh, "abcdef", 6) == 6);
REQUIRE(sceFiosFHTell(fh) == 6 && sceFiosFHGetSize(fh) == 6);
REQUIRE(sceFiosFHSeek(fh, 1, SCE_FIOS_SEEK_SET) == 1);
char buffer[16] = {};
REQUIRE(sceFiosFHReadSync(nullptr, fh, buffer, 3) == 3 && std::memcmp(buffer, "bcd", 3) == 0);
REQUIRE(sceFiosFHSeek(fh, -1, SCE_FIOS_SEEK_END) == 5 && sceFiosFHSeek(fh, 1, SCE_FIOS_SEEK_CUR) == 6);
REQUIRE(sceFiosFHReadSync(nullptr, fh, buffer, 3) == 0);
REQUIRE(sceFiosFHSeek(fh, 0, 7) == SCE_FIOS_ERROR_BAD_OFFSET);
REQUIRE(sceFiosFHSeek(fh, -100, SCE_FIOS_SEEK_SET) == SCE_FIOS_ERROR_BAD_OFFSET);
REQUIRE(sceFiosFHPwriteSync(nullptr, fh, "XY", 2, 1) == 2 && sceFiosFHTell(fh) == 6);
std::memset(buffer, 0, sizeof(buffer));
REQUIRE(sceFiosFHPreadSync(nullptr, fh, buffer, 6, 0) == 6 && std::memcmp(buffer, "aXYdef", 6) == 0);
REQUIRE(sceFiosFHPreadSync(nullptr, fh, buffer, 4, -1) == SCE_FIOS_ERROR_BAD_OFFSET);
REQUIRE(sceFiosFHReadSync(nullptr, fh, buffer, -1) == SCE_FIOS_ERROR_BAD_SIZE);
REQUIRE(sceFiosFHReadSync(nullptr, fh, nullptr, 4) == SCE_FIOS_ERROR_BAD_PTR);
char first[2] = {};
char second[3] = {};
const SceFiosBuffer reads[2] = {{first, sizeof(first)}, {second, sizeof(second)}};
REQUIRE(sceFiosFHPreadvSync(nullptr, fh, reads, 2, 1) == 5 && std::memcmp(first, "XY", 2) == 0 && std::memcmp(second, "def", 3) == 0);
REQUIRE(sceFiosFHSeek(fh, 0, SCE_FIOS_SEEK_SET) == 0);
REQUIRE(sceFiosFHReadvSync(nullptr, fh, reads, 2) == 5 && std::memcmp(first, "aX", 2) == 0 && std::memcmp(second, "Yde", 3) == 0);
const SceFiosBuffer writes[2] = {{const_cast<char*>("12"), 2}, {const_cast<char*>("345"), 3}};
REQUIRE(sceFiosFHPwritevSync(nullptr, fh, writes, 2, 6) == 5 && sceFiosFHGetSize(fh) == 11);
REQUIRE(sceFiosFHSeek(fh, 0, SCE_FIOS_SEEK_END) == 11);
REQUIRE(sceFiosFHWritevSync(nullptr, fh, writes, 2) == 5 && sceFiosFHGetSize(fh) == 16);
REQUIRE(sceFiosFHReadvSync(nullptr, fh, reads, 0) == SCE_FIOS_ERROR_BAD_IOVCNT);
REQUIRE(sceFiosFHReadvSync(nullptr, fh, reads, 5000) == SCE_FIOS_ERROR_BAD_IOVCNT);
REQUIRE(sceFiosFHReadvSync(nullptr, fh, nullptr, 1) == SCE_FIOS_ERROR_BAD_PTR);
SceFiosStat stat{};
REQUIRE(sceFiosFHStatSync(nullptr, fh, &stat) == SCE_FIOS_OK && stat.fileSize == 16);
REQUIRE(sceFiosFHStatSync(nullptr, fh, nullptr) == SCE_FIOS_ERROR_BAD_PTR);
REQUIRE(sceFiosFHTruncateSync(nullptr, fh, 4) == SCE_FIOS_OK && sceFiosFHGetSize(fh) == 4);
REQUIRE(sceFiosFHTruncateSync(nullptr, fh, -1) == SCE_FIOS_ERROR_BAD_SIZE);
REQUIRE(sceFiosFHSyncSync(nullptr, fh) == SCE_FIOS_OK);
const SceFiosOp op = sceFiosFHPread(nullptr, fh, buffer, 4, 0);
REQUIRE(sceFiosOpGetRequestCount(op) == 4 && sceFiosOpGetOffset(op) == 0 && path == sceFiosOpGetPath(op));
REQUIRE(sceFiosOpSyncWaitForIO(op) == 4 && std::memcmp(buffer, "aXYd", 4) == 0);
REQUIRE(sceFiosFHCloseSync(nullptr, fh) == SCE_FIOS_OK);
REQUIRE(!sceFiosIsValidHandle(fh) && sceFiosFHGetPath(fh) == nullptr && sceFiosFHGetOpenParams(fh) == nullptr);
REQUIRE(sceFiosFHCloseSync(nullptr, fh) == SCE_FIOS_ERROR_BAD_FH);
REQUIRE(sceFiosFHReadSync(nullptr, fh, buffer, 1) == SCE_FIOS_ERROR_BAD_FH);
REQUIRE(sceFiosFHSeek(fh, 0, 0) == SCE_FIOS_ERROR_BAD_FH && sceFiosFHTell(fh) == SCE_FIOS_ERROR_BAD_FH);
REQUIRE(sceFiosFHGetSize(fh) == SCE_FIOS_ERROR_BAD_FH);
REQUIRE(sceFiosFHStatSync(nullptr, fh, &stat) == SCE_FIOS_ERROR_BAD_FH);
REQUIRE(sceFiosFHSyncSync(nullptr, fh) == SCE_FIOS_ERROR_BAD_FH);
SceFiosOpenParams reader{};
reader.openFlags = SCE_FIOS_O_READ;
REQUIRE(sceFiosFHOpenSync(nullptr, &fh, path.c_str(), &reader) == SCE_FIOS_OK);
REQUIRE(sceFiosFHWriteSync(nullptr, fh, "z", 1) == SCE_FIOS_ERROR_READ_ONLY);
REQUIRE(sceFiosFHTruncateSync(nullptr, fh, 1) == SCE_FIOS_ERROR_READ_ONLY);
REQUIRE(sceFiosFHPreadSync(nullptr, fh, buffer, 4, 0) == 4);
sceFiosCloseAllFiles();
REQUIRE(!sceFiosIsValidHandle(fh) && sceFiosGetAllFHs(nullptr, 0) == 0);
SceFiosOpenParams writeOnly{};
writeOnly.openFlags = SCE_FIOS_O_WRITE | SCE_FIOS_O_TRUNC;
REQUIRE(sceFiosFHOpenSync(nullptr, &fh, path.c_str(), &writeOnly) == SCE_FIOS_OK);
REQUIRE(sceFiosFHGetSize(fh) == 0 && sceFiosFHReadSync(nullptr, fh, buffer, 1) == SCE_FIOS_ERROR_WRITE_ONLY);
REQUIRE(sceFiosFHCloseSync(nullptr, fh) == SCE_FIOS_OK);
SceFiosOpenParams appender{};
appender.openFlags = SCE_FIOS_O_WRITE | SCE_FIOS_O_APPEND | SCE_FIOS_O_CREAT;
const std::string created = directory + "/appended.bin";
REQUIRE(sceFiosFHOpenWithModeSync(nullptr, &fh, created.c_str(), &appender, 0644) == SCE_FIOS_OK);
REQUIRE(sceFiosFHWriteSync(nullptr, fh, "ab", 2) == 2 && sceFiosFHWriteSync(nullptr, fh, "cd", 2) == 2);
REQUIRE(sceFiosFHCloseSync(nullptr, fh) == SCE_FIOS_OK);
REQUIRE(sceFiosFileGetSizeSync(nullptr, created.c_str()) == 4);
g_completed = 0;
const SceFiosOpAttr attr = CountingAttr();
const SceFiosOp opened = sceFiosFHOpen(&attr, &fh, created.c_str(), &reader);
REQUIRE(g_completed == 1 && sceFiosOpGetError(opened) == SCE_FIOS_OK && sceFiosIsValidHandle(fh));
sceFiosOpDelete(opened);
const SceFiosOp closed = sceFiosFHClose(&attr, fh);
REQUIRE(g_completed == 2 && sceFiosOpSyncWait(closed) == SCE_FIOS_OK);
sceFiosSuspend();
const SceFiosOp deferred = sceFiosFHOpen(nullptr, &fh, created.c_str(), &reader);
REQUIRE(!sceFiosOpIsDone(deferred));
sceFiosResume();
REQUIRE(sceFiosOpSyncWait(deferred) == SCE_FIOS_OK && sceFiosIsValidHandle(fh));
sceFiosShutdownAndCancelOps();
REQUIRE(!sceFiosIsValidHandle(fh));
REQUIRE(sceFiosDeleteSync(nullptr, created.c_str()) == SCE_FIOS_OK);
REQUIRE(sceFiosDeleteSync(nullptr, path.c_str()) == SCE_FIOS_OK);
}
int main() {
const std::filesystem::path directory = "fios2_probe";
std::filesystem::remove_all(directory);
std::filesystem::create_directories(directory);
const std::string file = (directory / "data.bin").generic_string();
{
std::ofstream out(file, std::ios::binary);
out << "0123456789";
}
CheckInitialization();
CheckQueries(directory.generic_string(), file);
CheckOperationLifecycle(file);
CheckCallbacks(file);
CheckSuspension(file);
CheckMutations(directory.generic_string());
CheckFileHandles(directory.generic_string());
sceFiosTerminate();
REQUIRE(!sceFiosIsInitialized(nullptr));
std::filesystem::remove_all(directory);
std::puts("guest_fios2 ok");
return 0;
}