161 lines
5.7 KiB
Zig
161 lines
5.7 KiB
Zig
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);
|
||
|
||
rearrange(grid_w, grid_h, tile_w, tile_h, img.data, outbuf);
|
||
|
||
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 rearrange(grid_w: u32, grid_h: u32, tile_w: u32, tile_h: u32, inbuf: []const u8, outbuf: []u8) void {
|
||
std.log.debug("rearrange: {}×{} grid of {}×{} tiles", .{ grid_w, grid_h, tile_w, tile_h });
|
||
|
||
const row_size = 4 * tile_w;
|
||
const row_stride = row_size * grid_w;
|
||
const tile_stride = row_stride * tile_h;
|
||
|
||
std.debug.assert(inbuf.len == tile_stride * grid_h);
|
||
std.debug.assert(outbuf.len == tile_stride * grid_h);
|
||
|
||
var outptr: usize = 0;
|
||
|
||
var tile_y: u32 = 0;
|
||
while (tile_y < grid_h) : (tile_y += 1) {
|
||
const tile_byte_offset = tile_y * tile_stride;
|
||
|
||
var tile_x: u32 = 0;
|
||
while (tile_x < grid_w) : (tile_x += 1) {
|
||
const column_byte_offset = tile_x * row_size;
|
||
|
||
var row: u32 = 0;
|
||
while (row < tile_h) : (row += 1) {
|
||
const row_byte_offset = row * row_stride + tile_byte_offset;
|
||
const byte_offset = row_byte_offset + column_byte_offset;
|
||
|
||
@memcpy(
|
||
outbuf[outptr .. outptr + row_size],
|
||
inbuf[byte_offset .. byte_offset + row_size],
|
||
);
|
||
|
||
outptr += row_size;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
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);
|
||
};
|
||
}
|