Application Context Implementation #4
							
								
								
									
										42
									
								
								build.zig
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								build.zig
									
									
									
									
									
								
							| @ -1,21 +1,28 @@ | ||||
| const std = @import("std"); | ||||
| 
 | ||||
| /// | ||||
| /// Builds the engine, tools, and dependencies of all. | ||||
| /// | ||||
| pub fn build(builder: *std.build.Builder) void { | ||||
|     const target = builder.standardTargetOptions(.{}); | ||||
|     const mode = builder.standardReleaseOptions(); | ||||
|     const ona_pkg = projectPkg("ona", &.{}); | ||||
| 
 | ||||
|     // Ona executable. | ||||
|     // Engine executable. | ||||
|     { | ||||
|         const ona_exe = builder.addExecutable("ona", "./src/main.zig"); | ||||
|         const engine_exe = builder.addExecutable("ona", "./src/engine/main.zig"); | ||||
|         const oar_pkg = projectPkg("oar", &.{ona_pkg}); | ||||
| 
 | ||||
|         ona_exe.setTarget(target); | ||||
|         ona_exe.setBuildMode(mode); | ||||
|         ona_exe.install(); | ||||
|         ona_exe.addIncludeDir("./ext"); | ||||
|         ona_exe.linkSystemLibrary("SDL2"); | ||||
|         ona_exe.linkLibC(); | ||||
|         engine_exe.addPackage(oar_pkg); | ||||
|         engine_exe.addPackage(ona_pkg); | ||||
|         engine_exe.setTarget(target); | ||||
|         engine_exe.setBuildMode(mode); | ||||
|         engine_exe.install(); | ||||
|         engine_exe.addIncludeDir("./ext"); | ||||
|         engine_exe.linkSystemLibrary("SDL2"); | ||||
|         engine_exe.linkLibC(); | ||||
| 
 | ||||
|         const run_cmd = ona_exe.run(); | ||||
|         const run_cmd = engine_exe.run(); | ||||
| 
 | ||||
|         run_cmd.step.dependOn(builder.getInstallStep()); | ||||
| 
 | ||||
| @ -24,6 +31,11 @@ pub fn build(builder: *std.build.Builder) void { | ||||
|         builder.step("run", "Run Ona application").dependOn(&run_cmd.step); | ||||
|     } | ||||
| 
 | ||||
|     // Oar executable. | ||||
|     { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     // Ona tests. | ||||
|     { | ||||
|         const ona_tests = builder.addTestExe("test", "./src/main.zig"); | ||||
| @ -33,3 +45,15 @@ pub fn build(builder: *std.build.Builder) void { | ||||
|         builder.step("test", "Run Ona unit tests").dependOn(&ona_tests.step); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// Returns a [std.build.Pkg] within the project codebase path at `name` with `dependencies` as its | ||||
| /// dependencies. | ||||
| /// | ||||
| fn projectPkg(comptime name: []const u8, dependencies: []const std.build.Pkg) std.build.Pkg { | ||||
|     return std.build.Pkg{ | ||||
|         .name = name, | ||||
|         .path = .{.path = "./src/" ++ name ++ "/main.zig"}, | ||||
|         .dependencies = dependencies, | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @ -1,9 +1,4 @@ | ||||
| const ext = @cImport({ | ||||
|     @cInclude("SDL2/SDL.h"); | ||||
| }); | ||||
| 
 | ||||
| const io = @import("./io.zig"); | ||||
| const stack = @import("./stack.zig"); | ||||
| const ona = @import("ona"); | ||||
| const std = @import("std"); | ||||
| const sys = @import("./sys.zig"); | ||||
| 
 | ||||
| @ -14,13 +9,6 @@ pub fn main() anyerror!void { | ||||
|     return nosuspend await async sys.runGraphics(anyerror, run); | ||||
| } | ||||
| 
 | ||||
| test { | ||||
|     _ = io; | ||||
|     _ = stack; | ||||
|     _ = std; | ||||
|     _ = sys; | ||||
| } | ||||
| 
 | ||||
| fn run(app: *sys.AppContext, graphics: *sys.GraphicsContext) anyerror!void { | ||||
|     var gpa = std.heap.GeneralPurposeAllocator(.{}){}; | ||||
| 
 | ||||
| @ -37,8 +25,7 @@ fn run(app: *sys.AppContext, graphics: *sys.GraphicsContext) anyerror!void { | ||||
| 
 | ||||
|         defer allocator.free(buffer); | ||||
| 
 | ||||
|         if ((try file_access.read(buffer)) != file_size) | ||||
|             return error.ScriptLoadFailure; | ||||
|         if ((try file_access.read(buffer)) != file_size) return error.ScriptLoadFailure; | ||||
| 
 | ||||
|         sys.Log.debug.write(buffer); | ||||
|     } | ||||
| @ -47,3 +34,7 @@ fn run(app: *sys.AppContext, graphics: *sys.GraphicsContext) anyerror!void { | ||||
|         graphics.present(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| test { | ||||
|     _ = sys; | ||||
| } | ||||
| @ -2,11 +2,8 @@ const ext = @cImport({ | ||||
|     @cInclude("SDL2/SDL.h"); | ||||
| }); | ||||
| 
 | ||||
| const io = @import("./io.zig"); | ||||
| const mem = @import("./mem.zig"); | ||||
| const meta = @import("./meta.zig"); | ||||
| const oar = @import("./oar.zig"); | ||||
| const stack = @import("./stack.zig"); | ||||
| const oar = @import("oar"); | ||||
| const ona = @import("ona"); | ||||
| const std = @import("std"); | ||||
| 
 | ||||
| /// | ||||
| @ -131,12 +128,14 @@ pub const AppContext = opaque { | ||||
|         } | ||||
| 
 | ||||
|         /// | ||||
|         /// Initializes a new [Implemenation] with `data_access` as the 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(allocator: std.mem.Allocator, data_access: FileAccess) InitError!Implementation { | ||||
|         fn init(allocator: std.mem.Allocator, | ||||
|             data_archive_file_access: ona.io.FileAccess) InitError!Implementation { | ||||
| 
 | ||||
|             const user_path_prefix = ext.SDL_GetPrefPath("ona", "ona") orelse | ||||
|                 return error.OutOfMemory; | ||||
| 
 | ||||
| @ -149,7 +148,7 @@ pub const AppContext = opaque { | ||||
|                 .user_path_prefix = user_path_prefix, | ||||
| 
 | ||||
|                 .data_file_system = .{.archive = .{ | ||||
|                     .instance = try oar.Archive.init(allocator, data_access), | ||||
|                     .instance = try oar.Archive.init(allocator, data_archive_file_access), | ||||
|                 }}, | ||||
| 
 | ||||
|                 .message_thread = null, | ||||
| @ -223,12 +222,12 @@ pub const AppContext = opaque { | ||||
|     /// | ||||
|     /// | ||||
|     pub fn schedule(app_context: *AppContext, procedure: anytype, | ||||
|         arguments: anytype) meta.FnReturn(@TypeOf(procedure)) { | ||||
|         arguments: anytype) ona.meta.FnReturn(@TypeOf(procedure)) { | ||||
| 
 | ||||
|         const Task = struct { | ||||
|             procedure: @TypeOf(procedure), | ||||
|             arguments: *@TypeOf(arguments), | ||||
|             result: meta.FnReturn(@TypeOf(procedure)), | ||||
|             result: ona.meta.FnReturn(@TypeOf(procedure)), | ||||
| 
 | ||||
|             const Task = @This(); | ||||
| 
 | ||||
| @ -263,102 +262,6 @@ pub const AppContext = opaque { | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| /// | ||||
| /// File-system agnostic abstraction for manipulating a file. | ||||
| /// | ||||
| 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, | ||||
|         skip: fn (*anyopaque, i64) 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. | ||||
|     /// | ||||
|     pub const Error = error { | ||||
|         FileInaccessible, | ||||
|     }; | ||||
| 
 | ||||
|     /// | ||||
|     /// 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) void { | ||||
|         return file_access.implementation.close(file_access.context); | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
|     /// Attempts to query the current cursor position for the file referenced by `file_access`. | ||||
|     /// | ||||
|     /// 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) Error!u64 { | ||||
|         return file_access.implementation.queryCursor(file_access.context); | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
|     /// Attempts to query the current length for the file referenced by `file_access`. | ||||
|     /// | ||||
|     /// 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) Error!u64 { | ||||
|         return file_access.implementation.queryLength(file_access.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, buffer: []u8) Error!usize { | ||||
|         return file_access.implementation.read(file_access.context, buffer); | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
|     /// Attempts to seek `file_access` from the beginning of the file to `cursor` bytes. | ||||
|     /// | ||||
|     /// Returns [Error] on failure. | ||||
|     /// | ||||
|     pub fn seek(file_access: *FileAccess, cursor: u64) Error!void { | ||||
|         return file_access.implementation.seek(file_access.context, cursor); | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
|     /// Attempts to seek `file_access` to the end of the file. | ||||
|     /// | ||||
|     /// Returns [Error] on failure. | ||||
|     /// | ||||
|     pub fn seekToEnd(file_access: *FileAccess) Error!void { | ||||
|         return file_access.implementation.seekToEnd(file_access.context); | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
|     /// Attempts to seek `file_access` by `offset` from the current file position. | ||||
|     /// | ||||
|     /// Returns [Error] on failure; | ||||
|     /// | ||||
|     pub fn skip(file_access: *FileAccess, offset: i64) Error!void { | ||||
|         return file_access.implementation.skip(file_access.context, offset); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| /// | ||||
| /// Platform-agnostic mechanism for working with an abstraction of the underlying file-system(s) | ||||
| /// available to the application in a sandboxed environment. | ||||
| @ -447,11 +350,13 @@ 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!ona.io.FileAccess { | ||||
|             switch (path.file_system.*) { | ||||
|                 .archive => |*archive| { | ||||
|                     if (mode != .readonly) return error.ModeUnsupported; | ||||
| 
 | ||||
|                     const FileAccess = ona.io.FileAccess; | ||||
| 
 | ||||
|                     for (archive.entry_table) |*entry| if (entry.owner == null) { | ||||
|                         const Implementation = struct { | ||||
|                             fn close(context: *anyopaque) void { | ||||
| @ -567,6 +472,8 @@ pub const FileSystem = union(enum) { | ||||
| 
 | ||||
|                     ext.SDL_ClearError(); | ||||
| 
 | ||||
|                     const FileAccess = ona.io.FileAccess; | ||||
| 
 | ||||
|                     const Implementation = struct { | ||||
|                         fn rwOpsCast(context: *anyopaque) *ext.SDL_RWops { | ||||
|                             return @ptrCast(*ext.SDL_RWops, @alignCast( | ||||
| @ -693,7 +600,7 @@ pub const FileSystem = union(enum) { | ||||
|             const last_sequence_index = sequences.len - 1; | ||||
| 
 | ||||
|             for (sequences) |sequence, index| if (sequence.len != 0) { | ||||
|                 var components = mem.Spliterator(u8){ | ||||
|                 var components = ona.mem.Spliterator(u8){ | ||||
|                     .source = sequence, | ||||
|                     .delimiter = "/", | ||||
|                 }; | ||||
| @ -1,18 +1,17 @@ | ||||
| const ona = @import("ona"); | ||||
| const std = @import("std"); | ||||
| const sys = @import("./sys.zig"); | ||||
| const table = @import("./table.zig"); | ||||
| 
 | ||||
| /// | ||||
| /// Thin file-wrapper and in-memory layout cache of an OAR archive file. | ||||
| /// | ||||
| pub const Archive = struct { | ||||
|     file_access: sys.FileAccess, | ||||
|     file_access: ona.io.FileAccess, | ||||
|     index_cache: IndexCache, | ||||
| 
 | ||||
|     /// | ||||
|     /// [OpenError.EntryNotFound] happens when an entry could not be found. | ||||
|     /// | ||||
|     pub const FindError = sys.FileAccess.Error || error { | ||||
|     pub const FindError = ona.io.FileAccess.Error || error { | ||||
|         EntryNotFound, | ||||
|     }; | ||||
| 
 | ||||
| @ -27,7 +26,7 @@ pub const Archive = struct { | ||||
|     /// As the archive is queried via [find], the cache is lazily assembled with the absolute | ||||
|     /// offsets of each queried file. | ||||
|     /// | ||||
|     const IndexCache = table.Hashed([]const u8, u64, table.string_context); | ||||
|     const IndexCache = ona.table.Hashed([]const u8, u64, ona.table.string_context); | ||||
| 
 | ||||
|     /// | ||||
|     /// Deinitializes the index cache of `archive`, freeing all associated memory. | ||||
| @ -49,6 +48,7 @@ pub const Archive = struct { | ||||
|             .header = find_header: { | ||||
|                 var header = Entry.Header{ | ||||
|                     .revision = 0, | ||||
|                     .path = Path.empty, | ||||
|                     .file_size = 0, | ||||
|                     .absolute_offset = 0 | ||||
|                 }; | ||||
| @ -73,7 +73,7 @@ pub const Archive = struct { | ||||
| 
 | ||||
|                     // Read first entry. | ||||
|                     while ((try archive.file_access.read(mem.asBytes(&header))) == header_size) { | ||||
|                         if (mem.eql(u8, entry_path, header.name_buffer[0 .. header.name_length])) { | ||||
|                         if (mem.eql(u8, entry_path, header.path.buffer[0 .. header.path.length])) { | ||||
|                             // If caching fails... oh well... | ||||
|                             archive.index_cache.insert(entry_path, header.absolute_offset) catch {}; | ||||
| 
 | ||||
| @ -110,7 +110,7 @@ pub const Archive = struct { | ||||
|     /// **Note** that `archive_file_access` does nothing to manage the lifetime of the open file. | ||||
|     /// | ||||
|     pub fn init(cache_allocator: std.mem.Allocator, | ||||
|         archive_file_access: sys.FileAccess) InitError!Archive { | ||||
|         archive_file_access: ona.io.FileAccess) InitError!Archive { | ||||
| 
 | ||||
|         return Archive{ | ||||
|             .index_cache = try IndexCache.init(cache_allocator), | ||||
| @ -123,7 +123,7 @@ pub const Archive = struct { | ||||
| /// Handles the state of an opened archive entry. | ||||
| /// | ||||
| pub const Entry = struct { | ||||
|     owner: ?*sys.FileAccess, | ||||
|     owner: ?*ona.io.FileAccess, | ||||
|     cursor: u64, | ||||
|     header: Header, | ||||
| 
 | ||||
| @ -135,8 +135,7 @@ pub const Entry = struct { | ||||
|     pub const Header = extern struct { | ||||
|         signature: [signature_magic.len]u8 = signature_magic, | ||||
|         revision: u8, | ||||
|         name_buffer: [255]u8 = std.mem.zeroes([255]u8), | ||||
|         name_length: u8 = 0, | ||||
|         path: Path, | ||||
|         file_size: u64, | ||||
|         absolute_offset: u64, | ||||
|         padding: [232]u8 = std.mem.zeroes([232]u8), | ||||
| @ -155,3 +154,16 @@ pub const Entry = struct { | ||||
|         pub const signature_magic = [3]u8{'o', 'a', 'r'}; | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
| /// | ||||
| /// | ||||
| /// | ||||
| pub const Path = extern struct { | ||||
|     buffer: [255]u8, | ||||
|     length: u8, | ||||
| 
 | ||||
|     /// | ||||
|     /// | ||||
|     /// | ||||
|     pub const empty = std.mem.zeroes(Path); | ||||
| }; | ||||
| @ -1,6 +1,102 @@ | ||||
| const stack = @import("./stack.zig"); | ||||
| const std = @import("std"); | ||||
| 
 | ||||
| /// | ||||
| /// File-system agnostic abstraction for manipulating a file. | ||||
| /// | ||||
| 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, | ||||
|         skip: fn (*anyopaque, i64) 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. | ||||
|     /// | ||||
|     pub const Error = error { | ||||
|         FileInaccessible, | ||||
|     }; | ||||
| 
 | ||||
|     /// | ||||
|     /// 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) void { | ||||
|         return file_access.implementation.close(file_access.context); | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
|     /// Attempts to query the current cursor position for the file referenced by `file_access`. | ||||
|     /// | ||||
|     /// 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) Error!u64 { | ||||
|         return file_access.implementation.queryCursor(file_access.context); | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
|     /// Attempts to query the current length for the file referenced by `file_access`. | ||||
|     /// | ||||
|     /// 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) Error!u64 { | ||||
|         return file_access.implementation.queryLength(file_access.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, buffer: []u8) Error!usize { | ||||
|         return file_access.implementation.read(file_access.context, buffer); | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
|     /// Attempts to seek `file_access` from the beginning of the file to `cursor` bytes. | ||||
|     /// | ||||
|     /// Returns [Error] on failure. | ||||
|     /// | ||||
|     pub fn seek(file_access: *FileAccess, cursor: u64) Error!void { | ||||
|         return file_access.implementation.seek(file_access.context, cursor); | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
|     /// Attempts to seek `file_access` to the end of the file. | ||||
|     /// | ||||
|     /// Returns [Error] on failure. | ||||
|     /// | ||||
|     pub fn seekToEnd(file_access: *FileAccess) Error!void { | ||||
|         return file_access.implementation.seekToEnd(file_access.context); | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
|     /// Attempts to seek `file_access` by `offset` from the current file position. | ||||
|     /// | ||||
|     /// Returns [Error] on failure; | ||||
|     /// | ||||
|     pub fn skip(file_access: *FileAccess, offset: i64) Error!void { | ||||
|         return file_access.implementation.skip(file_access.context, offset); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| /// | ||||
| /// Opaque interface to a "writable" resource, such as a block device, memory buffer, or network | ||||
| /// socket. | ||||
							
								
								
									
										25
									
								
								src/ona/main.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/ona/main.zig
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| 
 | ||||
| /// | ||||
| /// | ||||
| /// | ||||
| pub const io = @import("./io.zig"); | ||||
| 
 | ||||
| /// | ||||
| /// | ||||
| /// | ||||
| pub const mem = @import("./mem.zig"); | ||||
| 
 | ||||
| /// | ||||
| /// | ||||
| /// | ||||
| pub const meta = @import("./meta.zig"); | ||||
| 
 | ||||
| /// | ||||
| /// | ||||
| /// | ||||
| pub const stack = @import("./stack.zig"); | ||||
| 
 | ||||
| /// | ||||
| /// | ||||
| /// | ||||
| pub const table = @import("./table.zig"); | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user