mirror of
https://gitee.com/anod/open_agb_firm.git
synced 2025-05-06 05:44:11 +08:00
加入zig写的打包脚本
This commit is contained in:
parent
5e503b55ff
commit
9afbcd5bf7
757
tools/cheat-builder/make-acl.zig
Normal file
757
tools/cheat-builder/make-acl.zig
Normal file
@ -0,0 +1,757 @@
|
||||
//! By convention, main.zig is where your main function lives in the case that
|
||||
//! you are building an executable. If you are making a library, the convention
|
||||
//! is to delete this file and start with root.zig instead.
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const vec = std.ArrayList;
|
||||
const map = std.AutoArrayHashMap;
|
||||
const hash = std.StringArrayHashMap;
|
||||
const blk = std.heap.ArenaAllocator;
|
||||
|
||||
const ParseError = error{
|
||||
Invalid,
|
||||
Outofrange,
|
||||
};
|
||||
const File = std.fs.File;
|
||||
const Utf8View = std.unicode.Utf8View;
|
||||
|
||||
const ParseState = enum {
|
||||
WaitLock,
|
||||
ReadLock,
|
||||
WaitHole,
|
||||
ReadHole,
|
||||
ReadKey,
|
||||
WaitPart,
|
||||
NeedPart,
|
||||
};
|
||||
const Passhole = [2][]const u8{ "text", "off" };
|
||||
const Hex = "0123456789abcdefABCDEF";
|
||||
|
||||
const Module = vec(u8);
|
||||
const LockKey = vec(Module);
|
||||
const LockHole = struct { name: vec(u8), keys: vec(LockKey) };
|
||||
const Lock = struct { name: vec(u8), holes: vec(LockHole) };
|
||||
const EntData = union(enum) { strs: *vec(*Module), keys: *vec(LockKey) };
|
||||
const Entry = struct { id: u32, data: EntData };
|
||||
fn cmpEntry(ctx: void, a: Entry, b: Entry) bool {
|
||||
_ = ctx;
|
||||
return a.id < b.id;
|
||||
}
|
||||
|
||||
const CheatMap = struct { order: []u8, serial: []u8 }; // data from json
|
||||
const CheatSet = struct { code: u32, sets: Module }; // cht单文件产出
|
||||
const CheatGrp = struct { serial: []u8, grp: vec(CheatSet) }; // cht组文件产出
|
||||
|
||||
const ParserCtx = struct {
|
||||
state: ParseState,
|
||||
token: vec(u8),
|
||||
line: u32,
|
||||
|
||||
file: []u8,
|
||||
code: u32,
|
||||
farm: Allocator,
|
||||
shop: Allocator,
|
||||
|
||||
locks: vec(Lock),
|
||||
curlk: ?Lock,
|
||||
curhl: ?LockHole,
|
||||
|
||||
strcache: ?hash(u16),
|
||||
|
||||
output: *vec(CheatSet),
|
||||
};
|
||||
var runs: i64 = 0;
|
||||
|
||||
pub fn main() !void {
|
||||
const now = std.time.microTimestamp();
|
||||
|
||||
// 追踪内存分配,确定没问题后就不用gpa而是arena了
|
||||
// var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
// defer std.debug.assert(gpa.deinit() == std.heap.Check.ok);
|
||||
var gpa = blk.init(std.heap.page_allocator);
|
||||
defer gpa.deinit();
|
||||
|
||||
const list = try loadList(gpa.allocator());
|
||||
defer list.deinit();
|
||||
|
||||
const roms = transform(gpa.allocator(), list) catch vec(CheatGrp).init(gpa.allocator());
|
||||
defer roms.deinit();
|
||||
std.debug.print("all rom has {d} and time {d}\n", .{ roms.items.len, std.time.microTimestamp() - now });
|
||||
|
||||
const content = calcfile: {
|
||||
var alloc = blk.init(gpa.allocator());
|
||||
defer alloc.deinit();
|
||||
|
||||
var res = vec(*CheatGrp).init(alloc.allocator());
|
||||
var idx = hash(*vec(CheatSet)).init(alloc.allocator());
|
||||
for (roms.items) |*rom| {
|
||||
if (rom.grp.items.len == 0) continue;
|
||||
|
||||
if (!idx.contains(rom.serial)) {
|
||||
var grp = try alloc.allocator().create(CheatGrp);
|
||||
grp.serial = rom.serial;
|
||||
grp.grp = vec(CheatSet).init(alloc.allocator());
|
||||
try res.append(grp);
|
||||
var cheats = &grp.grp;
|
||||
try cheats.appendSlice(rom.grp.items);
|
||||
try idx.put(rom.serial, cheats);
|
||||
} else {
|
||||
const pcheats = idx.get(rom.serial).?;
|
||||
try pcheats.appendSlice(rom.grp.items);
|
||||
}
|
||||
}
|
||||
|
||||
const filedata = try format(alloc.allocator(), gpa.allocator(), &res);
|
||||
break :calcfile filedata;
|
||||
};
|
||||
defer gpa.allocator().free(content);
|
||||
|
||||
try std.fs.cwd().writeFile(.{ .sub_path = "gba.acl", .data = content, .flags = .{ .truncate = true } });
|
||||
// std.debug.print("check runs {d}\n", .{std.time.microTimestamp() - now});
|
||||
|
||||
// 如果用gpa才需要手动释放这几个没有释放的内存
|
||||
// for (roms.items) |*grp| {
|
||||
// gpa.allocator().free(grp.serial);
|
||||
// for (grp.grp.items) |*set| {
|
||||
// set.sets.deinit();
|
||||
// }
|
||||
// grp.grp.deinit();
|
||||
// }
|
||||
}
|
||||
|
||||
fn readAll(allocator: Allocator, file: File) ![]u8 {
|
||||
const file_size: usize = @intCast(try file.getEndPos());
|
||||
const file_contents = try allocator.alloc(u8, file_size);
|
||||
|
||||
const readed = try file.readAll(file_contents);
|
||||
if (readed != file_size) {
|
||||
return error.EndOfStream;
|
||||
}
|
||||
return file_contents;
|
||||
}
|
||||
|
||||
fn loadList(allocator: Allocator) !vec(CheatMap) {
|
||||
const file = try std.fs.cwd().openFile("./serial.json", .{});
|
||||
defer file.close();
|
||||
|
||||
const file_contents = try readAll(allocator, file);
|
||||
defer allocator.free(file_contents);
|
||||
|
||||
var entries = try std.json.parseFromSlice(std.json.Value, allocator, file_contents, .{});
|
||||
defer entries.deinit();
|
||||
|
||||
var iter = entries.value.object.iterator();
|
||||
var list = try vec(CheatMap).initCapacity(allocator, entries.value.object.count());
|
||||
while (iter.next()) |*entry| {
|
||||
const key = entry.key_ptr;
|
||||
const value = entry.value_ptr;
|
||||
if (!std.mem.eql(u8, value.string, "????")) {
|
||||
const nkey = try allocator.alloc(u8, key.len);
|
||||
const nval = try allocator.alloc(u8, value.string.len);
|
||||
std.mem.copyForwards(u8, nkey, key.*);
|
||||
std.mem.copyForwards(u8, nval, value.string);
|
||||
try list.append(CheatMap{ .order = nkey, .serial = nval });
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
fn transform(allocator: Allocator, list: vec(CheatMap)) !vec(CheatGrp) {
|
||||
var alloc = blk.init(allocator);
|
||||
defer alloc.deinit();
|
||||
|
||||
const innalloc = alloc.allocator();
|
||||
|
||||
var retval = vec(CheatGrp).init(allocator);
|
||||
for (list.items) |*chtmap| {
|
||||
const order = chtmap.order;
|
||||
const serial = chtmap.serial;
|
||||
defer allocator.free(order);
|
||||
|
||||
const file = try std.fmt.allocPrint(innalloc, "./gba/{s}.u8", .{order});
|
||||
|
||||
var cheats = vec(CheatSet).init(allocator);
|
||||
|
||||
const fd = std.fs.cwd().openFile(file, .{}) catch null;
|
||||
if (fd) |chtfile| {
|
||||
defer chtfile.close();
|
||||
|
||||
const chtdata = try readAll(innalloc, chtfile);
|
||||
|
||||
if (chtdata.len > 0) {
|
||||
parse(innalloc, allocator, &cheats, chtdata, serial, order) catch |err| {
|
||||
std.debug.print("got error {any} in {s}!\n", .{ err, file });
|
||||
};
|
||||
try retval.append(CheatGrp{ .serial = serial, .grp = cheats });
|
||||
} else try retval.append(CheatGrp{ .serial = serial, .grp = cheats });
|
||||
} else try retval.append(CheatGrp{ .serial = serial, .grp = cheats });
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
fn parse(innalloc: Allocator, outeralloc: Allocator, output: *vec(CheatSet), chtdata: []u8, serial: []u8, order: []u8) !void {
|
||||
var context = ParserCtx{
|
||||
.state = ParseState.WaitLock,
|
||||
.token = vec(u8).init(innalloc),
|
||||
.line = 1,
|
||||
|
||||
.file = order,
|
||||
.farm = innalloc,
|
||||
.shop = outeralloc,
|
||||
.code = calc_code: {
|
||||
var r: u32 = 0;
|
||||
for (serial) |v| r = v | r << 8;
|
||||
break :calc_code r;
|
||||
},
|
||||
|
||||
.locks = vec(Lock).init(innalloc),
|
||||
.curlk = null,
|
||||
.curhl = null,
|
||||
|
||||
.strcache = null,
|
||||
|
||||
.output = output,
|
||||
};
|
||||
defer context.locks.deinit();
|
||||
defer context.token.deinit();
|
||||
|
||||
var data = (try Utf8View.init(chtdata)).iterator();
|
||||
while (data.nextCodepointSlice()) |codepoint| {
|
||||
try incr(codepoint, &context);
|
||||
}
|
||||
done(&context);
|
||||
}
|
||||
|
||||
fn done(ctx: *ParserCtx) void {
|
||||
switch (ctx.state) {
|
||||
ParseState.WaitPart, ParseState.NeedPart => {},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
inline fn parserr(str: []u8, ctx: *ParserCtx) !void {
|
||||
std.debug.print("Cheat Invad: {s} at line {d} for file {s}.u8\n", .{ str, ctx.line, ctx.file });
|
||||
return ParseError.Invalid;
|
||||
}
|
||||
|
||||
inline fn tokenStr(ctx: *ParserCtx) vec(u8) {
|
||||
const s = ctx.token;
|
||||
ctx.token = vec(u8).init(ctx.farm);
|
||||
return s;
|
||||
}
|
||||
|
||||
fn incr(ch: []const u8, ctx: *ParserCtx) !void {
|
||||
const state = ctx.state;
|
||||
const allocator = ctx.farm;
|
||||
switch (ch[0]) {
|
||||
'[' => {
|
||||
if (state == ParseState.WaitLock or state == ParseState.ReadHole or state == ParseState.NeedPart) {
|
||||
ctx.state = ParseState.ReadLock;
|
||||
if (ctx.curlk) |lock| {
|
||||
try ctx.locks.append(lock);
|
||||
}
|
||||
ctx.curlk = Lock{ .name = vec(u8).init(allocator), .holes = vec(LockHole).init(allocator) };
|
||||
} else if (state == ParseState.WaitPart) {} else try parserr(try std.fmt.allocPrint(allocator, "error occur [ on {s}", .{@tagName(state)}), ctx);
|
||||
},
|
||||
']' => {
|
||||
if (state == ParseState.ReadLock) {
|
||||
ctx.state = ParseState.WaitHole;
|
||||
const name = tokenStr(ctx);
|
||||
if (std.ascii.eqlIgnoreCase(name.items, "gameinfo")) {
|
||||
try lade(ctx);
|
||||
ctx.state = ParseState.WaitPart;
|
||||
ctx.locks = vec(Lock).init(allocator);
|
||||
ctx.curhl = null;
|
||||
ctx.curlk = null;
|
||||
} else if (name.items.len > 0) {
|
||||
ctx.curlk = Lock{
|
||||
.name = name,
|
||||
.holes = vec(LockHole).init(allocator),
|
||||
};
|
||||
ctx.curhl = null;
|
||||
} else try parserr(try std.fmt.allocPrint(allocator, "empty lock name", .{}), ctx);
|
||||
} else if (state == ParseState.WaitPart) {} else if (state == ParseState.NeedPart) {
|
||||
ctx.state = ParseState.WaitPart;
|
||||
} else try parserr(try std.fmt.allocPrint(allocator, "error occur ] on {s}", .{@tagName(state)}), ctx);
|
||||
},
|
||||
'\r', '\n' => {
|
||||
if (state == ParseState.WaitHole) {
|
||||
ctx.state = ParseState.ReadHole;
|
||||
} else if (state == ParseState.ReadKey) {
|
||||
const token = tokenStr(ctx);
|
||||
var pass = false;
|
||||
if (ctx.curhl) |*hole| {
|
||||
if (token.items.len > 0) {
|
||||
var key = &hole.keys.items[hole.keys.items.len - 1];
|
||||
try key.append(token);
|
||||
}
|
||||
|
||||
pass = for (Passhole) |keyword| {
|
||||
if (std.ascii.eqlIgnoreCase(keyword, hole.name.items)) {
|
||||
break true;
|
||||
}
|
||||
} else false;
|
||||
}
|
||||
if (!pass) {
|
||||
if (ctx.curlk != null) {
|
||||
if (ctx.curhl) |hole| {
|
||||
try ctx.curlk.?.holes.append(hole);
|
||||
} else unreachable;
|
||||
}
|
||||
ctx.curhl = null;
|
||||
}
|
||||
ctx.state = ParseState.ReadHole;
|
||||
} else if (state == ParseState.ReadLock) {
|
||||
unreachable;
|
||||
} else if (state == ParseState.ReadHole) {
|
||||
const token = tokenStr(ctx);
|
||||
if (token.items.len > 0) {
|
||||
const onlyspace = for (token.items) |c| {
|
||||
if (c != ' ' and c != '\t') {
|
||||
break false;
|
||||
}
|
||||
} else true;
|
||||
if (onlyspace == false) {
|
||||
try parserr(try std.fmt.allocPrint(allocator, "error occur newline on {s}", .{@tagName(state)}), ctx);
|
||||
}
|
||||
}
|
||||
} else if (state == ParseState.WaitPart) {
|
||||
ctx.state = ParseState.NeedPart;
|
||||
} else {}
|
||||
if (ch[0] == '\n') ctx.line = ctx.line + 1;
|
||||
},
|
||||
'=' => {
|
||||
if (state == ParseState.ReadHole) {
|
||||
ctx.state = ParseState.ReadKey;
|
||||
ctx.curhl = LockHole{ .name = tokenStr(ctx), .keys = initkey: {
|
||||
var res = vec(LockKey).init(allocator);
|
||||
try res.append(vec(Module).init(allocator));
|
||||
break :initkey res;
|
||||
} };
|
||||
} else if (state == ParseState.ReadLock) {
|
||||
try ctx.token.appendSlice(ch);
|
||||
} else if (state == ParseState.WaitPart) {} else if (state == ParseState.NeedPart) {
|
||||
ctx.state = ParseState.WaitPart;
|
||||
} else try parserr(try std.fmt.allocPrint(allocator, "error occur = on {s}", .{@tagName(state)}), ctx);
|
||||
},
|
||||
',' => {
|
||||
if (state == ParseState.ReadKey) {
|
||||
const token = tokenStr(ctx);
|
||||
if (token.items.len > 0) {
|
||||
if (ctx.curhl) |*hole| {
|
||||
var key = &hole.keys.items[hole.keys.items.len - 1];
|
||||
try key.append(token);
|
||||
} else unreachable;
|
||||
}
|
||||
} else if (state == ParseState.ReadHole) {
|
||||
const token = tokenStr(ctx);
|
||||
var holes = &ctx.curlk.?.holes;
|
||||
const hole = holes.pop();
|
||||
if (token.items.len > 0) {
|
||||
var key = &hole.keys.items[hole.keys.items.len - 1];
|
||||
try key.append(token);
|
||||
}
|
||||
ctx.curhl = hole;
|
||||
ctx.state = ParseState.ReadKey;
|
||||
} else if (state == ParseState.ReadLock) {
|
||||
try ctx.token.appendSlice(ch);
|
||||
} else if (state == ParseState.WaitPart) {} else if (state == ParseState.NeedPart) {
|
||||
ctx.state = ParseState.WaitPart;
|
||||
} else try parserr(try std.fmt.allocPrint(allocator, "error occur , on {s}", .{@tagName(state)}), ctx);
|
||||
},
|
||||
';' => {
|
||||
if (state == ParseState.ReadKey) {
|
||||
const token = tokenStr(ctx);
|
||||
if (token.items.len > 0) {
|
||||
const keys = ctx.curhl.?.keys;
|
||||
var key = &keys.items[keys.items.len - 1];
|
||||
try key.append(token);
|
||||
}
|
||||
try ctx.curhl.?.keys.append(vec(Module).init(allocator));
|
||||
} else if (state == ParseState.ReadLock) {
|
||||
try ctx.token.appendSlice(ch);
|
||||
} else if (state == ParseState.ReadHole) {
|
||||
var holes = &ctx.curlk.?.holes;
|
||||
var hole = holes.pop();
|
||||
const token = tokenStr(ctx);
|
||||
if (token.items.len > 0) {
|
||||
var cmd = &hole.keys.items[hole.keys.items.len - 1];
|
||||
try cmd.append(token);
|
||||
}
|
||||
try hole.keys.append(vec(Module).init(allocator));
|
||||
ctx.curhl = hole;
|
||||
ctx.state = ParseState.ReadKey;
|
||||
} else if (state == ParseState.WaitPart) {} else if (state == ParseState.NeedPart) {
|
||||
ctx.state = ParseState.WaitPart;
|
||||
} else try parserr(try std.fmt.allocPrint(allocator, "error occur ; on {s}", .{@tagName(state)}), ctx);
|
||||
},
|
||||
' ' => {
|
||||
if (state == ParseState.ReadLock or state == ParseState.ReadHole) {
|
||||
try ctx.token.appendSlice(ch);
|
||||
} else if (state == ParseState.NeedPart) {
|
||||
ctx.state = ParseState.WaitPart;
|
||||
} else {}
|
||||
},
|
||||
else => {
|
||||
if (state == ParseState.ReadLock or state == ParseState.ReadHole) {
|
||||
try ctx.token.appendSlice(ch);
|
||||
} else if (state == ParseState.ReadKey) {
|
||||
const ishex = for (Hex) |c| {
|
||||
if (c == ch[0]) {
|
||||
break true;
|
||||
}
|
||||
} else false;
|
||||
if (ishex) {
|
||||
try ctx.token.appendSlice(ch);
|
||||
} else if (ctx.curhl) |*hole| {
|
||||
if (std.ascii.eqlIgnoreCase(hole.name.items, "text")) {
|
||||
try ctx.token.appendSlice(ch);
|
||||
}
|
||||
} else try parserr(try std.fmt.allocPrint(allocator, "error occur {s} on {s}", .{ ch, @tagName(state) }), ctx);
|
||||
} else if (state == ParseState.WaitPart) {} else if (state == ParseState.NeedPart) {
|
||||
ctx.state = ParseState.WaitPart;
|
||||
} else try parserr(try std.fmt.allocPrint(allocator, "error occur {s} on {s}", .{ ch, @tagName(state) }), ctx);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
inline fn makeId(a: usize, b: usize) u32 {
|
||||
return @intCast(a | (b << 16));
|
||||
}
|
||||
|
||||
inline fn getalign(n: usize, basic: usize) usize {
|
||||
const ext = n % basic;
|
||||
return if (ext == 0) n else n + basic - ext;
|
||||
}
|
||||
|
||||
fn findIndex(t: []u8, tr: []u8) i32 {
|
||||
const rlen: usize = tr.len;
|
||||
const tlen: usize = t.len;
|
||||
if (tlen < rlen) return -1;
|
||||
const limit = tlen - rlen;
|
||||
for (0..limit) |i| {
|
||||
if (t[i + rlen] != 0) continue;
|
||||
|
||||
const eq = for (0..rlen) |j| {
|
||||
if (t[i + j] != tr[j]) {
|
||||
break false;
|
||||
}
|
||||
} else true;
|
||||
if (eq) return @intCast(i);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
fn fromTaddr(taddr: vec(u8)) !u32 {
|
||||
const n = try std.fmt.parseInt(u32, taddr.items, 16);
|
||||
if (n == 0 or n > 0x41f_ffff) {
|
||||
return error.Outofrange;
|
||||
}
|
||||
|
||||
return if (n < 0x3_ffff) n | 0x200_0000 else if (0 == (n & 0xf00_0000)) (n & 0xffff) | 0x300_0000 else n;
|
||||
}
|
||||
|
||||
fn assembleCheat(ctx: *ParserCtx, list: *vec(LockKey), dup: *map(usize, u16), hole: u16) !vec(u32) {
|
||||
const alloc = ctx.farm;
|
||||
|
||||
var addrval = map(u32, u32).init(alloc);
|
||||
for (list.items) |*command| {
|
||||
if (command.items.len == 0) {
|
||||
continue;
|
||||
}
|
||||
const addr = try fromTaddr(command.items[0]);
|
||||
const len = command.items.len - 1;
|
||||
for (0..len) |pos| {
|
||||
const target = command.items[pos + 1];
|
||||
try addrval.put(@intCast(addr + pos), try std.fmt.parseInt(u32, target.items, 16));
|
||||
}
|
||||
}
|
||||
|
||||
const ordered = calcordered: {
|
||||
var res = vec([2]u32).init(alloc);
|
||||
var iter = addrval.iterator();
|
||||
while (iter.next()) |kvp| {
|
||||
try res.append(.{ kvp.key_ptr.*, kvp.value_ptr.* });
|
||||
}
|
||||
std.sort.block([2]u32, res.items, {}, cmpOrdered);
|
||||
break :calcordered res;
|
||||
};
|
||||
var blocks = vec(u32).init(alloc);
|
||||
var curr: [3]u32 = .{ 0, 0, 0 };
|
||||
for (ordered.items) |cmd| {
|
||||
const addr = cmd[0];
|
||||
const value = cmd[1];
|
||||
try dup.put(addr, hole);
|
||||
|
||||
if (curr[2] == 0) {
|
||||
curr = .{ addr, value, 1 };
|
||||
} else {
|
||||
if (curr[1] == value and curr[0] + curr[2] == addr) {
|
||||
curr[2] = curr[2] + 1;
|
||||
} else {
|
||||
try blocks.append(curr[0]);
|
||||
try blocks.append((curr[2] << 8) | curr[1]);
|
||||
curr = .{ addr, value, 1 };
|
||||
}
|
||||
}
|
||||
}
|
||||
if (curr[2] != 0) {
|
||||
try blocks.append(curr[0]);
|
||||
try blocks.append((curr[2] << 8) | curr[1]);
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
fn cmpNames(ctx: void, a: *Module, b: *Module) bool {
|
||||
_ = ctx;
|
||||
if (a.items.len == b.items.len) {
|
||||
return std.mem.lessThan(u8, a.items, b.items);
|
||||
} else return b.items.len < a.items.len;
|
||||
}
|
||||
|
||||
fn cmpOrdered(ctx: void, a: [2]u32, b: [2]u32) bool {
|
||||
_ = ctx;
|
||||
return a[0] < b[0];
|
||||
}
|
||||
|
||||
fn utpush(str: []const u8, table: *vec(u8)) !u16 {
|
||||
const off = table.items.len;
|
||||
try table.ensureTotalCapacity(off + str.len + 1);
|
||||
try table.appendSlice(str);
|
||||
try table.append(0);
|
||||
return @intCast(off);
|
||||
}
|
||||
|
||||
fn utaddr(ctx: *ParserCtx, str: []u8, table: *vec(u8)) !u16 {
|
||||
var strcache = &ctx.strcache.?;
|
||||
if (strcache.contains(str)) {
|
||||
const res = strcache.get(str);
|
||||
return res.?;
|
||||
} else {
|
||||
const idx = findIndex(table.items, str);
|
||||
const off: u16 = if (idx < 0) try utpush(str, table) else @intCast(idx);
|
||||
try strcache.put(str, off);
|
||||
return off;
|
||||
}
|
||||
}
|
||||
|
||||
fn addString(ctx: *ParserCtx, list: *const vec(*Module), table: *vec(u8)) !vec(u16) {
|
||||
var retval = try vec(u16).initCapacity(ctx.farm, list.items.len);
|
||||
for (list.items) |str| {
|
||||
try retval.append(try utaddr(ctx, str.items, table));
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
fn addStrIndex(list: *const vec(u16), table: *vec(u16)) !u32 {
|
||||
if (list.items.len == 0) return 0;
|
||||
|
||||
const ret = table.items.len;
|
||||
try table.ensureTotalCapacity(ret + list.items.len);
|
||||
try table.appendSlice(list.items);
|
||||
|
||||
return @intCast(ret * 2);
|
||||
}
|
||||
|
||||
fn addCommand(list: vec(u32), table: *vec(u32)) !u32 {
|
||||
const ret = table.items.len;
|
||||
try table.ensureTotalCapacity(ret + list.items.len);
|
||||
try table.appendSlice(list.items);
|
||||
|
||||
return @intCast(ret * 4);
|
||||
}
|
||||
|
||||
fn lade(ctx: *ParserCtx) !void {
|
||||
// const st = std.time.microTimestamp();
|
||||
// defer runs = runs - st + std.time.microTimestamp();
|
||||
const alloc = ctx.farm;
|
||||
var rootstr = vec(*Module).init(alloc);
|
||||
var names = vec(*Module).init(alloc);
|
||||
var enttable = vec(Entry).init(alloc);
|
||||
|
||||
const locks = ctx.locks;
|
||||
for (locks.items, 0..) |*l, i| {
|
||||
try rootstr.append(&l.name);
|
||||
try names.append(&l.name);
|
||||
|
||||
var holestr = try ctx.farm.create(vec(*Module));
|
||||
holestr.* = vec(*Module).init(ctx.farm);
|
||||
const colname = l.holes.items.len > 1;
|
||||
for (l.holes.items, 0..) |*hole, index| {
|
||||
try holestr.append(&hole.name);
|
||||
if (colname) {
|
||||
try names.append(&hole.name);
|
||||
}
|
||||
const id = makeId(i + 1, index + 1);
|
||||
try enttable.append(Entry{ .id = id, .data = EntData{ .keys = &hole.keys } });
|
||||
}
|
||||
const id = makeId(i + 1, 0);
|
||||
try enttable.append(Entry{ .id = id, .data = EntData{ .strs = holestr } });
|
||||
}
|
||||
try enttable.append(Entry{ .id = makeId(0, 0), .data = EntData{ .strs = &rootstr } });
|
||||
|
||||
if (enttable.items.len > 0xffff) {
|
||||
try parserr(try std.fmt.allocPrint(alloc, "too many entries in file {s}", .{ctx.file}), ctx);
|
||||
}
|
||||
|
||||
std.sort.block(Entry, enttable.items, {}, cmpEntry);
|
||||
std.sort.block(*Module, names.items, {}, cmpNames);
|
||||
|
||||
var strtable = vec(u8).init(alloc);
|
||||
var idxtable = vec(u16).init(alloc);
|
||||
var cmdtable = vec(u32).init(alloc);
|
||||
|
||||
ctx.strcache = hash(u16).init(alloc);
|
||||
_ = try addString(ctx, &names, &strtable);
|
||||
var entbytelist = vec([3]u32).init(alloc);
|
||||
const emptylist = vec(*Module).init(alloc);
|
||||
var holedup = map(usize, u16).init(alloc);
|
||||
|
||||
for (enttable.items) |*entry| {
|
||||
const id = entry.id;
|
||||
if (id < 0x1_0000) {
|
||||
const labels = if (id == 0) entry.data.strs else if (entry.data.strs.items.len > 1) entry.data.strs else &emptylist;
|
||||
const idxlist = try addString(ctx, labels, &strtable);
|
||||
const location = try addStrIndex(&idxlist, &idxtable);
|
||||
try entbytelist.append([3]u32{ id, location, @intCast(idxlist.items.len) });
|
||||
} else {
|
||||
const armbytecode = try assembleCheat(ctx, entry.data.keys, &holedup, @intCast(id & 0xffff));
|
||||
const location = try addCommand(armbytecode, &cmdtable);
|
||||
try entbytelist.append([3]u32{ id, location, @intCast(armbytecode.items.len) });
|
||||
}
|
||||
}
|
||||
|
||||
const code = calccode: {
|
||||
var res = ctx.code;
|
||||
for (cmdtable.items) |cmd| res = res ^ cmd;
|
||||
break :calccode res;
|
||||
};
|
||||
|
||||
const sets = try pack(ctx, &entbytelist, &strtable, &idxtable, &cmdtable);
|
||||
try ctx.output.append(CheatSet{ .code = code, .sets = sets });
|
||||
}
|
||||
|
||||
fn pack(ctx: *ParserCtx, entlist: *vec([3]u32), strlist: *vec(u8), idxlist: *vec(u16), cmdlist: *vec(u32)) !vec(u8) {
|
||||
const external = ctx.shop;
|
||||
|
||||
const entrysize = @sizeOf([3]u32);
|
||||
const strbase = 2 + entlist.items.len * entrysize;
|
||||
const idxbase = strbase + strlist.items.len;
|
||||
const cmdbase = idxbase + idxlist.items.len * 2;
|
||||
const noalign_size = cmdbase + cmdlist.items.len * 4;
|
||||
const size = getalign(noalign_size, 32);
|
||||
const endian = std.builtin.Endian.little;
|
||||
|
||||
var result = try vec(u8).initCapacity(external, size);
|
||||
try result.writer().writeInt(u16, @intCast(entlist.items.len), endian);
|
||||
|
||||
for (entlist.items) |entry| {
|
||||
const locbase = if (entry[0] > 0xffff) cmdbase else idxbase;
|
||||
try result.writer().writeInt(u32, entry[0], endian);
|
||||
try result.writer().writeInt(u32, @intCast(locbase + entry[1]), endian);
|
||||
try result.writer().writeInt(u32, entry[2], endian);
|
||||
}
|
||||
|
||||
try result.writer().writeAll(strlist.items);
|
||||
try result.writer().writeAll(std.mem.sliceAsBytes(idxlist.items));
|
||||
try result.writer().writeAll(std.mem.sliceAsBytes(cmdlist.items));
|
||||
if (noalign_size < size) {
|
||||
try result.writer().writeByteNTimes(0, size - noalign_size);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn cmpCheatGrp(ctx: void, a: *CheatGrp, b: *CheatGrp) bool {
|
||||
_ = ctx;
|
||||
return std.mem.lessThan(u8, a.serial, b.serial);
|
||||
}
|
||||
|
||||
fn format(alloc: Allocator, global: Allocator, cheats: *vec(*CheatGrp)) ![]const u8 {
|
||||
std.sort.block(*CheatGrp, cheats.items, {}, cmpCheatGrp);
|
||||
std.debug.print("valid rom has {d}\n", .{cheats.items.len});
|
||||
|
||||
var sers: ?vec(u8) = null;
|
||||
var offs: ?vec(u16) = null;
|
||||
var chtc: ?usize = null;
|
||||
var maxl: ?usize = null;
|
||||
{
|
||||
var item1 = vec(u8).init(alloc);
|
||||
var item2 = vec(u16).init(alloc);
|
||||
var item3: usize = 0;
|
||||
var item4: usize = 0;
|
||||
var item5: []const u8 = "";
|
||||
var last: usize = 0;
|
||||
for (cheats.items) |cheat| {
|
||||
const val = cheat.serial[0..3];
|
||||
if (!std.mem.eql(u8, val, item5)) {
|
||||
if (item2.items.len > 0 and item3 - last > item4) {
|
||||
item4 = item3 - last;
|
||||
}
|
||||
try item1.appendSlice(val);
|
||||
try item2.append(@intCast(item3));
|
||||
last = item3;
|
||||
item5 = val;
|
||||
}
|
||||
item3 = item3 + cheat.grp.items.len;
|
||||
}
|
||||
if (item2.items.len > 0) {
|
||||
if (item3 - last > item4) {
|
||||
item4 = item3 - last;
|
||||
}
|
||||
try item2.append(@intCast(item3));
|
||||
}
|
||||
sers = item1;
|
||||
offs = item2;
|
||||
chtc = item3;
|
||||
maxl = item4;
|
||||
}
|
||||
|
||||
var chts: ?vec(u32) = null;
|
||||
var expanded: ?vec(u8) = null;
|
||||
{
|
||||
var item1 = vec(u32).init(alloc);
|
||||
var item2 = vec(u8).init(alloc);
|
||||
const item3 = getalign(8 + sers.?.items.len + offs.?.items.len * 2 + chtc.? * 8, 32);
|
||||
for (cheats.items) |cheat| {
|
||||
const val = cheat.serial[3];
|
||||
for (cheat.grp.items) |*set| {
|
||||
const off = item3 + item2.items.len;
|
||||
try item1.append(@intCast(val | (off << 3)));
|
||||
try item1.append(set.code);
|
||||
try item2.ensureTotalCapacity(item2.items.len + set.sets.items.len);
|
||||
try item2.appendSlice(set.sets.items);
|
||||
}
|
||||
}
|
||||
chts = item1;
|
||||
expanded = item2;
|
||||
}
|
||||
std.debug.print("name: {d} cheats: {d} maxl: {d}\n", .{ sers.?.items.len, chtc.?, maxl.? });
|
||||
|
||||
const serialbase = 8;
|
||||
const offsetbase = serialbase + sers.?.items.len;
|
||||
const cheatbase = offsetbase + offs.?.items.len * 2;
|
||||
const cheatend = cheatbase + chts.?.items.len * 4;
|
||||
const expandbase = getalign(cheatend, 32);
|
||||
const total = expandbase + expanded.?.items.len;
|
||||
const endian = std.builtin.Endian.little;
|
||||
|
||||
var retval = try global.alloc(u8, total);
|
||||
retval[0] = 'A';
|
||||
retval[1] = 'C';
|
||||
retval[2] = 'L';
|
||||
retval[3] = 1;
|
||||
|
||||
std.mem.writeInt(u16, retval[4..6], @intCast(sers.?.items.len / 3), endian);
|
||||
std.mem.writeInt(u16, retval[6..8], @intCast(maxl.?), endian);
|
||||
|
||||
std.mem.copyForwards(u8, retval[serialbase..offsetbase], sers.?.items);
|
||||
std.mem.copyForwards(u8, retval[offsetbase..cheatbase], std.mem.sliceAsBytes(offs.?.items));
|
||||
std.mem.copyForwards(u8, retval[cheatbase..cheatend], std.mem.sliceAsBytes(chts.?.items));
|
||||
if (cheatend < expandbase) {
|
||||
@memset(retval[cheatend..expandbase], 0);
|
||||
}
|
||||
std.mem.copyForwards(u8, retval[expandbase..], expanded.?.items);
|
||||
|
||||
return retval;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user