From a362d3963c879af47661f54b0a698729e142619f Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Wed, 27 Sep 2023 14:00:31 -0700 Subject: [PATCH] resinator: Update to latest, fix for big endian arch --- src/resinator/bmp.zig | 2 +- src/resinator/compile.zig | 34 ++++++++++++++++++---------------- src/resinator/errors.zig | 16 ++++++++++++++-- src/resinator/literals.zig | 14 +++++++------- src/resinator/res.zig | 15 +++++++-------- 5 files changed, 47 insertions(+), 34 deletions(-) diff --git a/src/resinator/bmp.zig b/src/resinator/bmp.zig index f6fdb9f280..0b3205c16b 100644 --- a/src/resinator/bmp.zig +++ b/src/resinator/bmp.zig @@ -92,7 +92,7 @@ pub fn read(reader: anytype, max_size: u64) ReadError!BitmapInfo { const id = std.mem.readIntNative(u16, file_header[0..2]); if (id != windows_format_id) return error.InvalidFileHeader; - bitmap_info.pixel_data_offset = std.mem.readIntNative(u32, file_header[10..14]); + bitmap_info.pixel_data_offset = std.mem.readIntLittle(u32, file_header[10..14]); if (bitmap_info.pixel_data_offset > max_size) return error.ImpossiblePixelDataOffset; bitmap_info.dib_header_size = reader.readIntLittle(u32) catch return error.UnexpectedEOF; diff --git a/src/resinator/compile.zig b/src/resinator/compile.zig index 063c42cd36..d0c4172d2f 100644 --- a/src/resinator/compile.zig +++ b/src/resinator/compile.zig @@ -571,7 +571,7 @@ pub const Compiler = struct { } try file.seekTo(entry.data_offset_from_start_of_file); - const header_bytes = file.reader().readBytesNoEof(16) catch { + var header_bytes = file.reader().readBytesNoEof(16) catch { return self.iconReadError( error.UnexpectedEOF, filename_utf8, @@ -647,8 +647,11 @@ pub const Compiler = struct { }, }, .dib => { - const bitmap_header: *const ico.BitmapHeader = @ptrCast(@alignCast(&header_bytes)); - const bitmap_version = ico.BitmapHeader.Version.get(std.mem.littleToNative(u32, bitmap_header.bcSize)); + var bitmap_header: *ico.BitmapHeader = @ptrCast(@alignCast(&header_bytes)); + if (builtin.cpu.arch.endian() == .Big) { + std.mem.byteSwapAllFields(ico.BitmapHeader, bitmap_header); + } + const bitmap_version = ico.BitmapHeader.Version.get(bitmap_header.bcSize); // The Win32 RC compiler only allows headers with // `bcSize == sizeof(BITMAPINFOHEADER)`, but it seems unlikely @@ -684,15 +687,15 @@ pub const Compiler = struct { .icon => { // The values in the icon's BITMAPINFOHEADER always take precedence over // the values in the IconDir, but not in the LOCALHEADER (see above). - entry.type_specific_data.icon.color_planes = std.mem.littleToNative(u16, bitmap_header.bcPlanes); - entry.type_specific_data.icon.bits_per_pixel = std.mem.littleToNative(u16, bitmap_header.bcBitCount); + entry.type_specific_data.icon.color_planes = bitmap_header.bcPlanes; + entry.type_specific_data.icon.bits_per_pixel = bitmap_header.bcBitCount; }, .cursor => { // Only cursors get the width/height from BITMAPINFOHEADER (icons don't) entry.width = @intCast(bitmap_header.bcWidth); entry.height = @intCast(bitmap_header.bcHeight); - entry.type_specific_data.cursor.hotspot_x = std.mem.littleToNative(u16, bitmap_header.bcPlanes); - entry.type_specific_data.cursor.hotspot_y = std.mem.littleToNative(u16, bitmap_header.bcBitCount); + entry.type_specific_data.cursor.hotspot_x = bitmap_header.bcPlanes; + entry.type_specific_data.cursor.hotspot_y = bitmap_header.bcBitCount; }, } }, @@ -826,9 +829,11 @@ pub const Compiler = struct { } if (bitmap_info.getExpectedPaletteByteLen() > 0) { try writeResourceDataNoPadding(writer, file_reader, @intCast(bitmap_info.getActualPaletteByteLen())); - const padding_bytes = bitmap_info.getMissingPaletteByteLen(); + // We know that the number of missing palette bytes is <= 4096 + // (see `bmp_too_many_missing_palette_bytes` error case above) + const padding_bytes: usize = @intCast(bitmap_info.getMissingPaletteByteLen()); if (padding_bytes > 0) { - try writer.writeByteNTimes(0, @intCast(padding_bytes)); + try writer.writeByteNTimes(0, padding_bytes); } } try file.seekTo(bitmap_info.pixel_data_offset); @@ -2855,7 +2860,7 @@ pub const SearchDir = struct { pub fn HeaderSlurpingReader(comptime size: usize, comptime ReaderType: anytype) type { return struct { child_reader: ReaderType, - bytes_read: u64 = 0, + bytes_read: usize = 0, slurped_header: [size]u8 = [_]u8{0x00} ** size, pub const Error = ReaderType.Error; @@ -2866,10 +2871,9 @@ pub fn HeaderSlurpingReader(comptime size: usize, comptime ReaderType: anytype) if (self.bytes_read < size) { const bytes_to_add = @min(amt, size - self.bytes_read); const end_index = self.bytes_read + bytes_to_add; - const dest = self.slurped_header[@intCast(self.bytes_read)..@intCast(end_index)]; - std.mem.copy(u8, dest, buf[0..bytes_to_add]); + std.mem.copy(u8, self.slurped_header[self.bytes_read..end_index], buf[0..bytes_to_add]); } - self.bytes_read += amt; + self.bytes_read +|= amt; return amt; } @@ -3196,9 +3200,7 @@ pub const StringTable = struct { // We already trimmed any trailing NULs, so we know it will be a new addition to the string. if (compiler.null_terminate_string_table_strings) string_len_in_utf16_code_units += 1; try data_writer.writeIntLittle(u16, string_len_in_utf16_code_units); - for (trimmed_string) |wc| { - try data_writer.writeIntLittle(u16, wc); - } + try data_writer.writeAll(std.mem.sliceAsBytes(trimmed_string)); if (compiler.null_terminate_string_table_strings) { try data_writer.writeIntLittle(u16, 0); } diff --git a/src/resinator/errors.zig b/src/resinator/errors.zig index 33cb19682b..badcfe7986 100644 --- a/src/resinator/errors.zig +++ b/src/resinator/errors.zig @@ -7,6 +7,7 @@ const res = @import("res.zig"); const ico = @import("ico.zig"); const bmp = @import("bmp.zig"); const parse = @import("parse.zig"); +const lang = @import("lang.zig"); const CodePage = @import("code_pages.zig").CodePage; pub const Diagnostics = struct { @@ -558,8 +559,19 @@ pub const ErrorDetails = struct { .hint => return, }, .string_already_defined => switch (self.type) { - // TODO: better printing of language, using constant names from WinNT.h - .err, .warning => return writer.print("string with id {d} (0x{X}) already defined for language {d},{d}", .{ self.extra.string_and_language.id, self.extra.string_and_language.id, self.extra.string_and_language.language.primary_language_id, self.extra.string_and_language.language.sublanguage_id }), + .err, .warning => { + const language_id = self.extra.string_and_language.language.asInt(); + const language_name = language_name: { + if (std.meta.intToEnum(lang.LanguageId, language_id)) |lang_enum_val| { + break :language_name @tagName(lang_enum_val); + } else |_| {} + if (language_id == lang.LOCALE_CUSTOM_UNSPECIFIED) { + break :language_name "LOCALE_CUSTOM_UNSPECIFIED"; + } + break :language_name ""; + }; + return writer.print("string with id {d} (0x{X}) already defined for language {s} (0x{X})", .{ self.extra.string_and_language.id, self.extra.string_and_language.id, language_name, language_id }); + }, .note => return writer.print("previous definition of string with id {d} (0x{X}) here", .{ self.extra.string_and_language.id, self.extra.string_and_language.id }), .hint => return, }, diff --git a/src/resinator/literals.zig b/src/resinator/literals.zig index 1d5258455b..e86083975c 100644 --- a/src/resinator/literals.zig +++ b/src/resinator/literals.zig @@ -395,7 +395,7 @@ pub fn parseQuotedString( while (try iterative_parser.next()) |parsed| { const c = parsed.codepoint; if (parsed.from_escaped_integer) { - try buf.append(@intCast(c)); + try buf.append(std.mem.nativeToLittle(T, @intCast(c))); } else { switch (literal_type) { .ascii => switch (options.output_code_page) { @@ -658,7 +658,7 @@ test "parse quoted wide string" { defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - try std.testing.expectEqualSentinel(u16, 0, &[_:0]u16{ 'h', 'e', 'l', 'l', 'o' }, try parseQuotedWideString(arena, .{ + try std.testing.expectEqualSentinel(u16, 0, std.unicode.utf8ToUtf16LeStringLiteral("hello"), try parseQuotedWideString(arena, .{ .slice = \\L"hello" , @@ -672,21 +672,21 @@ test "parse quoted wide string" { .code_page = .windows1252, }, .{})); // hex max of 4 digits - try std.testing.expectEqualSentinel(u16, 0, &[_:0]u16{ 0xFFFF, 'f' }, try parseQuotedWideString(arena, .{ + try std.testing.expectEqualSentinel(u16, 0, &[_:0]u16{ std.mem.nativeToLittle(u16, 0xFFFF), std.mem.nativeToLittle(u16, 'f') }, try parseQuotedWideString(arena, .{ .slice = \\L"\XfFfFf" , .code_page = .windows1252, }, .{})); // octal max of 7 digits - try std.testing.expectEqualSentinel(u16, 0, &[_:0]u16{ 0x9493, '3', '3' }, try parseQuotedWideString(arena, .{ + try std.testing.expectEqualSentinel(u16, 0, &[_:0]u16{ std.mem.nativeToLittle(u16, 0x9493), std.mem.nativeToLittle(u16, '3'), std.mem.nativeToLittle(u16, '3') }, try parseQuotedWideString(arena, .{ .slice = \\L"\111222333" , .code_page = .windows1252, }, .{})); // octal overflow - try std.testing.expectEqualSentinel(u16, 0, &[_:0]u16{0xFF01}, try parseQuotedWideString(arena, .{ + try std.testing.expectEqualSentinel(u16, 0, &[_:0]u16{std.mem.nativeToLittle(u16, 0xFF01)}, try parseQuotedWideString(arena, .{ .slice = \\L"\777401" , @@ -757,12 +757,12 @@ test "parse quoted ascii string as wide string" { .{}, )); // Maximum escape sequence value is also determined by the L prefix - try std.testing.expectEqualSentinel(u16, 0, std.unicode.utf8ToUtf16LeStringLiteral("\x1234"), try parseQuotedStringAsWideString( + try std.testing.expectEqualSentinel(u16, 0, &[_:0]u16{ std.mem.nativeToLittle(u16, 0x12), std.mem.nativeToLittle(u16, '3'), std.mem.nativeToLittle(u16, '4') }, try parseQuotedStringAsWideString( arena, .{ .slice = "\"\\x1234\"", .code_page = .windows1252 }, .{}, )); - try std.testing.expectEqualSentinel(u16, 0, &[_:0]u16{0x1234}, try parseQuotedStringAsWideString( + try std.testing.expectEqualSentinel(u16, 0, &[_:0]u16{std.mem.nativeToLittle(u16, 0x1234)}, try parseQuotedStringAsWideString( arena, .{ .slice = "L\"\\x1234\"", .code_page = .windows1252 }, .{}, diff --git a/src/resinator/res.zig b/src/resinator/res.zig index 48edeeccbc..d331644f61 100644 --- a/src/resinator/res.zig +++ b/src/resinator/res.zig @@ -218,6 +218,7 @@ pub const ControlClass = enum(u16) { }; pub const NameOrOrdinal = union(enum) { + // UTF-16 LE name: [:0]const u16, ordinal: u16, @@ -245,9 +246,7 @@ pub const NameOrOrdinal = union(enum) { pub fn write(self: NameOrOrdinal, writer: anytype) !void { switch (self) { .name => |name| { - for (name[0 .. name.len + 1]) |code_unit| { - try writer.writeIntLittle(u16, code_unit); - } + try writer.writeAll(std.mem.sliceAsBytes(name[0 .. name.len + 1])); }, .ordinal => |ordinal| { try writer.writeIntLittle(u16, 0xffff); @@ -281,7 +280,7 @@ pub const NameOrOrdinal = union(enum) { try buf.append(std.mem.nativeToLittle(u16, '�')); } else if (c < 0x7F) { // ASCII chars in names are always converted to uppercase - try buf.append(std.ascii.toUpper(@intCast(c))); + try buf.append(std.mem.nativeToLittle(u16, std.ascii.toUpper(@intCast(c)))); } else if (c < 0x10000) { const short: u16 = @intCast(c); try buf.append(std.mem.nativeToLittle(u16, short)); @@ -518,10 +517,10 @@ test "NameOrOrdinal" { var expected_u8_bytes = "00614982008907933748980730280674788429543776231864944218790698304852300002973622122844631429099469274282385299397783838528QFFL7SHNSIETG0QKLR1UYPBTUV1PMFQRRA0VJDG354GQEDJMUPGPP1W1EXVNTZVEIZ6K3IPQM1AWGEYALMEODYVEZGOD3MFMGEY8FNR4JUETTB1PZDEWSNDRGZUA8SNXP3NGO"; var buf: [256:0]u16 = undefined; for (expected_u8_bytes, 0..) |byte, i| { - buf[i] = byte; + buf[i] = std.mem.nativeToLittle(u16, byte); } // surrogate pair that is now orphaned - buf[255] = 0xD801; + buf[255] = std.mem.nativeToLittle(u16, 0xD801); break :blk buf; }; try expectNameOrOrdinal( @@ -908,7 +907,7 @@ pub const ForcedOrdinal = struct { var result: u16 = 0; for (utf16) |code_unit| { if (result != 0) result *%= 10; - result +%= code_unit -% '0'; + result +%= std.mem.littleToNative(u16, code_unit) -% '0'; } return result; } @@ -929,7 +928,7 @@ test "forced ordinal" { try std.testing.expectEqual(@as(u16, 0x4AF0), ForcedOrdinal.fromBytes(.{ .slice = "0\u{10100}", .code_page = .utf8 })); // From UTF-16 - try std.testing.expectEqual(@as(u16, 0x122), ForcedOrdinal.fromUtf16Le(&[_:0]u16{ '0', 'Œ' })); + try std.testing.expectEqual(@as(u16, 0x122), ForcedOrdinal.fromUtf16Le(&[_:0]u16{ std.mem.nativeToLittle(u16, '0'), std.mem.nativeToLittle(u16, 'Œ') })); try std.testing.expectEqual(@as(u16, 0x4AF0), ForcedOrdinal.fromUtf16Le(std.unicode.utf8ToUtf16LeStringLiteral("0\u{10100}"))); }