From e0f1f4cfafb8ceadc8989e5e6def73595ebfd81a Mon Sep 17 00:00:00 2001 From: RDW Date: Sat, 21 Mar 2026 23:52:50 +0100 Subject: [PATCH 1/2] Win32: Implement platform APIs to manage file handles No point in adding read/write operations as the arenas don't yet support doing what needs to happen. Will add that shortly (POC already complete, still needs some more cleaning up to merge). --- Core/Assertions.hpp | 7 ++- Core/Platforms/Linux.hpp | 1 + Core/Platforms/MacOS.hpp | 1 + Core/Platforms/Win32.hpp | 121 ++++++++++++++++++++++++++++++++++++++- Core/RagLite2.hpp | 11 +++- 5 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 Core/Platforms/Linux.hpp create mode 100644 Core/Platforms/MacOS.hpp diff --git a/Core/Assertions.hpp b/Core/Assertions.hpp index de5e0a24..d8f83b55 100644 --- a/Core/Assertions.hpp +++ b/Core/Assertions.hpp @@ -11,4 +11,9 @@ #define NOOP ((void)0) #define ASSUME(condition, failureMessage) NOOP -#endif \ No newline at end of file +#endif + +#define EXPAND_AS_STRING(x) #x +#define TOSTRING(x) EXPAND_AS_STRING(x) + +#define FROM_HERE __FILE__ ":" TOSTRING(__LINE__) \ No newline at end of file diff --git a/Core/Platforms/Linux.hpp b/Core/Platforms/Linux.hpp new file mode 100644 index 00000000..06e7d48e --- /dev/null +++ b/Core/Platforms/Linux.hpp @@ -0,0 +1 @@ +// PLACEHOLDER \ No newline at end of file diff --git a/Core/Platforms/MacOS.hpp b/Core/Platforms/MacOS.hpp new file mode 100644 index 00000000..06e7d48e --- /dev/null +++ b/Core/Platforms/MacOS.hpp @@ -0,0 +1 @@ +// PLACEHOLDER \ No newline at end of file diff --git a/Core/Platforms/Win32.hpp b/Core/Platforms/Win32.hpp index d3e4bb21..d344bd83 100644 --- a/Core/Platforms/Win32.hpp +++ b/Core/Platforms/Win32.hpp @@ -6,4 +6,123 @@ #include #include #include -#include \ No newline at end of file +#include + +typedef struct { + DWORD accessPermissions; + DWORD creationDisposition; + DWORD sharingMode; + DWORD extraFlags; +} platform_policy_t; + +typedef struct { + const char* message; + const char* source; + uint32_t code; +} platform_error_t; + +typedef struct { + platform_error_t errorDetails; + // NOTE: For portability, could store void* descriptor but then need to assert that sizeof(void*) == sizeof(HANDLE) + // NOTE: Since the platform runtime will already know OS-specific types and each platform should have its own, meh + HANDLE handle; + +#ifdef RAGLITE_DEBUG_ANNOTATIONS + platform_policy_t creationPolicy; +#endif +} platform_handle_t; + +GLOBAL platform_error_t PLATFORM_ERROR_NONE = { + .message = "OK", + .source = FROM_HERE, + .code = ERROR_SUCCESS, +}; + +INTERNAL inline void PlatformSetFileError(platform_handle_t& fileHandle, const char* message, const char* sourceLocation) { + // TODO: Get platform error message via FormatErrorString (needs a new API that pushes the error string to an arena) + fileHandle.errorDetails = { + .message = message, + .source = sourceLocation, + .code = GetLastError() + }; +} + +INTERNAL inline const char* PlatformGetFileError(platform_handle_t& fileHandle) { + // TODO: Should support arena parameter that the error string can be pushed onto (then move to utility layer?) + // NOTE: For now, just returns the hardcoded error message so that there is at least some information - if not ideal + return fileHandle.errorDetails.message; // Cannot concatenate without string building/arenas - fix this up later +} + +INTERNAL inline bool PlatformNoFileErrors(platform_handle_t& fileHandle) { + // NOTE: It's unlikely this code would change, but relying on zeroized structs to always match it still seems wrong + return fileHandle.errorDetails.code == PLATFORM_ERROR_NONE.code; +} + +INTERNAL inline bool PlatformIsValidFileHandle(platform_handle_t& fileHandle) { + return fileHandle.handle != INVALID_HANDLE_VALUE && fileHandle.handle != NULL; +} + +INTERNAL inline platform_policy_t PlatformPolicyReadOnly() { + platform_policy_t policy = { + .accessPermissions = GENERIC_READ, + .creationDisposition = OPEN_EXISTING, + .sharingMode = FILE_SHARE_READ, + .extraFlags = FILE_ATTRIBUTE_NORMAL, + }; + return policy; +} + +// NOTE: These probably aren't quite right - will have to revisit later when writing/deleting files is actually needed +INTERNAL inline platform_policy_t PlatformPolicyReadWrite() { + platform_policy_t policy = { + .accessPermissions = GENERIC_READ | GENERIC_WRITE, + .creationDisposition = OPEN_ALWAYS, + .sharingMode = FILE_SHARE_READ | FILE_SHARE_DELETE, + .extraFlags = FILE_ATTRIBUTE_NORMAL, + }; + return policy; +} + +INTERNAL platform_handle_t PlatformOpenFileHandle(const char* fileSystemPath, platform_policy_t modePreset) { + platform_handle_t fileHandle = {}; + + DWORD accessPermissions = modePreset.accessPermissions; + DWORD creationDisposition = modePreset.creationDisposition; + DWORD sharingMode = modePreset.sharingMode; + DWORD attributeFlags = modePreset.extraFlags; + HANDLE templateFile = NULL; + +#ifdef RAGLITE_DEBUG_ANNOTATIONS + fileHandle.creationPolicy = modePreset; +#endif + + LPSECURITY_ATTRIBUTES securityAttributes = NULL; // Don't care (for now) + // TODO: Should use CreateFileW and convert from UTF8 to UTF16, but that requires scratch space and testing -> later + fileHandle.handle = CreateFileA(fileSystemPath, accessPermissions, sharingMode, securityAttributes, creationDisposition, attributeFlags, templateFile); + + if(PlatformIsValidFileHandle(fileHandle)) fileHandle.errorDetails = PLATFORM_ERROR_NONE; + else PlatformSetFileError(fileHandle, "CreateFile returned INVALID_HANDLE_VALUE", FROM_HERE); + + return fileHandle; +} + +INTERNAL void PlatformCloseFileHandle(platform_handle_t& fileHandle) { + if(!PlatformIsValidFileHandle(fileHandle)) return; + + HANDLE handle = fileHandle.handle; + CloseHandle(handle); + fileHandle.handle = INVALID_HANDLE_VALUE; +} + +INTERNAL size_t PlatformGetFileSize(platform_handle_t& fileHandle) { + if(!PlatformIsValidFileHandle(fileHandle)) return 0; + + LARGE_INTEGER fileSize; + BOOL success = GetFileSizeEx(fileHandle.handle, &fileSize); + if(!success) { + PlatformSetFileError(fileHandle, "GetFileSizeEx returned FALSE", FROM_HERE); + return 0; + } + + return (size_t)fileSize.QuadPart; +} \ No newline at end of file diff --git a/Core/RagLite2.hpp b/Core/RagLite2.hpp index 8524dd1a..005a77e4 100644 --- a/Core/RagLite2.hpp +++ b/Core/RagLite2.hpp @@ -36,6 +36,7 @@ #ifdef NDEBUG #define RAGLITE_DEFAULT_APP DummyTest #else +#define RAGLITE_DEBUG_ANNOTATIONS #define RAGLITE_DEBUG_ASSERTIONS #define RAGLITE_DEFAULT_APP PatternTest #define RAGLITE_PREDICTABLE_MEMORY @@ -82,4 +83,12 @@ typedef struct gamepad_controller_state { typedef struct volatile_simulation_state { int32 offsetX; int32 offsetY; -} simulation_state_t; \ No newline at end of file +} simulation_state_t; + +#ifdef RAGLITE_PLATFORM_WINDOWS +#include "Platforms/Win32.hpp" +#elifdef RAGLITE_PLATFORM_MACOS +#include "Platforms/MacOS.hpp" +#elifdef RAGLITE_PLATFORM_LINUX +#include "Platforms/Linux.hpp" +#endif From 40ca3f9c5e1c34cdabb32718c464d4db4de42e5d Mon Sep 17 00:00:00 2001 From: RDW Date: Sun, 22 Mar 2026 14:00:57 +0100 Subject: [PATCH 2/2] Tools: Enable the file formats CLI to query input file sizes Not super useful, but it doesn't require memory arenas or any other platform APIs. Deferring dealing with STDIN and STDOUT streams as that'll be more complex and not as useful overall. Same for hashing or buffered reading; will need to review the platform API and MSDN docs. There probably isn't one design that works across the board without causing headaches at some point, so I won't even try to design one here. --- Tools/RagnarokTools.cpp | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/Tools/RagnarokTools.cpp b/Tools/RagnarokTools.cpp index 8c44e60b..8dcfcce8 100644 --- a/Tools/RagnarokTools.cpp +++ b/Tools/RagnarokTools.cpp @@ -4,8 +4,7 @@ #define GLOBAL static #define INTERNAL static -// TODO: Use the exported types from the platform API (unfinished/WIP) -typedef void* platform_handle_t; +#include "../Core/RagLite2.hpp" // TODO: Compute this automatically (requires a bit of annoying boilerplate, but it's not too difficult) GLOBAL const char* THIS_EXECUTABLE = "RagnarokTools.exe"; @@ -133,15 +132,15 @@ typedef struct { } opcode_list_t; INTERNAL void PlaceholderNotYetImplemented(roff_request_t requestDetails, platform_handle_t inputFileHandle, platform_handle_t outputFileHandle) { - printf("[DISPATCH] Using platform-specific input file handle: 0x%p\n", inputFileHandle); - printf("[DISPATCH] Using platform-specific output file handle: 0x%p\n", outputFileHandle); + printf("[DISPATCH] Using platform-specific input file handle: 0x%p\n", &inputFileHandle); + printf("[DISPATCH] Using platform-specific output file handle: 0x%p\n", &outputFileHandle); printf("[NYI] Alas... It is with great sorrow that I must inform you: This feature has not been implemented yet\n"); printf("[NYI] Some day, the missing functionality may indeed be available - but, clearly, today is not that day\n"); printf("[NYI] In due time, the patient shall see this great work come to fruition (or implement it yourself maybe)\n"); - if(!inputFileHandle) fprintf(stderr, "[DISPATCH] Request aborted: Cannot read from an invalid OS file handle\n"); - if(!outputFileHandle) fprintf(stderr, "[DISPATCH] Request aborted: Cannot write to an invalid OS file handle\n"); + if(!PlatformNoFileErrors(inputFileHandle)) fprintf(stderr, "[DISPATCH] Request aborted: Cannot read from an invalid OS file handle\n"); + if(!PlatformNoFileErrors(outputFileHandle)) fprintf(stderr, "[DISPATCH] Request aborted: Cannot write to an invalid OS file handle\n"); } INTERNAL void DisplayFormatInfo(roff_request_t requestDetails, platform_handle_t inputFileHandle, platform_handle_t outputFileHandle) { @@ -157,7 +156,7 @@ INTERNAL void DisplayFormatInfo(roff_request_t requestDetails, platform_handle_t // TODO: It would probably be better to display (only) the supported operations for this format here // TODO: Might be useful to print some file metadata here (size, hash, maybe even the header?) - printf("[NYI] A more useful file description for OS handle 0x%p should eventually appear here\n", inputFileHandle); + printf("[NYI] A more useful file description for OS handle 0x%p should eventually appear here\n", &inputFileHandle); } INTERNAL opcode_list_t GetSupportedFormatOperations(roff_format_t fileFormat) { @@ -325,16 +324,24 @@ int main(size_t argCount, const char** arguments) { // TODO: Preallocate a temporary memory arena for the format-specific decoders here - // TODO: Open platform handle to input file or stream - platform_handle_t inputFileHandle = NULL; + platform_handle_t inputFileHandle = {}; if(!requestDetails.inputSource) { printf("[NYI] Reading from STDIN isn't currently supported, but should be very soon (... famous last words?)\n"); + // TODO: Open platform handle to stdin } else { - printf("[NYI] Reading files isn't currently supported, but should be very soon (... famous last words?)\n"); + inputFileHandle = PlatformOpenFileHandle(requestDetails.inputSource, PlatformPolicyReadOnly()); + if(!PlatformNoFileErrors(inputFileHandle)) { + fprintf(stderr, "Failed to open %s (platform reported error: %s)\n", requestDetails.inputSource, PlatformGetFileError(inputFileHandle)); + fprintf(stderr, "Make sure the file exists in the working directory and is readable by this process\n"); + return 1; + } + size_t fileSize = PlatformGetFileSize(inputFileHandle); + printf("[NYI] Reading file contents from %s (%zd bytes)\n", requestDetails.inputSource, fileSize); + PlatformCloseFileHandle(inputFileHandle); } // TODO: Open platform handle to output file or stream - platform_handle_t outputFileHandle = NULL; + platform_handle_t outputFileHandle = {}; if(!requestDetails.outputDestination) { printf("[NYI] Writing to STDOUT isn't currently supported, but should be very soon (... famous last words?)\n"); } else {