feat(libc): implement guest formatted stream output

This commit is contained in:
SamG-Coder 2026-09-25 06:35:32 +10:00
parent b8c1097db4
commit f49cace1ca
3 changed files with 84 additions and 0 deletions

View File

@ -4,6 +4,7 @@
#include <cstdarg>
#include "SceTypes.hpp"
#include "prx/libc/include/VarArgsAbi.hpp"
#include "prx/libc/include/FileStream.hpp"
#ifdef _WIN32
#include "prx/libc/include/WindowsFormatting.hpp"
@ -11,6 +12,37 @@
extern "C" {
int APS5_VABI vfprintf_nid_postfix(FileStream* stream, const char* format, VaList* args) {
auto* native = GetNativeStream(stream);
#ifdef _WIN32
std::string buffer;
const int count = LibcDetail::FormatWindows(nullptr, 0, format, args, &buffer);
const int result = std::fwrite(buffer.data(), 1, static_cast<size_t>(count), native) ==
static_cast<size_t>(count) ? count : -1;
#else
const int result = std::vfprintf(native, format, *reinterpret_cast<std::va_list*>(args));
#endif
stream->SyncStatus();
return result;
}
int APS5_VABI fprintf_nid_postfix(FileStream* stream, const char* format, ...) {
#ifdef _WIN32
__builtin_sysv_va_list args;
__builtin_sysv_va_start(args, format);
#else
std::va_list args;
va_start(args, format);
#endif
const int result = vfprintf_nid_postfix(stream, format, reinterpret_cast<VaList*>(args));
#ifdef _WIN32
__builtin_sysv_va_end(args);
#else
va_end(args);
#endif
return result;
}
#ifdef _WIN32
int APS5_VABI printf_nid_postfix(const char* format, ...) {

View File

@ -2,11 +2,15 @@
#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstdlib>
#include <cstring>
#include <cstdarg>
#include <cstdint>
extern "C" {
extern FileStream* __stdinp_nid_postfix;
extern FileStream* __stdoutp_nid_postfix;
extern FileStream* __stderrp_nid_postfix;
extern int __isthreaded_nid_postfix;
int APS5_VABI fprintf_nid_postfix(FileStream*, const char*, ...);
int APS5_VABI vfprintf_nid_postfix(FileStream*, const char*, void*);
int APS5_VABI fgetc_nid_postfix(FileStream*);
int APS5_VABI fputc_nid_postfix(int, FileStream*);
int APS5_VABI __srget_nid_postfix(FileStream*);
@ -19,6 +23,22 @@ void APS5_VABI clearerr_nid_postfix(FileStream*);
int APS5_VABI setvbuf_nid_postfix(FileStream*, char*, int, std::size_t);
}
static void Require(bool value) { if (!value) std::abort(); }
static int APS5_VABI WriteFormatted(FileStream* stream, const char* format, ...) {
#ifdef _WIN32
__builtin_sysv_va_list args;
__builtin_sysv_va_start(args, format);
#else
std::va_list args;
va_start(args, format);
#endif
const int result = vfprintf_nid_postfix(stream, format, args);
#ifdef _WIN32
__builtin_sysv_va_end(args);
#else
va_end(args);
#endif
return result;
}
int main() {
Require(__isthreaded_nid_postfix == 1);
Require(__stdoutp_nid_postfix == &_Stdout_nid_postfix);
@ -46,4 +66,17 @@ int main() {
Require(!feof_nid_postfix(&stream) && !(guest.flags & 0x20));
stream.Close();
Require(guest.flags == 0 && guest.descriptor == -1);
FileStream formatted(std::tmpfile());
const char expected[] = "guest 4294967297 1.25 1 2 3 4 5 6 7 8\n";
Require(fprintf_nid_postfix(&formatted, "%s %ld %.2f %d %d %d %d %d %d %d %d\n",
"guest", std::int64_t{4294967297}, 1.25, 1, 2, 3, 4, 5, 6, 7, 8) == sizeof(expected) - 1);
Require(WriteFormatted(&formatted, "%*.*f:%s", 6, 2, 3.5, "end") == 10);
std::rewind(formatted.GetHandle());
char output[128]{};
Require(fgets_nid_postfix(output, sizeof(output), &formatted) == output);
Require(std::strcmp(output, expected) == 0);
Require(fgets_nid_postfix(output, sizeof(output), &formatted) == output);
Require(std::strcmp(output, " 3.50:end") == 0);
formatted.Close();
}

View File

@ -217,3 +217,22 @@ and closure. All fourteen suites pass and the compatibility libraries rebuild.
Latest startup resolves the stream globals and stops at `8nY19bKoiZk` (`fcntl`).
The game has not reached its entry point or displayed a frame.
## Follow-up: descriptor flags and formatted stream output
Added fcntl F_GETFL/F_SETFL for tracked UDP sockets. Nonblocking state is shared
with FIONBIO ioctl and protected against concurrent mode updates. Unsupported
flags are rejected; ordinary file descriptors and other fcntl commands are not
implemented. The UDP test checks both APIs agree, mode transitions, and unchanged
state after rejected flags.
Added fprintf and vfprintf using the guest FileStream wrapper and the existing
SysV variadic formatter on Windows. Tests write and read back mixed strings,
64-bit guest long integers, floating-point values, register-overflow arguments,
and dynamic width/precision through an explicit guest va_list. Write failure
returns a negative result and refreshes the guest stream status.
All fourteen CTest suites pass and patched compatibility libraries rebuild.
The unchanged converted game now resolves fcntl and fprintf, and startup stops
at `smbQukfxYJM` (`getenv`). This remains an import-resolution result: guest
entry-point execution and a rendered frame are still unverified.