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 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 {