diff --git a/build.zig b/build.zig index 2a81c07..f0a0a5b 100644 --- a/build.zig +++ b/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, + }; +} diff --git a/src/main.zig b/src/engine/main.zig similarity index 75% rename from src/main.zig rename to src/engine/main.zig index 2f85626..98a5af0 100644 --- a/src/main.zig +++ b/src/engine/main.zig @@ -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; +} diff --git a/src/sys.zig b/src/engine/sys.zig similarity index 87% rename from src/sys.zig rename to src/engine/sys.zig index cdda969..709c689 100644 --- a/src/sys.zig +++ b/src/engine/sys.zig @@ -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 = "/", }; diff --git a/src/oar.zig b/src/oar/main.zig similarity index 87% rename from src/oar.zig rename to src/oar/main.zig index 743057a..98429ed 100644 --- a/src/oar.zig +++ b/src/oar/main.zig @@ -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); +}; diff --git a/src/io.zig b/src/ona/io.zig similarity index 57% rename from src/io.zig rename to src/ona/io.zig index 58a3c38..6ac1d87 100644 --- a/src/io.zig +++ b/src/ona/io.zig @@ -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. diff --git a/src/ona/main.zig b/src/ona/main.zig new file mode 100644 index 0000000..96ef49c --- /dev/null +++ b/src/ona/main.zig @@ -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"); diff --git a/src/mem.zig b/src/ona/mem.zig similarity index 100% rename from src/mem.zig rename to src/ona/mem.zig diff --git a/src/meta.zig b/src/ona/meta.zig similarity index 100% rename from src/meta.zig rename to src/ona/meta.zig diff --git a/src/stack.zig b/src/ona/stack.zig similarity index 100% rename from src/stack.zig rename to src/ona/stack.zig diff --git a/src/table.zig b/src/ona/table.zig similarity index 100% rename from src/table.zig rename to src/ona/table.zig