Implement Oar file reading logic in archive file system
This commit is contained in:
		
							parent
							
								
									287a054d22
								
							
						
					
					
						commit
						961e79200d
					
				| @ -27,7 +27,7 @@ fn run(app: *sys.AppContext, graphics: *sys.GraphicsContext) anyerror!void { | ||||
|     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(); | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										38
									
								
								src/oar.zig
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								src/oar.zig
									
									
									
									
									
								
							| @ -9,36 +9,34 @@ const sys = @import("./sys.zig"); | ||||
| pub const Entry = extern struct { | ||||
|     signature: [3]u8 = signature_magic, | ||||
|     revision: u8, | ||||
|     name_length: u8 = 0, | ||||
|     name_buffer: [255]u8 = std.mem.zeroes([255]u8), | ||||
|     name_length: u8 = 0, | ||||
|     file_size: u64, | ||||
|     padding: [244]u8 = std.mem.zeroes([244]u8), | ||||
|     file_offset: u64, | ||||
|     padding: [232]u8 = std.mem.zeroes([232]u8), | ||||
| 
 | ||||
|     /// | ||||
|     /// Returns `true` if `entry` correctly identifies itself as a valid Oar entry, otherwise | ||||
|     /// `false`. | ||||
|     /// | ||||
|     pub fn isValid(entry: Entry) bool { | ||||
|         return std.mem.eql(u8, &entry.signature, signature_magic[0 ..]); | ||||
|     comptime { | ||||
|         const entry_size = @sizeOf(Entry); | ||||
| 
 | ||||
|         if (entry_size != 512) | ||||
|             @compileError("Entry is " ++ std.fmt.comptimePrint("{d}", .{entry_size}) ++ " bytes"); | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
|     /// 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 { | ||||
|         var entry = std.mem.zeroes(Entry); | ||||
|         const origin = try file_access.queryCursor(); | ||||
|     pub const FindError = sys.FileAccess.Error || error { | ||||
|         EntryNotFound, | ||||
|     }; | ||||
| 
 | ||||
|         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 entry; | ||||
|         return error.EntryNotFound; | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
|  | ||||
							
								
								
									
										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 | ||||
|         /// to read from and `user_path_prefix` as the native writable user data directory. | ||||
|         /// Initializes a new [Implemenation] with `data_archive_file_access` as the data archive to | ||||
|         /// read from and `user_path_prefix` as the native writable user data directory. | ||||
|         /// | ||||
|         /// 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 { | ||||
| 
 | ||||
|             return Implementation{ | ||||
|                 .message_semaphore = ext.SDL_CreateSemaphore(0) orelse return error.OutOfSemaphores, | ||||
|                 .message_mutex = ext.SDL_CreateMutex() orelse return error.OutOfMutexes, | ||||
|                 .data_file_system = .{.archive = data_archive_path}, | ||||
|                 .user_file_system = .{.native = user_path_prefix}, | ||||
|                 .data_file_system = .{.archive = .{.file_access = data_archive_file_access}}, | ||||
|                 .user_file_system = .{.native = .{.path_prefix = user_path_prefix}}, | ||||
|                 .message_thread = null, | ||||
|             }; | ||||
|         } | ||||
| @ -204,7 +204,7 @@ pub const AppContext = opaque { | ||||
|     /// | ||||
|     /// 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; | ||||
|     } | ||||
| 
 | ||||
| @ -245,7 +245,7 @@ pub const AppContext = opaque { | ||||
|     /// | ||||
|     /// 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; | ||||
|     } | ||||
| }; | ||||
| @ -253,7 +253,22 @@ pub const AppContext = opaque { | ||||
| /// | ||||
| /// 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 | ||||
|     /// pointing to a file or the file becomming invalid for whatever reason. | ||||
| @ -262,13 +277,6 @@ pub const FileAccess = opaque { | ||||
|         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 | ||||
|     /// it and releasing any associated resources. | ||||
| @ -277,8 +285,7 @@ pub const FileAccess = opaque { | ||||
|     /// wasted effort. | ||||
|     /// | ||||
|     pub fn close(file_access: *FileAccess) void { | ||||
|         if (ext.SDL_RWclose(file_access.asRwOps()) != 0) | ||||
|             ext.SDL_LogWarn(ext.SDL_LOG_CATEGORY_APPLICATION, "Closed an invalid file reference"); | ||||
|         return file_access.implementation.close(file_access.context); | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
| @ -288,13 +295,7 @@ pub const FileAccess = opaque { | ||||
|     /// [Error] on failure. | ||||
|     /// | ||||
|     pub fn queryCursor(file_access: *FileAccess) Error!u64 { | ||||
|         ext.SDL_ClearError(); | ||||
| 
 | ||||
|         const sought = ext.SDL_RWtell(file_access.asRwOps()); | ||||
| 
 | ||||
|         if (sought < 0) return error.FileInaccessible; | ||||
| 
 | ||||
|         return @intCast(u64, sought); | ||||
|         return file_access.implementation.queryCursor(file_access.context); | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
| @ -304,13 +305,7 @@ pub const FileAccess = opaque { | ||||
|     /// failed to be queried. | ||||
|     /// | ||||
|     pub fn queryLength(file_access: *FileAccess) Error!u64 { | ||||
|         ext.SDL_ClearError(); | ||||
| 
 | ||||
|         const sought = ext.SDL_RWsize(file_access.asRwOps()); | ||||
| 
 | ||||
|         if (sought < 0) return error.FileInaccessible; | ||||
| 
 | ||||
|         return @intCast(u64, sought); | ||||
|         return file_access.implementation.queryLength(file_access.context); | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
| @ -319,14 +314,7 @@ pub const FileAccess = opaque { | ||||
|     /// 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 { | ||||
|         ext.SDL_ClearError(); | ||||
| 
 | ||||
|         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; | ||||
|         return file_access.implementation.read(file_access.context, buffer); | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
| @ -335,19 +323,7 @@ pub const FileAccess = opaque { | ||||
|     /// Returns [Error] on failure. | ||||
|     /// | ||||
|     pub fn seek(file_access: *FileAccess, cursor: u64) Error!void { | ||||
|         var to_seek = 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); | ||||
|         } | ||||
|         return file_access.implementation.seek(file_access.context, cursor); | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
| @ -357,10 +333,7 @@ pub const FileAccess = opaque { | ||||
|     /// Returns [Error] on failure. | ||||
|     /// | ||||
|     pub fn seekToEnd(file_access: *FileAccess) Error!void { | ||||
|         ext.SDL_ClearError(); | ||||
| 
 | ||||
|         if (ext.SDL_RWseek(file_access.asRwOps(), 0, ext.RW_SEEK_END) < 0) | ||||
|             return error.FileInaccessible; | ||||
|         return file_access.implementation.seekToEnd(file_access.context); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| @ -369,14 +342,33 @@ pub const FileAccess = opaque { | ||||
| /// available to the application in a sandboxed environment. | ||||
| /// | ||||
| pub const FileSystem = union(enum) { | ||||
|     native: []const u8, | ||||
|     archive: []const u8, | ||||
|     native: struct { | ||||
|         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]. | ||||
|     /// | ||||
|     pub const Path = struct { | ||||
|         file_system: *const FileSystem, | ||||
|         file_system: *FileSystem, | ||||
|         length: 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 | ||||
|         /// unavailable. | ||||
|         /// | ||||
|         /// If the number of known [FileAccess] handles has been exhausted, [OpenError.OutOfFiles] | ||||
|         /// is used to communicate this. | ||||
|         /// | ||||
|         pub const OpenError = error { | ||||
|             FileNotFound, | ||||
|             ModeUnsupported, | ||||
|             OutOfFiles, | ||||
|         }; | ||||
| 
 | ||||
|         /// | ||||
| @ -442,59 +438,205 @@ pub const FileSystem = union(enum) { | ||||
|         /// Returns a [FileAccess] reference that provides access to the file referenced by `path` | ||||
|         /// 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.*) { | ||||
|                 .archive => |archive| { | ||||
|                     if (archive.len == 0) return error.FileNotFound; | ||||
| 
 | ||||
|                 .archive => |*archive| { | ||||
|                     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( | ||||
|                         &path_buffer, "rb") orelse return error.FileNotFound); | ||||
|                                 fn close(context: *anyopaque) void { | ||||
|                                     archiveEntryCast(context).using = null; | ||||
|                                 } | ||||
| 
 | ||||
|                     while (oar.Entry.read(file_access) catch return error.FileNotFound) |entry| { | ||||
|                         if (!entry.isValid()) break; | ||||
|                                 fn queryCursor(context: *anyopaque) FileAccess.Error!u64 { | ||||
|                                     const archive_entry = archiveEntryCast(context); | ||||
| 
 | ||||
|                         if (std.mem.eql(u8, entry.name_buffer[0 .. entry.name_length], | ||||
|                             path.buffer[0 .. path.length])) return file_access; | ||||
|                                     if (archive_entry.using == null) return error.FileInaccessible; | ||||
| 
 | ||||
|                         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| { | ||||
|                     if (native.len == 0) return error.FileNotFound; | ||||
|                     if (native.path_prefix.len == 0) return error.FileNotFound; | ||||
| 
 | ||||
|                     var path_buffer = std.mem.zeroes([4096]u8); | ||||
|                     const seperator = '/'; | ||||
|                     const seperator_length = @boolToInt(native[native.len - 1] != seperator); | ||||
| 
 | ||||
|                     if ((native.len + seperator_length + path.length) >= path_buffer.len) | ||||
|                         return error.FileNotFound; | ||||
|                     const seperator_length = @boolToInt(native.path_prefix[ | ||||
|                         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) | ||||
|                         path_buffer[native.len] = seperator; | ||||
|                         path_buffer[native.path_prefix.len] = seperator; | ||||
| 
 | ||||
|                     std.mem.copy(u8, path_buffer[native.len .. path_buffer.len], | ||||
|                         path.buffer[0 .. path.length]); | ||||
|                     std.mem.copy(u8, path_buffer[native.path_prefix.len .. | ||||
|                         path_buffer.len], path.buffer[0 .. path.length]); | ||||
| 
 | ||||
|                     ext.SDL_ClearError(); | ||||
| 
 | ||||
|                     return @ptrCast(*FileAccess, ext.SDL_RWFromFile(&path_buffer, switch (mode) { | ||||
|                         .readonly => "rb", | ||||
|                         .overwrite => "wb", | ||||
|                         .append => "ab", | ||||
|                     }) orelse return error.FileNotFound); | ||||
|                     const Implementation = struct { | ||||
|                         fn rwOpsCast(context: *anyopaque) *ext.SDL_RWops { | ||||
|                             return @ptrCast(*ext.SDL_RWops, @alignCast( | ||||
|                                 @alignOf(ext.SDL_RWops), context)); | ||||
|                         } | ||||
| 
 | ||||
|                         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 [PathError] if it could not be created. | ||||
|     /// | ||||
|     pub fn joinedPath(file_system: *const FileSystem, | ||||
|         sequences: []const []const u8) PathError!Path { | ||||
| 
 | ||||
|     pub fn joinedPath(file_system: *FileSystem, sequences: []const []const u8) PathError!Path { | ||||
|         var path = Path{ | ||||
|             .file_system = file_system, | ||||
|             .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` | ||||
| /// as the underlying memory allocation strategy for its graphical runtime. | ||||
| /// Runs a graphical application referenced by `run` with `error` as its error set. | ||||
| /// | ||||
| /// Should an error from `run` occur, an `Error` is returned, otherwise a [RunError] is returned if | ||||
| /// the underlying runtime fails and is logged. | ||||
| @ -694,8 +833,15 @@ pub fn runGraphics(comptime Error: anytype, | ||||
| 
 | ||||
|     defer ext.SDL_free(user_path_prefix); | ||||
| 
 | ||||
|     var app_context = AppContext.Implementation.init("./data.oar", user_path_prefix | ||||
|         [0 .. std.mem.len(user_path_prefix)]) catch |err| { | ||||
|     var cwd_file_system = FileSystem{.native =.{.path_prefix = "./"}}; | ||||
| 
 | ||||
|     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) { | ||||
|             error.OutOfMemory => "Failed to allocate necessary memory", | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user