AnyPS5/core/libs/tests/GuestFiopen.cpp

163 lines
5.7 KiB
C++

#include "prx/libc/include/FileStream.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <string>
extern "C" {
FileStream* APS5_VABI _ZSt7_FiopenPKcNSt5_IosbIiE9_OpenmodeEi_nid_postfix(const char*, int, int);
FileStream* APS5_VABI fopen_nid_postfix(const char*, const char*);
int APS5_VABI fopen_s_nid_postfix(FileStream**, const char*, const char*);
int APS5_VABI fclose_nid_postfix(FileStream*);
std::int64_t APS5_VABI ftello_nid_postfix(FileStream*);
}
static void Check(bool value, int line) {
if (!value) {
std::fprintf(stderr, "Fiopen check failed at line %d\n", line);
std::abort();
}
}
#define Require(value) Check((value), __LINE__)
constexpr int In = 0x01, Out = 0x02, Ate = 0x04, App = 0x08, Trunc = 0x10, Nocreate = 0x20, Noreplace = 0x40,
Binary = 0x80;
constexpr int GuestEinval = 22;
constexpr int GuestEnoent = 2;
static FileStream* Open(const char* name, int mode) {
return _ZSt7_FiopenPKcNSt5_IosbIiE9_OpenmodeEi_nid_postfix(name, mode, 0x1b6);
}
static std::string Contents(const char* name) {
auto* stream = fopen_nid_postfix(name, "rb");
Require(stream != nullptr);
std::string text;
for (int c; (c = std::fgetc(stream->GetHandle())) != EOF;) text += static_cast<char>(c);
fclose_nid_postfix(stream);
return text;
}
static void Write(FileStream* stream, const char* text) {
Require(stream != nullptr);
Require(std::fputs(text, stream->GetHandle()) >= 0);
fclose_nid_postfix(stream);
}
static bool Exists(const char* name) {
auto* stream = fopen_nid_postfix(name, "r");
if (stream) fclose_nid_postfix(stream);
return stream != nullptr;
}
int main() {
const std::filesystem::path dir = "guest_fiopen_dir";
std::filesystem::remove_all(dir);
std::filesystem::create_directory(dir);
const char* file = "guest_fiopen_dir/a";
const char* missing = "guest_fiopen_dir/missing";
for (int mode : {0, Ate, Trunc, Binary, Nocreate, Noreplace, In | Trunc, In | Trunc | Binary, Out | App | Trunc,
In | Out | App | Trunc, Trunc | Binary, App | Trunc}) {
Require(Open(missing, mode) == nullptr);
Require(!Exists(missing));
}
Write(Open(file, Out), "abc");
Require(Contents(file) == "abc");
FileStream* checkedStream = &_Stdout_nid_postfix;
Require(fopen_s_nid_postfix(&checkedStream, nullptr, "rb") == GuestEinval);
Require(checkedStream == nullptr);
checkedStream = &_Stderr_nid_postfix;
Require(fopen_s_nid_postfix(&checkedStream, file, nullptr) == GuestEinval);
Require(checkedStream == nullptr);
Require(fopen_s_nid_postfix(nullptr, file, "rb") == GuestEinval);
checkedStream = &_Stdout_nid_postfix;
Require(fopen_s_nid_postfix(&checkedStream, file, "rb") == 0);
Require(checkedStream != nullptr);
Require(fclose_nid_postfix(checkedStream) == 0);
checkedStream = &_Stdout_nid_postfix;
Require(fopen_s_nid_postfix(&checkedStream, missing, "rb") == GuestEnoent);
Require(checkedStream == nullptr);
auto* stream = Open(file, In);
Require(stream != nullptr && std::fgetc(stream->GetHandle()) == 'a');
fclose_nid_postfix(stream);
stream = Open(file, In);
Require(stream != nullptr);
std::fputc('x', stream->GetHandle());
fclose_nid_postfix(stream);
Require(Contents(file) == "abc");
Write(Open(file, App), "d");
Require(Contents(file) == "abcd");
Write(Open(file, Out | App | Binary), "e");
Require(Contents(file) == "abcde");
stream = Open(file, In | Ate);
Require(stream != nullptr && ftello_nid_postfix(stream) == 5);
fclose_nid_postfix(stream);
stream = Open(file, In | Binary);
Require(stream != nullptr && ftello_nid_postfix(stream) == 0);
fclose_nid_postfix(stream);
Require(Open(file, Out | Noreplace) == nullptr);
Require(Open(file, In | Out | Binary | Noreplace) == nullptr);
Require(Open(file, App | Noreplace) == nullptr);
Require(Contents(file) == "abcde");
stream = Open(file, In | Noreplace);
Require(stream != nullptr);
fclose_nid_postfix(stream);
Require(Open(missing, Out | Nocreate) == nullptr);
Require(!Exists(missing));
Write(Open(file, Out | Nocreate), "X");
Require(Contents(file) == "Xbcde");
stream = Open(file, Out | Nocreate | Binary | Ate);
Require(stream != nullptr && ftello_nid_postfix(stream) == 5);
Write(stream, "f");
Require(Contents(file) == "Xbcdef");
stream = Open(file, In | Out);
Require(stream != nullptr && std::fgetc(stream->GetHandle()) == 'X');
fclose_nid_postfix(stream);
Require(Contents(file) == "Xbcdef");
Write(Open(file, In | Out | App), "g");
Require(Contents(file) == "Xbcdefg");
Write(Open(file, In | Out | Trunc | Binary), "h");
Require(Contents(file) == "h");
Write(Open(file, Out | Trunc), "ij");
Require(Contents(file) == "ij");
Write(Open(file, Out | Binary), "k");
Require(Contents(file) == "k");
Write(Open(missing, Out | Noreplace), "new");
Require(Contents(missing) == "new");
Write(Open(missing, Out | Trunc | Nocreate), "w+");
Require(Contents(missing) == "w+");
std::filesystem::remove(dir / "missing");
Write(Open(missing, Out | Trunc | Nocreate), "created");
Require(Contents(missing) == "created");
stream = Open(file, In | 0x100 | 0x8000);
Require(stream != nullptr && std::fgetc(stream->GetHandle()) == 'k');
fclose_nid_postfix(stream);
#ifndef _WIN32
auto* full = std::fopen("/dev/full", "wb");
Require(full != nullptr);
auto* fullStream = new FileStream(full, true);
Require(std::fwrite("buffered", 1, 8, full) == 8);
Require(fclose_nid_postfix(fullStream) == EOF);
#endif
std::filesystem::remove_all(dir);
return 0;
}