Application Context Implementation #4
							
								
								
									
										10
									
								
								src/main.zig
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								src/main.zig
									
									
									
									
									
								
							| @ -27,20 +27,20 @@ fn run(app: *sys.AppContext, graphics: *sys.GraphicsContext) anyerror!void { | ||||
|     defer _ = gpa.deinit(); | ||||
| 
 | ||||
|     { | ||||
|         const file_access = try (try app.data().joinedPath(&.{"ona.lua"})).open(app, .readonly); | ||||
|         const file_access = try (try app.data().joinedPath(&.{"ona.lua"})).open(.readonly); | ||||
| 
 | ||||
|         defer file_access.close(app); | ||||
|         defer file_access.close(); | ||||
| 
 | ||||
|         const file_size = try file_access.queryLength(app); | ||||
|         const file_size = try file_access.queryLength(); | ||||
|         const allocator = gpa.allocator(); | ||||
|         const buffer = try allocator.alloc(u8, file_size); | ||||
| 
 | ||||
|         defer allocator.free(buffer); | ||||
| 
 | ||||
|         if ((try file_access.read(app, buffer)) != file_size) | ||||
|         if ((try file_access.read(buffer)) != file_size) | ||||
|             return error.ScriptLoadFailure; | ||||
| 
 | ||||
|         sys.Log.debug.write(app, buffer); | ||||
|         sys.Log.debug.write(buffer); | ||||
|     } | ||||
| 
 | ||||
|     while (graphics.poll()) |_| { | ||||
|  | ||||
							
								
								
									
										10
									
								
								src/meta.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/meta.zig
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| /// | ||||
| /// Returns the return type of the function type `Fn`. | ||||
| /// | ||||
| pub fn FnReturn(comptime Fn: type) type { | ||||
|     const type_info = @typeInfo(Fn); | ||||
| 
 | ||||
|     if (type_info != .Fn) @compileError("`Fn` must be a function type"); | ||||
| 
 | ||||
|     return type_info.Fn.return_type orelse void; | ||||
| } | ||||
							
								
								
									
										590
									
								
								src/sys.zig
									
									
									
									
									
								
							
							
						
						
									
										590
									
								
								src/sys.zig
									
									
									
									
									
								
							| @ -5,6 +5,7 @@ const ext = @cImport({ | ||||
| const io = @import("./io.zig"); | ||||
| const math = @import("./math.zig"); | ||||
| const mem = @import("./mem.zig"); | ||||
| const meta = @import("./meta.zig"); | ||||
| const oar = @import("./oar.zig"); | ||||
| const stack = @import("./stack.zig"); | ||||
| const std = @import("std"); | ||||
| @ -18,7 +19,6 @@ pub const AppContext = opaque { | ||||
|     /// | ||||
|     const Message = struct { | ||||
|         next: ?*Message = null, | ||||
|         frame: anyframe, | ||||
| 
 | ||||
|         kind: union(enum) { | ||||
|             quit, | ||||
| @ -26,6 +26,7 @@ pub const AppContext = opaque { | ||||
|             task: struct { | ||||
|                 data: *anyopaque, | ||||
|                 action: fn (*anyopaque) void, | ||||
|                 frame: anyframe, | ||||
|             }, | ||||
|         }, | ||||
|     }; | ||||
| @ -85,12 +86,9 @@ pub const AppContext = opaque { | ||||
|         /// processes quit and waits for them to do so before freeing any resources. | ||||
|         /// | ||||
|         fn deinit(implementation: *Implementation) void { | ||||
|             var message = Message{ | ||||
|                 .frame = @frame(), | ||||
|                 .kind = .quit, | ||||
|             }; | ||||
|             var message = Message{.kind = .quit}; | ||||
| 
 | ||||
|             @ptrCast(*AppContext, implementation).schedule(&message); | ||||
|             implementation.enqueue(&message); | ||||
| 
 | ||||
|             { | ||||
|                 var status = @as(c_int, 0); | ||||
| @ -108,13 +106,37 @@ pub const AppContext = opaque { | ||||
|             ext.SDL_DestroySemaphore(implementation.message_semaphore); | ||||
|         } | ||||
| 
 | ||||
|         /// | ||||
|         /// Enqueues `message` to the message processor of `implementation` to be processed at a | ||||
|         /// later, non-deterministic point in time. | ||||
|         /// | ||||
|         fn enqueue(implementation: *Implementation, message: *Message) void { | ||||
|             { | ||||
|                 // TODO: Error check these. | ||||
|                 _ = ext.SDL_LockMutex(implementation.message_mutex); | ||||
| 
 | ||||
|                 defer _ = ext.SDL_UnlockMutex(implementation.message_mutex); | ||||
| 
 | ||||
|                 if (implementation.messages) |messages| { | ||||
|                     messages.next = message; | ||||
|                 } else { | ||||
|                     implementation.messages = message; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             // TODO: Error check this. | ||||
|             _ = ext.SDL_SemPost(implementation.message_semaphore); | ||||
|         } | ||||
| 
 | ||||
|         /// | ||||
|         /// 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. | ||||
|         /// | ||||
|         /// Returns the created [Implementation] value on success or [InitError] on failure. | ||||
|         /// | ||||
|         fn init(data_archive_path: []const u8, user_path_prefix: []const u8) InitError!Implementation { | ||||
|         fn init(data_archive_path: []const u8, | ||||
|             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, | ||||
| @ -132,26 +154,31 @@ pub const AppContext = opaque { | ||||
|         /// occured. | ||||
|         /// | ||||
|         fn processTasks(userdata: ?*anyopaque) callconv(.C) c_int { | ||||
|             const implementation = Implementation.cast(@ptrCast(*AppContext, userdata orelse unreachable)); | ||||
|             const implementation = Implementation.cast( | ||||
|                 @ptrCast(*AppContext, userdata orelse unreachable)); | ||||
| 
 | ||||
|             while (true) { | ||||
|                 // TODO: Error check these. | ||||
|                 _ = ext.SDL_SemWait(implementation.message_semaphore); | ||||
|                 _ = ext.SDL_LockMutex(implementation.message_mutex); | ||||
| 
 | ||||
|                 defer _ = ext.SDL_UnlockMutex(implementation.message_mutex); | ||||
| 
 | ||||
|                 while (implementation.messages) |messages| { | ||||
|                     switch (messages.kind) { | ||||
|                         .quit => return 0, | ||||
|                         .task => |task| task.action(task.data), | ||||
|                     } | ||||
|                         .quit => { | ||||
|                             return 0; | ||||
|                         }, | ||||
| 
 | ||||
|                     resume messages.frame; | ||||
|                         .task => |task| { | ||||
|                             task.action(task.data); | ||||
| 
 | ||||
|                             resume task.frame; | ||||
|                         }, | ||||
|                     } | ||||
| 
 | ||||
|                     implementation.messages = messages.next; | ||||
|                 } | ||||
| 
 | ||||
|                 // TODO: Error check this. | ||||
|                 _ = ext.SDL_SemWait(implementation.message_semaphore); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| @ -183,25 +210,37 @@ pub const AppContext = opaque { | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
|     /// Enqueues `message` to the message processor of `app_context` to be processed at a later, non- | ||||
|     /// deterministic point. | ||||
|     /// | ||||
|     pub fn schedule(app_context: *AppContext, message: *Message) void { | ||||
|         const implementation = Implementation.cast(app_context); | ||||
|     /// | ||||
|     pub fn schedule(app_context: *AppContext, procedure: anytype, arguments: anytype) meta.FnReturn(@TypeOf(procedure)) { | ||||
|         const Task = struct { | ||||
|             procedure: @TypeOf(procedure), | ||||
|             arguments: *@TypeOf(arguments), | ||||
|             result: meta.FnReturn(@TypeOf(procedure)), | ||||
| 
 | ||||
|         // TODO: Error check these. | ||||
|         _ = ext.SDL_LockMutex(implementation.message_mutex); | ||||
|             const Task = @This(); | ||||
| 
 | ||||
|         defer _ = ext.SDL_UnlockMutex(implementation.message_mutex); | ||||
|             fn process(userdata: *anyopaque) void { | ||||
|                 const task = @ptrCast(*Task, @alignCast(@alignOf(Task), userdata)); | ||||
| 
 | ||||
|         if (implementation.messages) |messages| { | ||||
|             messages.next = message; | ||||
|         } else { | ||||
|             implementation.messages = message; | ||||
|         } | ||||
|                 task.result = @call(.{}, task.procedure, task.arguments.*); | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         // TODO: Error check this. | ||||
|         _ = ext.SDL_SemPost(implementation.message_semaphore); | ||||
|         var task = Task{ | ||||
|             .procedure = procedure, | ||||
|             .arguments = &arguments, | ||||
|         }; | ||||
| 
 | ||||
|         var message = AppContext.Message{ | ||||
|             .kind = .{.task = .{ | ||||
|                 .data = &task, | ||||
|                 .action = Task.process, | ||||
|                 .frame = @frame(), | ||||
|             }}, | ||||
|         }; | ||||
| 
 | ||||
|         suspend Implementation.cast(app_context).enqueue(&message); | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
| @ -232,39 +271,15 @@ pub const FileAccess = opaque { | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
|     /// Close the file referenced by `file_access`, invalidating the reference to it and releasing | ||||
|     /// any associated resources. | ||||
|     /// Close the file referenced by `file_access` on the main thread, invalidating the reference to | ||||
|     /// it and releasing any associated resources. | ||||
|     /// | ||||
|     /// Freeing an invalid `file_access` has no effect on the file and logs a warning over the | ||||
|     /// wasted effort. | ||||
|     /// | ||||
|     pub fn close(file_access: *FileAccess, app_context: *AppContext) void { | ||||
|         const Task = struct { | ||||
|             file_access: *FileAccess, | ||||
| 
 | ||||
|             const Task = @This(); | ||||
| 
 | ||||
|             fn process(data: *anyopaque) void { | ||||
|                 const task = @ptrCast(*Task, @alignCast(@alignOf(Task), data)); | ||||
| 
 | ||||
|                 if (ext.SDL_RWclose(task.file_access.asRwOps()) != 0) | ||||
|                     ext.SDL_LogWarn(ext.SDL_LOG_CATEGORY_APPLICATION, | ||||
|                         "Closed an invalid file reference"); | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         var task = Task{.file_access = file_access}; | ||||
| 
 | ||||
|         var message = AppContext.Message{ | ||||
|             .frame = @frame(), | ||||
| 
 | ||||
|             .kind = .{.task = .{ | ||||
|                 .data = &task, | ||||
|                 .action = Task.process, | ||||
|             }}, | ||||
|         }; | ||||
| 
 | ||||
|         suspend app_context.schedule(&message); | ||||
|     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"); | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
| @ -273,47 +288,14 @@ pub const FileAccess = opaque { | ||||
|     /// Returns the number of bytes into the file that the cursor is relative to its beginning or a | ||||
|     /// [Error] on failure. | ||||
|     /// | ||||
|     pub fn queryCursor(file_access: *FileAccess, app_context: *AppContext) Error!u64 { | ||||
|         const Task = struct { | ||||
|             file_access: *FileAccess, | ||||
|             result: Error!u64, | ||||
|     pub fn queryCursor(file_access: *FileAccess) Error!u64 { | ||||
|         ext.SDL_ClearError(); | ||||
| 
 | ||||
|             const Task = @This(); | ||||
|         const sought = ext.SDL_RWtell(file_access.asRwOps()); | ||||
| 
 | ||||
|             fn process(data: *anyopaque) void { | ||||
|                 const task = @ptrCast(*Task, @alignCast(@alignOf(Task), data)); | ||||
|         if (sought < 0) return error.FileInaccessible; | ||||
| 
 | ||||
|                 ext.SDL_ClearError(); | ||||
| 
 | ||||
|                 const sought = ext.SDL_RWtell(task.file_access.asRwOps()); | ||||
| 
 | ||||
|                 if (sought < 0) { | ||||
|                     task.result = error.FileInaccessible; | ||||
| 
 | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|                 task.result = @intCast(u64, sought); | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         var task = Task{ | ||||
|             .file_access = file_access, | ||||
|             .result = error.FileInaccessible, | ||||
|         }; | ||||
| 
 | ||||
|         var message = AppContext.Message{ | ||||
|             .frame = @frame(), | ||||
| 
 | ||||
|             .kind = .{.task = .{ | ||||
|                 .data = &task, | ||||
|                 .action = Task.process, | ||||
|             }}, | ||||
|         }; | ||||
| 
 | ||||
|         suspend app_context.schedule(&message); | ||||
| 
 | ||||
|         return task.result; | ||||
|         return @intCast(u64, sought); | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
| @ -322,156 +304,51 @@ pub const FileAccess = opaque { | ||||
|     /// Returns the current length of the file at the time of the operation or a [Error] if the file | ||||
|     /// failed to be queried. | ||||
|     /// | ||||
|     pub fn queryLength(file_access: *FileAccess, app_context: *AppContext) Error!u64 { | ||||
|         const Task = struct { | ||||
|             file_access: *FileAccess, | ||||
|             result: Error!usize, | ||||
|     pub fn queryLength(file_access: *FileAccess) Error!u64 { | ||||
|         ext.SDL_ClearError(); | ||||
| 
 | ||||
|             const Task = @This(); | ||||
|         const sought = ext.SDL_RWsize(file_access.asRwOps()); | ||||
| 
 | ||||
|             fn process(data: *anyopaque) void { | ||||
|                 const task = @ptrCast(*Task, @alignCast(@alignOf(Task), data)); | ||||
|         if (sought < 0) return error.FileInaccessible; | ||||
| 
 | ||||
|                 ext.SDL_ClearError(); | ||||
| 
 | ||||
|                 const sought = ext.SDL_RWsize(task.file_access.asRwOps()); | ||||
| 
 | ||||
|                 if (sought < 0) { | ||||
|                     task.result = error.FileInaccessible; | ||||
| 
 | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|                 task.result = @intCast(u64, sought); | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         var task = Task{ | ||||
|             .file_access = file_access, | ||||
|             .result = error.FileInaccessible, | ||||
|         }; | ||||
| 
 | ||||
|         var message = AppContext.Message{ | ||||
|             .frame = @frame(), | ||||
| 
 | ||||
|             .kind = .{.task = .{ | ||||
|                 .data = &task, | ||||
|                 .action = Task.process, | ||||
|             }}, | ||||
|         }; | ||||
| 
 | ||||
|         suspend app_context.schedule(&message); | ||||
| 
 | ||||
|         return task.result; | ||||
|         return @intCast(u64, sought); | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
|     /// Attempts to read `file_access` from the its current position into `buffer`, while using | ||||
|     /// `app_context` as the execution context. | ||||
|     /// Attempts to read `file_access` from the its current position into `buffer`. | ||||
|     /// | ||||
|     /// Returns the number of bytes that were available to be read, otherwise an [Error] on failure. | ||||
|     /// | ||||
|     pub fn read(file_access: *FileAccess, app_context: *AppContext, buffer: []u8) Error!usize { | ||||
|         const Task = struct { | ||||
|             file_access: *FileAccess, | ||||
|             buffer: []u8, | ||||
|             result: Error!usize, | ||||
|     pub fn read(file_access: *FileAccess, buffer: []u8) Error!usize { | ||||
|         ext.SDL_ClearError(); | ||||
| 
 | ||||
|             const Task = @This(); | ||||
|         const buffer_read = | ||||
|             ext.SDL_RWread(file_access.asRwOps(), buffer.ptr, @sizeOf(u8), buffer.len); | ||||
| 
 | ||||
|             fn process(data: *anyopaque) void { | ||||
|                 const task = @ptrCast(*Task, @alignCast(@alignOf(Task), data)); | ||||
|         if ((buffer_read == 0) and (ext.SDL_GetError() != null)) return error.FileInaccessible; | ||||
| 
 | ||||
|                 ext.SDL_ClearError(); | ||||
| 
 | ||||
|                 const buffer_read = ext.SDL_RWread(task.file_access.asRwOps(), | ||||
|                     task.buffer.ptr, @sizeOf(u8), task.buffer.len); | ||||
| 
 | ||||
|                 if ((buffer_read == 0) and (ext.SDL_GetError() != null)) { | ||||
|                     task.result = error.FileInaccessible; | ||||
| 
 | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|                 task.result = buffer_read; | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         var task = Task{ | ||||
|             .file_access = file_access, | ||||
|             .buffer = buffer, | ||||
|             .result = error.FileInaccessible, | ||||
|         }; | ||||
| 
 | ||||
|         var message = AppContext.Message{ | ||||
|             .frame = @frame(), | ||||
| 
 | ||||
|             .kind = .{.task = .{ | ||||
|                 .data = &task, | ||||
|                 .action = Task.process, | ||||
|             }}, | ||||
|         }; | ||||
| 
 | ||||
|         suspend app_context.schedule(&message); | ||||
| 
 | ||||
|         return task.result; | ||||
|         return buffer_read; | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
|     /// Attempts to seek `file_access` from the beginning of the file to `cursor` bytes while using | ||||
|     /// `app_context` as the execution context. | ||||
|     /// Attempts to seek `file_access` from the beginning of the file to `cursor` bytes. | ||||
|     /// | ||||
|     /// Returns [Error] on failure. | ||||
|     /// | ||||
|     pub fn seek(file_access: *FileAccess, app_context: *AppContext, cursor: u64) Error!void { | ||||
|         const Task = struct { | ||||
|             file_access: *FileAccess, | ||||
|             cursor: u64, | ||||
|             result: Error!void, | ||||
|     pub fn seek(file_access: *FileAccess, cursor: u64) Error!void { | ||||
|         var to_seek = cursor; | ||||
| 
 | ||||
|             const Task = @This(); | ||||
|         while (to_seek != 0) { | ||||
|             const sought = @intCast(i64, std.math.min(to_seek, std.math.maxInt(i64))); | ||||
| 
 | ||||
|             fn process(data: *anyopaque) void { | ||||
|                 const task = @ptrCast(*Task, @alignCast(@alignOf(Task), data)); | ||||
|             ext.SDL_ClearError(); | ||||
| 
 | ||||
|                 if (task.cursor >= std.math.maxInt(i64)) { | ||||
|                     task.result = error.OutOfRange; | ||||
|             if (ext.SDL_RWseek(file_access.asRwOps(), sought, ext.RW_SEEK_CUR) < 0) | ||||
|                 return error.FileInaccessible; | ||||
| 
 | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|                 ext.SDL_ClearError(); | ||||
| 
 | ||||
|                 if (ext.SDL_RWseek(task.file_access.asRwOps(), | ||||
|                     @intCast(i64, task.cursor), ext.RW_SEEK_SET) < 0) { | ||||
| 
 | ||||
|                     task.result = error.FileInaccessible; | ||||
| 
 | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|                 task.result = {}; | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         var task = Task{ | ||||
|             .file_access = file_access, | ||||
|             .cursor = cursor, | ||||
|             .result = error.FileInaccessible, | ||||
|         }; | ||||
| 
 | ||||
|         var message = AppContext.Message{ | ||||
|             .frame = @frame(), | ||||
| 
 | ||||
|             .kind = .{.task = .{ | ||||
|                 .data = &task, | ||||
|                 .action = Task.process, | ||||
|             }}, | ||||
|         }; | ||||
| 
 | ||||
|         suspend app_context.schedule(&message); | ||||
| 
 | ||||
|         return task.result; | ||||
|             // Cannot be less than zero because it is derived from `read`. | ||||
|             to_seek -= @intCast(u64, sought); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
| @ -480,45 +357,11 @@ pub const FileAccess = opaque { | ||||
|     /// | ||||
|     /// Returns [Error] on failure. | ||||
|     /// | ||||
|     pub fn seekToEnd(file_access: *FileAccess, app_context: *AppContext) Error!void { | ||||
|         const Task = struct { | ||||
|             file_access: *FileAccess, | ||||
|             result: Error!void, | ||||
|     pub fn seekToEnd(file_access: *FileAccess) Error!void { | ||||
|         ext.SDL_ClearError(); | ||||
| 
 | ||||
|             const Task = @This(); | ||||
| 
 | ||||
|             fn process(data: *anyopaque) void { | ||||
|                 const task = @ptrCast(*Task, @alignCast(@alignOf(Task), data)); | ||||
| 
 | ||||
|                 ext.SDL_ClearError(); | ||||
| 
 | ||||
|                 if (ext.SDL_RWseek(task.file_access.asRwOps(), 0, ext.RW_SEEK_END) < 0) { | ||||
|                     task.result = error.FileInaccessible; | ||||
| 
 | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|                 task.result = {}; | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         var task = Task{ | ||||
|             .file_access = file_access, | ||||
|             .result = error.FileInaccessible, | ||||
|         }; | ||||
| 
 | ||||
|         var message = AppContext.Message{ | ||||
|             .frame = @frame(), | ||||
| 
 | ||||
|             .kind = .{.task = .{ | ||||
|                 .data = &task, | ||||
|                 .action = Task.process, | ||||
|             }}, | ||||
|         }; | ||||
| 
 | ||||
|         suspend app_context.schedule(&message); | ||||
| 
 | ||||
|         return task.result; | ||||
|         if (ext.SDL_RWseek(file_access.asRwOps(), 0, ext.RW_SEEK_END) < 0) | ||||
|             return error.FileInaccessible; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| @ -595,167 +438,72 @@ pub const FileSystem = union(enum) { | ||||
| 
 | ||||
|         /// | ||||
|         /// Attempts to open the file identified by `path` with `mode` as the mode for opening the | ||||
|         /// file and `app_context` as the execution context. | ||||
|         /// file. | ||||
|         /// | ||||
|         /// Returns a [FileAccess] reference that provides access to the file referenced by `path` | ||||
|         /// or a [OpenError] if it failed. | ||||
|         /// | ||||
|         pub fn open(path: Path, app_context: *AppContext, mode: OpenMode) OpenError!*FileAccess { | ||||
|             const Task = struct { | ||||
|                 path: *const FileSystem.Path, | ||||
|                 app_context: *AppContext, | ||||
|                 mode: OpenMode, | ||||
|                 result: OpenError!*FileAccess, | ||||
|         pub fn open(path: Path, mode: OpenMode) OpenError!*FileAccess { | ||||
|             switch (path.file_system.*) { | ||||
|                 .archive => |archive| { | ||||
|                     if (archive.len == 0) return error.FileNotFound; | ||||
| 
 | ||||
|                 const Task = @This(); | ||||
|                     if (mode != .readonly) return error.ModeUnsupported; | ||||
| 
 | ||||
|                 fn process(data: *anyopaque) void { | ||||
|                     const task = @ptrCast(*Task, @alignCast(@alignOf(Task), data)); | ||||
|                     var path_buffer = std.mem.zeroes([4096]u8); | ||||
| 
 | ||||
|                     switch (task.path.file_system.*) { | ||||
|                         .archive => |archive| { | ||||
|                             if (archive.len == 0) { | ||||
|                                 task.result = error.FileNotFound; | ||||
|                     if (archive.len >= path_buffer.len) return error.FileNotFound; | ||||
| 
 | ||||
|                                 return; | ||||
|                             } | ||||
|                     std.mem.copy(u8, path_buffer[0 ..], archive); | ||||
| 
 | ||||
|                             if (task.mode != .readonly) { | ||||
|                                 task.result = error.ModeUnsupported; | ||||
|                     const file_access = @ptrCast(*FileAccess, ext.SDL_RWFromFile( | ||||
|                         &path_buffer, "rb") orelse return error.FileNotFound); | ||||
| 
 | ||||
|                                 return; | ||||
|                             } | ||||
|                     while (true) { | ||||
|                         var entry = std.mem.zeroes(oar.Entry); | ||||
|                         const entry_buffer = std.mem.asBytes(&entry); | ||||
| 
 | ||||
|                             var path_buffer = std.mem.zeroes([4096]u8); | ||||
|                         if ((file_access.read(entry_buffer) catch return | ||||
|                             error.FileNotFound) != entry_buffer.len) return error.FileNotFound; | ||||
| 
 | ||||
|                             if (archive.len >= path_buffer.len) { | ||||
|                                 task.result = error.FileNotFound; | ||||
|                         if (std.mem.eql(u8, entry.name_buffer[0 .. entry. | ||||
|                             name_length], path.buffer[0 .. path.length])) { | ||||
| 
 | ||||
|                                 return; | ||||
|                             } | ||||
|                             return file_access; | ||||
|                         } | ||||
| 
 | ||||
|                             std.mem.copy(u8, path_buffer[0 ..], archive); | ||||
| 
 | ||||
|                             ext.SDL_ClearError(); | ||||
| 
 | ||||
|                             const rw_ops = ext.SDL_RWFromFile(&path_buffer, "rb") orelse { | ||||
|                                 task.result = error.FileNotFound; | ||||
| 
 | ||||
|                                 return; | ||||
|                             }; | ||||
| 
 | ||||
|                             while (true) { | ||||
|                                 var entry = std.mem.zeroes(oar.Entry); | ||||
|                                 const entry_buffer = std.mem.asBytes(&entry); | ||||
| 
 | ||||
|                                 ext.SDL_ClearError(); | ||||
| 
 | ||||
|                                 if (ext.SDL_RWread(rw_ops, entry_buffer, @sizeOf(u8), | ||||
|                                     entry_buffer.len) != entry_buffer.len) { | ||||
| 
 | ||||
|                                     task.result = error.FileNotFound; | ||||
| 
 | ||||
|                                     return; | ||||
|                                 } | ||||
| 
 | ||||
|                                 if (std.mem.eql(u8, entry.name_buffer[0 .. entry.name_length], | ||||
|                                     task.path.buffer[0 .. task.path.length])) { | ||||
| 
 | ||||
|                                     task.result = @ptrCast(*FileAccess, rw_ops); | ||||
| 
 | ||||
|                                     return; | ||||
|                                 } | ||||
| 
 | ||||
|                                 { | ||||
|                                     var to_read = math.roundUp(u64, | ||||
|                                         entry.file_size, entry_buffer.len); | ||||
| 
 | ||||
|                                     while (to_read != 0) { | ||||
|                                         const read = @intCast(i64, std.math.min( | ||||
|                                             to_read, std.math.maxInt(i64))); | ||||
| 
 | ||||
|                                         ext.SDL_ClearError(); | ||||
| 
 | ||||
|                                         if (ext.SDL_RWseek(rw_ops, read, ext.RW_SEEK_CUR) < 0) { | ||||
|                                             task.result = error.FileNotFound; | ||||
| 
 | ||||
|                                             return; | ||||
|                                         } | ||||
| 
 | ||||
|                                         // Cannot be less than zero because it is derived from | ||||
|                                         // `read`. | ||||
|                                         to_read -= @intCast(u64, read); | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                         }, | ||||
| 
 | ||||
|                         .native => |native| { | ||||
|                             if (native.len == 0) { | ||||
|                                 task.result = error.FileNotFound; | ||||
| 
 | ||||
|                                 return; | ||||
|                             } | ||||
| 
 | ||||
|                             var path_buffer = std.mem.zeroes([4096]u8); | ||||
|                             const seperator = '/'; | ||||
| 
 | ||||
|                             const seperator_length = | ||||
|                                 @boolToInt(native[native.len - 1] != seperator); | ||||
| 
 | ||||
|                             if ((native.len + seperator_length + | ||||
|                                 task.path.length) >= path_buffer.len) { | ||||
| 
 | ||||
|                                 task.result = error.FileNotFound; | ||||
| 
 | ||||
|                                 return; | ||||
|                             } | ||||
| 
 | ||||
|                             std.mem.copy(u8, path_buffer[0 ..], native); | ||||
| 
 | ||||
|                             if (seperator_length != 0) | ||||
|                                 path_buffer[native.len] = seperator; | ||||
| 
 | ||||
|                             std.mem.copy(u8, path_buffer[native.len .. path_buffer.len], | ||||
|                                 task.path.buffer[0 .. task.path.length]); | ||||
| 
 | ||||
|                             ext.SDL_ClearError(); | ||||
| 
 | ||||
|                             task.result = @ptrCast(*FileAccess, ext.SDL_RWFromFile( | ||||
|                                 &path_buffer, switch (task.mode) { | ||||
|                                     .readonly => "rb", | ||||
|                                     .overwrite => "wb", | ||||
|                                     .append => "ab", | ||||
|                                 }) orelse { | ||||
| 
 | ||||
|                                 task.result = error.FileNotFound; | ||||
| 
 | ||||
|                                 return; | ||||
|                             }); | ||||
|                         }, | ||||
|                         file_access.seek(math.roundUp(u64, entry.file_size, | ||||
|                             entry_buffer.len)) catch return error.FileNotFound; | ||||
|                     } | ||||
|                 } | ||||
|             }; | ||||
|                 }, | ||||
| 
 | ||||
|             var task = Task{ | ||||
|                 .mode = mode, | ||||
|                 .path = &path, | ||||
|                 .app_context = app_context, | ||||
|                 .result = error.FileNotFound, | ||||
|             }; | ||||
|                 .native => |native| { | ||||
|                     if (native.len == 0) return error.FileNotFound; | ||||
| 
 | ||||
|             var message = AppContext.Message{ | ||||
|                 .frame = @frame(), | ||||
|                     var path_buffer = std.mem.zeroes([4096]u8); | ||||
|                     const seperator = '/'; | ||||
|                     const seperator_length = @boolToInt(native[native.len - 1] != seperator); | ||||
| 
 | ||||
|                 .kind = .{.task = .{ | ||||
|                     .data = &task, | ||||
|                     .action = Task.process, | ||||
|                 }}, | ||||
|             }; | ||||
|                     if ((native.len + seperator_length + path.length) >= path_buffer.len) | ||||
|                         return error.FileNotFound; | ||||
| 
 | ||||
|             suspend app_context.schedule(&message); | ||||
|                     std.mem.copy(u8, path_buffer[0 ..], native); | ||||
| 
 | ||||
|             return task.result; | ||||
|                     if (seperator_length != 0) | ||||
|                         path_buffer[native.len] = seperator; | ||||
| 
 | ||||
|                     std.mem.copy(u8, path_buffer[native.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); | ||||
|                 }, | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
| @ -884,39 +632,11 @@ pub const Log = enum(u32) { | ||||
|     warning = ext.SDL_LOG_PRIORITY_WARN, | ||||
| 
 | ||||
|     /// | ||||
|     /// Writes `utf8_message` as the log kind identified by `log` with `app_context` as the execution | ||||
|     /// context. | ||||
|     /// Writes `utf8_message` as the log kind identified by `log`. | ||||
|     /// | ||||
|     pub fn write(log: Log, app_context: *AppContext, utf8_message: []const u8) void { | ||||
|         const Task = struct { | ||||
|             log: Log, | ||||
|             utf8_message: []const u8, | ||||
| 
 | ||||
|             const Task = @This(); | ||||
| 
 | ||||
|             fn process(data: *anyopaque) void { | ||||
|                 const task = @ptrCast(*Task, @alignCast(@alignOf(Task), data)); | ||||
| 
 | ||||
|                 ext.SDL_LogMessage(ext.SDL_LOG_CATEGORY_APPLICATION, @enumToInt(task.log), | ||||
|                     "%.*s", task.utf8_message.len, task.utf8_message.ptr); | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         var task = Task{ | ||||
|             .log = log, | ||||
|             .utf8_message = utf8_message, | ||||
|         }; | ||||
| 
 | ||||
|         var message = AppContext.Message{ | ||||
|             .frame = @frame(), | ||||
| 
 | ||||
|             .kind = .{.task = .{ | ||||
|                 .data = &task, | ||||
|                 .action = Task.process, | ||||
|             }} | ||||
|         }; | ||||
| 
 | ||||
|         suspend app_context.schedule(&message); | ||||
|     pub fn write(log: Log, utf8_message: []const u8) void { | ||||
|         ext.SDL_LogMessage(ext.SDL_LOG_CATEGORY_APPLICATION, | ||||
|             @enumToInt(log), "%.*s", utf8_message.len, utf8_message.ptr); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user