diff --git a/src/cvtres.zig b/src/cvtres.zig index 82f378c..12676c1 100644 --- a/src/cvtres.zig +++ b/src/cvtres.zig @@ -36,6 +36,10 @@ pub const Resource = struct { if (self.data.len != 0) return false; return true; } + + pub fn isDlgInclude(resource: Resource) bool { + return resource.type_value == .ordinal and resource.type_value.ordinal == @intFromEnum(res.RT.DLGINCLUDE); + } }; pub const ParsedResources = struct { @@ -56,6 +60,7 @@ pub const ParsedResources = struct { pub const ParseResOptions = struct { skip_zero_data_resources: bool = true, + skip_dlginclude_resources: bool = true, max_size: u64, }; @@ -83,6 +88,8 @@ pub fn parseResInto(resources: *ParsedResources, reader: anytype, options: Parse const resource_and_size = try parseResource(allocator, reader, bytes_remaining); if (options.skip_zero_data_resources and resource_and_size.resource.data.len == 0) { resource_and_size.resource.deinit(allocator); + } else if (options.skip_dlginclude_resources and resource_and_size.resource.isDlgInclude()) { + resource_and_size.resource.deinit(allocator); } else { errdefer resource_and_size.resource.deinit(allocator); try resources.list.append(allocator, resource_and_size.resource); diff --git a/test/fuzzy_cvtres.zig b/test/fuzzy_cvtres.zig index 7df1fba..94d8e95 100644 --- a/test/fuzzy_cvtres.zig +++ b/test/fuzzy_cvtres.zig @@ -7,7 +7,7 @@ const resinator = @import("resinator"); test "cvtres fuzz" { const allocator = std.testing.allocator; var random = std.Random.DefaultPrng.init(std.testing.random_seed); - var rand = random.random(); + const rand = random.random(); var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); @@ -101,3 +101,41 @@ test "cvtres fuzz" { }; } } + +test "all reserved types" { + const allocator = std.testing.allocator; + var random = std.Random.DefaultPrng.init(std.testing.random_seed); + const rand = random.random(); + + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + + const tmp_path = try tmp.dir.realpathAlloc(allocator, "."); + defer allocator.free(tmp_path); + + var data_buffer = std.ArrayList(u8).init(allocator); + defer data_buffer.deinit(); + + var res_buffer = std.ArrayList(u8).init(allocator); + defer res_buffer.deinit(); + + try utils.writePreface(res_buffer.writer()); + + // https://learn.microsoft.com/en-us/windows/win32/menurc/user-defined-resource + // > The numbers 1 through 255 are reserved for existing and future predefined resource types. + for (1..256) |predefined_type| { + _ = try utils.writeRandomValidResource(allocator, rand, res_buffer.writer(), .{ + .set_type = .{ .ordinal = @intCast(predefined_type) }, + .set_data = "foo", + }); + } + + // also write it to the top-level tmp dir for debugging + if (fuzzy_options.fuzzy_debug) + try std.fs.cwd().writeFile(.{ .sub_path = ".zig-cache/tmp/fuzzy_cvtres_reserved_types.res", .data = res_buffer.items }); + + try utils.expectSameCvtResOutput(allocator, res_buffer.items, .{ + .cwd = tmp.dir, + .cwd_path = tmp_path, + }); +} diff --git a/test/utils.zig b/test/utils.zig index 57db069..d5d9f2a 100644 --- a/test/utils.zig +++ b/test/utils.zig @@ -852,11 +852,22 @@ pub const RandomResourceOptions = struct { /// Returns the data size of the resource that was written pub fn writeRandomValidResource(allocator: Allocator, rand: std.Random, writer: anytype, options: RandomResourceOptions) !u32 { const data_size: u32 = if (options.set_data) |data| @intCast(data.len) else rand.uintAtMostBiased(u32, 150); - const name_value = options.set_name orelse try getRandomNameOrOrdinal(allocator, rand, 32); - defer if (options.set_name == null) name_value.deinit(allocator); const type_value = options.set_type orelse try getRandomNameOrOrdinal(allocator, rand, 32); defer if (options.set_type == null) type_value.deinit(allocator); + const name_value = name_value: { + // Avoid a cvtres.exe miscompilation where DLGINCLUDE resources are excluded, but their name + // value is written into the strings section anyway (even though it is not referenced by anything). + // This is a hacky approach, but we just force the ID to be an ordinal whenever the type is DLGINCLUDE. + if (type_value == .ordinal and type_value.ordinal == @intFromEnum(resinator.res.RT.DLGINCLUDE)) { + if (options.set_name != null and options.set_name.? == .ordinal) + break :name_value options.set_name.?; + break :name_value resinator.res.NameOrOrdinal{ .ordinal = rand.int(u16) }; + } + break :name_value options.set_name orelse try getRandomNameOrOrdinal(allocator, rand, 32); + }; + defer if (options.set_name == null) name_value.deinit(allocator); + const header = resinator.compile.Compiler.ResourceHeader{ .name_value = name_value, .type_value = type_value,