Remove global data in file system API
continuous-integration/drone/push Build is failing Details

This commit is contained in:
kayomn 2022-10-06 10:24:39 +01:00
parent d2f4c0afe1
commit 979c2a73f3
2 changed files with 196 additions and 158 deletions

View File

@ -21,30 +21,29 @@ test {
_ = sys; _ = sys;
} }
fn run(event_loop: *sys.EventLoop, graphics: *sys.GraphicsContext) anyerror!void { fn run(ev: *sys.EventLoop, fs: *const sys.FileSystem, gr: *sys.GraphicsContext) anyerror!void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit(); defer _ = gpa.deinit();
{ {
const file_access = try event_loop.open(.readonly, const file_access = try ev.open(.readonly, try fs.data.joinedPath(&.{"ona.lua"}));
try sys.FileSystem.data.joinedPath(&.{"data", "ona.lua"}));
defer event_loop.close(file_access); defer ev.close(file_access);
const file_size = try file_access.size(event_loop); const file_size = try file_access.size(ev);
const allocator = gpa.allocator(); const allocator = gpa.allocator();
const buffer = try allocator.alloc(u8, file_size); const buffer = try allocator.alloc(u8, file_size);
defer allocator.free(buffer); defer allocator.free(buffer);
if ((try event_loop.readFile(file_access, buffer)) != file_size) if ((try ev.readFile(file_access, buffer)) != file_size)
return error.ScriptLoadFailure; return error.ScriptLoadFailure;
event_loop.log(.debug, buffer); ev.log(.debug, buffer);
} }
while (graphics.poll()) |_| { while (gr.poll()) |_| {
graphics.present(); gr.present();
} }
} }

View File

@ -61,7 +61,6 @@ pub const EventLoop = opaque {
/// Internal state of the event loop hidden from the API consumer. /// Internal state of the event loop hidden from the API consumer.
/// ///
const Implementation = struct { const Implementation = struct {
user_prefix: []u8,
file_system_semaphore: *ext.SDL_sem, file_system_semaphore: *ext.SDL_sem,
file_system_mutex: *ext.SDL_mutex, file_system_mutex: *ext.SDL_mutex,
file_system_thread: ?*ext.SDL_Thread, file_system_thread: ?*ext.SDL_Thread,
@ -71,6 +70,8 @@ pub const EventLoop = opaque {
/// ///
/// ///
const InitError = error { const InitError = error {
DataFileNotFound,
DataFileInvalid,
OutOfSemaphores, OutOfSemaphores,
OutOfMutexes, OutOfMutexes,
OutOfMemory, OutOfMemory,
@ -147,22 +148,19 @@ pub const EventLoop = opaque {
/// ///
/// ///
fn init() InitError!Implementation { fn init() InitError!Implementation {
const data_file_access = @ptrCast(*FileAccess,
ext.SDL_RWFromFile("./data.tar", "r+") orelse return error.DataFileNotFound);
return Implementation{ return Implementation{
.user_prefix = create_pref_path: { .data_archive = tar.Archive.init(data_file_access) catch |err| switch (err) {
const path = ext.SDL_GetPrefPath("ona", "ona") orelse { error.Invalid, error.Inaccessible => return error.DataFileInvalid,
ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION,
"Failed to load user path");
return error.OutOfMemory;
};
break: create_pref_path path[0 .. std.mem.len(path)];
}, },
.file_system_semaphore = ext.SDL_CreateSemaphore(0) .file_system_semaphore = ext.SDL_CreateSemaphore(0)
orelse return error.OutOfSemaphores, orelse return error.OutOfSemaphores,
.file_system_mutex = ext.SDL_CreateMutex() orelse return error.OutOfMutexes, .file_system_mutex = ext.SDL_CreateMutex() orelse return error.OutOfMutexes,
.data_file = data_file_access,
.file_system_thread = null, .file_system_thread = null,
}; };
} }
@ -181,51 +179,12 @@ pub const EventLoop = opaque {
while (implementation.file_system_messages) |messages| { while (implementation.file_system_messages) |messages| {
switch (messages.request) { switch (messages.request) {
.exit => return 0, .exit => return 0,
.log => |*log_request| .log(log_request.kind, log_request.message),
.log => |*log_request| ext.SDL_LogMessage(ext.SDL_LOG_CATEGORY_APPLICATION, .open => |*open_request| open_request.result =
@enumToInt(log_request.kind), log_request.message.ptr), .open(open_request.mode, open_request.file_system_path),
.open => |*open_request| { .close => |*close_request| .close(close_request.file_access),
switch (open_request.file_system_path.file_system) {
.data => {
// TODO: Implement
open_request.result = error.NotFound;
},
.user => {
var path_buffer = std.mem.zeroes([4096]u8);
var path = stack.Fixed(u8){.buffer = path_buffer[0 .. ]};
path.pushAll(implementation.user_prefix) catch {
open_request.result = error.NotFound;
continue;
};
if (!open_request.file_system_path.write(path.writer())) {
open_request.result = error.NotFound;
continue;
}
if (ext.SDL_RWFromFile(&path_buffer, switch (open_request.mode) {
.readonly => "rb",
.overwrite => "wb",
.append => "ab",
})) |rw_ops| {
open_request.result = @ptrCast(*FileAccess, rw_ops);
} else {
open_request.result = error.NotFound;
}
},
}
},
.close => |*close_request| {
// TODO: Use this result somehow.
_ = ext.SDL_RWclose(@ptrCast(*ext.SDL_RWops, @alignCast(
@alignOf(ext.SDL_RWops), close_request.file_access)));
},
.read_file => |read_request| { .read_file => |read_request| {
// TODO: Implement. // TODO: Implement.
@ -270,63 +229,6 @@ pub const EventLoop = opaque {
} }
}; };
///
/// [LogKind.info] represents a log message which is purely informative and does not indicate
/// any kind of issue.
///
/// [LogKind.debug] represents a log message which is purely for debugging purposes and will
/// only occurs in debug builds.
///
/// [LogKind.warning] represents a log message which is a warning about a issue that does not
/// break anything important but is not ideal.
///
pub const LogKind = enum(u32) {
info = ext.SDL_LOG_PRIORITY_INFO,
debug = ext.SDL_LOG_PRIORITY_DEBUG,
warning = ext.SDL_LOG_PRIORITY_WARN,
};
///
/// [OpenError.NotFound] is a catch-all for when a file could not be located to be opened. This
/// may be as simple as it doesn't exist or the because the underlying file-system will not /
/// cannot give access to it at this time.
///
pub const OpenError = error {
NotFound,
};
///
/// [OpenMode.readonly] indicates that an existing file is opened in a read-only state,
/// disallowing write access.
///
/// [OpenMode.overwrite] indicates that an empty file has been created or an existing file has
/// been completely overwritten into.
///
/// [OpenMode.append] indicates that an existing file that has been opened for reading from and
/// writing to on the end of existing data.
///
pub const OpenMode = enum {
readonly,
overwrite,
append,
};
///
/// [SeekOrigin.head] indicates that a seek operation will seek from the offset origin of the
/// file beginning, or "head".
///
/// [SeekOrigin.tail] indicates that a seek operation will seek from the offset origin of the
/// file end, or "tail".
///
/// [SeekOrigin.cursor] indicates that a seek operation will seek from the current position of
/// the file cursor.
///
pub const SeekOrigin = enum {
head,
tail,
cursor,
};
/// ///
/// Closes access to the file referenced by `file_access` via `event_loop`. /// Closes access to the file referenced by `file_access` via `event_loop`.
/// ///
@ -488,15 +390,15 @@ pub const FileError = error {
/// Platform-agnostic mechanism for working with an abstraction of the underlying file-system(s) /// Platform-agnostic mechanism for working with an abstraction of the underlying file-system(s)
/// available to the application in a sandboxed environment. /// available to the application in a sandboxed environment.
/// ///
pub const FileSystem = enum { pub const FileSystem = struct {
data, data: Root,
user, user: Root,
/// ///
/// Platform-agnostic mechanism for referencing files and directories on a [FileSystem]. /// Platform-agnostic mechanism for referencing files and directories on a [FileSystem].
/// ///
pub const Path = struct { pub const Path = struct {
file_system: FileSystem, root: *const Root,
length: u16, length: u16,
buffer: [max]u8, buffer: [max]u8,
@ -521,7 +423,7 @@ pub const FileSystem = enum {
/// byte. Because of this, it is not safe to asume that a path may hold [max] individual /// byte. Because of this, it is not safe to asume that a path may hold [max] individual
/// characters. /// characters.
/// ///
pub const max = 1000; pub const max = 512;
/// ///
/// ///
@ -540,12 +442,17 @@ pub const FileSystem = enum {
}; };
/// ///
/// Creates and returns a [Path] value in the file system to the location specified by the
/// joining of the `sequences` path values.
/// ///
pub fn joinedPath(file_system: FileSystem, sequences: []const []const u8) PathError!Path { ///
pub const Root = struct {
prefix: []const u8,
///
///
///
pub fn joinedPath(root: Root, sequences: []const []const u8) PathError!Path {
var path = Path{ var path = Path{
.file_system = file_system, .root = root,
.buffer = std.mem.zeroes([Path.max]u8), .buffer = std.mem.zeroes([Path.max]u8),
.length = 0, .length = 0,
}; };
@ -587,6 +494,7 @@ pub const FileSystem = enum {
return path; return path;
} }
}; };
};
/// ///
/// ///
@ -629,13 +537,125 @@ pub const GraphicsContext = opaque {
/// ///
/// ///
pub fn GraphicsRunner(comptime Errors: type) type { pub fn GraphicsRunner(comptime Errors: type) type {
return fn (*EventLoop, *GraphicsContext) callconv(.Async) Errors!void; return fn (*EventLoop, *FileSystem, *GraphicsContext) callconv(.Async) Errors!void;
}
///
/// [LogKind.info] represents a log message which is purely informative and does not indicate
/// any kind of issue.
///
/// [LogKind.debug] represents a log message which is purely for debugging purposes and will
/// only occurs in debug builds.
///
/// [LogKind.warning] represents a log message which is a warning about a issue that does not
/// break anything important but is not ideal.
///
pub const LogKind = enum(u32) {
info = ext.SDL_LOG_PRIORITY_INFO,
debug = ext.SDL_LOG_PRIORITY_DEBUG,
warning = ext.SDL_LOG_PRIORITY_WARN,
};
///
/// [OpenError.NotFound] is a catch-all for when a file could not be located to be opened. This
/// may be as simple as it doesn't exist or the because the underlying file-system will not /
/// cannot give access to it at this time.
///
pub const OpenError = error {
NotFound,
};
///
/// [OpenMode.readonly] indicates that an existing file is opened in a read-only state,
/// disallowing write access.
///
/// [OpenMode.overwrite] indicates that an empty file has been created or an existing file has
/// been completely overwritten into.
///
/// [OpenMode.append] indicates that an existing file that has been opened for reading from and
/// writing to on the end of existing data.
///
pub const OpenMode = enum {
readonly,
overwrite,
append,
};
///
///
///
pub const RunError = error {
InitFailure,
AlreadyRunning,
};
///
/// [SeekOrigin.head] indicates that a seek operation will seek from the offset origin of the
/// file beginning, or "head".
///
/// [SeekOrigin.tail] indicates that a seek operation will seek from the offset origin of the
/// file end, or "tail".
///
/// [SeekOrigin.cursor] indicates that a seek operation will seek from the current position of
/// the file cursor.
///
pub const SeekOrigin = enum {
head,
tail,
cursor,
};
///
///
///
pub fn close(file_access: *FileAccess) void {
if (!ext.SDL_RWclose(@ptrCast(*ext.SDL_RWops,
@alignCast(@alignOf(ext.SDL_RWops), file_access)))) {
ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION,
"Failed to close file - may have been already closed");
}
} }
/// ///
/// ///
/// ///
pub fn runGraphics(comptime Errors: anytype, comptime run: GraphicsRunner(Errors)) Errors!void { pub fn log(kind: LogKind, message: []const u8) void {
ext.SDL_LogMessage(ext.SDL_LOG_CATEGORY_APPLICATION,
@enumToInt(kind), "%.*s", message.len, message.ptr);
}
///
///
///
pub fn open(mode: OpenMode, file_system_path: FileSystem.Path) OpenError!*FileAccess {
switch (file_system_path.file_system) {
.data => {
// TODO: Implement
return error.NotFound;
},
.user => {
var path_buffer = std.mem.zeroes([4096]u8);
var path = stack.Fixed(u8){.buffer = path_buffer[0 .. ]};
path.pushAll("/home/kayomn/.local/share") catch return error.NotFound;
if (file_system_path.write(path.writer())) return error.NotFound;
return @ptrCast(*FileAccess, ext.SDL_RWFromFile(&path_buffer, switch (mode) {
.readonly => "rb",
.overwrite => "wb",
.append => "ab",
})) orelse error.NotFound;
},
}
}
///
///
///
pub fn runGraphics(comptime Errors: anytype, comptime run: GraphicsRunner(Errors)) (RunError || Errors)!void {
if (ext.SDL_Init(ext.SDL_INIT_EVERYTHING) != 0) { if (ext.SDL_Init(ext.SDL_INIT_EVERYTHING) != 0) {
ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize runtime"); ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize runtime");
@ -669,6 +689,25 @@ pub fn runGraphics(comptime Errors: anytype, comptime run: GraphicsRunner(Errors
defer ext.SDL_DestroyRenderer(renderer); defer ext.SDL_DestroyRenderer(renderer);
var file_system = FileSystem{
.user = .{.prefix = create_pref_path: {
const path = ext.SDL_GetPrefPath("ona", "ona") orelse {
ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION,
"Failed to load user path");
return error.InitFailure;
};
break: create_pref_path path[0 .. std.mem.len(path)];
}},
.data = .{.prefix = "./"},
};
defer {
ext.SDL_free(file_system.user.prefix);
}
var event_loop = EventLoop.Implementation.init() catch |err| { var event_loop = EventLoop.Implementation.init() catch |err| {
switch (err) { switch (err) {
error.OutOfMemory => ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION, error.OutOfMemory => ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION,