Application Context Implementation #4
| @ -27,7 +27,7 @@ fn run(app: *sys.AppContext, graphics: *sys.GraphicsContext) anyerror!void { | |||||||
|     defer _ = gpa.deinit(); |     defer _ = gpa.deinit(); | ||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
|         const file_access = try (try app.data().joinedPath(&.{"ona.lua"})).open(.readonly); |         var file_access = try (try app.data().joinedPath(&.{"ona.lua"})).open(.readonly); | ||||||
| 
 | 
 | ||||||
|         defer file_access.close(); |         defer file_access.close(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										38
									
								
								src/oar.zig
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								src/oar.zig
									
									
									
									
									
								
							| @ -9,36 +9,34 @@ const sys = @import("./sys.zig"); | |||||||
| pub const Entry = extern struct { | pub const Entry = extern struct { | ||||||
|     signature: [3]u8 = signature_magic, |     signature: [3]u8 = signature_magic, | ||||||
|     revision: u8, |     revision: u8, | ||||||
|     name_length: u8 = 0, |  | ||||||
|     name_buffer: [255]u8 = std.mem.zeroes([255]u8), |     name_buffer: [255]u8 = std.mem.zeroes([255]u8), | ||||||
|  |     name_length: u8 = 0, | ||||||
|     file_size: u64, |     file_size: u64, | ||||||
|     padding: [244]u8 = std.mem.zeroes([244]u8), |     file_offset: u64, | ||||||
|  |     padding: [232]u8 = std.mem.zeroes([232]u8), | ||||||
| 
 | 
 | ||||||
|     /// |     comptime { | ||||||
|     /// Returns `true` if `entry` correctly identifies itself as a valid Oar entry, otherwise |         const entry_size = @sizeOf(Entry); | ||||||
|     /// `false`. | 
 | ||||||
|     /// |         if (entry_size != 512) | ||||||
|     pub fn isValid(entry: Entry) bool { |             @compileError("Entry is " ++ std.fmt.comptimePrint("{d}", .{entry_size}) ++ " bytes"); | ||||||
|         return std.mem.eql(u8, &entry.signature, signature_magic[0 ..]); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// |     /// | ||||||
|     /// Attempts to read an [Entry] from `file_access` at its current cursor position. |  | ||||||
|     /// |     /// | ||||||
|     /// Returns the read [Entry] value or `null` if the end of file is reached before completing the |  | ||||||
|     /// read. |  | ||||||
|     /// |     /// | ||||||
|     pub fn read(file_access: *sys.FileAccess) sys.FileAccess.Error!?Entry { |     pub const FindError = sys.FileAccess.Error || error { | ||||||
|         var entry = std.mem.zeroes(Entry); |         EntryNotFound, | ||||||
|         const origin = try file_access.queryCursor(); |     }; | ||||||
| 
 | 
 | ||||||
|         if ((try file_access.read(std.mem.asBytes(&entry))) != @sizeOf(Entry)) { |     /// | ||||||
|             try file_access.seek(origin); |     /// | ||||||
|  |     /// | ||||||
|  |     pub fn find(file_access: sys.FileAccess, entry_name: []const u8) FindError!Entry { | ||||||
|  |         _ = file_access; | ||||||
|  |         _ = entry_name; | ||||||
| 
 | 
 | ||||||
|             return null; |         return error.EntryNotFound; | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return entry; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// |     /// | ||||||
|  | |||||||
							
								
								
									
										334
									
								
								src/sys.zig
									
									
									
									
									
								
							
							
						
						
									
										334
									
								
								src/sys.zig
									
									
									
									
									
								
							| @ -128,19 +128,19 @@ pub const AppContext = opaque { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /// |         /// | ||||||
|         /// Initializes a new [Implemenation] with `data_archive_path` as the read-only data archive |         /// Initializes a new [Implemenation] with `data_archive_file_access` as the data archive to | ||||||
|         /// to read from and `user_path_prefix` as the native writable user data directory. |         /// read from and `user_path_prefix` as the native writable user data directory. | ||||||
|         /// |         /// | ||||||
|         /// Returns the created [Implementation] value on success or [InitError] on failure. |         /// Returns the created [Implementation] value on success or [InitError] on failure. | ||||||
|         /// |         /// | ||||||
|         fn init(data_archive_path: []const u8, |         fn init(data_archive_file_access: FileAccess, | ||||||
|             user_path_prefix: []const u8) InitError!Implementation { |             user_path_prefix: []const u8) InitError!Implementation { | ||||||
| 
 | 
 | ||||||
|             return Implementation{ |             return Implementation{ | ||||||
|                 .message_semaphore = ext.SDL_CreateSemaphore(0) orelse return error.OutOfSemaphores, |                 .message_semaphore = ext.SDL_CreateSemaphore(0) orelse return error.OutOfSemaphores, | ||||||
|                 .message_mutex = ext.SDL_CreateMutex() orelse return error.OutOfMutexes, |                 .message_mutex = ext.SDL_CreateMutex() orelse return error.OutOfMutexes, | ||||||
|                 .data_file_system = .{.archive = data_archive_path}, |                 .data_file_system = .{.archive = .{.file_access = data_archive_file_access}}, | ||||||
|                 .user_file_system = .{.native = user_path_prefix}, |                 .user_file_system = .{.native = .{.path_prefix = user_path_prefix}}, | ||||||
|                 .message_thread = null, |                 .message_thread = null, | ||||||
|             }; |             }; | ||||||
|         } |         } | ||||||
| @ -204,7 +204,7 @@ pub const AppContext = opaque { | |||||||
|     /// |     /// | ||||||
|     /// Returns a reference to the currently loaded data file-system. |     /// Returns a reference to the currently loaded data file-system. | ||||||
|     /// |     /// | ||||||
|     pub fn data(app_context: *AppContext) *const FileSystem { |     pub fn data(app_context: *AppContext) *FileSystem { | ||||||
|         return &Implementation.cast(app_context).data_file_system; |         return &Implementation.cast(app_context).data_file_system; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -245,7 +245,7 @@ pub const AppContext = opaque { | |||||||
|     /// |     /// | ||||||
|     /// Returns a reference to the currently loaded user file-system. |     /// Returns a reference to the currently loaded user file-system. | ||||||
|     /// |     /// | ||||||
|     pub fn user(app_context: *AppContext) *const FileSystem { |     pub fn user(app_context: *AppContext) *FileSystem { | ||||||
|         return &Implementation.cast(app_context).user_file_system; |         return &Implementation.cast(app_context).user_file_system; | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| @ -253,7 +253,22 @@ pub const AppContext = opaque { | |||||||
| /// | /// | ||||||
| /// File-system agnostic abstraction for manipulating a file. | /// File-system agnostic abstraction for manipulating a file. | ||||||
| /// | /// | ||||||
| pub const FileAccess = opaque { | pub const FileAccess = struct { | ||||||
|  |     context: *anyopaque, | ||||||
|  |     implementation: *const Implementation, | ||||||
|  | 
 | ||||||
|  |     /// | ||||||
|  |     /// Provides a set of implementation-specific behaviors to a [FileAccess] instance. | ||||||
|  |     /// | ||||||
|  |     pub const Implementation = struct { | ||||||
|  |         close: fn (*anyopaque) void, | ||||||
|  |         queryCursor: fn (*anyopaque) Error!u64, | ||||||
|  |         queryLength: fn (*anyopaque) Error!u64, | ||||||
|  |         read: fn (*anyopaque, []u8) Error!usize, | ||||||
|  |         seek: fn (*anyopaque, u64) Error!void, | ||||||
|  |         seekToEnd: fn (*anyopaque) Error!void, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|     /// |     /// | ||||||
|     /// [Error.FileInaccessible] is a generic catch-all for a [FileAccess] reference no longer |     /// [Error.FileInaccessible] is a generic catch-all for a [FileAccess] reference no longer | ||||||
|     /// pointing to a file or the file becomming invalid for whatever reason. |     /// pointing to a file or the file becomming invalid for whatever reason. | ||||||
| @ -262,13 +277,6 @@ pub const FileAccess = opaque { | |||||||
|         FileInaccessible, |         FileInaccessible, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     /// |  | ||||||
|     /// Returns `file_access` casted to a [ext.SDL_RWops]. |  | ||||||
|     /// |  | ||||||
|     fn asRwOps(file_access: *FileAccess) *ext.SDL_RWops { |  | ||||||
|         return @ptrCast(*ext.SDL_RWops, @alignCast(@alignOf(ext.SDL_RWops), file_access)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /// |     /// | ||||||
|     /// Close the file referenced by `file_access` on the main thread, invalidating the reference to |     /// Close the file referenced by `file_access` on the main thread, invalidating the reference to | ||||||
|     /// it and releasing any associated resources. |     /// it and releasing any associated resources. | ||||||
| @ -277,8 +285,7 @@ pub const FileAccess = opaque { | |||||||
|     /// wasted effort. |     /// wasted effort. | ||||||
|     /// |     /// | ||||||
|     pub fn close(file_access: *FileAccess) void { |     pub fn close(file_access: *FileAccess) void { | ||||||
|         if (ext.SDL_RWclose(file_access.asRwOps()) != 0) |         return file_access.implementation.close(file_access.context); | ||||||
|             ext.SDL_LogWarn(ext.SDL_LOG_CATEGORY_APPLICATION, "Closed an invalid file reference"); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// |     /// | ||||||
| @ -288,13 +295,7 @@ pub const FileAccess = opaque { | |||||||
|     /// [Error] on failure. |     /// [Error] on failure. | ||||||
|     /// |     /// | ||||||
|     pub fn queryCursor(file_access: *FileAccess) Error!u64 { |     pub fn queryCursor(file_access: *FileAccess) Error!u64 { | ||||||
|         ext.SDL_ClearError(); |         return file_access.implementation.queryCursor(file_access.context); | ||||||
| 
 |  | ||||||
|         const sought = ext.SDL_RWtell(file_access.asRwOps()); |  | ||||||
| 
 |  | ||||||
|         if (sought < 0) return error.FileInaccessible; |  | ||||||
| 
 |  | ||||||
|         return @intCast(u64, sought); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// |     /// | ||||||
| @ -304,13 +305,7 @@ pub const FileAccess = opaque { | |||||||
|     /// failed to be queried. |     /// failed to be queried. | ||||||
|     /// |     /// | ||||||
|     pub fn queryLength(file_access: *FileAccess) Error!u64 { |     pub fn queryLength(file_access: *FileAccess) Error!u64 { | ||||||
|         ext.SDL_ClearError(); |         return file_access.implementation.queryLength(file_access.context); | ||||||
| 
 |  | ||||||
|         const sought = ext.SDL_RWsize(file_access.asRwOps()); |  | ||||||
| 
 |  | ||||||
|         if (sought < 0) return error.FileInaccessible; |  | ||||||
| 
 |  | ||||||
|         return @intCast(u64, sought); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// |     /// | ||||||
| @ -319,14 +314,7 @@ pub const FileAccess = opaque { | |||||||
|     /// Returns the number of bytes that were available to be read, otherwise an [Error] on failure. |     /// Returns the number of bytes that were available to be read, otherwise an [Error] on failure. | ||||||
|     /// |     /// | ||||||
|     pub fn read(file_access: *FileAccess, buffer: []u8) Error!usize { |     pub fn read(file_access: *FileAccess, buffer: []u8) Error!usize { | ||||||
|         ext.SDL_ClearError(); |         return file_access.implementation.read(file_access.context, buffer); | ||||||
| 
 |  | ||||||
|         const buffer_read = |  | ||||||
|             ext.SDL_RWread(file_access.asRwOps(), buffer.ptr, @sizeOf(u8), buffer.len); |  | ||||||
| 
 |  | ||||||
|         if ((buffer_read == 0) and (ext.SDL_GetError() != null)) return error.FileInaccessible; |  | ||||||
| 
 |  | ||||||
|         return buffer_read; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// |     /// | ||||||
| @ -335,19 +323,7 @@ pub const FileAccess = opaque { | |||||||
|     /// Returns [Error] on failure. |     /// Returns [Error] on failure. | ||||||
|     /// |     /// | ||||||
|     pub fn seek(file_access: *FileAccess, cursor: u64) Error!void { |     pub fn seek(file_access: *FileAccess, cursor: u64) Error!void { | ||||||
|         var to_seek = cursor; |         return file_access.implementation.seek(file_access.context, cursor); | ||||||
| 
 |  | ||||||
|         while (to_seek != 0) { |  | ||||||
|             const sought = @intCast(i64, std.math.min(to_seek, std.math.maxInt(i64))); |  | ||||||
| 
 |  | ||||||
|             ext.SDL_ClearError(); |  | ||||||
| 
 |  | ||||||
|             if (ext.SDL_RWseek(file_access.asRwOps(), sought, ext.RW_SEEK_CUR) < 0) |  | ||||||
|                 return error.FileInaccessible; |  | ||||||
| 
 |  | ||||||
|             // Cannot be less than zero because it is derived from `read`. |  | ||||||
|             to_seek -= @intCast(u64, sought); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// |     /// | ||||||
| @ -357,10 +333,7 @@ pub const FileAccess = opaque { | |||||||
|     /// Returns [Error] on failure. |     /// Returns [Error] on failure. | ||||||
|     /// |     /// | ||||||
|     pub fn seekToEnd(file_access: *FileAccess) Error!void { |     pub fn seekToEnd(file_access: *FileAccess) Error!void { | ||||||
|         ext.SDL_ClearError(); |         return file_access.implementation.seekToEnd(file_access.context); | ||||||
| 
 |  | ||||||
|         if (ext.SDL_RWseek(file_access.asRwOps(), 0, ext.RW_SEEK_END) < 0) |  | ||||||
|             return error.FileInaccessible; |  | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -369,14 +342,33 @@ pub const FileAccess = opaque { | |||||||
| /// available to the application in a sandboxed environment. | /// available to the application in a sandboxed environment. | ||||||
| /// | /// | ||||||
| pub const FileSystem = union(enum) { | pub const FileSystem = union(enum) { | ||||||
|     native: []const u8, |     native: struct { | ||||||
|     archive: []const u8, |         path_prefix: []const u8, | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     archive: struct { | ||||||
|  |         file_access: FileAccess, | ||||||
|  | 
 | ||||||
|  |         entry_table: [max_open_entries]ArchiveEntry = | ||||||
|  |             std.mem.zeroes([max_open_entries]ArchiveEntry), | ||||||
|  | 
 | ||||||
|  |         const max_open_entries = 16; | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     /// | ||||||
|  |     /// Handles the state of an opened archive entry. | ||||||
|  |     /// | ||||||
|  |     const ArchiveEntry = struct { | ||||||
|  |         using: ?*FileAccess, | ||||||
|  |         header: oar.Entry, | ||||||
|  |         cursor: u64, | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|     /// |     /// | ||||||
|     /// Platform-agnostic mechanism for referencing files and directories on a [FileSystem]. |     /// Platform-agnostic mechanism for referencing files and directories on a [FileSystem]. | ||||||
|     /// |     /// | ||||||
|     pub const Path = struct { |     pub const Path = struct { | ||||||
|         file_system: *const FileSystem, |         file_system: *FileSystem, | ||||||
|         length: u8, |         length: u8, | ||||||
|         buffer: [max]u8, |         buffer: [max]u8, | ||||||
| 
 | 
 | ||||||
| @ -390,9 +382,13 @@ pub const FileSystem = union(enum) { | |||||||
|         /// should be tried or, if no mode other is suitable, that the resource is effectively |         /// should be tried or, if no mode other is suitable, that the resource is effectively | ||||||
|         /// unavailable. |         /// unavailable. | ||||||
|         /// |         /// | ||||||
|  |         /// If the number of known [FileAccess] handles has been exhausted, [OpenError.OutOfFiles] | ||||||
|  |         /// is used to communicate this. | ||||||
|  |         /// | ||||||
|         pub const OpenError = error { |         pub const OpenError = error { | ||||||
|             FileNotFound, |             FileNotFound, | ||||||
|             ModeUnsupported, |             ModeUnsupported, | ||||||
|  |             OutOfFiles, | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         /// |         /// | ||||||
| @ -442,59 +438,205 @@ pub const FileSystem = union(enum) { | |||||||
|         /// Returns a [FileAccess] reference that provides access to the file referenced by `path` |         /// Returns a [FileAccess] reference that provides access to the file referenced by `path` | ||||||
|         /// or a [OpenError] if it failed. |         /// or a [OpenError] if it failed. | ||||||
|         /// |         /// | ||||||
|         pub fn open(path: Path, mode: OpenMode) OpenError!*FileAccess { |         pub fn open(path: Path, mode: OpenMode) OpenError!FileAccess { | ||||||
|             switch (path.file_system.*) { |             switch (path.file_system.*) { | ||||||
|                 .archive => |archive| { |                 .archive => |*archive| { | ||||||
|                     if (archive.len == 0) return error.FileNotFound; |  | ||||||
| 
 |  | ||||||
|                     if (mode != .readonly) return error.ModeUnsupported; |                     if (mode != .readonly) return error.ModeUnsupported; | ||||||
| 
 | 
 | ||||||
|                     var path_buffer = std.mem.zeroes([4096]u8); |                     for (archive.entry_table) |_, index| { | ||||||
|  |                         if (archive.entry_table[index].using == null) { | ||||||
|  |                             archive.entry_table[index] = .{ | ||||||
|  |                                 .header = oar.Entry.find(archive.file_access, path. | ||||||
|  |                                     buffer[0 .. path.length]) catch return error.FileNotFound, | ||||||
| 
 | 
 | ||||||
|                     if (archive.len >= path_buffer.len) return error.FileNotFound; |                                 .using = &archive.file_access, | ||||||
|  |                                 .cursor = 0, | ||||||
|  |                             }; | ||||||
| 
 | 
 | ||||||
|                     std.mem.copy(u8, path_buffer[0 ..], archive); |                             const Implementation = struct { | ||||||
|  |                                 fn archiveEntryCast(context: *anyopaque) *ArchiveEntry { | ||||||
|  |                                     return @ptrCast(*ArchiveEntry, @alignCast( | ||||||
|  |                                         @alignOf(ArchiveEntry), context)); | ||||||
|  |                                 } | ||||||
| 
 | 
 | ||||||
|                     const file_access = @ptrCast(*FileAccess, ext.SDL_RWFromFile( |                                 fn close(context: *anyopaque) void { | ||||||
|                         &path_buffer, "rb") orelse return error.FileNotFound); |                                     archiveEntryCast(context).using = null; | ||||||
|  |                                 } | ||||||
| 
 | 
 | ||||||
|                     while (oar.Entry.read(file_access) catch return error.FileNotFound) |entry| { |                                 fn queryCursor(context: *anyopaque) FileAccess.Error!u64 { | ||||||
|                         if (!entry.isValid()) break; |                                     const archive_entry = archiveEntryCast(context); | ||||||
| 
 | 
 | ||||||
|                         if (std.mem.eql(u8, entry.name_buffer[0 .. entry.name_length], |                                     if (archive_entry.using == null) return error.FileInaccessible; | ||||||
|                             path.buffer[0 .. path.length])) return file_access; |  | ||||||
| 
 | 
 | ||||||
|                         file_access.seek(entry.file_size) catch break; |                                     return archive_entry.cursor; | ||||||
|  |                                 } | ||||||
|  | 
 | ||||||
|  |                                 fn queryLength(context: *anyopaque) FileAccess.Error!u64 { | ||||||
|  |                                     const archive_entry = archiveEntryCast(context); | ||||||
|  | 
 | ||||||
|  |                                     if (archive_entry.using == null) return error.FileInaccessible; | ||||||
|  | 
 | ||||||
|  |                                     return archive_entry.header.file_size; | ||||||
|  |                                 } | ||||||
|  | 
 | ||||||
|  |                                 fn read(context: *anyopaque, buffer: []u8) FileAccess.Error!usize { | ||||||
|  |                                     const archive_entry = archiveEntryCast(context); | ||||||
|  | 
 | ||||||
|  |                                     const file_access = archive_entry.using orelse | ||||||
|  |                                         return error.FileInaccessible; | ||||||
|  | 
 | ||||||
|  |                                     if (archive_entry.cursor >= archive_entry.header.file_size) | ||||||
|  |                                         return error.FileInaccessible; | ||||||
|  | 
 | ||||||
|  |                                     try file_access.seek(archive_entry.header.file_offset); | ||||||
|  | 
 | ||||||
|  |                                     return file_access.read(buffer[0 .. std.math.min( | ||||||
|  |                                         buffer.len, archive_entry.header.file_size)]); | ||||||
|  |                                 } | ||||||
|  | 
 | ||||||
|  |                                 fn seek(context: *anyopaque, cursor: usize) FileAccess.Error!void { | ||||||
|  |                                     const archive_entry = archiveEntryCast(context); | ||||||
|  | 
 | ||||||
|  |                                     if (archive_entry.using == null) return error.FileInaccessible; | ||||||
|  | 
 | ||||||
|  |                                     archive_entry.cursor = cursor; | ||||||
|  |                                 } | ||||||
|  | 
 | ||||||
|  |                                 fn seekToEnd(context: *anyopaque) FileAccess.Error!void { | ||||||
|  |                                     const archive_entry = archiveEntryCast(context); | ||||||
|  | 
 | ||||||
|  |                                     if (archive_entry.using == null) return error.FileInaccessible; | ||||||
|  | 
 | ||||||
|  |                                     archive_entry.cursor = archive_entry.header.file_size; | ||||||
|  |                                 } | ||||||
|  |                             }; | ||||||
|  | 
 | ||||||
|  |                             return FileAccess{ | ||||||
|  |                                 .context = &archive.entry_table[index], | ||||||
|  | 
 | ||||||
|  |                                 .implementation = &.{ | ||||||
|  |                                     .close = Implementation.close, | ||||||
|  |                                     .queryCursor = Implementation.queryCursor, | ||||||
|  |                                     .queryLength = Implementation.queryLength, | ||||||
|  |                                     .read = Implementation.read, | ||||||
|  |                                     .seek = Implementation.seek, | ||||||
|  |                                     .seekToEnd = Implementation.seekToEnd, | ||||||
|  |                                 }, | ||||||
|  |                             }; | ||||||
|  |                         } | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                     return error.FileNotFound; |                     return error.OutOfFiles; | ||||||
|                 }, |                 }, | ||||||
| 
 | 
 | ||||||
|                 .native => |native| { |                 .native => |native| { | ||||||
|                     if (native.len == 0) return error.FileNotFound; |                     if (native.path_prefix.len == 0) return error.FileNotFound; | ||||||
| 
 | 
 | ||||||
|                     var path_buffer = std.mem.zeroes([4096]u8); |                     var path_buffer = std.mem.zeroes([4096]u8); | ||||||
|                     const seperator = '/'; |                     const seperator = '/'; | ||||||
|                     const seperator_length = @boolToInt(native[native.len - 1] != seperator); |  | ||||||
| 
 | 
 | ||||||
|                     if ((native.len + seperator_length + path.length) >= path_buffer.len) |                     const seperator_length = @boolToInt(native.path_prefix[ | ||||||
|                         return error.FileNotFound; |                         native.path_prefix.len - 1] != seperator); | ||||||
| 
 | 
 | ||||||
|                     std.mem.copy(u8, path_buffer[0 ..], native); |                     if ((native.path_prefix.len + seperator_length + path.length) >= | ||||||
|  |                         path_buffer.len) return error.FileNotFound; | ||||||
|  | 
 | ||||||
|  |                     std.mem.copy(u8, path_buffer[0 ..], native.path_prefix); | ||||||
| 
 | 
 | ||||||
|                     if (seperator_length != 0) |                     if (seperator_length != 0) | ||||||
|                         path_buffer[native.len] = seperator; |                         path_buffer[native.path_prefix.len] = seperator; | ||||||
| 
 | 
 | ||||||
|                     std.mem.copy(u8, path_buffer[native.len .. path_buffer.len], |                     std.mem.copy(u8, path_buffer[native.path_prefix.len .. | ||||||
|                         path.buffer[0 .. path.length]); |                         path_buffer.len], path.buffer[0 .. path.length]); | ||||||
| 
 | 
 | ||||||
|                     ext.SDL_ClearError(); |                     ext.SDL_ClearError(); | ||||||
| 
 | 
 | ||||||
|                     return @ptrCast(*FileAccess, ext.SDL_RWFromFile(&path_buffer, switch (mode) { |                     const Implementation = struct { | ||||||
|                         .readonly => "rb", |                         fn rwOpsCast(context: *anyopaque) *ext.SDL_RWops { | ||||||
|                         .overwrite => "wb", |                             return @ptrCast(*ext.SDL_RWops, @alignCast( | ||||||
|                         .append => "ab", |                                 @alignOf(ext.SDL_RWops), context)); | ||||||
|                     }) orelse return error.FileNotFound); |                         } | ||||||
|  | 
 | ||||||
|  |                         fn close(context: *anyopaque) void { | ||||||
|  |                             ext.SDL_ClearError(); | ||||||
|  | 
 | ||||||
|  |                             if (ext.SDL_RWclose(rwOpsCast(context)) != 0) | ||||||
|  |                                 ext.SDL_LogWarn(ext.SDL_LOG_CATEGORY_APPLICATION, ext.SDL_GetError()); | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         fn queryCursor(context: *anyopaque) FileAccess.Error!u64 { | ||||||
|  |                             ext.SDL_ClearError(); | ||||||
|  | 
 | ||||||
|  |                             const sought = ext.SDL_RWtell(rwOpsCast(context)); | ||||||
|  | 
 | ||||||
|  |                             if (sought < 0) return error.FileInaccessible; | ||||||
|  | 
 | ||||||
|  |                             return @intCast(u64, sought); | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         fn queryLength(context: *anyopaque) FileAccess.Error!u64 { | ||||||
|  |                             ext.SDL_ClearError(); | ||||||
|  | 
 | ||||||
|  |                             const sought = ext.SDL_RWsize(rwOpsCast(context)); | ||||||
|  | 
 | ||||||
|  |                             if (sought < 0) return error.FileInaccessible; | ||||||
|  | 
 | ||||||
|  |                             return @intCast(u64, sought); | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         fn read(context: *anyopaque, buffer: []u8) FileAccess.Error!usize { | ||||||
|  |                             ext.SDL_ClearError(); | ||||||
|  | 
 | ||||||
|  |                             const buffer_read = ext.SDL_RWread(rwOpsCast( | ||||||
|  |                                 context), buffer.ptr, @sizeOf(u8), buffer.len); | ||||||
|  | 
 | ||||||
|  |                             if ((buffer_read == 0) and (ext.SDL_GetError() != null)) | ||||||
|  |                                 return error.FileInaccessible; | ||||||
|  | 
 | ||||||
|  |                             return buffer_read; | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         fn seek(context: *anyopaque, cursor: usize) FileAccess.Error!void { | ||||||
|  |                             var to_seek = cursor; | ||||||
|  | 
 | ||||||
|  |                             while (to_seek != 0) { | ||||||
|  |                                 const math = std.math; | ||||||
|  |                                 const sought = @intCast(i64, math.min(to_seek, math.maxInt(i64))); | ||||||
|  | 
 | ||||||
|  |                                 ext.SDL_ClearError(); | ||||||
|  | 
 | ||||||
|  |                                 if (ext.SDL_RWseek(rwOpsCast(context), sought, ext.RW_SEEK_CUR) < 0) | ||||||
|  |                                     return error.FileInaccessible; | ||||||
|  | 
 | ||||||
|  |                                 // Cannot be less than zero because it is derived from `read`. | ||||||
|  |                                 to_seek -= @intCast(u64, sought); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         fn seekToEnd(context: *anyopaque) FileAccess.Error!void { | ||||||
|  |                             ext.SDL_ClearError(); | ||||||
|  | 
 | ||||||
|  |                             if (ext.SDL_RWseek(rwOpsCast(context), 0, ext.RW_SEEK_END) < 0) | ||||||
|  |                                 return error.FileInaccessible; | ||||||
|  |                         } | ||||||
|  |                     }; | ||||||
|  | 
 | ||||||
|  |                     return FileAccess{ | ||||||
|  |                         .context = ext.SDL_RWFromFile(&path_buffer, switch (mode) { | ||||||
|  |                             .readonly => "rb", | ||||||
|  |                             .overwrite => "wb", | ||||||
|  |                             .append => "ab", | ||||||
|  |                         }) orelse return error.FileNotFound, | ||||||
|  | 
 | ||||||
|  |                         .implementation = &.{ | ||||||
|  |                             .close = Implementation.close, | ||||||
|  |                             .queryCursor = Implementation.queryCursor, | ||||||
|  |                             .queryLength = Implementation.queryLength, | ||||||
|  |                             .read = Implementation.read, | ||||||
|  |                             .seek = Implementation.seek, | ||||||
|  |                             .seekToEnd = Implementation.seekToEnd, | ||||||
|  |                         }, | ||||||
|  |                     }; | ||||||
|                 }, |                 }, | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @ -515,9 +657,7 @@ pub const FileSystem = union(enum) { | |||||||
|     /// A [Path] value is returned containing the fully qualified path from the file-system root or |     /// A [Path] value is returned containing the fully qualified path from the file-system root or | ||||||
|     /// a [PathError] if it could not be created. |     /// a [PathError] if it could not be created. | ||||||
|     /// |     /// | ||||||
|     pub fn joinedPath(file_system: *const FileSystem, |     pub fn joinedPath(file_system: *FileSystem, sequences: []const []const u8) PathError!Path { | ||||||
|         sequences: []const []const u8) PathError!Path { |  | ||||||
| 
 |  | ||||||
|         var path = Path{ |         var path = Path{ | ||||||
|             .file_system = file_system, |             .file_system = file_system, | ||||||
|             .buffer = std.mem.zeroes([Path.max]u8), |             .buffer = std.mem.zeroes([Path.max]u8), | ||||||
| @ -644,8 +784,7 @@ pub const RunError = error { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// | /// | ||||||
| /// Runs a graphical application referenced by `run` with `error` as its error set and `allocator` | /// Runs a graphical application referenced by `run` with `error` as its error set. | ||||||
| /// as the underlying memory allocation strategy for its graphical runtime. |  | ||||||
| /// | /// | ||||||
| /// Should an error from `run` occur, an `Error` is returned, otherwise a [RunError] is returned if | /// Should an error from `run` occur, an `Error` is returned, otherwise a [RunError] is returned if | ||||||
| /// the underlying runtime fails and is logged. | /// the underlying runtime fails and is logged. | ||||||
| @ -694,8 +833,15 @@ pub fn runGraphics(comptime Error: anytype, | |||||||
| 
 | 
 | ||||||
|     defer ext.SDL_free(user_path_prefix); |     defer ext.SDL_free(user_path_prefix); | ||||||
| 
 | 
 | ||||||
|     var app_context = AppContext.Implementation.init("./data.oar", user_path_prefix |     var cwd_file_system = FileSystem{.native =.{.path_prefix = "./"}}; | ||||||
|         [0 .. std.mem.len(user_path_prefix)]) catch |err| { | 
 | ||||||
|  |     var data_archive_file_access = try (try cwd_file_system. | ||||||
|  |         joinedPath(&.{"./data.oar"})).open(.readonly); | ||||||
|  | 
 | ||||||
|  |     defer data_archive_file_access.close(); | ||||||
|  | 
 | ||||||
|  |     var app_context = AppContext.Implementation.init(data_archive_file_access, | ||||||
|  |         user_path_prefix[0 .. std.mem.len(user_path_prefix)]) catch |err| { | ||||||
| 
 | 
 | ||||||
|         ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION, switch (err) { |         ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION, switch (err) { | ||||||
|             error.OutOfMemory => "Failed to allocate necessary memory", |             error.OutOfMemory => "Failed to allocate necessary memory", | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user