summaryrefslogtreecommitdiff
path: root/src/dump.zig
blob: a41b2befd67462ac763e8c0568fd9abc23dc629c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
const std = @import("std");
const adler32 = std.hash.Adler32;
const openFlags = std.fs.File.OpenFlags{
    .read = true,
    .write = false,
    .lock = std.fs.File.Lock.None,
};
const output_flags = std.fs.File.CreateFlags{
    .read = false,
    .truncate = true,
    .exclusive = false,
    .lock = std.fs.File.Lock.None,
};

fn help(args: [][]u8) u8 {
    std.debug.warn(
        \\Usage: {} {} [-h] [-o OUTPUT] input ...
        \\
        \\Optional arguments:
        \\  -h, --help              show this help message and exit
        \\  -o, --output OUTPUT     output header file (by default, stdout)
        \\
        \\Positional arguments:
        \\  input                   one or more input files
        \\
    , .{ args[0], args[1] });
    return if (args.len < 3) 255 else 0;
}

pub fn cmd(args: [][]u8) u8 {
    if (args.len < 3) {
        return help(args);
    }

    const State = enum {
        Start,
        OptionOutput,
    };

    var output_file: ?std.fs.File = null;
    defer if (output_file != null) output_file.?.close();

    var input_index: ?usize = null;
    var state: State = State.Start;

    parse_args: for (args[2..args.len]) |c, i| {
        switch (state) {
            State.Start => {
                switch (adler32.hash(c)) {
                    adler32.hash("-h"), adler32.hash("--help") => return help(args),
                    adler32.hash("-o"), adler32.hash("--output") => state = State.OptionOutput,
                    else => {
                        if (input_index == null) {
                            input_index = i;
                            break :parse_args;
                        }
                    },
                }
            },
            State.OptionOutput => {
                output_file = switch (std.fs.path.isAbsolute(c)) {
                    true => std.fs.createFileAbsolute(c, output_flags) catch return 128,
                    false => std.fs.cwd().createFile(c, output_flags) catch return 128,
                };
                state = State.Start;
            },
        }
    }

    var output = (output_file orelse std.io.getStdOut()).outStream();

    if (input_index == null)
        return help(args);

    for (args[2 + input_index.? .. args.len]) |path| {
        output.print("// {}\n", .{path}) catch return 1;
        hexdump(output, path, path) catch std.debug.warn("// Could not read file '{}'\n", .{path});
    }

    return 0;
}

fn hexdump(out: std.fs.File.OutStream, path: []const u8, name: []const u8) !void {
    var file = switch (std.fs.path.isAbsolute(path)) {
        true => try std.fs.openFileAbsolute(path, openFlags),
        false => try std.fs.cwd().openFile(path, openFlags),
    };
    defer file.close();

    var buffer: [16]u8 = undefined;
    var r: u64 = undefined;
    var l: u64 = 0;

    try out.print("constexpr uint8_t {}_data[] = {{\n", .{name});
    while (true) {
        r = file.read(&buffer) catch break;
        if (r <= 0)
            break;

        l += r;
        try out.print("  ", .{});
        for (buffer[0..r]) |c| {
            try out.print("0x{x:2}, ", .{c});
        }
        try out.print("\n", .{});
    }
    try out.print("}};\n", .{});
    try out.print("constexpr size_t {}_len = {};\n\n", .{ name, l });
}