280 lines
8.7 KiB
Zig
280 lines
8.7 KiB
Zig
pub const CookResultEnum = enum {
|
|
Success,
|
|
Failure,
|
|
};
|
|
|
|
pub const FileBytes = struct {
|
|
path: []const u8,
|
|
bytes: []u8,
|
|
};
|
|
|
|
pub const ExtraFiles = std.ArrayList(FileBytes);
|
|
|
|
pub const CookResult = struct {
|
|
bytes: std.ArrayList(u8),
|
|
result: CookResultEnum = .Success,
|
|
extraFiles: ?ExtraFiles = null,
|
|
|
|
pub fn deinit(self: *@This()) void {
|
|
self.bytes.deinit();
|
|
}
|
|
};
|
|
|
|
pub const CookParams = struct {
|
|
noSave: bool = false,
|
|
fileName: []const u8,
|
|
cookFileName: []const u8,
|
|
};
|
|
pub const CookInfo = struct {
|
|
assetType: []const u8 = "none",
|
|
};
|
|
|
|
pub const CookRegistry = struct {
|
|
allocator: std.mem.Allocator,
|
|
cooks: std.StringHashMapUnmanaged(CookFunction) = .{},
|
|
generates: std.StringHashMapUnmanaged(CookGenerateFunction) = .{},
|
|
|
|
typesByExt: std.StringHashMapUnmanaged([]const u8) = .{},
|
|
|
|
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
|
const self = try allocator.create(@This());
|
|
self.* = .{
|
|
.allocator = allocator,
|
|
};
|
|
|
|
gCookRegistry = self;
|
|
return self;
|
|
}
|
|
|
|
pub fn install(self: *@This(), typeName: []const u8, generate: CookGenerateFunction, cook: CookFunction, extensions: []const []const u8) !void {
|
|
try self.generates.put(self.allocator, typeName, generate);
|
|
try self.cooks.put(self.allocator, typeName, cook);
|
|
|
|
for (extensions) |ext| {
|
|
try self.typesByExt.put(self.allocator, ext, typeName);
|
|
}
|
|
}
|
|
|
|
pub fn destroy(self: *@This()) void {
|
|
self.cooks.deinit(self.allocator);
|
|
self.typesByExt.deinit(self.allocator);
|
|
self.generates.deinit(self.allocator);
|
|
|
|
self.allocator.destroy(self);
|
|
}
|
|
};
|
|
|
|
var gCookRegistry: *CookRegistry = undefined;
|
|
var gCooking: bool = false;
|
|
|
|
pub fn getRegistry() *CookRegistry {
|
|
return gCookRegistry;
|
|
}
|
|
|
|
pub fn startup(allocator: std.mem.Allocator) !void {
|
|
gCooking = true;
|
|
_ = try CookRegistry.create(allocator);
|
|
}
|
|
|
|
pub fn shutdown() void {
|
|
if (gCooking) {
|
|
gCookRegistry.destroy();
|
|
gCooking = false;
|
|
}
|
|
}
|
|
|
|
pub const GenerateError = error{UnableToGenerate};
|
|
pub const LoadError = error{BadFile};
|
|
|
|
pub const CookGenerateFunction = *const fn (allocator: std.mem.Allocator, filePath: []const u8, out: *std.ArrayList(u8)) GenerateError!void;
|
|
pub const CookFunction = *const fn (allocator: std.mem.Allocator, dir: std.fs.Dir, path: []const u8, params: CookParams) CookResult;
|
|
|
|
pub fn loadFileAlloc(allocator: std.mem.Allocator, dir: std.fs.Dir, path: []const u8) ![]align(8) u8 {
|
|
var file = try dir.openFile(path, .{});
|
|
defer file.close();
|
|
const filesize = (try file.stat()).size + 1; // add null byte
|
|
const buffer: []align(8) u8 = try allocator.alignedAlloc(u8, 8, filesize);
|
|
errdefer allocator.free(buffer);
|
|
try file.reader().readNoEof(buffer[0 .. buffer.len - 1]);
|
|
buffer[buffer.len - 1] = 0;
|
|
|
|
return buffer;
|
|
}
|
|
|
|
pub fn getExtension(path: []const u8) ?[]const u8 {
|
|
var extension: []const u8 = path;
|
|
var i: i32 = @intCast(path.len - 1);
|
|
while (i > 0) : (i -= 1) {
|
|
if (path[@intCast(i)] == '.') {
|
|
extension = path[@intCast(i)..];
|
|
return extension;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
pub fn generateCookFile(allocator: std.mem.Allocator, dir: std.fs.Dir, path: []const u8) !void {
|
|
if (getExtension(path)) |extension| {
|
|
if (gCookRegistry.typesByExt.get(extension)) |assetType| {
|
|
var cookFilePath = std.ArrayList(u8).init(allocator);
|
|
defer cookFilePath.deinit();
|
|
const generateFunction = gCookRegistry.generates.get(assetType).?;
|
|
|
|
var outPath: std.ArrayListUnmanaged(u8) = .{};
|
|
defer outPath.deinit(allocator);
|
|
|
|
try outPath.appendSlice(allocator, path);
|
|
try outPath.appendSlice(allocator, ".cook");
|
|
|
|
try generateFunction(allocator, path, &cookFilePath);
|
|
|
|
const file = try dir.createFile(outPath.items, .{});
|
|
defer file.close();
|
|
|
|
try file.writeAll(cookFilePath.items);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn cookFile(allocator: std.mem.Allocator, dir: std.fs.Dir, path: []const u8) !void {
|
|
std.debug.print("{s}\n", .{path});
|
|
const extension = getExtension(path).?;
|
|
|
|
if (std.mem.eql(u8, extension, ".cook")) {
|
|
return;
|
|
}
|
|
|
|
var cookFilePath = std.ArrayList(u8).init(allocator);
|
|
defer cookFilePath.deinit();
|
|
|
|
try cookFilePath.appendSlice(path);
|
|
try cookFilePath.appendSlice(".cook");
|
|
|
|
if (gCookRegistry.typesByExt.get(extension)) |assetType| {
|
|
const cookFunction = gCookRegistry.cooks.get(assetType).?;
|
|
|
|
dir.access(cookFilePath.items, .{}) catch return;
|
|
|
|
var results = cookFunction(allocator, dir, path, .{
|
|
.cookFileName = cookFilePath.items,
|
|
.fileName = path,
|
|
});
|
|
defer results.deinit();
|
|
|
|
var outPath = std.ArrayList(u8).init(allocator);
|
|
defer outPath.deinit();
|
|
|
|
try outPath.appendSlice("_cooked/");
|
|
try outPath.appendSlice(path);
|
|
try outPath.appendSlice(".");
|
|
try outPath.appendSlice(assetType);
|
|
|
|
{
|
|
var basePath: []const u8 = outPath.items;
|
|
var index: usize = basePath.len - 1;
|
|
while (basePath[index] != '/' and basePath[index] != '\\') : (index -= 1) {}
|
|
basePath = basePath[0..index];
|
|
try dir.makePath(basePath);
|
|
}
|
|
|
|
if (results.result == .Success) {
|
|
const file = try dir.createFile(outPath.items, .{});
|
|
defer file.close();
|
|
try file.writeAll(results.bytes.items);
|
|
}
|
|
return;
|
|
} else {
|
|
unreachable;
|
|
}
|
|
}
|
|
|
|
pub fn generateAllCookFiles(allocator: std.mem.Allocator, dir: std.fs.Dir) !void {
|
|
var iter = dir.iterate();
|
|
while (try iter.next()) |next| {
|
|
switch (next.kind) {
|
|
.file => {
|
|
try generateCookFile(allocator, dir, next.name);
|
|
},
|
|
.directory => {
|
|
if (!std.mem.eql(u8, "_cooked", next.name)) {
|
|
var subDir = try dir.openDir(next.name, .{ .iterate = true });
|
|
defer subDir.close();
|
|
|
|
try generateAllCookFiles(allocator, subDir);
|
|
}
|
|
},
|
|
else => {},
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn cookAllFiles(allocator: std.mem.Allocator, dir: std.fs.Dir) !void {
|
|
var walker = try dir.walk(allocator);
|
|
defer walker.deinit();
|
|
|
|
while (try walker.next()) |next| {
|
|
switch (next.kind) {
|
|
.file => {
|
|
if (std.mem.startsWith(u8, next.path, "_cooked")) {
|
|
continue;
|
|
}
|
|
|
|
if (std.mem.startsWith(u8, next.path, ".gitignore")) {
|
|
continue;
|
|
}
|
|
|
|
try cookFile(allocator, dir, next.path);
|
|
},
|
|
else => {},
|
|
}
|
|
}
|
|
}
|
|
|
|
test "testing cooking" {
|
|
const alloc = std.testing.allocator;
|
|
|
|
const Test = struct {
|
|
const TestConfig = struct {
|
|
info: CookInfo = .{ .assetType = "Texture" }, // there must always be a CookInfo field
|
|
sourceType: []const u8 = "png",
|
|
};
|
|
|
|
pub fn generateFunction(allocator: std.mem.Allocator, path: []const u8, out: *std.ArrayList(u8)) GenerateError!void {
|
|
_ = allocator;
|
|
_ = path;
|
|
out.clearRetainingCapacity();
|
|
|
|
std.json.stringify(TestConfig{}, .{ .whitespace = .indent_4 }, out.writer()) catch return GenerateError.UnableToGenerate;
|
|
}
|
|
|
|
pub fn cookFunction(allocator: std.mem.Allocator, dir: std.fs.Dir, path: []const u8, params: CookParams) CookResult {
|
|
_ = params;
|
|
const rawFileBytes = loadFileAlloc(allocator, dir, path) catch unreachable;
|
|
defer allocator.free(rawFileBytes);
|
|
const rv = std.ArrayList(u8).init(allocator);
|
|
|
|
return .{
|
|
.bytes = rv,
|
|
.result = .Success,
|
|
};
|
|
}
|
|
};
|
|
|
|
const dir = try std.fs.cwd().openDir("test", .{ .iterate = true });
|
|
|
|
const registry = try CookRegistry.create(alloc);
|
|
defer registry.destroy();
|
|
try registry.generates.put(registry.allocator, "Texture", Test.generateFunction);
|
|
try registry.cooks.put(registry.allocator, "Texture", Test.cookFunction);
|
|
try registry.typesByExt.put(registry.allocator, ".png", "Texture");
|
|
|
|
// scan and generate all texture files
|
|
try generateAllCookFiles(alloc, dir);
|
|
try cookAllFiles(alloc, dir);
|
|
// try generateCookFile(alloc, dir, "icon.png");
|
|
// try cookFile(alloc, dir, "icon.png");
|
|
}
|
|
|
|
const std = @import("std");
|