Remove global data in file system API
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is failing
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	continuous-integration/drone/push Build is failing
				
			This commit is contained in:
		
							parent
							
								
									d2f4c0afe1
								
							
						
					
					
						commit
						979c2a73f3
					
				
							
								
								
									
										17
									
								
								src/main.zig
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								src/main.zig
									
									
									
									
									
								
							| @ -21,30 +21,29 @@ test { | ||||
|     _ = sys; | ||||
| } | ||||
| 
 | ||||
| fn run(event_loop: *sys.EventLoop, graphics: *sys.GraphicsContext) anyerror!void { | ||||
| fn run(ev: *sys.EventLoop, fs: *const sys.FileSystem, gr: *sys.GraphicsContext) anyerror!void { | ||||
|     var gpa = std.heap.GeneralPurposeAllocator(.{}){}; | ||||
| 
 | ||||
|     defer _ = gpa.deinit(); | ||||
| 
 | ||||
|     { | ||||
|         const file_access = try event_loop.open(.readonly, | ||||
|             try sys.FileSystem.data.joinedPath(&.{"data", "ona.lua"})); | ||||
|         const file_access = try ev.open(.readonly, try fs.data.joinedPath(&.{"ona.lua"})); | ||||
| 
 | ||||
|         defer event_loop.close(file_access); | ||||
|         defer ev.close(file_access); | ||||
| 
 | ||||
|         const file_size = try file_access.size(event_loop); | ||||
|         const file_size = try file_access.size(ev); | ||||
|         const allocator = gpa.allocator(); | ||||
|         const buffer = try allocator.alloc(u8, file_size); | ||||
| 
 | ||||
|         defer allocator.free(buffer); | ||||
| 
 | ||||
|         if ((try event_loop.readFile(file_access, buffer)) != file_size) | ||||
|         if ((try ev.readFile(file_access, buffer)) != file_size) | ||||
|             return error.ScriptLoadFailure; | ||||
| 
 | ||||
|         event_loop.log(.debug, buffer); | ||||
|         ev.log(.debug, buffer); | ||||
|     } | ||||
| 
 | ||||
|     while (graphics.poll()) |_| { | ||||
|         graphics.present(); | ||||
|     while (gr.poll()) |_| { | ||||
|         gr.present(); | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										337
									
								
								src/sys.zig
									
									
									
									
									
								
							
							
						
						
									
										337
									
								
								src/sys.zig
									
									
									
									
									
								
							| @ -61,7 +61,6 @@ pub const EventLoop = opaque { | ||||
|     /// Internal state of the event loop hidden from the API consumer. | ||||
|     /// | ||||
|     const Implementation = struct { | ||||
|         user_prefix: []u8, | ||||
|         file_system_semaphore: *ext.SDL_sem, | ||||
|         file_system_mutex: *ext.SDL_mutex, | ||||
|         file_system_thread: ?*ext.SDL_Thread, | ||||
| @ -71,6 +70,8 @@ pub const EventLoop = opaque { | ||||
|         /// | ||||
|         /// | ||||
|         const InitError = error { | ||||
|             DataFileNotFound, | ||||
|             DataFileInvalid, | ||||
|             OutOfSemaphores, | ||||
|             OutOfMutexes, | ||||
|             OutOfMemory, | ||||
| @ -147,22 +148,19 @@ pub const EventLoop = opaque { | ||||
|         /// | ||||
|         /// | ||||
|         fn init() InitError!Implementation { | ||||
|             const data_file_access = @ptrCast(*FileAccess, | ||||
|                 ext.SDL_RWFromFile("./data.tar", "r+") orelse return error.DataFileNotFound); | ||||
| 
 | ||||
|             return Implementation{ | ||||
|                 .user_prefix = create_pref_path: { | ||||
|                     const path = ext.SDL_GetPrefPath("ona", "ona") orelse { | ||||
|                         ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION, | ||||
|                             "Failed to load user path"); | ||||
| 
 | ||||
|                         return error.OutOfMemory; | ||||
|                     }; | ||||
| 
 | ||||
|                     break: create_pref_path path[0 .. std.mem.len(path)]; | ||||
|                 .data_archive = tar.Archive.init(data_file_access) catch |err| switch (err) { | ||||
|                     error.Invalid, error.Inaccessible => return error.DataFileInvalid, | ||||
|                 }, | ||||
| 
 | ||||
|                 .file_system_semaphore = ext.SDL_CreateSemaphore(0) | ||||
|                     orelse return error.OutOfSemaphores, | ||||
| 
 | ||||
|                 .file_system_mutex = ext.SDL_CreateMutex() orelse return error.OutOfMutexes, | ||||
|                 .data_file = data_file_access, | ||||
|                 .file_system_thread = null, | ||||
|             }; | ||||
|         } | ||||
| @ -181,51 +179,12 @@ pub const EventLoop = opaque { | ||||
|                 while (implementation.file_system_messages) |messages| { | ||||
|                     switch (messages.request) { | ||||
|                         .exit => return 0, | ||||
|                         .log => |*log_request| .log(log_request.kind, log_request.message), | ||||
| 
 | ||||
|                         .log => |*log_request| ext.SDL_LogMessage(ext.SDL_LOG_CATEGORY_APPLICATION, | ||||
|                                     @enumToInt(log_request.kind), log_request.message.ptr), | ||||
|                         .open => |*open_request| open_request.result = | ||||
|                             .open(open_request.mode, open_request.file_system_path), | ||||
| 
 | ||||
|                         .open => |*open_request| { | ||||
|                             switch (open_request.file_system_path.file_system) { | ||||
|                                 .data => { | ||||
|                                     // TODO: Implement | ||||
|                                     open_request.result = error.NotFound; | ||||
|                                 }, | ||||
| 
 | ||||
|                                 .user => { | ||||
|                                     var path_buffer = std.mem.zeroes([4096]u8); | ||||
|                                     var path = stack.Fixed(u8){.buffer = path_buffer[0 .. ]}; | ||||
| 
 | ||||
|                                     path.pushAll(implementation.user_prefix) catch { | ||||
|                                         open_request.result = error.NotFound; | ||||
| 
 | ||||
|                                         continue; | ||||
|                                     }; | ||||
| 
 | ||||
|                                     if (!open_request.file_system_path.write(path.writer())) { | ||||
|                                         open_request.result = error.NotFound; | ||||
| 
 | ||||
|                                         continue; | ||||
|                                     } | ||||
| 
 | ||||
|                                     if (ext.SDL_RWFromFile(&path_buffer, switch (open_request.mode) { | ||||
|                                         .readonly => "rb", | ||||
|                                         .overwrite => "wb", | ||||
|                                         .append => "ab", | ||||
|                                     })) |rw_ops| { | ||||
|                                         open_request.result = @ptrCast(*FileAccess, rw_ops); | ||||
|                                     } else { | ||||
|                                         open_request.result = error.NotFound; | ||||
|                                     } | ||||
|                                 }, | ||||
|                             } | ||||
|                         }, | ||||
| 
 | ||||
|                         .close => |*close_request| { | ||||
|                             // TODO: Use this result somehow. | ||||
|                             _ = ext.SDL_RWclose(@ptrCast(*ext.SDL_RWops, @alignCast( | ||||
|                                     @alignOf(ext.SDL_RWops), close_request.file_access))); | ||||
|                         }, | ||||
|                         .close => |*close_request| .close(close_request.file_access), | ||||
| 
 | ||||
|                         .read_file => |read_request| { | ||||
|                             // TODO: Implement. | ||||
| @ -270,63 +229,6 @@ pub const EventLoop = opaque { | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     /// | ||||
|     /// [LogKind.info] represents a log message which is purely informative and does not indicate | ||||
|     /// any kind of issue. | ||||
|     /// | ||||
|     /// [LogKind.debug] represents a log message which is purely for debugging purposes and will | ||||
|     /// only occurs in debug builds. | ||||
|     /// | ||||
|     /// [LogKind.warning] represents a log message which is a warning about a issue that does not | ||||
|     /// break anything important but is not ideal. | ||||
|     /// | ||||
|     pub const LogKind = enum(u32) { | ||||
|         info = ext.SDL_LOG_PRIORITY_INFO, | ||||
|         debug = ext.SDL_LOG_PRIORITY_DEBUG, | ||||
|         warning = ext.SDL_LOG_PRIORITY_WARN, | ||||
|     }; | ||||
| 
 | ||||
|     /// | ||||
|     /// [OpenError.NotFound] is a catch-all for when a file could not be located to be opened. This | ||||
|     /// may be as simple as it doesn't exist or the because the underlying file-system will not / | ||||
|     /// cannot give access to it at this time. | ||||
|     /// | ||||
|     pub const OpenError = error { | ||||
|         NotFound, | ||||
|     }; | ||||
| 
 | ||||
|     /// | ||||
|     /// [OpenMode.readonly] indicates that an existing file is opened in a read-only state, | ||||
|     /// disallowing write access. | ||||
|     /// | ||||
|     /// [OpenMode.overwrite] indicates that an empty file has been created or an existing file has | ||||
|     /// been completely overwritten into. | ||||
|     /// | ||||
|     /// [OpenMode.append] indicates that an existing file that has been opened for reading from and | ||||
|     /// writing to on the end of existing data. | ||||
|     /// | ||||
|     pub const OpenMode = enum { | ||||
|         readonly, | ||||
|         overwrite, | ||||
|         append, | ||||
|     }; | ||||
| 
 | ||||
|     /// | ||||
|     /// [SeekOrigin.head] indicates that a seek operation will seek from the offset origin of the | ||||
|     /// file beginning, or "head". | ||||
|     /// | ||||
|     /// [SeekOrigin.tail] indicates that a seek operation will seek from the offset origin of the | ||||
|     /// file end, or "tail". | ||||
|     /// | ||||
|     /// [SeekOrigin.cursor] indicates that a seek operation will seek from the current position of | ||||
|     /// the file cursor. | ||||
|     /// | ||||
|     pub const SeekOrigin = enum { | ||||
|         head, | ||||
|         tail, | ||||
|         cursor, | ||||
|     }; | ||||
| 
 | ||||
|     /// | ||||
|     /// Closes access to the file referenced by `file_access` via `event_loop`. | ||||
|     /// | ||||
| @ -488,15 +390,15 @@ pub const FileError = error { | ||||
| /// Platform-agnostic mechanism for working with an abstraction of the underlying file-system(s) | ||||
| /// available to the application in a sandboxed environment. | ||||
| /// | ||||
| pub const FileSystem = enum { | ||||
|     data, | ||||
|     user, | ||||
| pub const FileSystem = struct { | ||||
|     data: Root, | ||||
|     user: Root, | ||||
| 
 | ||||
|     /// | ||||
|     /// Platform-agnostic mechanism for referencing files and directories on a [FileSystem]. | ||||
|     /// | ||||
|     pub const Path = struct { | ||||
|         file_system: FileSystem, | ||||
|         root: *const Root, | ||||
|         length: u16, | ||||
|         buffer: [max]u8, | ||||
| 
 | ||||
| @ -521,7 +423,7 @@ pub const FileSystem = enum { | ||||
|         /// byte. Because of this, it is not safe to asume that a path may hold [max] individual | ||||
|         /// characters. | ||||
|         /// | ||||
|         pub const max = 1000; | ||||
|         pub const max = 512; | ||||
| 
 | ||||
|         /// | ||||
|         /// | ||||
| @ -540,52 +442,58 @@ pub const FileSystem = enum { | ||||
|     }; | ||||
| 
 | ||||
|     /// | ||||
|     /// Creates and returns a [Path] value in the file system to the location specified by the | ||||
|     /// joining of the `sequences` path values. | ||||
|     /// | ||||
|     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), | ||||
|             .length = 0, | ||||
|         }; | ||||
|     /// | ||||
|     pub const Root = struct { | ||||
|         prefix: []const u8, | ||||
| 
 | ||||
|         if (sequences.len != 0) { | ||||
|             const last_sequence_index = sequences.len - 1; | ||||
|         /// | ||||
|         /// | ||||
|         /// | ||||
|         pub fn joinedPath(root: Root, sequences: []const []const u8) PathError!Path { | ||||
|             var path = Path{ | ||||
|                 .root = root, | ||||
|                 .buffer = std.mem.zeroes([Path.max]u8), | ||||
|                 .length = 0, | ||||
|             }; | ||||
| 
 | ||||
|             for (sequences) |sequence, index| if (sequence.len != 0) { | ||||
|                 var components = mem.Spliterator(u8){ | ||||
|                     .source = sequence, | ||||
|                     .delimiter = "/", | ||||
|                 }; | ||||
|             if (sequences.len != 0) { | ||||
|                 const last_sequence_index = sequences.len - 1; | ||||
| 
 | ||||
|                 while (components.next()) |component| if (component.len != 0) { | ||||
|                     for (component) |byte| { | ||||
|                         if (path.length == Path.max) return error.TooLong; | ||||
|                 for (sequences) |sequence, index| if (sequence.len != 0) { | ||||
|                     var components = mem.Spliterator(u8){ | ||||
|                         .source = sequence, | ||||
|                         .delimiter = "/", | ||||
|                     }; | ||||
| 
 | ||||
|                         path.buffer[path.length] = byte; | ||||
|                         path.length += 1; | ||||
|                     } | ||||
|                     while (components.next()) |component| if (component.len != 0) { | ||||
|                         for (component) |byte| { | ||||
|                             if (path.length == Path.max) return error.TooLong; | ||||
| 
 | ||||
|                     if (components.hasNext()) { | ||||
|                             path.buffer[path.length] = byte; | ||||
|                             path.length += 1; | ||||
|                         } | ||||
| 
 | ||||
|                         if (components.hasNext()) { | ||||
|                             if (path.length == Path.max) return error.TooLong; | ||||
| 
 | ||||
|                             path.buffer[path.length] = '/'; | ||||
|                             path.length += 1; | ||||
|                         } | ||||
|                     }; | ||||
| 
 | ||||
|                     if (index < last_sequence_index) { | ||||
|                         if (path.length == Path.max) return error.TooLong; | ||||
| 
 | ||||
|                         path.buffer[path.length] = '/'; | ||||
|                         path.length += 1; | ||||
|                     } | ||||
|                 }; | ||||
|             } | ||||
| 
 | ||||
|                 if (index < last_sequence_index) { | ||||
|                     if (path.length == Path.max) return error.TooLong; | ||||
| 
 | ||||
|                     path.buffer[path.length] = '/'; | ||||
|                     path.length += 1; | ||||
|                 } | ||||
|             }; | ||||
|             return path; | ||||
|         } | ||||
| 
 | ||||
|         return path; | ||||
|     } | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
| /// | ||||
| @ -629,13 +537,125 @@ pub const GraphicsContext = opaque { | ||||
| /// | ||||
| /// | ||||
| pub fn GraphicsRunner(comptime Errors: type) type { | ||||
|     return fn (*EventLoop, *GraphicsContext) callconv(.Async) Errors!void; | ||||
|     return fn (*EventLoop, *FileSystem, *GraphicsContext) callconv(.Async) Errors!void; | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// [LogKind.info] represents a log message which is purely informative and does not indicate | ||||
| /// any kind of issue. | ||||
| /// | ||||
| /// [LogKind.debug] represents a log message which is purely for debugging purposes and will | ||||
| /// only occurs in debug builds. | ||||
| /// | ||||
| /// [LogKind.warning] represents a log message which is a warning about a issue that does not | ||||
| /// break anything important but is not ideal. | ||||
| /// | ||||
| pub const LogKind = enum(u32) { | ||||
|     info = ext.SDL_LOG_PRIORITY_INFO, | ||||
|     debug = ext.SDL_LOG_PRIORITY_DEBUG, | ||||
|     warning = ext.SDL_LOG_PRIORITY_WARN, | ||||
| }; | ||||
| 
 | ||||
| /// | ||||
| /// [OpenError.NotFound] is a catch-all for when a file could not be located to be opened. This | ||||
| /// may be as simple as it doesn't exist or the because the underlying file-system will not / | ||||
| /// cannot give access to it at this time. | ||||
| /// | ||||
| pub const OpenError = error { | ||||
|     NotFound, | ||||
| }; | ||||
| 
 | ||||
| /// | ||||
| /// [OpenMode.readonly] indicates that an existing file is opened in a read-only state, | ||||
| /// disallowing write access. | ||||
| /// | ||||
| /// [OpenMode.overwrite] indicates that an empty file has been created or an existing file has | ||||
| /// been completely overwritten into. | ||||
| /// | ||||
| /// [OpenMode.append] indicates that an existing file that has been opened for reading from and | ||||
| /// writing to on the end of existing data. | ||||
| /// | ||||
| pub const OpenMode = enum { | ||||
|     readonly, | ||||
|     overwrite, | ||||
|     append, | ||||
| }; | ||||
| 
 | ||||
| /// | ||||
| /// | ||||
| /// | ||||
| pub const RunError = error { | ||||
|     InitFailure, | ||||
|     AlreadyRunning, | ||||
| }; | ||||
| 
 | ||||
| /// | ||||
| /// [SeekOrigin.head] indicates that a seek operation will seek from the offset origin of the | ||||
| /// file beginning, or "head". | ||||
| /// | ||||
| /// [SeekOrigin.tail] indicates that a seek operation will seek from the offset origin of the | ||||
| /// file end, or "tail". | ||||
| /// | ||||
| /// [SeekOrigin.cursor] indicates that a seek operation will seek from the current position of | ||||
| /// the file cursor. | ||||
| /// | ||||
| pub const SeekOrigin = enum { | ||||
|     head, | ||||
|     tail, | ||||
|     cursor, | ||||
| }; | ||||
| 
 | ||||
| /// | ||||
| /// | ||||
| /// | ||||
| pub fn close(file_access: *FileAccess) void { | ||||
|     if (!ext.SDL_RWclose(@ptrCast(*ext.SDL_RWops, | ||||
|         @alignCast(@alignOf(ext.SDL_RWops), file_access)))) { | ||||
| 
 | ||||
|         ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION, | ||||
|             "Failed to close file - may have been already closed"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// | ||||
| /// | ||||
| pub fn runGraphics(comptime Errors: anytype, comptime run: GraphicsRunner(Errors)) Errors!void { | ||||
| pub fn log(kind: LogKind, message: []const u8) void { | ||||
|     ext.SDL_LogMessage(ext.SDL_LOG_CATEGORY_APPLICATION, | ||||
|         @enumToInt(kind), "%.*s", message.len, message.ptr); | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// | ||||
| /// | ||||
| pub fn open(mode: OpenMode, file_system_path: FileSystem.Path) OpenError!*FileAccess { | ||||
|     switch (file_system_path.file_system) { | ||||
|         .data => { | ||||
|             // TODO: Implement | ||||
|             return error.NotFound; | ||||
|         }, | ||||
| 
 | ||||
|         .user => { | ||||
|             var path_buffer = std.mem.zeroes([4096]u8); | ||||
|             var path = stack.Fixed(u8){.buffer = path_buffer[0 .. ]}; | ||||
| 
 | ||||
|             path.pushAll("/home/kayomn/.local/share") catch return error.NotFound; | ||||
| 
 | ||||
|             if (file_system_path.write(path.writer())) return error.NotFound; | ||||
| 
 | ||||
|             return @ptrCast(*FileAccess, ext.SDL_RWFromFile(&path_buffer, switch (mode) { | ||||
|                 .readonly => "rb", | ||||
|                 .overwrite => "wb", | ||||
|                 .append => "ab", | ||||
|             })) orelse error.NotFound; | ||||
|         }, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// | ||||
| /// | ||||
| pub fn runGraphics(comptime Errors: anytype, comptime run: GraphicsRunner(Errors)) (RunError || Errors)!void { | ||||
|     if (ext.SDL_Init(ext.SDL_INIT_EVERYTHING) != 0) { | ||||
|         ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize runtime"); | ||||
| 
 | ||||
| @ -669,6 +689,25 @@ pub fn runGraphics(comptime Errors: anytype, comptime run: GraphicsRunner(Errors | ||||
| 
 | ||||
|     defer ext.SDL_DestroyRenderer(renderer); | ||||
| 
 | ||||
|     var file_system = FileSystem{ | ||||
|         .user = .{.prefix = create_pref_path: { | ||||
|             const path = ext.SDL_GetPrefPath("ona", "ona") orelse { | ||||
|                 ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION, | ||||
|                     "Failed to load user path"); | ||||
| 
 | ||||
|                 return error.InitFailure; | ||||
|             }; | ||||
| 
 | ||||
|             break: create_pref_path path[0 .. std.mem.len(path)]; | ||||
|         }}, | ||||
| 
 | ||||
|         .data = .{.prefix = "./"}, | ||||
|     }; | ||||
| 
 | ||||
|     defer { | ||||
|         ext.SDL_free(file_system.user.prefix); | ||||
|     } | ||||
| 
 | ||||
|     var event_loop = EventLoop.Implementation.init() catch |err| { | ||||
|         switch (err) { | ||||
|             error.OutOfMemory => ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION, | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user