From 0d098211b75c7b6861aa06aff98d8cef09507280 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Fri, 22 Mar 2024 09:25:53 -0400 Subject: [PATCH] LibRIFF+LibGfx/ISOBMFF: Make ChunkID (de)serialization self-consistent Previously, ChunkID's from_big_endian_number() and as_big_endian_number() weren't inverses of each other. ChunkID::from_big_endian_number() used to take an u32 that contained `('f' << 24) | ('t' << 16) | ('y' << 8) | 'p'`, that is 'f', 't', 'y', 'p' in memory on big-endian and 'p', 'y', 't', 'f' on little-endian, and return a ChunkID for 'f', 't', 'y', 'p'. ChunkID::as_big_endian_number() used to return an u32 that for a ChunkID storing 'f', 't', 'y', 'p' was always 'f', 't', 'y', 'p' in memory on both little-endian and big-endian, that is it stored `('f' << 24) | ('t' << 16) | ('y' << 8) | 'p'` on big-endian and `('p' << 24) | ('y' << 16) | ('t' << 8) | 'f'` on little-endian. `ChunkID::from_big_endian_number(0x11223344).as_big_endian_number()` returned 0x44332211. This change makes the two methods self-consistent: they now take and return a u32 that always has the first ChunkID part in the highest bits of the u32 (`'f' << 24`), and so on. That also means they return a u32 that in-memory looks differently on big-endian and little-endian. Since that's normal for numbers, this also renames the two methods to just `from_number()` and `to_number()`. With the semantics cleared up, change the one use in ISOBMFF to read a BigEndian for chunk headers and brand codes. This has the effect of tags now being printed in the right order. Before: ```sh % Build/lagom/bin/isobmff ~/Downloads/sample1.jp2 Unknown Box (' Pj') [ 4 bytes ] ('pytf') (version = 0, flags = 0x0) - major_brand = ' 2pj' - minor_version = 0 - compatible_brands = { ' 2pj' } Unknown Box ('h2pj') [ 37 bytes ] Unknown Box ('fniu') [ 92 bytes ] Unknown Box (' lmx') [ 2736 bytes ] Unknown Box ('c2pj') [ 667336 bytes ] ``` After: ```sh % Build/lagom/bin/isobmff ~/Downloads/sample1.jp2 hmm 0x11223344 0x11223344 Unknown Box ('jP ') [ 4 bytes ] ('ftyp' ) (version = 0, flags = 0x0) - major_brand = 'jp2 ' - minor_version = 0 - compatible_brands = { 'jp2 ' } Unknown Box ('jp2h') [ 37 bytes ] Unknown Box ('uinf') [ 92 bytes ] Unknown Box ('xml ') [ 2736 bytes ] Unknown Box ('jp2c') [ 667336 bytes ] ``` --- .../LibGfx/ImageFormats/ISOBMFF/Boxes.cpp | 6 +++--- .../Libraries/LibGfx/ImageFormats/ISOBMFF/Enums.h | 8 ++++---- Userland/Libraries/LibRIFF/ChunkID.h | 14 +++++++++++--- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Boxes.cpp b/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Boxes.cpp index efa6a0903e4..ad9413527c3 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Boxes.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Boxes.cpp @@ -12,7 +12,7 @@ ErrorOr read_box_header(Stream& stream) { BoxHeader header; u64 total_size = TRY(stream.read_value>()); - header.type = TRY(stream.read_value()); + header.type = TRY(stream.read_value>()); u64 data_size_read = sizeof(u32) + sizeof(BoxType); @@ -68,7 +68,7 @@ void UnknownBox::dump(String const& prepend) const ErrorOr FileTypeBox::read_from_stream(BoxStream& stream) { // unsigned int(32) major_brand; - major_brand = TRY(stream.read_value()); + major_brand = TRY(stream.read_value>()); // unsigned int(32) minor_version; minor_version = TRY(stream.read_value>()); @@ -77,7 +77,7 @@ ErrorOr FileTypeBox::read_from_stream(BoxStream& stream) return Error::from_string_literal("FileTypeBox compatible_brands contains a partial brand"); for (auto minor_brand_count = stream.remaining() / sizeof(BrandIdentifier); minor_brand_count > 0; minor_brand_count--) - TRY(compatible_brands.try_append(TRY(stream.read_value()))); + TRY(compatible_brands.try_append(TRY(stream.read_value>()))); return {}; } diff --git a/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Enums.h b/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Enums.h index 4716dc30c52..02073340314 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Enums.h +++ b/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Enums.h @@ -23,7 +23,7 @@ namespace Gfx::ISOBMFF { enum class BoxType : u32 { None = 0, -#define ENUMERATE_ONE(box_name, box_4cc) box_name = RIFF::ChunkID(#box_4cc).as_big_endian_number(), +#define ENUMERATE_ONE(box_name, box_4cc) box_name = RIFF::ChunkID(#box_4cc).as_number(), ENUMERATE_ALL() @@ -54,7 +54,7 @@ static bool is_valid_box_type(BoxType type) enum class BrandIdentifier : u32 { None = 0, -#define ENUMERATE_ONE(brand_4cc) brand_4cc = RIFF::ChunkID(#brand_4cc).as_big_endian_number(), +#define ENUMERATE_ONE(brand_4cc) brand_4cc = RIFF::ChunkID(#brand_4cc).as_number(), ENUMERATE_ALL() @@ -70,7 +70,7 @@ struct AK::Formatter : Formatter { ErrorOr format(FormatBuilder& builder, Gfx::ISOBMFF::BoxType const& box_type) { StringView format_string = Gfx::ISOBMFF::is_valid_box_type(box_type) ? "({})"sv : "Unknown Box ({})"sv; - return Formatter::format(builder, format_string, RIFF::ChunkID::from_big_endian_number(to_underlying(box_type))); + return Formatter::format(builder, format_string, RIFF::ChunkID::from_number(to_underlying(box_type))); } }; @@ -78,6 +78,6 @@ template<> struct AK::Formatter : Formatter { ErrorOr format(FormatBuilder& builder, Gfx::ISOBMFF::BrandIdentifier const& brand_identifier) { - return Formatter::format(builder, "{}"sv, RIFF::ChunkID::from_big_endian_number(to_underlying(brand_identifier))); + return Formatter::format(builder, "{}"sv, RIFF::ChunkID::from_number(to_underlying(brand_identifier))); } }; diff --git a/Userland/Libraries/LibRIFF/ChunkID.h b/Userland/Libraries/LibRIFF/ChunkID.h index a47e2860bf7..54401841c1a 100644 --- a/Userland/Libraries/LibRIFF/ChunkID.h +++ b/Userland/Libraries/LibRIFF/ChunkID.h @@ -32,14 +32,22 @@ struct ChunkID { constexpr ChunkID(ChunkID const&) = default; constexpr ChunkID(ChunkID&&) = default; constexpr ChunkID& operator=(ChunkID const&) = default; - static constexpr ChunkID from_big_endian_number(u32 number) { return bit_cast>(AK::convert_between_host_and_big_endian(number)); } + static constexpr ChunkID from_number(u32 number) + { + return Array { { + static_cast((number >> 24) & 0xff), + static_cast((number >> 16) & 0xff), + static_cast((number >> 8) & 0xff), + static_cast(number & 0xff), + } }; + } static ErrorOr read_from_stream(Stream& stream); StringView as_ascii_string() const; - constexpr u32 as_big_endian_number() const + constexpr u32 as_number() const { - return AK::convert_between_host_and_big_endian((id_data[0] << 24) | (id_data[1] << 16) | (id_data[2] << 8) | id_data[3]); + return (id_data[0] << 24) | (id_data[1] << 16) | (id_data[2] << 8) | id_data[3]; } bool operator==(ChunkID const&) const = default;