ona/source/ona/files.zig

202 lines
5.7 KiB
Zig

const coral = @import("coral");
const ext = @import("./ext.zig");
pub const FileAccessor = struct {
context: *anyopaque,
actions: *const struct {
open_readable: *const fn (context: *anyopaque, file_path: []const u8) OpenError!*ReadableFile,
open_writable: *const fn (context: *anyopaque, file_path: []const u8) OpenError!*WritableFile,
query: *const fn (context: *anyopaque, file_path: []const u8) QueryError!FileInfo,
},
pub fn bind(comptime State: type, state: *State) FileAccessor {
const Actions = struct {
fn as_concrete(context: *anyopaque) *State {
return @ptrCast(*State, @alignCast(@alignOf(State), context));
}
fn open_readable(context: *anyopaque, file_path: []const u8) OpenError!*ReadableFile {
return as_concrete(context).open_readable(file_path);
}
fn open_writable(context: *anyopaque, file_path: []const u8) OpenError!*WritableFile {
return as_concrete(context).open_writable(file_path);
}
fn query(context: *anyopaque, file_path: []const u8) QueryError!FileInfo {
return as_concrete(context).query(file_path);
}
};
return .{
.context = @ptrCast(*anyopaque, state),
.actions = &.{
.open_readable = Actions.open_readable,
.open_writable = Actions.open_writable,
.query = Actions.query,
},
};
}
pub fn open_readable(self: FileAccessor, file_path: []const u8) OpenError!*ReadableFile {
return self.actions.open_readable(self.context, file_path);
}
pub fn open_writable(self: FileAccessor, file_path: []const u8) OpenError!*WritableFile {
return self.actions.open_readable(self.context, file_path);
}
pub fn query(self: FileAccessor, file_path: []const u8) QueryError!FileInfo {
return self.actions.query(self.context, file_path);
}
};
pub const FileInfo = struct {
size: u64,
};
pub const FileSandbox = struct {
prefix: []const u8,
flags: packed struct {
is_readable: bool = false,
is_writable: bool = false,
is_queryable: bool = false,
},
const native_path_max = 4095;
fn native_path_of(file_sandbox: *FileSandbox, file_path: []const u8) [native_path_max + 1]u8 {
var native_path = [_]u8{0} ** (native_path_max + 1);
if ((file_sandbox.prefix.len + file_path.len) < native_path_max) {
coral.io.copy(&native_path, file_sandbox.prefix);
coral.io.copy(native_path[file_sandbox.prefix.len ..], file_path);
}
return native_path;
}
pub fn open_readable(file_sandbox: *FileSandbox, file_path: []const u8) OpenError!*ReadableFile {
if (!file_sandbox.flags.is_readable) return error.AccessDenied;
return @ptrCast(*ReadableFile, ext.SDL_RWFromFile(&file_sandbox.native_path_of(file_path), "rb") orelse {
return error.FileNotFound;
});
}
pub fn open_writable(file_sandbox: *FileSandbox, file_path: []const u8) OpenError!*WritableFile {
if (!file_sandbox.flags.is_writable) return error.AccessDenied;
return @ptrCast(*WritableFile, ext.SDL_RWFromFile(&file_sandbox.native_path_of(file_path), "wb") orelse {
return error.FileNotFound;
});
}
pub fn query(file_sandbox: *FileSandbox, file_path: []const u8) QueryError!FileInfo {
if (!file_sandbox.flags.is_queryable) return error.AccessDenied;
const rw_ops = ext.SDL_RWFromFile(&file_sandbox.native_path_of(file_path), "rb") orelse {
return error.FileNotFound;
};
defer _ = ext.SDL_RWclose(rw_ops);
const file_size = ext.SDL_RWsize(rw_ops);
if (file_size < 0) return error.FileNotFound;
return FileInfo{
.size = @intCast(u64, file_size),
};
}
};
pub const OpenError = QueryError || error {TooManyFiles};
pub const ReadableFile = opaque {
pub fn as_reader(self: *ReadableFile) coral.io.Reader {
return coral.io.Reader.bind(self, ReadableFile);
}
fn as_rw_ops(self: *ReadableFile) *ext.SDL_RWops {
return @ptrCast(*ext.SDL_RWops, @alignCast(@alignOf(ext.SDL_RWops), self));
}
pub fn close(self: *ReadableFile) bool {
return ext.SDL_RWclose(self.as_rw_ops()) != 0;
}
pub fn read(self: *ReadableFile, buffer: []u8) coral.io.ReadError!usize {
ext.SDL_ClearError();
const buffer_read = ext.SDL_RWread(self.as_rw_ops(), buffer.ptr, @sizeOf(u8), buffer.len);
if ((buffer_read == 0) and (ext.SDL_GetError().* != 0)) return error.IoUnavailable;
return buffer_read;
}
pub fn rewind(self: *ReadableFile) SeekError!void {
return self.seek(0);
}
pub fn seek(self: *ReadableFile, absolute: u64) SeekError!void {
ext.SDL_ClearError();
// TODO: Fix int cast.
const sought = ext.SDL_RWseek(self.as_rw_ops(), @intCast(i64, absolute), ext.RW_SEEK_SET);
if ((sought == -1) and (ext.SDL_GetError().* != 0)) return error.IoUnavailable;
}
};
pub const QueryError = error {
FileNotFound,
AccessDenied,
};
pub const SeekError = error {
IoUnavailable,
};
pub const WritableFile = opaque {
pub fn as_writer(self: *WritableFile) coral.io.Writer {
return coral.io.Writer.bind(WritableFile, self);
}
fn as_rw_ops(self: *WritableFile) *ext.SDL_RWops {
return @ptrCast(*ext.SDL_RWops, @alignCast(@alignOf(ext.SDL_RWops), self));
}
pub fn close(self: *WritableFile) bool {
return ext.SDL_RWclose(self.as_rw_ops()) != 0;
}
pub fn rewind(self: *WritableFile) SeekError!void {
return self.seek(0);
}
pub fn seek(self: *WritableFile, absolute: u64) SeekError!void {
ext.SDL_ClearError();
// TODO: Fix int cast.
const sought = ext.SDL_RWseek(self.as_rw_ops(), @intCast(i64, absolute), ext.RW_SEEK_SET);
if ((sought == -1) and (ext.SDL_GetError().* != 0)) return error.IoUnavailable;
}
pub fn write(self: *WritableFile, buffer: []const u8) coral.io.WriteError!usize {
ext.SDL_ClearError();
const buffer_read = ext.SDL_RWwrite(self.as_rw_ops(), buffer.ptr, @sizeOf(u8), buffer.len);
if ((buffer_read == 0) and (ext.SDL_GetError().* != 0)) return error.IoUnavailable;
return buffer_read;
}
};