mirror of
https://github.com/ran-j/PS2Recomp.git
synced 2026-09-26 08:51:05 -04:00
Feature/mc0 IO + Win IO fixes (#57)
* mc0: map memory card paths to mcRoot and related io tests * Fix mc0 test setup to initialize temp paths * fix: implementing WIN32 I/O support in syscalls * fix: avoid fioWrite deadlock Do not lock the fd map while calling getHostFile; use the syscall file mutex around fwrite instead
This commit is contained in:
committed by
GitHub
parent
c4555f6723
commit
cf5f57180f
@@ -351,6 +351,7 @@ public:
|
||||
std::filesystem::path elfDirectory;
|
||||
std::filesystem::path hostRoot;
|
||||
std::filesystem::path cdRoot;
|
||||
std::filesystem::path mcRoot;
|
||||
std::filesystem::path cdImage;
|
||||
};
|
||||
|
||||
|
||||
@@ -118,6 +118,7 @@ namespace
|
||||
defaults.elfDirectory = ec ? std::filesystem::path(".") : cwd.lexically_normal();
|
||||
defaults.hostRoot = defaults.elfDirectory;
|
||||
defaults.cdRoot = defaults.elfDirectory;
|
||||
defaults.mcRoot = defaults.elfDirectory / "mc0";
|
||||
return defaults;
|
||||
}();
|
||||
|
||||
@@ -404,6 +405,7 @@ void PS2Runtime::setIoPaths(const IoPaths &paths)
|
||||
normalized.elfDirectory = normalizeAbsolutePath(normalized.elfDirectory);
|
||||
normalized.hostRoot = normalizeAbsolutePath(normalized.hostRoot);
|
||||
normalized.cdRoot = normalizeAbsolutePath(normalized.cdRoot);
|
||||
normalized.mcRoot = normalizeAbsolutePath(normalized.mcRoot);
|
||||
normalized.cdImage = normalizeAbsolutePath(normalized.cdImage);
|
||||
|
||||
if (normalized.elfDirectory.empty() && !normalized.elfPath.empty())
|
||||
@@ -419,6 +421,10 @@ void PS2Runtime::setIoPaths(const IoPaths &paths)
|
||||
{
|
||||
normalized.cdRoot = normalized.elfDirectory;
|
||||
}
|
||||
if (normalized.mcRoot.empty())
|
||||
{
|
||||
normalized.mcRoot = normalized.elfDirectory / "mc0";
|
||||
}
|
||||
|
||||
runtimeIoPaths() = normalized;
|
||||
}
|
||||
@@ -436,6 +442,7 @@ void PS2Runtime::configureIoPathsFromElf(const std::string &elfPath)
|
||||
{
|
||||
paths.hostRoot = paths.elfDirectory;
|
||||
paths.cdRoot = paths.elfDirectory;
|
||||
paths.mcRoot = paths.elfDirectory / "mc0";
|
||||
}
|
||||
|
||||
paths.cdImage.clear();
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
namespace
|
||||
{
|
||||
constexpr char kMc0Prefix[] = "mc0:";
|
||||
|
||||
std::string toLowerAscii(std::string value)
|
||||
{
|
||||
std::transform(value.begin(), value.end(), value.begin(),
|
||||
@@ -77,4 +79,21 @@ namespace
|
||||
const std::filesystem::path cwd = std::filesystem::current_path(ec);
|
||||
return ec ? std::filesystem::path(".") : cwd.lexically_normal();
|
||||
}
|
||||
|
||||
std::filesystem::path getConfiguredMcRoot()
|
||||
{
|
||||
const PS2Runtime::IoPaths &paths = PS2Runtime::getIoPaths();
|
||||
if (!paths.mcRoot.empty())
|
||||
{
|
||||
return paths.mcRoot;
|
||||
}
|
||||
if (!paths.elfDirectory.empty())
|
||||
{
|
||||
return paths.elfDirectory / "mc0";
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
const std::filesystem::path cwd = std::filesystem::current_path(ec);
|
||||
return ec ? std::filesystem::path("mc0") : (cwd / "mc0").lexically_normal();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,6 +359,12 @@ std::string translatePs2Path(const char *ps2Path)
|
||||
return resolveWithBase(getConfiguredCdRoot(), pathStr.substr(prefixLength));
|
||||
}
|
||||
|
||||
if (lower.rfind(kMc0Prefix, 0) == 0)
|
||||
{
|
||||
const std::size_t prefixLength = sizeof(kMc0Prefix) - 1;
|
||||
return resolveWithBase(getConfiguredMcRoot(), pathStr.substr(prefixLength));
|
||||
}
|
||||
|
||||
if (!pathStr.empty() && (pathStr.front() == '/' || pathStr.front() == '\\'))
|
||||
{
|
||||
return resolveWithBase(getConfiguredCdRoot(), pathStr);
|
||||
|
||||
@@ -182,22 +182,22 @@ void fioWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
return;
|
||||
}
|
||||
|
||||
FILE *fp = getHostFile(ps2Fd);
|
||||
if (!fp)
|
||||
{
|
||||
setReturnS32(ctx, -1); // -EFAULT
|
||||
return;
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
setReturnS32(ctx, 0); // Wrote 0 bytes
|
||||
return;
|
||||
}
|
||||
|
||||
size_t bytesWritten = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_fd_mutex);
|
||||
FILE *fp = getHostFile(ps2Fd);
|
||||
if (!fp)
|
||||
{
|
||||
setReturnS32(ctx, -1); // -EFAULT
|
||||
return;
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
setReturnS32(ctx, 0); // Wrote 0 bytes
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(g_sys_fd_mutex);
|
||||
bytesWritten = ::fwrite(hostBuf, 1, size, fp);
|
||||
if (bytesWritten < size && ferror(fp))
|
||||
{
|
||||
@@ -273,7 +273,7 @@ void fioLseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
void fioMkdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
uint32_t pathAddr = getRegU32(ctx, 4); // $a0
|
||||
// int mode = (int)getRegU32(ctx, 5);
|
||||
// int mode = (int)getRegU32(ctx, 5); // $a1 - ignored on host
|
||||
|
||||
const char *ps2Path = reinterpret_cast<const char *>(getConstMemPtr(rdram, pathAddr));
|
||||
if (!ps2Path)
|
||||
@@ -289,20 +289,18 @@ void fioMkdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
setReturnS32(ctx, -1);
|
||||
return;
|
||||
}
|
||||
std::error_code ec;
|
||||
bool success = std::filesystem::create_directory(hostPath, ec);
|
||||
|
||||
#ifdef _WIN32
|
||||
int ret = -1;
|
||||
#else
|
||||
int ret = ::mkdir(hostPath.c_str(), 0775);
|
||||
#endif
|
||||
|
||||
if (ret != 0)
|
||||
if (!success && ec)
|
||||
{
|
||||
std::cerr << "fioMkdir error: mkdir failed for '" << hostPath << "': " << strerror(errno) << std::endl;
|
||||
setReturnS32(ctx, -1); // errno
|
||||
std::cerr << "fioMkdir error: create_directory failed for '" << hostPath
|
||||
<< "': " << ec.message() << std::endl;
|
||||
setReturnS32(ctx, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "fioMkdir: Created directory '" << hostPath << "'" << std::endl;
|
||||
setReturnS32(ctx, 0); // Success
|
||||
}
|
||||
}
|
||||
@@ -326,21 +324,18 @@ void fioChdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
return;
|
||||
}
|
||||
|
||||
std::cerr << "fioChdir: Attempting host chdir to '" << hostPath << "' (Stub - Check side effects)" << std::endl;
|
||||
std::error_code ec;
|
||||
std::filesystem::current_path(hostPath, ec);
|
||||
|
||||
#ifdef _WIN32
|
||||
int ret = -1;
|
||||
#else
|
||||
int ret = ::chdir(hostPath.c_str());
|
||||
#endif
|
||||
|
||||
if (ret != 0)
|
||||
if (ec)
|
||||
{
|
||||
std::cerr << "fioChdir error: chdir failed for '" << hostPath << "': " << strerror(errno) << std::endl;
|
||||
std::cerr << "fioChdir error: current_path failed for '" << hostPath
|
||||
<< "': " << ec.message() << std::endl;
|
||||
setReturnS32(ctx, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "fioChdir: Changed directory to '" << hostPath << "'" << std::endl;
|
||||
setReturnS32(ctx, 0); // Success
|
||||
}
|
||||
}
|
||||
@@ -363,19 +358,18 @@ void fioRmdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
int ret = -1;
|
||||
#else
|
||||
int ret = ::rmdir(hostPath.c_str());
|
||||
#endif
|
||||
std::error_code ec;
|
||||
bool success = std::filesystem::remove(hostPath, ec);
|
||||
|
||||
if (ret != 0)
|
||||
if (!success || ec)
|
||||
{
|
||||
std::cerr << "fioRmdir error: rmdir failed for '" << hostPath << "': " << strerror(errno) << std::endl;
|
||||
std::cerr << "fioRmdir error: remove failed for '" << hostPath
|
||||
<< "': " << ec.message() << std::endl;
|
||||
setReturnS32(ctx, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "fioRmdir: Removed directory '" << hostPath << "'" << std::endl;
|
||||
setReturnS32(ctx, 0); // Success
|
||||
}
|
||||
}
|
||||
@@ -432,19 +426,18 @@ void fioRemove(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
int ret = -1;
|
||||
#else
|
||||
int ret = ::unlink(hostPath.c_str());
|
||||
#endif
|
||||
std::error_code ec;
|
||||
bool success = std::filesystem::remove(hostPath, ec);
|
||||
|
||||
if (ret != 0)
|
||||
if (!success || ec)
|
||||
{
|
||||
std::cerr << "fioRemove error: unlink failed for '" << hostPath << "': " << strerror(errno) << std::endl;
|
||||
std::cerr << "fioRemove error: remove failed for '" << hostPath
|
||||
<< "': " << ec.message() << std::endl;
|
||||
setReturnS32(ctx, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "fioRemove: Removed file '" << hostPath << "'" << std::endl;
|
||||
setReturnS32(ctx, 0); // Success
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ add_executable(ps2x_tests
|
||||
src/code_generator_tests.cpp
|
||||
src/r5900_decoder_tests.cpp
|
||||
src/elf_analyzer_tests.cpp
|
||||
src/ps2_runtime_io_tests.cpp
|
||||
)
|
||||
|
||||
option(PRINT_GENERATED_CODE "Print generated code in tests" OFF)
|
||||
@@ -21,9 +22,11 @@ target_include_directories(ps2x_tests PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/ps2xRecomp/include
|
||||
${CMAKE_SOURCE_DIR}/ps2xAnalyzer/include
|
||||
${CMAKE_SOURCE_DIR}/ps2xRuntime/include
|
||||
)
|
||||
|
||||
target_link_libraries(ps2x_tests PRIVATE
|
||||
ps2_recomp_lib
|
||||
ps2_analyzer_lib
|
||||
ps2_runtime
|
||||
)
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
void register_code_generator_tests();
|
||||
void register_r5900_decoder_tests();
|
||||
void register_elf_analyzer_tests();
|
||||
void register_ps2_runtime_io_tests();
|
||||
|
||||
int main()
|
||||
{
|
||||
register_code_generator_tests();
|
||||
register_r5900_decoder_tests();
|
||||
register_elf_analyzer_tests();
|
||||
register_ps2_runtime_io_tests();
|
||||
return MiniTest::Run();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
#include "MiniTest.h"
|
||||
#include "ps2_runtime.h"
|
||||
#include "ps2_syscalls.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <chrono>
|
||||
|
||||
using namespace ps2_syscalls;
|
||||
|
||||
namespace
|
||||
{
|
||||
// Guest memory address ranges for test data
|
||||
constexpr uint32_t GUEST_STRING_AREA_START = 0x1000;
|
||||
constexpr uint32_t GUEST_BUFFER_AREA_START = 0x2000;
|
||||
|
||||
// Common file I/O flag combinations
|
||||
constexpr uint32_t PS2_FIO_WRITE_CREATE_TRUNC =
|
||||
PS2_FIO_O_WRONLY | PS2_FIO_O_CREAT | PS2_FIO_O_TRUNC;
|
||||
|
||||
void setRegU32(R5900Context &ctx, int reg, uint32_t value)
|
||||
{
|
||||
ctx.r[reg] = _mm_set_epi64x(0, static_cast<int64_t>(value));
|
||||
}
|
||||
|
||||
int32_t getRegS32(const R5900Context *ctx, int reg)
|
||||
{
|
||||
return static_cast<int32_t>(::getRegU32(ctx, reg));
|
||||
}
|
||||
|
||||
void writeGuestString(uint8_t *rdram, uint32_t addr, const std::string &value)
|
||||
{
|
||||
std::memcpy(rdram + addr, value.c_str(), value.size() + 1);
|
||||
}
|
||||
|
||||
struct TempPaths
|
||||
{
|
||||
std::filesystem::path base;
|
||||
std::filesystem::path mcRoot;
|
||||
std::filesystem::path cdRoot;
|
||||
|
||||
~TempPaths()
|
||||
{
|
||||
std::error_code ec;
|
||||
std::filesystem::remove_all(base, ec);
|
||||
}
|
||||
};
|
||||
|
||||
TempPaths makeTempPaths()
|
||||
{
|
||||
TempPaths paths;
|
||||
const auto now = std::chrono::steady_clock::now().time_since_epoch().count();
|
||||
paths.base = std::filesystem::temp_directory_path()
|
||||
/ ("ps2recomp-mc0-" + std::to_string(now));
|
||||
paths.mcRoot = paths.base / "mcroot";
|
||||
paths.cdRoot = paths.base / "cdroot";
|
||||
std::filesystem::create_directories(paths.mcRoot);
|
||||
std::filesystem::create_directories(paths.cdRoot);
|
||||
return paths;
|
||||
}
|
||||
|
||||
struct TestContext
|
||||
{
|
||||
TempPaths paths;
|
||||
std::vector<uint8_t> rdram;
|
||||
R5900Context ctx;
|
||||
|
||||
TestContext() : paths(makeTempPaths()), rdram(PS2_RAM_SIZE, 0)
|
||||
{
|
||||
PS2Runtime::IoPaths ioPaths;
|
||||
ioPaths.elfDirectory = paths.cdRoot;
|
||||
ioPaths.hostRoot = paths.cdRoot;
|
||||
ioPaths.cdRoot = paths.cdRoot;
|
||||
ioPaths.mcRoot = paths.mcRoot;
|
||||
PS2Runtime::setIoPaths(ioPaths);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void register_ps2_runtime_io_tests()
|
||||
{
|
||||
MiniTest::Case("PS2RuntimeIO", [](TestCase &tc)
|
||||
{
|
||||
tc.Run("mc0 directory creation", [](TestCase &t)
|
||||
{
|
||||
TestContext test;
|
||||
|
||||
const std::string dirPath = "mc0:/SAVEDATA";
|
||||
const uint32_t dirAddr = GUEST_STRING_AREA_START;
|
||||
writeGuestString(test.rdram.data(), dirAddr, dirPath);
|
||||
|
||||
setRegU32(test.ctx, 4, dirAddr);
|
||||
fioMkdir(test.rdram.data(), &test.ctx, nullptr);
|
||||
|
||||
const int32_t result = getRegS32(&test.ctx, 2);
|
||||
t.IsTrue(result >= 0, "fioMkdir should succeed for mc0: directory");
|
||||
|
||||
const std::filesystem::path expected = test.paths.mcRoot / "SAVEDATA";
|
||||
t.IsTrue(std::filesystem::exists(expected),
|
||||
"Directory should exist under mcRoot");
|
||||
t.IsTrue(std::filesystem::is_directory(expected),
|
||||
"Created path should be a directory");
|
||||
});
|
||||
|
||||
tc.Run("mc0 file write operations", [](TestCase &t)
|
||||
{
|
||||
TestContext test;
|
||||
|
||||
// Setup: create directory first
|
||||
const std::string dirPath = "mc0:/SAVEDATA";
|
||||
const uint32_t dirAddr = GUEST_STRING_AREA_START;
|
||||
writeGuestString(test.rdram.data(), dirAddr, dirPath);
|
||||
setRegU32(test.ctx, 4, dirAddr);
|
||||
fioMkdir(test.rdram.data(), &test.ctx, nullptr);
|
||||
|
||||
// Test: open file for writing
|
||||
const std::string filePath = "mc0:/SAVEDATA/test.txt";
|
||||
const uint32_t fileAddr = GUEST_STRING_AREA_START + 0x100;
|
||||
writeGuestString(test.rdram.data(), fileAddr, filePath);
|
||||
|
||||
setRegU32(test.ctx, 4, fileAddr);
|
||||
setRegU32(test.ctx, 5, PS2_FIO_WRITE_CREATE_TRUNC);
|
||||
fioOpen(test.rdram.data(), &test.ctx, nullptr);
|
||||
|
||||
const int32_t fd = getRegS32(&test.ctx, 2);
|
||||
t.IsTrue(fd >= 0, "fioOpen should return valid file descriptor");
|
||||
|
||||
// Write payload
|
||||
const std::string payload = "hello mc0";
|
||||
const uint32_t bufAddr = GUEST_BUFFER_AREA_START;
|
||||
std::memcpy(test.rdram.data() + bufAddr, payload.data(), payload.size());
|
||||
|
||||
setRegU32(test.ctx, 4, static_cast<uint32_t>(fd));
|
||||
setRegU32(test.ctx, 5, bufAddr);
|
||||
setRegU32(test.ctx, 6, static_cast<uint32_t>(payload.size()));
|
||||
fioWrite(test.rdram.data(), &test.ctx, nullptr);
|
||||
|
||||
const int32_t bytesWritten = getRegS32(&test.ctx, 2);
|
||||
t.Equals(bytesWritten, static_cast<int32_t>(payload.size()),
|
||||
"fioWrite should write all bytes");
|
||||
|
||||
// Close file
|
||||
setRegU32(test.ctx, 4, static_cast<uint32_t>(fd));
|
||||
fioClose(test.rdram.data(), &test.ctx, nullptr);
|
||||
|
||||
const int32_t closeResult = getRegS32(&test.ctx, 2);
|
||||
t.IsTrue(closeResult >= 0, "fioClose should succeed");
|
||||
|
||||
// Verify on host filesystem
|
||||
const std::filesystem::path expectedPath =
|
||||
test.paths.mcRoot / "SAVEDATA" / "test.txt";
|
||||
t.IsTrue(std::filesystem::exists(expectedPath),
|
||||
"File should exist under mcRoot");
|
||||
|
||||
std::ifstream in(expectedPath, std::ios::binary);
|
||||
std::string readback(
|
||||
(std::istreambuf_iterator<char>(in)),
|
||||
std::istreambuf_iterator<char>());
|
||||
t.Equals(readback, payload, "File content should match written payload");
|
||||
});
|
||||
|
||||
tc.Run("mc0 file read operations", [](TestCase &t)
|
||||
{
|
||||
TestContext test;
|
||||
|
||||
// Setup: create directory and write file
|
||||
const std::string dirPath = "mc0:/SAVEDATA";
|
||||
const uint32_t dirAddr = GUEST_STRING_AREA_START;
|
||||
writeGuestString(test.rdram.data(), dirAddr, dirPath);
|
||||
setRegU32(test.ctx, 4, dirAddr);
|
||||
fioMkdir(test.rdram.data(), &test.ctx, nullptr);
|
||||
|
||||
const std::string filePath = "mc0:/SAVEDATA/test.txt";
|
||||
const uint32_t fileAddr = GUEST_STRING_AREA_START + 0x100;
|
||||
writeGuestString(test.rdram.data(), fileAddr, filePath);
|
||||
|
||||
// Write data
|
||||
const std::string payload = "hello mc0 read test";
|
||||
const uint32_t writeBufAddr = GUEST_BUFFER_AREA_START;
|
||||
std::memcpy(test.rdram.data() + writeBufAddr, payload.data(), payload.size());
|
||||
|
||||
setRegU32(test.ctx, 4, fileAddr);
|
||||
setRegU32(test.ctx, 5, PS2_FIO_WRITE_CREATE_TRUNC);
|
||||
fioOpen(test.rdram.data(), &test.ctx, nullptr);
|
||||
int32_t fd = getRegS32(&test.ctx, 2);
|
||||
|
||||
setRegU32(test.ctx, 4, static_cast<uint32_t>(fd));
|
||||
setRegU32(test.ctx, 5, writeBufAddr);
|
||||
setRegU32(test.ctx, 6, static_cast<uint32_t>(payload.size()));
|
||||
fioWrite(test.rdram.data(), &test.ctx, nullptr);
|
||||
|
||||
setRegU32(test.ctx, 4, static_cast<uint32_t>(fd));
|
||||
fioClose(test.rdram.data(), &test.ctx, nullptr);
|
||||
|
||||
// Test: read back via fioRead
|
||||
setRegU32(test.ctx, 4, fileAddr);
|
||||
setRegU32(test.ctx, 5, PS2_FIO_O_RDONLY);
|
||||
fioOpen(test.rdram.data(), &test.ctx, nullptr);
|
||||
fd = getRegS32(&test.ctx, 2);
|
||||
t.IsTrue(fd >= 0, "fioOpen for reading should succeed");
|
||||
|
||||
// Read into different buffer area
|
||||
const uint32_t readBufAddr = GUEST_BUFFER_AREA_START + 0x1000;
|
||||
std::memset(test.rdram.data() + readBufAddr, 0, payload.size());
|
||||
|
||||
setRegU32(test.ctx, 4, static_cast<uint32_t>(fd));
|
||||
setRegU32(test.ctx, 5, readBufAddr);
|
||||
setRegU32(test.ctx, 6, static_cast<uint32_t>(payload.size()));
|
||||
fioRead(test.rdram.data(), &test.ctx, nullptr);
|
||||
|
||||
const int32_t bytesRead = getRegS32(&test.ctx, 2);
|
||||
t.Equals(bytesRead, static_cast<int32_t>(payload.size()),
|
||||
"fioRead should read all bytes");
|
||||
|
||||
std::string readback(
|
||||
reinterpret_cast<const char*>(test.rdram.data() + readBufAddr),
|
||||
payload.size()
|
||||
);
|
||||
t.Equals(readback, payload, "fioRead content should match original");
|
||||
|
||||
setRegU32(test.ctx, 4, static_cast<uint32_t>(fd));
|
||||
fioClose(test.rdram.data(), &test.ctx, nullptr);
|
||||
});
|
||||
|
||||
tc.Run("mc0 paths isolated from cdRoot", [](TestCase &t)
|
||||
{
|
||||
TestContext test;
|
||||
|
||||
const std::string dirPath = "mc0:/ISOLATED";
|
||||
const std::string filePath = "mc0:/ISOLATED/test.txt";
|
||||
const uint32_t dirAddr = GUEST_STRING_AREA_START;
|
||||
const uint32_t fileAddr = GUEST_STRING_AREA_START + 0x100;
|
||||
|
||||
writeGuestString(test.rdram.data(), dirAddr, dirPath);
|
||||
writeGuestString(test.rdram.data(), fileAddr, filePath);
|
||||
|
||||
// Create directory and file on mc0:
|
||||
setRegU32(test.ctx, 4, dirAddr);
|
||||
fioMkdir(test.rdram.data(), &test.ctx, nullptr);
|
||||
|
||||
setRegU32(test.ctx, 4, fileAddr);
|
||||
setRegU32(test.ctx, 5, PS2_FIO_WRITE_CREATE_TRUNC);
|
||||
fioOpen(test.rdram.data(), &test.ctx, nullptr);
|
||||
const int32_t fd = getRegS32(&test.ctx, 2);
|
||||
|
||||
const std::string payload = "isolation test";
|
||||
const uint32_t bufAddr = GUEST_BUFFER_AREA_START;
|
||||
std::memcpy(test.rdram.data() + bufAddr, payload.data(), payload.size());
|
||||
|
||||
setRegU32(test.ctx, 4, static_cast<uint32_t>(fd));
|
||||
setRegU32(test.ctx, 5, bufAddr);
|
||||
setRegU32(test.ctx, 6, static_cast<uint32_t>(payload.size()));
|
||||
fioWrite(test.rdram.data(), &test.ctx, nullptr);
|
||||
|
||||
setRegU32(test.ctx, 4, static_cast<uint32_t>(fd));
|
||||
fioClose(test.rdram.data(), &test.ctx, nullptr);
|
||||
|
||||
// Verify isolation
|
||||
const std::filesystem::path expectedMc =
|
||||
test.paths.mcRoot / "ISOLATED" / "test.txt";
|
||||
const std::filesystem::path unexpectedCd =
|
||||
test.paths.cdRoot / "ISOLATED" / "test.txt";
|
||||
|
||||
t.IsTrue(std::filesystem::exists(expectedMc),
|
||||
"mc0: file should exist under mcRoot");
|
||||
t.IsFalse(std::filesystem::exists(unexpectedCd),
|
||||
"mc0: file should NOT exist under cdRoot");
|
||||
|
||||
// Verify mcRoot directory structure
|
||||
t.IsTrue(std::filesystem::exists(test.paths.mcRoot / "ISOLATED"),
|
||||
"mc0: directory should exist under mcRoot");
|
||||
t.IsFalse(std::filesystem::exists(test.paths.cdRoot / "ISOLATED"),
|
||||
"mc0: directory should NOT exist under cdRoot");
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user