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.
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
///
|
||||
///
|
||||
///
|
||||
pub const io = @import("./io.zig");
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
pub const mem = @import("./mem.zig");
|
||||
|
||||
///
|
||||
kayomn marked this conversation as resolved
Outdated
|
||||
///
|
||||
///
|
||||
pub const meta = @import("./meta.zig");
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
pub const stack = @import("./stack.zig");
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
pub const table = @import("./table.zig");
|
Loading…
Reference in New Issue
Missing documentation comment.