mirror of
https://github.com/marlersoft/win32json
synced 2026-06-21 13:57:33 +00:00
initial generate implementation
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
*.zig text eol=lf
|
||||
*.zon text eol=lf
|
||||
@@ -0,0 +1,2 @@
|
||||
.zig-cache/
|
||||
zig-out/
|
||||
@@ -0,0 +1,5 @@
|
||||
# win32json - generate branch
|
||||
|
||||
Generates the JSON win32 metadata files.
|
||||
|
||||
Run `zig build` to generate/install the JSON files to `zig-out`.
|
||||
@@ -0,0 +1,69 @@
|
||||
const std = @import("std");
|
||||
const Build = std.Build;
|
||||
|
||||
const version = "25.0.28-preview";
|
||||
|
||||
pub fn build(b: *Build) !void {
|
||||
const metadata_nupkg_file = blk: {
|
||||
const download_winmd_nupkg = b.addSystemCommand(&.{
|
||||
"curl",
|
||||
"https://www.nuget.org/api/v2/package/Microsoft.Windows.SDK.Win32Metadata/" ++ version,
|
||||
"--location",
|
||||
"--output",
|
||||
});
|
||||
break :blk download_winmd_nupkg.addOutputFileArg("win32metadata.nupkg");
|
||||
};
|
||||
|
||||
const metadata_nupkg_out = blk: {
|
||||
const zipcmdline = b.dependency("zipcmdline", .{
|
||||
.target = b.graph.host,
|
||||
});
|
||||
const unzip_exe = zipcmdline.artifact("unzip");
|
||||
const unzip_metadata = b.addRunArtifact(unzip_exe);
|
||||
unzip_metadata.addFileArg(metadata_nupkg_file);
|
||||
unzip_metadata.addArg("-d");
|
||||
break :blk unzip_metadata.addOutputDirectoryArg("nupkg");
|
||||
};
|
||||
|
||||
const winmd = metadata_nupkg_out.path(b, "Windows.Win32.winmd");
|
||||
const gen_step = b.step("gen", "Generate JSON files (in .zig-cache)");
|
||||
|
||||
const winmd_dep = b.dependency("winmd", .{});
|
||||
const gen_out_dir = blk: {
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "genjson",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/genjson.zig"),
|
||||
.target = b.graph.host,
|
||||
}),
|
||||
});
|
||||
exe.root_module.addImport("winmd", winmd_dep.module("winmd"));
|
||||
const run = b.addRunArtifact(exe);
|
||||
run.addFileArg(winmd);
|
||||
const out_dir = run.addOutputDirectoryArg(".");
|
||||
gen_step.dependOn(&run.step);
|
||||
break :blk out_dir;
|
||||
};
|
||||
|
||||
b.installDirectory(.{
|
||||
.source_dir = gen_out_dir,
|
||||
.install_dir = .prefix,
|
||||
.install_subdir = ".",
|
||||
});
|
||||
|
||||
const test_step = b.step("test", "Run all the tests");
|
||||
{
|
||||
const validate_exe = b.addExecutable(.{
|
||||
.name = "validate",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/validate.zig"),
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
}),
|
||||
});
|
||||
const validate = b.addRunArtifact(validate_exe);
|
||||
validate.addDirectoryArg(gen_out_dir);
|
||||
validate.expectExitCode(0);
|
||||
test_step.dependOn(&validate.step);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
.{
|
||||
.name = .win32jsongenerate,
|
||||
.fingerprint = 0xf9e0adc32fc8efe2,
|
||||
.version = "0.0.0",
|
||||
.zig_version = "0.15.2",
|
||||
.minimum_zig_version = "0.15.0",
|
||||
.dependencies = .{
|
||||
.zipcmdline = .{
|
||||
.url = "git+https://github.com/marler8997/zipcmdline#a6de65875023c52d2f56f21a31c6487266f8105f",
|
||||
.hash = "zipcmdline-0.0.0-DFii07hmAwCFNhtMpml64Pj_hr8kN4WesClygZErQHsk",
|
||||
},
|
||||
.winmd = .{
|
||||
.url = "git+https://github.com/marler8997/winmd-zig#d0d8daff01b4fb1a6324c79a855c34ced5bbc8d2",
|
||||
.hash = "winmd-0.0.0-2QIrG4MpAQCCh0ReC0jZKAps9Frn3jbbR--W7szyGAOS",
|
||||
},
|
||||
},
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
"src",
|
||||
},
|
||||
}
|
||||
+2629
File diff suppressed because it is too large
Load Diff
+214
@@ -0,0 +1,214 @@
|
||||
pub fn apiPatchMap(al: std.mem.Allocator) error{OutOfMemory}!std.StringHashMapUnmanaged(ApiPatches) {
|
||||
var api_patches: std.StringHashMapUnmanaged(ApiPatches) = .{};
|
||||
|
||||
{
|
||||
const api = try addApiPatches(al, &api_patches, "System.Console");
|
||||
try api.func(al, .{ .name = "WriteConsoleA", .optional_params = .copy(&.{
|
||||
.{ .name = "lpReserved" },
|
||||
}) });
|
||||
try api.func(al, .{ .name = "WriteConsoleW", .optional_params = .copy(&.{
|
||||
.{ .name = "lpReserved" },
|
||||
}) });
|
||||
}
|
||||
{
|
||||
const api = try addApiPatches(al, &api_patches, "System.Memory");
|
||||
try api.func(al, .{ .name = "CreateFileMappingA", .optional_return = .{} });
|
||||
try api.func(al, .{ .name = "CreateFileMappingW", .optional_return = .{} });
|
||||
try api.func(al, .{ .name = "MapViewOfFile", .optional_return = .{} });
|
||||
try api.func(al, .{ .name = "MapViewOfFileEx", .optional_return = .{} });
|
||||
}
|
||||
{
|
||||
const api = try addApiPatches(al, &api_patches, "UI.WindowsAndMessaging");
|
||||
try api.func(al, .{ .name = "CreateWindowExA", .optional_return = .{} });
|
||||
try api.func(al, .{ .name = "CreateWindowExW", .optional_return = .{} });
|
||||
try api.func(al, .{ .name = "ShowWindow", .optional_params = .copy(&.{
|
||||
.{ .name = "hWnd" },
|
||||
}) });
|
||||
try api.struct_(al, .{ .name = "WNDCLASSA", .optional_fields = .copy(&.{
|
||||
.{ .name = "hIcon" },
|
||||
.{ .name = "hCursor" },
|
||||
.{ .name = "hbrBackground" },
|
||||
.{ .name = "lpszMenuName" },
|
||||
}) });
|
||||
try api.struct_(al, .{ .name = "WNDCLASSW", .optional_fields = .copy(&.{
|
||||
.{ .name = "hIcon" },
|
||||
.{ .name = "hCursor" },
|
||||
.{ .name = "hbrBackground" },
|
||||
.{ .name = "lpszMenuName" },
|
||||
}) });
|
||||
}
|
||||
{
|
||||
const api = try addApiPatches(al, &api_patches, "Graphics.Gdi");
|
||||
try api.func(al, .{ .name = "CreateFontA", .optional_return = .{} });
|
||||
try api.func(al, .{ .name = "CreateFontW", .optional_return = .{} });
|
||||
}
|
||||
return api_patches;
|
||||
}
|
||||
|
||||
pub fn verifyApiPatches(api_patch_map: *const std.StringHashMapUnmanaged(ApiPatches)) void {
|
||||
var it = api_patch_map.iterator();
|
||||
while (it.next()) |entry| {
|
||||
entry.value_ptr.verify();
|
||||
}
|
||||
}
|
||||
|
||||
fn addApiPatches(
|
||||
al: std.mem.Allocator,
|
||||
api_patches: *std.StringHashMapUnmanaged(ApiPatches),
|
||||
name: []const u8,
|
||||
) error{OutOfMemory}!*ApiPatches {
|
||||
const entry = try api_patches.getOrPut(al, name);
|
||||
if (entry.found_existing) std.debug.panic("api '{s}' patched multiple times", .{name});
|
||||
entry.value_ptr.* = .{
|
||||
.name = name,
|
||||
.func_map = .{},
|
||||
.struct_map = .{},
|
||||
};
|
||||
return entry.value_ptr;
|
||||
}
|
||||
|
||||
pub const ApiPatches = struct {
|
||||
name: []const u8,
|
||||
func_map: std.StringHashMapUnmanaged(FuncPatches),
|
||||
struct_map: std.StringHashMapUnmanaged(StructPatches),
|
||||
|
||||
pub const none: ApiPatches = .{
|
||||
.name = "",
|
||||
.func_map = .{},
|
||||
.struct_map = .{},
|
||||
};
|
||||
|
||||
pub fn verify(api_patches: *const ApiPatches) void {
|
||||
{
|
||||
var it = api_patches.func_map.iterator();
|
||||
while (it.next()) |entry| {
|
||||
entry.value_ptr.verify(api_patches.name);
|
||||
}
|
||||
}
|
||||
{
|
||||
var it = api_patches.struct_map.iterator();
|
||||
while (it.next()) |entry| {
|
||||
entry.value_ptr.verify(api_patches.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn func(
|
||||
api_patches: *ApiPatches,
|
||||
al: std.mem.Allocator,
|
||||
func_patches: FuncPatches,
|
||||
) error{OutOfMemory}!void {
|
||||
const entry = try api_patches.func_map.getOrPut(al, func_patches.name);
|
||||
if (entry.found_existing) std.debug.panic("api '{s}' function '{s}' patched multiple times", .{ api_patches.name, func_patches.name });
|
||||
entry.value_ptr.* = func_patches;
|
||||
}
|
||||
pub fn struct_(
|
||||
api_patches: *ApiPatches,
|
||||
al: std.mem.Allocator,
|
||||
struct_patches: StructPatches,
|
||||
) error{OutOfMemory}!void {
|
||||
const entry = try api_patches.struct_map.getOrPut(al, struct_patches.name);
|
||||
if (entry.found_existing) std.debug.panic("api '{s}' struct '{s}' patched multiple times", .{ api_patches.name, struct_patches.name });
|
||||
entry.value_ptr.* = struct_patches;
|
||||
}
|
||||
pub fn finish(api_patches: *ApiPatches) void {
|
||||
_ = api_patches;
|
||||
}
|
||||
};
|
||||
|
||||
fn BoundedArray(comptime T: type, comptime max: usize) type {
|
||||
return struct {
|
||||
count: std.math.IntFittingRange(0, max),
|
||||
buffer: [max]T,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub const empty: Self = .{ .count = 0, .buffer = undefined };
|
||||
|
||||
pub fn copy(source: []const T) Self {
|
||||
if (source.len > max) std.debug.panic("slice {} too long (max {})", .{ source.len, max });
|
||||
var result: Self = .{ .count = @intCast(source.len), .buffer = undefined };
|
||||
@memcpy(result.buffer[0..source.len], source);
|
||||
return result;
|
||||
}
|
||||
pub fn slice(self: *const Self) []const T {
|
||||
return self.buffer[0..self.count];
|
||||
}
|
||||
pub fn sliceMut(self: *Self) []T {
|
||||
return self.buffer[0..self.count];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const StructPatches = struct {
|
||||
name: []const u8,
|
||||
optional_fields: BoundedArray(NamedOptional, 4) = .empty,
|
||||
|
||||
pub const none: StructPatches = .{ .name = "" };
|
||||
|
||||
pub fn verify(patches: *const StructPatches, api_name: []const u8) void {
|
||||
for (patches.optional_fields.slice()) |f| if (!f.applied) std.debug.panic(
|
||||
"api '{s}' struct '{s}' field '{s}' optional not applied",
|
||||
.{ api_name, patches.name, f.name },
|
||||
);
|
||||
}
|
||||
|
||||
pub fn queryOptionalField(patches: *StructPatches, name: []const u8) bool {
|
||||
for (patches.optional_fields.sliceMut()) |*p| {
|
||||
if (std.mem.eql(u8, name, p.name)) {
|
||||
std.debug.assert(!p.applied);
|
||||
p.applied = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
pub const FuncPatches = struct {
|
||||
name: []const u8,
|
||||
optional_return: ?OptionalReturn = null,
|
||||
optional_params: BoundedArray(NamedOptional, 1) = .empty,
|
||||
|
||||
pub const none: FuncPatches = .{ .name = "" };
|
||||
|
||||
pub fn verify(patches: *const FuncPatches, api_name: []const u8) void {
|
||||
if (patches.optional_return) |r| if (!r.applied) std.debug.panic(
|
||||
"api '{s}' function '{s}' optional return not applied",
|
||||
.{ api_name, patches.name },
|
||||
);
|
||||
for (patches.optional_params.slice()) |p| if (!p.applied) std.debug.panic(
|
||||
"api '{s}' function '{s}' param '{s}' optional not applied",
|
||||
.{ api_name, patches.name, p.name },
|
||||
);
|
||||
}
|
||||
|
||||
pub fn queryOptionalReturn(patches: *FuncPatches) bool {
|
||||
const optional_return: *OptionalReturn = &(patches.optional_return orelse return false);
|
||||
std.debug.assert(!optional_return.applied);
|
||||
optional_return.applied = true;
|
||||
return true;
|
||||
}
|
||||
pub fn queryOptionalParam(patches: *FuncPatches, name: []const u8) bool {
|
||||
for (patches.optional_params.sliceMut()) |*p| {
|
||||
if (std.mem.eql(u8, name, p.name)) {
|
||||
std.debug.assert(!p.applied);
|
||||
p.applied = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
const OptionalReturn = struct {
|
||||
applied: bool = false,
|
||||
};
|
||||
const NamedOptional = struct {
|
||||
name: []const u8,
|
||||
applied: bool = false,
|
||||
};
|
||||
|
||||
pub fn oom(e: error{OutOfMemory}) noreturn {
|
||||
@panic(@errorName(e));
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
@@ -0,0 +1,60 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn main() !void {
|
||||
var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
const arena = arena_instance.allocator();
|
||||
const all_args = try std.process.argsAlloc(arena);
|
||||
if (all_args.len != 2) @panic("unexpected number of cmdline args");
|
||||
const json_path = all_args[1];
|
||||
std.log.info("verifying JSON files in: {s}", .{json_path});
|
||||
var file_count: u32 = 0;
|
||||
var json_dir = try std.fs.cwd().openDir(json_path, .{ .iterate = true });
|
||||
defer json_dir.close();
|
||||
var it = json_dir.iterate();
|
||||
while (try it.next()) |entry| {
|
||||
switch (entry.kind) {
|
||||
.file => {},
|
||||
else => |kind| std.debug.panic(
|
||||
"unexpected directory entry kind '{t}' for '{s}' in '{s}'",
|
||||
.{ kind, entry.name, json_path },
|
||||
),
|
||||
}
|
||||
std.debug.assert(std.mem.endsWith(u8, entry.name, ".json"));
|
||||
try checkFile(arena, json_path, json_dir, entry.name);
|
||||
file_count += 1;
|
||||
}
|
||||
std.log.info("verified {} files", .{file_count});
|
||||
}
|
||||
|
||||
fn checkFile(allocator: std.mem.Allocator, dir_path: []const u8, dir: std.fs.Dir, sub_path: []const u8) !void {
|
||||
const content = blk: {
|
||||
var file = dir.openFile(sub_path, .{}) catch |err| {
|
||||
std.log.err("open '{s}/{s}' failed with {s}", .{ dir_path, sub_path, @errorName(err) });
|
||||
std.process.exit(0xff);
|
||||
};
|
||||
defer file.close();
|
||||
break :blk try file.readToEndAlloc(allocator, std.math.maxInt(usize));
|
||||
};
|
||||
defer allocator.free(content);
|
||||
|
||||
var diagnostics = std.json.Diagnostics{};
|
||||
var scanner = std.json.Scanner.initCompleteInput(allocator, content);
|
||||
defer scanner.deinit();
|
||||
scanner.enableDiagnostics(&diagnostics);
|
||||
const json = std.json.parseFromTokenSourceLeaky(
|
||||
std.json.Value,
|
||||
allocator,
|
||||
&scanner,
|
||||
.{},
|
||||
) catch |err| {
|
||||
std.log.err(
|
||||
"{s}{c}{s}:{}:{}: {s}",
|
||||
.{
|
||||
dir_path, std.fs.path.sep, sub_path,
|
||||
diagnostics.getLine(), diagnostics.getColumn(), @errorName(err),
|
||||
},
|
||||
);
|
||||
std.process.exit(0xff);
|
||||
};
|
||||
_ = json;
|
||||
}
|
||||
Reference in New Issue
Block a user