Application Context Implementation #4
							
								
								
									
										126
									
								
								src/oar.zig
									
									
									
									
									
								
							
							
						
						
									
										126
									
								
								src/oar.zig
									
									
									
									
									
								
							@ -1,29 +1,16 @@
 | 
			
		||||
const std = @import("std");
 | 
			
		||||
const sys = @import("./sys.zig");
 | 
			
		||||
const table = @import("./table.zig");
 | 
			
		||||
 | 
			
		||||
///
 | 
			
		||||
/// An entry block of an Oar archive file.
 | 
			
		||||
///
 | 
			
		||||
/// Typically, following this block in memory is the file data it holds the meta-information for.
 | 
			
		||||
///
 | 
			
		||||
pub const Entry = extern struct {
 | 
			
		||||
    signature: [3]u8 = signature_magic,
 | 
			
		||||
    revision: u8,
 | 
			
		||||
    name_buffer: [255]u8 = std.mem.zeroes([255]u8),
 | 
			
		||||
    name_length: u8 = 0,
 | 
			
		||||
    file_size: u64,
 | 
			
		||||
    file_offset: u64,
 | 
			
		||||
    padding: [232]u8 = std.mem.zeroes([232]u8),
 | 
			
		||||
 | 
			
		||||
    comptime {
 | 
			
		||||
        const entry_size = @sizeOf(Entry);
 | 
			
		||||
 | 
			
		||||
        if (entry_size != 512)
 | 
			
		||||
            @compileError("Entry is " ++ std.fmt.comptimePrint("{d}", .{entry_size}) ++ " bytes");
 | 
			
		||||
    }
 | 
			
		||||
pub const Archive = struct {
 | 
			
		||||
    file_access: sys.FileAccess,
 | 
			
		||||
    index_cache: IndexCache,
 | 
			
		||||
 | 
			
		||||
    ///
 | 
			
		||||
    /// [FindError.EntryNotFound] happens when an entry could not be found.
 | 
			
		||||
    /// [OpenError.EntryNotFound] happens when an entry could not be found.
 | 
			
		||||
    ///
 | 
			
		||||
    pub const FindError = sys.FileAccess.Error || error {
 | 
			
		||||
        EntryNotFound,
 | 
			
		||||
@ -32,15 +19,110 @@ pub const Entry = extern struct {
 | 
			
		||||
    ///
 | 
			
		||||
    ///
 | 
			
		||||
    ///
 | 
			
		||||
    pub fn find(file_access: sys.FileAccess, entry_name: []const u8) FindError!Entry {
 | 
			
		||||
        _ = file_access;
 | 
			
		||||
        _ = entry_name;
 | 
			
		||||
    pub const InitError = std.mem.Allocator.Error;
 | 
			
		||||
 | 
			
		||||
    ///
 | 
			
		||||
    ///
 | 
			
		||||
    ///
 | 
			
		||||
    const IndexCache = table.Hashed([]const u8, Entry.Header, table.string_context);
 | 
			
		||||
 | 
			
		||||
    ///
 | 
			
		||||
    /// Finds an entry matching `entry_path` in `archive`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// The found [Entry] value is returned or a [FindError] if it failed to be found.
 | 
			
		||||
    ///
 | 
			
		||||
    pub fn find(archive: *Archive, entry_path: []const u8) FindError!Entry {
 | 
			
		||||
        return Entry{
 | 
			
		||||
            .header = find_header: {
 | 
			
		||||
                if (archive.index_cache.lookup(entry_path)) |entry_header|
 | 
			
		||||
                    break: find_header entry_header.*;
 | 
			
		||||
 | 
			
		||||
                // Start from beginning of archive.
 | 
			
		||||
                try archive.file_access.seek(0);
 | 
			
		||||
 | 
			
		||||
                var entry_header = Entry.Header{
 | 
			
		||||
                    .revision = 0,
 | 
			
		||||
                    .file_size = 0,
 | 
			
		||||
                    .file_offset = 0
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                const read_buffer = std.mem.asBytes(&entry_header);
 | 
			
		||||
 | 
			
		||||
                // Read first entry.
 | 
			
		||||
                while ((try archive.file_access.read(read_buffer)) == @sizeOf(Entry.Header)) {
 | 
			
		||||
                    if (std.mem.eql(u8, entry_path, entry_header.
 | 
			
		||||
                        name_buffer[0 .. entry_header.name_length])) {
 | 
			
		||||
 | 
			
		||||
                        // If caching fails... oh well...
 | 
			
		||||
                        archive.index_cache.insert(entry_path, entry_header) catch {};
 | 
			
		||||
 | 
			
		||||
                        break: find_header entry_header;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Move over file data following the entry.
 | 
			
		||||
                    var to_skip = entry_header.file_size;
 | 
			
		||||
 | 
			
		||||
                    while (to_skip != 0) {
 | 
			
		||||
                        const skipped = std.math.min(to_skip, std.math.maxInt(i64));
 | 
			
		||||
 | 
			
		||||
                        try archive.file_access.skip(@intCast(i64, skipped));
 | 
			
		||||
 | 
			
		||||
                        to_skip -= skipped;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return error.EntryNotFound;
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            .owner = &archive.file_access,
 | 
			
		||||
            .cursor = 0,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ///
 | 
			
		||||
    ///
 | 
			
		||||
    ///
 | 
			
		||||
    pub fn init(allocator: std.mem.Allocator, archive_file_access: sys.FileAccess) InitError!Archive {
 | 
			
		||||
        return Archive{
 | 
			
		||||
            .index_cache = try IndexCache.init(allocator),
 | 
			
		||||
            .file_access = archive_file_access,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
///
 | 
			
		||||
/// Handles the state of an opened archive entry.
 | 
			
		||||
///
 | 
			
		||||
pub const Entry = struct {
 | 
			
		||||
    owner: ?*sys.FileAccess,
 | 
			
		||||
    cursor: u64,
 | 
			
		||||
    header: Header,
 | 
			
		||||
 | 
			
		||||
    ///
 | 
			
		||||
    /// An entry block of an Oar archive file.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Typically, following the block in memory is the file data it holds the meta-information for.
 | 
			
		||||
    ///
 | 
			
		||||
    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,
 | 
			
		||||
        file_size: u64,
 | 
			
		||||
        file_offset: u64,
 | 
			
		||||
        padding: [232]u8 = std.mem.zeroes([232]u8),
 | 
			
		||||
 | 
			
		||||
        comptime {
 | 
			
		||||
            const entry_size = @sizeOf(Header);
 | 
			
		||||
 | 
			
		||||
            if (entry_size != 512)
 | 
			
		||||
                @compileError("Entry is " ++
 | 
			
		||||
                    std.fmt.comptimePrint("{d}", .{entry_size}) ++ " bytes");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ///
 | 
			
		||||
        /// Magic identifier used to validate [Entry] data.
 | 
			
		||||
        ///
 | 
			
		||||
    const signature_magic = [3]u8{'o', 'a', 'r'};
 | 
			
		||||
        pub const signature_magic = [3]u8{'o', 'a', 'r'};
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										160
									
								
								src/sys.zig
									
									
									
									
									
								
							
							
						
						
									
										160
									
								
								src/sys.zig
									
									
									
									
									
								
							@ -8,7 +8,6 @@ const meta = @import("./meta.zig");
 | 
			
		||||
const oar = @import("./oar.zig");
 | 
			
		||||
const stack = @import("./stack.zig");
 | 
			
		||||
const std = @import("std");
 | 
			
		||||
const table = @import("./table.zig");
 | 
			
		||||
 | 
			
		||||
///
 | 
			
		||||
/// A thread-safe platform abstraction over multiplexing system I/O processing and event handling.
 | 
			
		||||
@ -131,28 +130,25 @@ pub const AppContext = opaque {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ///
 | 
			
		||||
        /// 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.
 | 
			
		||||
        /// 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.
 | 
			
		||||
        ///
 | 
			
		||||
        /// Returns the created [Implementation] value on success or [InitError] on failure.
 | 
			
		||||
        ///
 | 
			
		||||
        fn init(allocator: std.mem.Allocator,
 | 
			
		||||
            data_archive_file_access: FileAccess) InitError!Implementation {
 | 
			
		||||
 | 
			
		||||
        fn init(allocator: std.mem.Allocator, data_access: FileAccess) InitError!Implementation {
 | 
			
		||||
            const user_path_prefix = ext.SDL_GetPrefPath("ona", "ona") orelse
 | 
			
		||||
                return error.OutOfMemory;
 | 
			
		||||
 | 
			
		||||
            return Implementation{
 | 
			
		||||
                .user_file_system = .{.native = .{.path_prefix =
 | 
			
		||||
                    user_path_prefix[0 .. std.mem.len(user_path_prefix)]}},
 | 
			
		||||
                .user_file_system = .{.native =
 | 
			
		||||
                    user_path_prefix[0 .. std.mem.len(user_path_prefix)]},
 | 
			
		||||
 | 
			
		||||
                .message_semaphore = ext.SDL_CreateSemaphore(0) orelse return error.OutOfSemaphores,
 | 
			
		||||
                .message_mutex = ext.SDL_CreateMutex() orelse return error.OutOfMutexes,
 | 
			
		||||
                .user_path_prefix = user_path_prefix,
 | 
			
		||||
 | 
			
		||||
                .data_file_system = .{.archive = .{
 | 
			
		||||
                    .file_access = data_archive_file_access,
 | 
			
		||||
                    .index_cache = try FileSystem.ArchiveIndexCache.init(allocator),
 | 
			
		||||
                    .instance = try oar.Archive.init(allocator, data_access),
 | 
			
		||||
                }},
 | 
			
		||||
 | 
			
		||||
                .message_thread = null,
 | 
			
		||||
@ -283,6 +279,7 @@ pub const FileAccess = struct {
 | 
			
		||||
        read: fn (*anyopaque, []u8) Error!usize,
 | 
			
		||||
        seek: fn (*anyopaque, u64) Error!void,
 | 
			
		||||
        seekToEnd: fn (*anyopaque) Error!void,
 | 
			
		||||
        skip: fn (*anyopaque, i64) Error!void,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    ///
 | 
			
		||||
@ -343,14 +340,22 @@ pub const FileAccess = struct {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ///
 | 
			
		||||
    /// Attempts to seek `file_access` to the end of the file while using `app_context` as the execution
 | 
			
		||||
    /// context.
 | 
			
		||||
    /// 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);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
///
 | 
			
		||||
@ -358,31 +363,15 @@ pub const FileAccess = struct {
 | 
			
		||||
/// available to the application in a sandboxed environment.
 | 
			
		||||
///
 | 
			
		||||
pub const FileSystem = union(enum) {
 | 
			
		||||
    native: struct {
 | 
			
		||||
        path_prefix: []const u8,
 | 
			
		||||
    },
 | 
			
		||||
    native: []const u8,
 | 
			
		||||
 | 
			
		||||
    archive: struct {
 | 
			
		||||
        file_access: FileAccess,
 | 
			
		||||
        index_cache: ArchiveIndexCache,
 | 
			
		||||
        instance: oar.Archive,
 | 
			
		||||
        entry_table: [max_open_entries]oar.Entry = std.mem.zeroes([max_open_entries]oar.Entry),
 | 
			
		||||
 | 
			
		||||
        entry_table: [max_open_entries]ArchiveEntry =
 | 
			
		||||
            std.mem.zeroes([max_open_entries]ArchiveEntry),
 | 
			
		||||
 | 
			
		||||
        const max_open_entries = 16;
 | 
			
		||||
        pub const max_open_entries = 16;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    ///
 | 
			
		||||
    /// Handles the state of an opened archive entry.
 | 
			
		||||
    ///
 | 
			
		||||
    const ArchiveEntry = struct {
 | 
			
		||||
        using: ?*FileAccess,
 | 
			
		||||
        header: oar.Entry,
 | 
			
		||||
        cursor: u64,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const ArchiveIndexCache = table.Hashed([]const u8, oar.Entry, table.string_context);
 | 
			
		||||
 | 
			
		||||
    ///
 | 
			
		||||
    /// Platform-agnostic mechanism for referencing files and directories on a [FileSystem].
 | 
			
		||||
    ///
 | 
			
		||||
@ -462,57 +451,37 @@ pub const FileSystem = union(enum) {
 | 
			
		||||
                .archive => |*archive| {
 | 
			
		||||
                    if (mode != .readonly) return error.ModeUnsupported;
 | 
			
		||||
 | 
			
		||||
                    for (archive.entry_table) |*entry| if (entry.using == null) {
 | 
			
		||||
                        const entry_path = path.buffer[0 .. path.length];
 | 
			
		||||
 | 
			
		||||
                        entry.* = .{
 | 
			
		||||
                            .header = find_header: {
 | 
			
		||||
                                if (archive.index_cache.lookup(entry_path)) |header|
 | 
			
		||||
                                    break: find_header header.*;
 | 
			
		||||
 | 
			
		||||
                                const header = oar.Entry.find(archive.file_access,
 | 
			
		||||
                                    entry_path) catch return error.FileNotFound;
 | 
			
		||||
 | 
			
		||||
                                // If caching fails... oh well...
 | 
			
		||||
                                archive.index_cache.insert(entry_path, header) catch {};
 | 
			
		||||
 | 
			
		||||
                                break: find_header header;
 | 
			
		||||
                            },
 | 
			
		||||
 | 
			
		||||
                            .using = &archive.file_access,
 | 
			
		||||
                            .cursor = 0,
 | 
			
		||||
                        };
 | 
			
		||||
 | 
			
		||||
                    for (archive.entry_table) |*entry| if (entry.owner == null) {
 | 
			
		||||
                        const Implementation = struct {
 | 
			
		||||
                            fn archiveEntryCast(context: *anyopaque) *ArchiveEntry {
 | 
			
		||||
                                return @ptrCast(*ArchiveEntry, @alignCast(
 | 
			
		||||
                                    @alignOf(ArchiveEntry), context));
 | 
			
		||||
                            fn close(context: *anyopaque) void {
 | 
			
		||||
                                entryCast(context).owner = null;
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            fn close(context: *anyopaque) void {
 | 
			
		||||
                                archiveEntryCast(context).using = null;
 | 
			
		||||
                            fn entryCast(context: *anyopaque) *oar.Entry {
 | 
			
		||||
                                return @ptrCast(*oar.Entry, @alignCast(
 | 
			
		||||
                                    @alignOf(oar.Entry), context));
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            fn queryCursor(context: *anyopaque) FileAccess.Error!u64 {
 | 
			
		||||
                                const archive_entry = archiveEntryCast(context);
 | 
			
		||||
                                const archive_entry = entryCast(context);
 | 
			
		||||
 | 
			
		||||
                                if (archive_entry.using == null) return error.FileInaccessible;
 | 
			
		||||
                                if (archive_entry.owner == null) return error.FileInaccessible;
 | 
			
		||||
 | 
			
		||||
                                return archive_entry.cursor;
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            fn queryLength(context: *anyopaque) FileAccess.Error!u64 {
 | 
			
		||||
                                const archive_entry = archiveEntryCast(context);
 | 
			
		||||
                                const archive_entry = entryCast(context);
 | 
			
		||||
 | 
			
		||||
                                if (archive_entry.using == null) return error.FileInaccessible;
 | 
			
		||||
                                if (archive_entry.owner == null) return error.FileInaccessible;
 | 
			
		||||
 | 
			
		||||
                                return archive_entry.header.file_size;
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            fn read(context: *anyopaque, buffer: []u8) FileAccess.Error!usize {
 | 
			
		||||
                                const archive_entry = archiveEntryCast(context);
 | 
			
		||||
                                const archive_entry = entryCast(context);
 | 
			
		||||
 | 
			
		||||
                                const file_access = archive_entry.using orelse
 | 
			
		||||
                                const file_access = archive_entry.owner orelse
 | 
			
		||||
                                    return error.FileInaccessible;
 | 
			
		||||
 | 
			
		||||
                                if (archive_entry.cursor >= archive_entry.header.file_size)
 | 
			
		||||
@ -525,20 +494,40 @@ pub const FileSystem = union(enum) {
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            fn seek(context: *anyopaque, cursor: usize) FileAccess.Error!void {
 | 
			
		||||
                                const archive_entry = archiveEntryCast(context);
 | 
			
		||||
                                const archive_entry = entryCast(context);
 | 
			
		||||
 | 
			
		||||
                                if (archive_entry.using == null) return error.FileInaccessible;
 | 
			
		||||
                                if (archive_entry.owner == null) return error.FileInaccessible;
 | 
			
		||||
 | 
			
		||||
                                archive_entry.cursor = cursor;
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            fn seekToEnd(context: *anyopaque) FileAccess.Error!void {
 | 
			
		||||
                                const archive_entry = archiveEntryCast(context);
 | 
			
		||||
                                const archive_entry = entryCast(context);
 | 
			
		||||
 | 
			
		||||
                                if (archive_entry.using == null) return error.FileInaccessible;
 | 
			
		||||
                                if (archive_entry.owner == null) return error.FileInaccessible;
 | 
			
		||||
 | 
			
		||||
                                archive_entry.cursor = archive_entry.header.file_size;
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            fn skip(context: *anyopaque, offset: i64) FileAccess.Error!void {
 | 
			
		||||
                                const math = std.math;
 | 
			
		||||
                                const archive_entry = entryCast(context);
 | 
			
		||||
 | 
			
		||||
                                if (archive_entry.owner == null) return error.FileInaccessible;
 | 
			
		||||
 | 
			
		||||
                                if (offset < 0) {
 | 
			
		||||
                                    archive_entry.cursor = math.max(0,
 | 
			
		||||
                                        archive_entry.cursor - math.absCast(offset));
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    archive_entry.cursor += @intCast(u64, offset);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        };
 | 
			
		||||
 | 
			
		||||
                        entry.* = archive.instance.find(path.buffer[0 .. path.length]) catch |err|
 | 
			
		||||
                            return switch (err) {
 | 
			
		||||
                                error.FileInaccessible => error.FileNotFound,
 | 
			
		||||
                                error.EntryNotFound => error.FileNotFound,
 | 
			
		||||
                            };
 | 
			
		||||
 | 
			
		||||
                        return FileAccess{
 | 
			
		||||
@ -551,6 +540,7 @@ pub const FileSystem = union(enum) {
 | 
			
		||||
                                .read = Implementation.read,
 | 
			
		||||
                                .seek = Implementation.seek,
 | 
			
		||||
                                .seekToEnd = Implementation.seekToEnd,
 | 
			
		||||
                                .skip = Implementation.skip,
 | 
			
		||||
                            },
 | 
			
		||||
                        };
 | 
			
		||||
                    };
 | 
			
		||||
@ -559,22 +549,19 @@ pub const FileSystem = union(enum) {
 | 
			
		||||
                },
 | 
			
		||||
 | 
			
		||||
                .native => |native| {
 | 
			
		||||
                    if (native.path_prefix.len == 0) return error.FileNotFound;
 | 
			
		||||
                    if (native.len == 0) return error.FileNotFound;
 | 
			
		||||
 | 
			
		||||
                    var path_buffer = std.mem.zeroes([4096]u8);
 | 
			
		||||
                    const seperator_length = @boolToInt(native[native.len - 1] != seperator);
 | 
			
		||||
 | 
			
		||||
                    const seperator_length = @boolToInt(native.path_prefix[
 | 
			
		||||
                        native.path_prefix.len - 1] != seperator);
 | 
			
		||||
 | 
			
		||||
                    if ((native.path_prefix.len + seperator_length + path.length) >=
 | 
			
		||||
                    if ((native.len + seperator_length + path.length) >=
 | 
			
		||||
                        path_buffer.len) return error.FileNotFound;
 | 
			
		||||
 | 
			
		||||
                    std.mem.copy(u8, path_buffer[0 ..], native.path_prefix);
 | 
			
		||||
                    std.mem.copy(u8, path_buffer[0 ..], native);
 | 
			
		||||
 | 
			
		||||
                    if (seperator_length != 0)
 | 
			
		||||
                        path_buffer[native.path_prefix.len] = seperator;
 | 
			
		||||
                    if (seperator_length != 0) path_buffer[native.len] = seperator;
 | 
			
		||||
 | 
			
		||||
                    std.mem.copy(u8, path_buffer[native.path_prefix.len ..
 | 
			
		||||
                    std.mem.copy(u8, path_buffer[native.len ..
 | 
			
		||||
                        path_buffer.len], path.buffer[0 .. path.length]);
 | 
			
		||||
 | 
			
		||||
                    ext.SDL_ClearError();
 | 
			
		||||
@ -629,15 +616,14 @@ pub const FileSystem = union(enum) {
 | 
			
		||||
 | 
			
		||||
                            while (to_seek != 0) {
 | 
			
		||||
                                const math = std.math;
 | 
			
		||||
                                const sought = @intCast(i64, math.min(to_seek, math.maxInt(i64)));
 | 
			
		||||
                                const sought = math.min(to_seek, math.maxInt(i64));
 | 
			
		||||
 | 
			
		||||
                                ext.SDL_ClearError();
 | 
			
		||||
 | 
			
		||||
                                if (ext.SDL_RWseek(rwOpsCast(context), sought, ext.RW_SEEK_CUR) < 0)
 | 
			
		||||
                                    return error.FileInaccessible;
 | 
			
		||||
                                if (ext.SDL_RWseek(rwOpsCast(context), @intCast(i64, sought),
 | 
			
		||||
                                    ext.RW_SEEK_CUR) < 0) return error.FileInaccessible;
 | 
			
		||||
 | 
			
		||||
                                // Cannot be less than zero because it is derived from `read`.
 | 
			
		||||
                                to_seek -= @intCast(u64, sought);
 | 
			
		||||
                                to_seek -= sought;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
@ -647,6 +633,13 @@ pub const FileSystem = union(enum) {
 | 
			
		||||
                            if (ext.SDL_RWseek(rwOpsCast(context), 0, ext.RW_SEEK_END) < 0)
 | 
			
		||||
                                return error.FileInaccessible;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        fn skip(context: *anyopaque, offset: i64) FileAccess.Error!void {
 | 
			
		||||
                            ext.SDL_ClearError();
 | 
			
		||||
 | 
			
		||||
                            if (ext.SDL_RWseek(rwOpsCast(context), offset, ext.RW_SEEK_SET) < 0)
 | 
			
		||||
                                return error.FileInaccessible;
 | 
			
		||||
                        }
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    return FileAccess{
 | 
			
		||||
@ -663,6 +656,7 @@ pub const FileSystem = union(enum) {
 | 
			
		||||
                            .read = Implementation.read,
 | 
			
		||||
                            .seek = Implementation.seek,
 | 
			
		||||
                            .seekToEnd = Implementation.seekToEnd,
 | 
			
		||||
                            .skip = Implementation.skip,
 | 
			
		||||
                        },
 | 
			
		||||
                    };
 | 
			
		||||
                },
 | 
			
		||||
@ -855,7 +849,7 @@ pub fn runGraphics(comptime Error: anytype,
 | 
			
		||||
 | 
			
		||||
    defer ext.SDL_DestroyRenderer(renderer);
 | 
			
		||||
 | 
			
		||||
    var cwd_file_system = FileSystem{.native =.{.path_prefix = "./"}};
 | 
			
		||||
    var cwd_file_system = FileSystem{.native ="./"};
 | 
			
		||||
    var data_access = try (try cwd_file_system.joinedPath(&.{"./data.oar"})).open(.readonly);
 | 
			
		||||
 | 
			
		||||
    defer data_access.close();
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user