~mil/mepo

ref: cd3c8e48ec2a6dce07743e2587ff829d07792e4e mepo/src/api/filedump.zig -rw-r--r-- 7.3 KiB
cd3c8e48Miles Alan Fix bugs related to filedump for timers a month ago
                                                                                
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
const Mepo = @import("../Mepo.zig");
const types = @import("../types.zig");
const std = @import("std");
const sdl = @import("../sdlshim.zig");

const utilfile = @import("../util/utilfile.zig");
const utilsdl = @import("../util/utilsdl.zig");
const utilprefs = @import("../util/utilprefs.zig");

pub const spec = .{
    .name = "filedump",
    .desc = "Save the current state of all preferences to a file.",
    .args = (&[_]types.MepoFnSpecArg{
        .{ .tag = .Text, .name = "datatypes", .desc = "Types of data to save (p = pins, r = preferences, b = bindings); ex. 'pr' would save both pins and preferences but not bindings" },
        .{ .tag = .Text, .name = "filepath", .desc = "Path to file to save state to" },
    })[0..],
    .execute = execute,
};

fn execute(mepo: *Mepo, args: [types.MepoFnNargs]types.MepoArg) !void {
    const save_types = args[0].Text;
    const filepath = args[1].Text;
    try filedump(mepo, save_types, filepath);
}

fn filedump(mepo: *Mepo, save_types: []const u8, filepath: []const u8) !void {
    var arena = std.heap.ArenaAllocator.init(mepo.allocator);
    defer arena.deinit();

    const expanded_path = try utilfile.wordexp_filepath(arena.allocator(), filepath);
    try utilfile.mkdirp_folder_basename(expanded_path);
    const mepolang_file = try std.fs.createFileAbsoluteZ(expanded_path, .{});
    defer mepolang_file.close();
    var lines = std.ArrayList([]const u8).init(arena.allocator());

    // 0. Comment line
    try lines.append(try std.fmt.allocPrint(arena.allocator(), "# Mepo state, generated by filedump ({s});", .{save_types}));

    // 1. Preferences
    if (std.mem.containsAtLeast(u8, save_types, 1, "r")) {
        try lines.append("\n# Preferences;");

        for (utilprefs.prefs_mapping) |pref| {
            var format_as: enum { Text, HexText, Number } = .Number;

            const numerical_val: f64 = switch (pref.value) {
                .b => @intToFloat(f64, @boolToInt(pref.value.b)),
                .u => @intToFloat(f64, pref.value.u),
                .u24 => v: {
                    format_as = .HexText;
                    break :v -1;
                },
                .f => pref.value.f,
                .t => v: {
                    format_as = .Text;
                    break :v -1;
                },
            };
            const prefset_statement = statement: {
                if (format_as == .Text) {
                    break :statement try std.fmt.allocPrint(arena.allocator(), "prefset_t {s} [{s}];", .{ pref.name, pref.value.t });
                } else if (format_as == .Number) {
                    break :statement try std.fmt.allocPrint(arena.allocator(), "prefset_n {s} {d:0.04};", .{ pref.name, numerical_val });
                } else if (format_as == .HexText) {
                    break :statement try std.fmt.allocPrint(arena.allocator(), "prefset_t {s} #{x};", .{ pref.name, pref.value.u24 });
                } else {
                    break :statement "";
                }
            };
            try lines.append(prefset_statement);
        }
    }

    // 2. Bindings
    if (std.mem.containsAtLeast(u8, save_types, 1, "b")) {
        try lines.append("\n# Bindings;");

        // Gestures
        var it_gestures = mepo.table_gestures.iterator();
        while (it_gestures.next()) |gest| {
            const direction: i8 = if (gest.key_ptr.*.direction == .In) 1 else -1;
            const act = if (gest.key_ptr.*.action == .Pan) "pan   " else "rotate";
            const statement_gest = try std.fmt.allocPrint(
                arena.allocator(),
                "bind_gesture {s} {d} {d} [{s}];",
                .{ act, gest.key_ptr.*.n_fingers, direction, gest.value_ptr.* },
            );
            try lines.append(statement_gest);
        }

        // Clicks
        var it_clicks = mepo.table_clicks.iterator();
        while (it_clicks.next()) |click| {
            const btn: u8 = if (click.key_ptr.*.button == sdl.SDL_BUTTON_LEFT) 1 else 2;
            const statement_click = try std.fmt.allocPrint(
                arena.allocator(),
                "bind_click {d} {d} [{s}];",
                .{ btn, click.key_ptr.*.clicks, click.value_ptr.* },
            );
            try lines.append(statement_click);
        }

        // Signals
        var it_sigs = mepo.table_signals.iterator();
        while (it_sigs.next()) |sig| {
            const signame = switch (sig.key_ptr.*) {
                std.os.SIG.USR1 => "USR1",
                std.os.SIG.USR2 => "USR2",
                std.os.SIG.INT => "INT",
                std.os.SIG.TERM => "TERM",
                else => continue,
            };

            const statement_signal = try std.fmt.allocPrint(
                arena.allocator(),
                "bind_signal {s} [{s}];",
                .{ signame, sig.value_ptr.* },
            );
            try lines.append(statement_signal);
        }

        // Timers
        var it_timers = mepo.table_timers.iterator();
        while (it_timers.next()) |sig| {
            const statement_timer = try std.fmt.allocPrint(
                arena.allocator(),
                "bind_timer {d} [{s}];",
                .{ sig.key_ptr.*.interval_seconds, sig.value_ptr.* },
            );
            try lines.append(statement_timer);
        }

        // Keybindings
        var it_keys = mepo.table_keybindings.iterator();
        while (it_keys.next()) |key| {
            const keymod = utilsdl.sdl_keymod_to_str(key.key_ptr.keymod);
            const statement_signal = try std.fmt.allocPrint(
                arena.allocator(),
                "bind_key [{s}] [{c}] [{s}];",
                .{ keymod, key.key_ptr.*.key, key.value_ptr.* },
            );
            try lines.append(statement_signal);
        }

        // UI Buttons
        for (mepo.uibuttons.items) |btn| {
            const pins_only_btn: u8 = if (btn.only_visible_when_has_pins) 1 else 0;
            const statement_button = try std.fmt.allocPrint(
                arena.allocator(),
                "bind_button {d} [{s}] [{s}];",
                .{ pins_only_btn, btn.text, btn.mepolang },
            );
            try lines.append(statement_button);
        }
    }

    // 3. Pins data
    if (std.mem.containsAtLeast(u8, save_types, 1, "p")) {
        try lines.append("\n# Pins;");

        for (mepo.pin_groups) |pg, pg_i| {
            for (pg.items) |pin| {
                const is_structural: u32 = b: {
                    if (pin.category == .Structural) break :b 1;
                    break :b 0;
                };
                const statement_pin_add = try std.fmt.allocPrint(
                    arena.allocator(),
                    "pin_add {d} {d} {d:.4} {d:.4} [{s}];",
                    .{ pg_i, is_structural, pin.lat, pin.lon, pin.handle },
                );
                try lines.append(statement_pin_add);

                var it = pin.metadata.iterator();
                while (it.next()) |kv| {
                    const statement_meta = try std.fmt.allocPrint(
                        arena.allocator(),
                        "pin_meta {d} [{s}] [{s}] [{s}];",
                        .{ pg_i, pin.handle, kv.key_ptr.*, kv.value_ptr.* },
                    );
                    try lines.append(statement_meta);
                }
            }
        }
    }

    const file_data = try std.mem.join(arena.allocator(), "\n", lines.items);
    try mepolang_file.writeAll(file_data);
}