From 84dff0c8864f480ece8931d1d3bd24ddc56ccea7 Mon Sep 17 00:00:00 2001 From: Fabian Neundorf Date: Fri, 5 Dec 2025 21:19:19 +0100 Subject: [PATCH] patterns: Add json support for glb files (#412) * patterns: Add json support for glb files This makes it possible to separate display the different buffer views, accessors and images (even visualizing them). Unfortunately the data within the JSON gets sometimes corrupted and this is the reason, why it parses the JSON multiple times at some places. * Use original style and only single json variable * patterns: Reuse json from global variable in gltf * patterns: Check component type in gltf only once * patterns: Fix gltf pattern and add formatting Removes the duplicate definition of `component_type_t` and also removes the need to pass the `component_type` to `stride_type_t`. --- patterns/gltf.hexpat | 178 +++++++++++++++++++++++++++++++++- patterns/png.hexpat | 11 ++- patterns/png2.hexpat | 224 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 405 insertions(+), 8 deletions(-) create mode 100644 patterns/png2.hexpat diff --git a/patterns/gltf.hexpat b/patterns/gltf.hexpat index 85d94b8..a208b96 100644 --- a/patterns/gltf.hexpat +++ b/patterns/gltf.hexpat @@ -22,13 +22,18 @@ * SOFTWARE. */ -#pragma author H. Utku Maden +#pragma author H. Utku Maden, xZise #pragma description GL Transmission Format binary 3D model (.glb) #pragma MIME model/gltf-binary +#pragma magic [67 6C 54 46] @ 0x00 import std.mem; import std.io; import type.magic; +import std.core; +#ifdef __IMHEX__ +import hex.type.json; +#endif /** * @brief The glTF magic section. @@ -53,7 +58,15 @@ enum gltf_chunk_type_t : u32 { struct gltf_chunk_t { u32 length; /**< Length of this chunk. */ gltf_chunk_type_t type [[format("gltf_format")]]; /**< Type of the chunk. JSON or BIN expected. */ - u8 string[length]; /**< The chunk data. */ +#ifndef __IMHEX__ + u8 data[length]; /**< The chunk data. */ +#endif +#ifdef __IMHEX__ + match (type) { + (gltf_chunk_type_t::JSON): hex::type::Json json; + (gltf_chunk_type_t::BIN): u8 data[length]; + } /**< The chunk data. */ +#endif }; fn gltf_format(gltf_chunk_type_t x) @@ -64,7 +77,162 @@ fn gltf_format(gltf_chunk_type_t x) return ""; }; -gltf_magic_t magic @ 0x00; -gltf_chunk_t chunks[while(!std::mem::eof())] @ $; +struct stride_type_t { + InnerType value [[inline]]; + if (Stride > 0) { + padding[Stride - sizeof(value)]; + } +}; -std::assert_warn(std::mem::size() == magic.length, "file size mismatch"); +enum component_types_t : u64 { + BYTE = 5120, + UNSIGNED_BYTE = 5121, + SHORT = 5122, + UNSIGNED_SHORT = 5123, + UNSIGNED_INT = 5125, + FLOAT = 5126, +}; + +fn component_type_format(component_types_t component_type) +{ + if (component_type == component_types_t::BYTE) return "s8"; +else if (component_type == component_types_t::UNSIGNED_BYTE) return "u8"; +else if (component_type == component_types_t::SHORT) return "s16"; +else if (component_type == component_types_t::UNSIGNED_SHORT) return "u16"; +else if (component_type == component_types_t::UNSIGNED_INT) return "u32"; +else if (component_type == component_types_t::FLOAT) return "float"; + + return std::format("{}", component_type); +}; + +struct component_type_t { + match (component_type) { + (component_types_t::BYTE): s8 value; + (component_types_t::UNSIGNED_BYTE): u8 value; + (component_types_t::SHORT): s16 value; + (component_types_t::UNSIGNED_SHORT): u16 value; + (component_types_t::UNSIGNED_INT): u32 value; + (component_types_t::FLOAT): float value; + } +}; + +struct scalar_t { + component_type_t scalar [[inline]]; +} [[static]]; + +struct vec2_t { + component_type_t x; + component_type_t y; +} [[static]]; + +struct vec3_t { + component_type_t x; + component_type_t y; + component_type_t z; +} [[static]]; + +struct vec4_t { + component_type_t x; + component_type_t y; + component_type_t z; + component_type_t w; +} [[static]]; + +struct mat2_t { + component_type_t a11, a21; + component_type_t a12, a22; +} [[static]]; + +struct mat3_t { + component_type_t a11, a21, a31; + component_type_t a12, a22, a32; + component_type_t a13, a23, a33; +} [[static]]; + +struct mat4_t { + component_type_t a11, a21, a31, a41; + component_type_t a12, a22, a32, a42; + component_type_t a13, a23, a33, a43; + component_type_t a14, a24, a34, a44; +} [[static]]; + +fn mem_cnt(auto value) { + return std::core::member_count(value); +}; + +fn has_mem(auto value, str member) { + return std::core::has_member(value, member); +}; + +struct accessor_t { + u64 accessor_index = std::core::array_index(); + u64 view_index = glb.json_chunk.json.accessors[accessor_index].bufferView [[export]]; + u64 view_offset = glb.json_chunk.json.bufferViews[view_index].byteOffset [[export]]; + if (has_mem(glb.json_chunk.json.bufferViews[view_index], "byteStride")) { + u64 byte_stride = glb.json_chunk.json.bufferViews[view_index].byteStride [[export]]; + } else { + u64 byte_stride = 0 [[export]]; + } + if (has_mem(glb.json_chunk.json.accessors[accessor_index], "byteOffset")) { + u64 accessor_offset = glb.json_chunk.json.accessors[accessor_index].byteOffset [[export]]; + } else { + u64 accessor_offset = 0 [[export]]; + } + view_offset = view_offset + accessor_offset; + u64 count_elements = glb.json_chunk.json.accessors[accessor_index].count; + component_types_t component_type = glb.json_chunk.json.accessors[accessor_index].componentType [[export]]; + str element_type = glb.json_chunk.json.accessors[accessor_index].type [[export]]; + + match (element_type) { + ("SCALAR"): stride_type_t, byte_stride> content[count_elements] @ view_offset + addressof(glb.chunks[0].data); + ("VEC2"): stride_type_t, byte_stride> content[count_elements] @ view_offset + addressof(glb.chunks[0].data); + ("VEC3"): stride_type_t, byte_stride> content[count_elements] @ view_offset + addressof(glb.chunks[0].data); + ("VEC4"): stride_type_t, byte_stride> content[count_elements] @ view_offset + addressof(glb.chunks[0].data); + ("MAT2"): stride_type_t, byte_stride> content[count_elements] @ view_offset + addressof(glb.chunks[0].data); + ("MAT3"): stride_type_t, byte_stride> content[count_elements] @ view_offset + addressof(glb.chunks[0].data); + ("MAT4"): stride_type_t, byte_stride> content[count_elements] @ view_offset + addressof(glb.chunks[0].data); + } +} [[format("accessor_format")]]; + +fn accessor_format(accessor_t accessor) { + return std::format("{}<{}>[{}]", accessor.element_type, accessor.component_type, accessor.count_elements); +}; + +struct image_buffer_t { + u64 image_index = std::core::array_index(); + u64 buffer_view_index = glb.json_chunk.json.images[image_index].bufferView; + u64 byte_offset = glb.json_chunk.json.bufferViews[buffer_view_index].byteOffset; + u64 byte_length = glb.json_chunk.json.bufferViews[buffer_view_index].byteLength; + u8 image[byte_length] @ addressof(glb.chunks[0].data) + byte_offset; +} [[hex::visualize("image", image)]]; + +struct buffer_view_t { + u64 buffer_view_index = std::core::array_index(); + u64 byte_offset = glb.json_chunk.json.bufferViews[buffer_view_index].byteOffset; + u64 byte_length = glb.json_chunk.json.bufferViews[buffer_view_index].byteLength; + u8 data[byte_length] @ addressof(glb.chunks[0].data) + byte_offset; +}; + +struct glb_file_t { + gltf_magic_t magic; + gltf_chunk_t json_chunk; + gltf_chunk_t chunks[while(!std::mem::eof())]; + + std::assert_warn(std::mem::size() == magic.length, "file size mismatch"); +}; + +glb_file_t glb @ 0x00; + +#ifdef __IMHEX__ +struct glb_objects_t { + if (std::core::member_count(glb.chunks) == 1) { + if (has_mem(glb.json_chunk.json, "images")) { + image_buffer_t images[mem_cnt(glb.json_chunk.json.images)]; + } + buffer_view_t buffer_views[mem_cnt(glb.json_chunk.json.bufferViews)]; + accessor_t accessors[mem_cnt(glb.json_chunk.json.accessors)]; + } +}; + +glb_objects_t objects @ 0x00; +#endif \ No newline at end of file diff --git a/patterns/png.hexpat b/patterns/png.hexpat index ebd7122..787d058 100644 --- a/patterns/png.hexpat +++ b/patterns/png.hexpat @@ -214,6 +214,11 @@ struct Chunks { chunk_t iend_chunk [[comment("Image End Chunk")]]; }; -u8 visualizer[std::mem::size()] @ 0x00 [[sealed, hex::visualize("image", this)]]; -header_t header @ 0x00 [[comment("PNG file signature"), name("Signature")]]; -Chunks chunks @ 0x08 [[name("Chunks")]]; \ No newline at end of file +struct Png { + header_t header [[comment("PNG file signature"), name("Signature")]]; + Chunks chunks [[name("Chunks")]]; + u128 length = $ - addressof(this); + u8 visualizer[length] @ addressof(this) [[sealed, hex::visualize("image", this), no_unique_address]]; +}; + +Png png @ 0x00; \ No newline at end of file diff --git a/patterns/png2.hexpat b/patterns/png2.hexpat new file mode 100644 index 0000000..787d058 --- /dev/null +++ b/patterns/png2.hexpat @@ -0,0 +1,224 @@ +#pragma description PNG image + +#pragma MIME image/png +#pragma endian big + +import std.mem; + +struct header_t { + u8 highBitByte; + char signature[3]; + char dosLineEnding[2]; + char dosEOF; + char unixLineEnding; +}; + +struct actl_t { + u32 frames [[comment("Total № of frames in animation")]]; + u32 plays [[comment("№ of times animation will loop")]]; +} [[comment("Animation control chunk"), name("acTL")]]; + +enum ColorType: u8 { + Grayscale = 0x0, + RGBTriple = 0x2, + Palette, + GrayscaleAlpha, + RGBA = 0x6 +}; + +enum Interlacing: u8 { + None, + Adam7 +}; + +struct ihdr_t { + u32 width [[comment("Image width")]]; + u32 height [[comment("Image height")]]; + u8 bit_depth; + ColorType color_type [[comment("PNG Image Type")]]; + u8 compression_method [[comment("Only 0x0 = zlib supported by most")]]; + u8 filter_method [[comment("Only 0x0 = adaptive supported by most")]]; + Interlacing interlacing; +}; + +enum sRGB: u8 { + Perceptual = 0x0, + RelativeColorimetric, + Saturation, + AbsoluteColorimetric +}; + +enum Unit: u8 { + Unknown, + Meter +}; + +struct phys_t { + u32 ppu_x [[comment("Pixels per unit, X axis")]]; + u32 ppu_y [[comment("Pixels per unit, Y axis")]]; + Unit unit; +}; + +enum BlendOp: u8 { + Source = 0x0, + Over +}; + +enum DisposeOp: u8 { + None = 0x0, + Background, + Previous +}; + +struct fctl_t { + u32 sequence_no [[comment("Sequence №")]]; + u32 width [[comment("Frame width")]]; + u32 height; + u32 xoff; + u32 yoff; + u16 delay_num; + u16 delay_den; + DisposeOp dispose_op; + BlendOp blend_op; +}; + +struct fdat_t { + u32 sequence_no; +}; + +fn text_len() { + u64 len = parent.parent.length - ($ - addressof(parent.keyword)); + return len; +}; + +struct itxt_t { + char keyword[]; + u8 compression_flag; + u8 compression_method; + char language_tag[]; + char translated_keyword[]; + char text[text_len()]; +}; + +struct ztxt_t { + char keyword[]; + u8 compression_method; + char text[text_len()]; +}; + +struct text_t { + char keyword[]; + char text[text_len()]; +}; + +struct iccp_t { + char keyword[]; + u8 compression_method; + u8 compressed_profile[text_len()]; +}; + +struct palette_entry_t { + u24 color; +} [[inline]]; + +struct chrm_t { + u32 white_point_x; + u32 white_point_y; + u32 red_x; + u32 red_y; + u32 green_x; + u32 green_y; + u32 blue_x; + u32 blue_y; +}; + +struct time_t { + u16 year; + u8 month; + u8 day; + u8 hour; + u8 minute; + u8 second; +}; + +struct chunk_t { + u32 length [[color("17BECF")]]; + char name[4]; + + #define IHDR_k "IHDR" + #define PLTE_k "PLTE" + #define sRGB_k "sRGB" + #define pHYs_k "pHYs" + #define iTXt_k "iTXt" + #define tEXt_k "tEXt" + #define zTXt_k "zTXt" + #define IDAT_k "IDAT" + #define IEND_k "IEND" + #define gAMA_k "gAMA" + #define iCCP_k "iCCP" + #define acTL_k "acTL" + #define fdAT_k "fdAT" + #define fcTL_k "fcTL" + #define cHRM_k "cHRM" + #define tIME_k "tIME" + + if (name == IHDR_k) { + ihdr_t ihdr [[comment("Image Header chunk"), name("IHDR")]]; + } else if (name == PLTE_k) { + palette_entry_t entries[length / 3]; + } else if (name == sRGB_k) { + sRGB srgb; + } else if (name == pHYs_k) { + phys_t phys; + } else if (name == acTL_k) { + actl_t actl [[comment("Animation control chunk")]]; + } else if (name == fcTL_k) { + fctl_t fctl [[comment("Frame control chunk")]]; + } else if (name == iTXt_k) { + itxt_t text; + } else if (name == gAMA_k) { + u32 gamma [[name("image gamma"), comment("4 byte unsigned integer representing gamma times 100000")]]; + } else if (name == iCCP_k) { + iccp_t iccp; + } else if (name == tEXt_k) { + text_t text; + } else if (name == zTXt_k) { + ztxt_t text; + } else if (name == iCCP_k) { + iccp_t iccp; + } else if (name == fdAT_k) { + fdat_t fdat [[comment("Frame data chunk")]]; + u8 data[length-sizeof(u32)]; + } else if (name == cHRM_k) { + chrm_t chrm; + } else if (name == tIME_k) { + time_t time; + } else { + u8 data[length]; + } + + u32 crc; +} [[name(chunkValueName(this))]]; + +fn chunkValueName(ref chunk_t chunk) { + return chunk.name; +}; + +struct chunk_set { + chunk_t chunks[while(builtin::std::mem::read_string($ + 4, 4) != "IEND")] [[inline]]; +} [[inline]]; + +struct Chunks { + chunk_t ihdr_chunk [[comment("PNG Header chunk")]]; + chunk_set set [[comment("PNG Chunks"), name("Chunks"), inline]]; + chunk_t iend_chunk [[comment("Image End Chunk")]]; +}; + +struct Png { + header_t header [[comment("PNG file signature"), name("Signature")]]; + Chunks chunks [[name("Chunks")]]; + u128 length = $ - addressof(this); + u8 visualizer[length] @ addressof(this) [[sealed, hex::visualize("image", this), no_unique_address]]; +}; + +Png png @ 0x00; \ No newline at end of file