102 lines
3.2 KiB
Zig
102 lines
3.2 KiB
Zig
const std = @import("std");
|
|
const uri_parser = @import("uri_parser.zig");
|
|
|
|
const ResourceManager = @This();
|
|
|
|
allocator: std.mem.Allocator,
|
|
paths: []const []const u8,
|
|
// TODO: Use comptime hash map for resource_types
|
|
resource_map: std.StringArrayHashMapUnmanaged(ResourceType) = .{},
|
|
resources: std.StringHashMapUnmanaged(Resource) = .{},
|
|
cwd: std.fs.Dir,
|
|
context: ?*anyopaque = null,
|
|
|
|
pub fn init(allocator: std.mem.Allocator, paths: []const []const u8, resource_types: []const ResourceType) !ResourceManager {
|
|
var cwd = try std.fs.openDirAbsolute(try std.fs.selfExeDirPathAlloc(allocator), .{});
|
|
errdefer cwd.close();
|
|
|
|
var resource_map: std.StringArrayHashMapUnmanaged(ResourceType) = .{};
|
|
for (resource_types) |res| {
|
|
try resource_map.put(allocator, res.name, res);
|
|
}
|
|
|
|
return ResourceManager{
|
|
.allocator = allocator,
|
|
.paths = paths,
|
|
.resource_map = resource_map,
|
|
.cwd = cwd,
|
|
};
|
|
}
|
|
|
|
pub const ResourceType = struct {
|
|
name: []const u8,
|
|
load: fn (context: ?*anyopaque, mem: []const u8) error{ InvalidResource, CorruptData }!*anyopaque,
|
|
unload: fn (context: ?*anyopaque, resource: *anyopaque) void,
|
|
};
|
|
|
|
pub fn setLoadContext(self: *ResourceManager, ctx: anytype) void {
|
|
var context = self.allocator.create(@TypeOf(ctx)) catch unreachable;
|
|
context.* = ctx;
|
|
self.context = context;
|
|
}
|
|
|
|
pub fn getResource(self: *ResourceManager, uri: []const u8) !Resource {
|
|
if (self.resources.get(uri)) |res|
|
|
return res;
|
|
|
|
var file: ?std.fs.File = null;
|
|
const uri_data = try uri_parser.parseUri(uri);
|
|
|
|
for (self.paths) |path| {
|
|
var dir = try self.cwd.openDir(path, .{});
|
|
defer dir.close();
|
|
|
|
file = dir.openFile(uri_data.path, .{}) catch |err| switch (err) {
|
|
error.FileNotFound => continue,
|
|
else => return err,
|
|
};
|
|
errdefer file.deinit();
|
|
}
|
|
|
|
if (file) |f| {
|
|
if (self.resource_map.get(uri_data.scheme)) |res_type| {
|
|
var data = try f.reader().readAllAlloc(self.allocator, std.math.maxInt(usize));
|
|
errdefer self.allocator.free(data);
|
|
|
|
const resource = try res_type.load(self.context, data);
|
|
errdefer res_type.unload(self.context, resource);
|
|
|
|
const res = Resource{
|
|
.uri = try self.allocator.dupe(u8, uri),
|
|
.resource = resource,
|
|
.size = data.len,
|
|
};
|
|
try self.resources.putNoClobber(self.allocator, uri, res);
|
|
return res;
|
|
}
|
|
return error.UnknownResourceType;
|
|
}
|
|
|
|
return error.ResourceNotFound;
|
|
}
|
|
|
|
pub fn unloadResource(self: *ResourceManager, res: Resource) void {
|
|
const uri_data = uri_parser.parseUri(res.uri) catch unreachable;
|
|
if (self.resource_map.get(uri_data.scheme)) |res_type| {
|
|
res_type.unload(self.context, res.resource);
|
|
}
|
|
|
|
_ = self.resources.remove(res.uri);
|
|
}
|
|
|
|
pub const Resource = struct {
|
|
uri: []const u8,
|
|
resource: *anyopaque,
|
|
size: u64,
|
|
|
|
// Returns the raw data, which you can use in any ways. Internally it is stored
|
|
// as an *anyopaque
|
|
pub fn getData(res: *const Resource, comptime T: type) *T {
|
|
return @ptrCast(*T, @alignCast(std.meta.alignment(*T), res.resource));
|
|
}
|
|
};
|