const std = @import("std"); const zstbi = @import("zstbi"); pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); zstbi.init(allocator); defer zstbi.deinit(); const cwd = std.fs.cwd(); var assets_dir = openDirOrExit(cwd, "assets", .{ .iterate = true }); defer assets_dir.close(); var library_dir = openDirOrExit(cwd, "library", .{}); defer library_dir.close(); visit(assets_dir, library_dir, allocator); } fn visit(assets_dir: std.fs.Dir, library_dir: std.fs.Dir, allocator: std.mem.Allocator) void { var it = assets_dir.iterate(); while (it.next() catch |err| blk: { std.log.err("Directory iteration interrupted due to an error: {s}", .{@errorName(err)}); break :blk null; }) |entry| { if (entry.kind == .directory) { var assets_subdir = assets_dir.openDir(entry.name, .{ .iterate = true }) catch |err| { std.log.warn("Skipping directory \"{s}\" due to an error: {s}", .{ entry.name, @errorName(err) }); continue; }; defer assets_subdir.close(); library_dir.makeDir(entry.name) catch |err| switch (err) { error.PathAlreadyExists => { // This is fine }, else => { std.log.warn("Skipping directory \"{s}\" due to an error while creating corresponding library directory: {s}", .{ entry.name, @errorName(err) }); continue; }, }; var library_subdir = library_dir.openDir(entry.name, .{}) catch |err| { std.log.warn("Skipping directory \"{s}\" due to an error while opening corresponding library directory: {s}", .{ entry.name, @errorName(err) }); continue; }; defer library_subdir.close(); visit(assets_subdir, library_subdir, allocator); continue; } if (entry.kind != .file) { std.log.warn("Skipping \"{s}\", which is not a file nor a directory.", .{entry.name}); continue; } std.log.info("Processing \"{s}\"...", .{entry.name}); const infile = assets_dir.openFile(entry.name, .{}) catch |err| { std.log.err("Could not open \"{s}\" file: {s}", .{ entry.name, @errorName(err) }); continue; }; defer infile.close(); const inbuf = infile.readToEndAlloc(allocator, std.math.maxInt(usize)) catch |err| { std.log.err("Could not read \"{s}\" file contents due to an error: {s}", .{ entry.name, @errorName(err) }); continue; }; defer allocator.free(inbuf); var img = zstbi.Image.loadFromMemory(inbuf, 4) catch |err| { std.log.err("Error reading \"{s}\" as an image file: {s}", .{ entry.name, @errorName(err) }); continue; }; defer img.deinit(); std.log.debug("size: {}×{} | components: {}", .{ img.width, img.height, img.num_components }); const grid_w = 4; const grid_h = 4; const tile_count = grid_w * grid_h; const tile_w = std.math.divExact(u32, img.width, grid_w) catch |err| { std.log.err("Cannot divide image width ({}) by {}: {s}", .{ img.width, grid_w, @errorName(err) }); continue; }; const tile_h = std.math.divExact(u32, img.height, grid_h) catch |err| { std.log.err("Cannot divide image height ({}) by {}: {s}", .{ img.height, grid_h, @errorName(err) }); continue; }; std.log.debug("tile size: {}×{}", .{ tile_w, tile_h }); const outbuf = allocator.alloc(u8, 4 * tile_w * tile_h * tile_count) catch { std.log.err("Ran out of memory while trying to allocate output buffer", .{}); continue; }; defer allocator.free(outbuf); @memcpy(outbuf, img.data); const out_name = std.fs.path.stem(entry.name); library_dir.writeFile(.{ .data = outbuf, .sub_path = out_name, }) catch |err| { std.log.err("Couldn't write \"{s}\" file corresponding to \"{s}\" asset file: {s}", .{ out_name, entry.name, @errorName(err) }); }; } } fn openDirOrExit(dir: std.fs.Dir, sub_path: []const u8, args: std.fs.Dir.OpenOptions) std.fs.Dir { return dir.openDir(sub_path, args) catch |err| { std.log.err("Could not open \"{s}\" directory: {s}", .{ sub_path, @errorName(err) }); std.posix.exit(1); }; }