From 8a6bb6612b6cdaefc4b52e696d1591fde0ffc973 Mon Sep 17 00:00:00 2001 From: Nik Date: Sun, 17 Nov 2024 13:51:51 +0100 Subject: [PATCH 01/29] includes/std: Fixed standard library errors with latest nightly --- includes/std/mem.pat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/std/mem.pat b/includes/std/mem.pat index 84d962ae..b7359b77 100644 --- a/includes/std/mem.pat +++ b/includes/std/mem.pat @@ -258,8 +258,8 @@ namespace auto std::mem { @tparam To The type to reinterpret to */ union Reinterpreter { - From from; - To to; + From from_value; + To to_value; }; From 6697fc23a4eb4092fb01296b18c0cd97add56fb4 Mon Sep 17 00:00:00 2001 From: paxcut <53811119+paxcut@users.noreply.github.com> Date: Sun, 17 Nov 2024 05:52:30 -0700 Subject: [PATCH 02/29] themes: Updated the custom themes to the new text editor color palettes (#255) * Updated the custom themes to the new text editor color palettes designed for full syntax highlighting, but the existing themes should look just like they did before. * impr: renamed pattern-placed-variable to calculated-pointer and added view fix: pe pattern could read past the end of file if symbol search option was turned on and input file had no symbols. In that case a StringTable array that has 1 element would be created and place at offset 0 where the size variable was set to a large number creating the error. The fix is to set the size of the array using numberOfSymbols>0 * Fix: that was a bad choice for the size of the stringTable array because even if findSymbols is not on the file can contain symbols. This was causing the pe unit test to fail. To get the right array size use sizeof(symbolTable)>0 which will be 1 only if findSymbols is true and the file actually contains symbols --- patterns/pe.hexpat | 2 +- themes/catppuccin-frappe.json | 35 ++++++++++++++++++++++++++------ themes/catppuccin-latte.json | 35 ++++++++++++++++++++++++++------ themes/catppuccin-macchiato.json | 35 ++++++++++++++++++++++++++------ themes/catppuccin-mocha.json | 35 ++++++++++++++++++++++++++------ themes/solarized_dark.json | 34 ++++++++++++++++++++++++++----- themes/vs_dark.json | 35 ++++++++++++++++++++++++++------ 7 files changed, 175 insertions(+), 36 deletions(-) diff --git a/patterns/pe.hexpat b/patterns/pe.hexpat index 575c6305..16bcff5a 100644 --- a/patterns/pe.hexpat +++ b/patterns/pe.hexpat @@ -1209,7 +1209,7 @@ struct StringTable { SymbolString strings[while($ < addressof(this) + size)]; } [[inline]]; -StringTable stringTable[checkForSymbols] @ addressof(symbolTable) + sizeof(symbolTable); +StringTable stringTable[sizeof(symbolTable)>0] @ addressof(symbolTable) + sizeof(symbolTable); // Rich Header bool checkForRichHeader in; diff --git a/themes/catppuccin-frappe.json b/themes/catppuccin-frappe.json index e318b24c..7ff19966 100644 --- a/themes/catppuccin-frappe.json +++ b/themes/catppuccin-frappe.json @@ -126,25 +126,48 @@ "title-text": "#C6D0F5FF" }, "text-editor": { + "preprocessor-deactivated": "#4F4F4F45", "background": "#303446FF", "breakpoint": "#E78284FF", "char-literal": "#A6D189FF", "comment": "#737994FF", + "doc-block-comment": "#737994FF", + "doc-comment": "#737994FF", + "doc-global-comment": "#737994FF", "current-line-edge": "#838BA77F", "current-line-fill": "#838BA77F", "current-line-fill-inactive": "#7379947F", "cursor": "#F2D5CFFF", "default": "#838BA7FF", + "default-text": "#838BA7FF", "error-marker": "#E782847F", - "identifier": "#949CBBFF", + "unknown-identifier": "#E782847F", + "error-text": "#E782847F", + "debug-text": "#E782847F", + "warning-text": "#E782847F", + "pattern-variable": "#949CBBFF", + "function-parameter": "#949CBBFF", + "function-variable": "#949CBBFF", + "view": "#949CBBFF", + "global-variable": "#949CBBFF", + "local-variable": "#949CBBFF", + "calculated-pointer": "#949CBBFF", + "placed-variable": "#949CBBFF", + "template-variable": "#949CBBFF", + "attribute": "#949CBBFF", + "function": "#949CBBFF", + "namespace": "#949CBBFF", + "typedef": "#949CBBFF", + "user-defined-type": "#949CBBFF", "keyword": "#CA9EE6FF", - "known-identifier": "#E78284FF", + "builtin-type": "#E78284FF", "line-number": "#838BA7FF", - "multi-line-comment": "#737994FF", + "block-comment": "#737994FF", "number": "#EF9F76FF", - "preproc-identifier": "#BABBF1FF", - "preprocessor": "#A5ADCEFF", - "punctuation": "#C6D0F5FF", + "preprocessor-identifier": "#BABBF1FF", + "directive": "#A5ADCEFF", + "operator": "#C6D0F5FF", + "separator": "#C6D0F5FF", "selection": "#6268807F", "string": "#A6D189FF" } diff --git a/themes/catppuccin-latte.json b/themes/catppuccin-latte.json index a5d331a2..29e66922 100644 --- a/themes/catppuccin-latte.json +++ b/themes/catppuccin-latte.json @@ -126,25 +126,48 @@ "title-text": "#4C4F69FF" }, "text-editor": { + "preprocessor-deactivated": "#4F4F4F45", "background": "#EFF1F5FF", "breakpoint": "#D20F39FF", "char-literal": "#40A02BFF", "comment": "#9CA0B0FF", + "doc-block-comment": "#9CA0B0FF", + "doc-comment": "#9CA0B0FF", + "doc-global-comment": "#9CA0B0FF", "current-line-edge": "#8C8FA17F", "current-line-fill": "#8C8FA17F", "current-line-fill-inactive": "#9CA0B07F", "cursor": "#DC8A78FF", "default": "#8C8FA1FF", + "default-text": "#8C8FA1FF", "error-marker": "#D20F397F", - "identifier": "#7C7F93FF", + "unknown-identifier": "#D20F397F", + "error-text": "#D20F397F", + "debug-text": "#D20F397F", + "warning-text": "#D20F397F", + "pattern-variable": "#7C7F93FF", + "function-parameter": "#7C7F93FF", + "function-variable": "#7C7F93FF", + "view": "#7C7F93FF", + "global-variable": "#7C7F93FF", + "local-variable": "#7C7F93FF", + "calculated-pointer": "#7C7F93FF", + "placed-variable": "#7C7F93FF", + "template-variable": "#7C7F93FF", + "attribute": "#7C7F93FF", + "function": "#7C7F93FF", + "namespace": "#7C7F93FF", + "typedef": "#7C7F93FF", + "user-defined-type": "#7C7F93FF", "keyword": "#8839EFFF", - "known-identifier": "#D20F39FF", + "builtin-type": "#D20F39FF", "line-number": "#8C8FA1FF", - "multi-line-comment": "#9CA0B0FF", + "block-comment": "#9CA0B0FF", "number": "#FE640BFF", - "preproc-identifier": "#7287FDFF", - "preprocessor": "#6C6F85FF", - "punctuation": "#4C4F69FF", + "preprocessor-identifier": "#7287FDFF", + "directive": "#6C6F85FF", + "operator": "#4C4F69FF", + "separator": "#4C4F69FF", "selection": "#ACB0BE7F", "string": "#40A02BFF" } diff --git a/themes/catppuccin-macchiato.json b/themes/catppuccin-macchiato.json index 25b556f4..e056cc22 100644 --- a/themes/catppuccin-macchiato.json +++ b/themes/catppuccin-macchiato.json @@ -126,25 +126,48 @@ "title-text": "#CAD3F5FF" }, "text-editor": { + "preprocessor-deactivated": "#4F4F4F45", "background": "#24273AFF", "breakpoint": "#ED8796FF", "char-literal": "#A6DA95FF", "comment": "#6E738DFF", + "doc-block-comment": "#6E738DFF", + "doc-comment": "#6E738DFF", + "doc-global-comment": "#6E738DFF", "current-line-edge": "#8087A27F", "current-line-fill": "#8087A27F", "current-line-fill-inactive": "#6E738D7F", "cursor": "#F4DBD6FF", "default": "#8087A2FF", + "default-text": "#8087A2FF", "error-marker": "#ED87967F", - "identifier": "#939AB7FF", + "unknown-identifier": "#ED87967F", + "error-text": "#ED87967F", + "debug-text": "#ED87967F", + "warning-text": "#ED87967F", + "pattern-variable": "#939AB7FF", + "function-parameter": "#939AB7FF", + "function-variable": "#939AB7FF", + "view": "#939AB7FF", + "global-variable": "#939AB7FF", + "local-variable": "#939AB7FF", + "calculated-pointer": "#939AB7FF", + "placed-variable": "#939AB7FF", + "template-variable": "#939AB7FF", + "attribute": "#939AB7FF", + "function": "#939AB7FF", + "namespace": "#939AB7FF", + "typedef": "#939AB7FF", + "user-defined-type": "#939AB7FF", "keyword": "#C6A0F6FF", - "known-identifier": "#ED8796FF", + "builtin-type": "#ED8796FF", "line-number": "#8087A2FF", - "multi-line-comment": "#6E738DFF", + "block-comment": "#6E738DFF", "number": "#F5A97FFF", - "preproc-identifier": "#B7BDF8FF", - "preprocessor": "#A5ADCBFF", - "punctuation": "#CAD3F5FF", + "preprocessor-identifier": "#B7BDF8FF", + "directive": "#A5ADCBFF", + "operator": "#CAD3F5FF", + "separator": "#CAD3F5FF", "selection": "#5B60787F", "string": "#A6DA95FF" } diff --git a/themes/catppuccin-mocha.json b/themes/catppuccin-mocha.json index b44a181a..67652505 100644 --- a/themes/catppuccin-mocha.json +++ b/themes/catppuccin-mocha.json @@ -126,25 +126,48 @@ "title-text": "#CDD6F4FF" }, "text-editor": { + "preprocessor-deactivated": "#4F4F4F45", "background": "#1E1E2EFF", "breakpoint": "#F38BA8FF", "char-literal": "#A6E3A1FF", "comment": "#6C7086FF", + "doc-block-comment": "#6C7086FF", + "doc-comment": "#6C7086FF", + "doc-global-comment": "#6C7086FF", "current-line-edge": "#7F849C7F", "current-line-fill": "#7F849C7F", "current-line-fill-inactive": "#6C70867F", "cursor": "#F5E0DCFF", "default": "#7F849CFF", + "default-text": "#7F849CFF", "error-marker": "#F38BA87F", - "identifier": "#9399B2FF", + "unknown-identifier": "#F38BA87F", + "error-text": "#F38BA87F", + "debug-text": "#F38BA87F", + "warning-text": "#F38BA87F", + "pattern-variable": "#9399B2FF", + "function-parameter": "#9399B2FF", + "function-variable": "#9399B2FF", + "view": "#9399B2FF", + "global-variable": "#9399B2FF", + "local-variable": "#9399B2FF", + "calculated-pointer": "#9399B2FF", + "placed-variable": "#9399B2FF", + "template-variable": "#9399B2FF", + "attribute": "#9399B2FF", + "function": "#9399B2FF", + "namespace": "#9399B2FF", + "typedef": "#9399B2FF", + "user-defined-type": "#9399B2FF", "keyword": "#CBA6F7FF", - "known-identifier": "#F38BA8FF", + "builtin-type": "#F38BA8FF", "line-number": "#7F849CFF", - "multi-line-comment": "#6C7086FF", + "block-comment": "#6C7086FF", "number": "#FAB387FF", - "preproc-identifier": "#B4BEFEFF", - "preprocessor": "#A6ADC8FF", - "punctuation": "#CDD6F4FF", + "preprocessor-identifier": "#B4BEFEFF", + "directive": "#A6ADC8FF", + "operator": "#CDD6F4FF", + "separator": "#CDD6F4FF", "selection": "#585B707F", "string": "#A6E3A1FF" } diff --git a/themes/solarized_dark.json b/themes/solarized_dark.json index 9d1a11d2..9b075646 100644 --- a/themes/solarized_dark.json +++ b/themes/solarized_dark.json @@ -126,25 +126,49 @@ "title-text": "#FFFFFFFF" }, "text-editor": { + "preprocessor-deactivated": "#4F4F4F45", "background": "#0B2A36FF", "breakpoint": "#0080F040", "char-literal": "#E0A070FF", "comment": "#206020FF", + "doc-block-comment": "#206020FF", + "doc-comment": "#206020FF", + "doc-global-comment": "#206020FF", "current-line-edge": "#A0A0A040", "current-line-fill": "#21506172", "current-line-fill-inactive": "#21506172", "cursor": "#E0E0E0FF", "default": "#7F7F7FFF", + "default-text": "#7F7F7FFF", "error-marker": "#FF200080", + "unknown-identifier": "#FF200080", + "error-text": "#FF200080", + "debug-text": "#FF200080", + "warning-text": "#FF200080", "identifier": "#AAAAAAFF", + "pattern-variable": "#AAAAAAFF", + "function-parameter": "#AAAAAAFF", + "function-variable": "#AAAAAAFF", + "view": "#AAAAAAFF", + "global-variable": "#AAAAAAFF", + "local-variable": "#AAAAAAFF", + "calculated-pointer": "#AAAAAAFF", + "placed-variable": "#AAAAAAFF", + "template-variable": "#AAAAAAFF", + "attribute": "#AAAAAAFF", + "function": "#AAAAAAFF", + "namespace": "#AAAAAAFF", + "typedef": "#AAAAAAFF", + "user-defined-type": "#AAAAAAFF", "keyword": "#569CD6FF", - "known-identifier": "#4DC69BFF", + "builtin-type": "#4DC69BFF", "line-number": "#007070FF", - "multi-line-comment": "#206040FF", + "block-comment": "#206040FF", "number": "#00FF00FF", - "preproc-identifier": "#A040C0FF", - "preprocessor": "#808040FF", - "punctuation": "#FFFFFFFF", + "preprocessor-identifier": "#A040C0FF", + "directive": "#808040FF", + "operator": "#FFFFFFFF", + "separator": "#FFFFFFFF", "selection": "#2060A080", "string": "#E07070FF" } diff --git a/themes/vs_dark.json b/themes/vs_dark.json index 419fe561..92b41bf0 100644 --- a/themes/vs_dark.json +++ b/themes/vs_dark.json @@ -128,25 +128,48 @@ "title-text": "#FFFFFFFF" }, "text-editor": { + "preprocessor-deactivated": "#4F4F4F45", "background": "#1F1F1FFF", "breakpoint": "#0080F040", "char-literal": "#E0A070FF", "comment": "#206020FF", + "doc-block-comment": "#206020FF", + "doc-comment": "#206020FF", + "doc-global-comment": "#206020FF", "current-line-edge": "#A0A0A040", "current-line-fill": "#00000040", "current-line-fill-inactive": "#80808040", "cursor": "#E0E0E0FF", "default": "#7F7F7FFF", + "default-text": "#7F7F7FFF", "error-marker": "#FF200080", - "identifier": "#AAAAAAFF", + "unknown-identifier": "#FF200080", + "error-text": "#FF200080", + "debug-text": "#FF200080", + "warning-text": "#FF200080", + "pattern-variable": "#AAAAAAFF", + "function-parameter": "#AAAAAAFF", + "function-variable": "#AAAAAAFF", + "view": "#AAAAAAFF", + "global-variable": "#AAAAAAFF", + "local-variable": "#AAAAAAFF", + "calculated-pointer": "#AAAAAAFF", + "placed-variable": "#AAAAAAFF", + "template-variable": "#AAAAAAFF", + "attribute": "#AAAAAAFF", + "function": "#AAAAAAFF", + "namespace": "#AAAAAAFF", + "typedef": "#AAAAAAFF", + "user-defined-type": "#AAAAAAFF", "keyword": "#569CD6FF", - "known-identifier": "#4DC69BFF", + "builtin-type": "#4DC69BFF", "line-number": "#007070FF", - "multi-line-comment": "#206040FF", + "block-comment": "#206040FF", "number": "#00FF00FF", - "preproc-identifier": "#A040C0FF", - "preprocessor": "#808040FF", - "punctuation": "#FFFFFFFF", + "preprocessor-identifier": "#A040C0FF", + "directive": "#808040FF", + "operator": "#FFFFFFFF", + "separator": "#FFFFFFFF", "selection": "#2060A080", "string": "#E07070FF" } From bf94cb72435ec4fddc7851f28af18acc0cce5c43 Mon Sep 17 00:00:00 2001 From: applecuckoo <113647417+applecuckoo@users.noreply.github.com> Date: Mon, 18 Nov 2024 01:53:18 +1300 Subject: [PATCH 03/29] patterns: Added new WebP and VGM patterns (#294) * README: fix square bracket * patterns: add WebP pattern * patterns/dds: add x-dds mimetype * patterns: add vgm pattern * patterns/vgm: remove old pointer * patterns/protobuf: fix field number handling * patterns/protobuf: add .pb file extension * patterns/uf2: updating the family IDs again * patterns/png: add cHRM and tIME chunks * patterns/png: whoops, old description snuck back in * new quantized-mesh pattern * add quantized-mesh to README, implement oct16 decoding --- README.md | 5 +- patterns/dds.hexpat | 1 + patterns/png.hexpat | 28 ++- patterns/protobuf.hexpat | 36 ++-- patterns/quantized-mesh.hexpat | 169 +++++++++++++++ patterns/uf2.hexpat | 10 + patterns/vgm.hexpat | 248 ++++++++++++++++++++++ patterns/webp.hexpat | 122 +++++++++++ tests/patterns/test_data/webp.hexpat.webp | Bin 0 -> 934 bytes 9 files changed, 604 insertions(+), 15 deletions(-) create mode 100644 patterns/quantized-mesh.hexpat create mode 100644 patterns/vgm.hexpat create mode 100644 patterns/webp.hexpat create mode 100644 tests/patterns/test_data/webp.hexpat.webp diff --git a/README.md b/README.md index 2acf9c32..4a6ff877 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi |------|------|------|-------------| | 3DS | | [`patterns/3ds.hexpat`](patterns/3ds.hexpat) | Autodesk 3DS Max Model file | | 7Z | | [`patterns/7z.hexpat`](patterns/7z.hexpat) | 7z File Format | -| ADTS | | [`patterns/adts.hexpat`(patterns/adts.hexpat) | ADTS/AAC audio files | +| ADTS | | [`patterns/adts.hexpat`](patterns/adts.hexpat) | ADTS/AAC audio files | | AFE2 | | [`patterns/afe2.hexpat`](patterns/afe2.hexpat) | Nintendo Switch Atmosphère CFW Fatal Error log | | ANI | `application/x-navi-animation` | [`patterns/ani.hexpat`](patterns/ani.hexpat) | Windows Animated Cursor file | | AR | `application/x-archive` | [`patterns/ar.hexpat`](patterns/ar.hexpat) | Static library archive files | @@ -115,6 +115,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | PYC | | [`patterns/pyc.hexpat`](patterns/pyc.hexpat) | Python bytecode files | | QBCL | | [`patterns/qbcl.hexpat`](patterns/qbcl.hexpat) | Qubicle voxel scene project file | | QOI | `image/qoi` | [`patterns/qoi.hexpat`](patterns/qoi.hexpat) | QOI image files | +| quantized-mesh | | [`patterns/quantized-mesh.hexpat`](patterns/quantized-mesh.hexpat) | Cesium quantized-mesh terrain | | RAS | `image/x-sun-raster` | [`patterns/ras.hexpat`](patterns/ras.hexpat) | RAS image files | | ReFS | | [`patterns/refs.hexpat`](patterns/refs.hexpat) | Microsoft Resilient File System | | RGBDS | | [`patterns/rgbds.hexpat`](patterns/rgbds.hexpat) | [RGBDS](https://rgbds.gbdev.io) object file format | @@ -136,10 +137,12 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | UF2 | | [`patterns/uf2.hexpat`](patterns/uf2.hexpat) | [USB Flashing Format](https://github.com/microsoft/uf2) | | VBMeta | | [`patterns/vbmeta.hexpat`](patterns/vbmeta.hexpat) | Android VBMeta image | | VDF | | [`patterns/vdf.hexpat`](patterns/vdf.hexpat) | Binary Value Data Format (.vdf) files | +| VGM | | [`patterns/vgm.hexpat`](patterns/vgm.hexpat) | VGM (Video Game Music) sound log | | VHDX | | [`patterns/vhdx.hexpat`](patterns/vhdx.hexpat) | Microsoft Hyper-V Virtual Hard Disk format | | WAV | `audio/x-wav` | [`patterns/wav.hexpat`](patterns/wav.hexpat) | RIFF header, WAVE header, PCM header | | WAS | | [`patterns\was_oskasoftware.hexpat`](patterns\was_oskasoftware.hexpat) | Oska Software DeskMates WAS/WA3 (WAVE/MP3 Set) file | WAD | | [`patterns/wad.hexpat`](patterns/wad.hexpat) | DOOM WAD Archive | +| WebP | `image/webp` | [`patterns/webp.hexpat`](patterns/webp.hexpat) | Google WebP image | | XBEH | `audio/x-xbox-executable` | [`patterns/xbeh.hexpat`](patterns/xbeh.hexpat) | Xbox executable | | XCI | | [`patterns/xci.hexpat`](patterns/xci.hexpat) | Nintendo Switch XCI cartridge ROM | | XGT | | [`patterns/xgt.hexpat`](patterns/xgstexture.hexpat) | Exient XGS Engine Texture | diff --git a/patterns/dds.hexpat b/patterns/dds.hexpat index a7c42a58..9cc60d76 100644 --- a/patterns/dds.hexpat +++ b/patterns/dds.hexpat @@ -1,6 +1,7 @@ #pragma description DirectDraw Surface #pragma MIME image/vnd-ms.dds +#pragma MIME image/x-dds #pragma endian little enum DXGI_FORMAT : u32 { diff --git a/patterns/png.hexpat b/patterns/png.hexpat index 8ab0d8ae..ebd7122e 100644 --- a/patterns/png.hexpat +++ b/patterns/png.hexpat @@ -1,4 +1,4 @@ -#pragma description PNG image +#pragma description PNG image #pragma MIME image/png #pragma endian big @@ -121,6 +121,26 @@ 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]; @@ -139,6 +159,8 @@ struct chunk_t { #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")]]; @@ -167,6 +189,10 @@ struct chunk_t { } 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]; } diff --git a/patterns/protobuf.hexpat b/patterns/protobuf.hexpat index ba401cc4..ce17cd7b 100644 --- a/patterns/protobuf.hexpat +++ b/patterns/protobuf.hexpat @@ -1,7 +1,8 @@ #pragma author WerWolv -#pragma description Google Protobuf +#pragma description Google Protobuf wire encoding (.pb) import std.core; +import std.io; import std.mem; import type.leb128; @@ -31,10 +32,19 @@ enum WireType : u8 { _32Bit = 5 }; -bitfield Key { - field_number : 5; - wire_type : 3; -} [[bitfield_order(std::core::BitfieldOrder::MostToLeastSignificant, 8)]]; +WireType wire_type; +u32 tag; +u32 field_number; + +struct Key { + type::uLEB128 keyDec; + field_number = u32(keyDec) >> 3; + wire_type = u32(keyDec) & 7; +}[[sealed, format("format_key")]]; + +fn format_key(Key keyObject) { + return std::format("{} with field number {}", wire_type, field_number); +}; union _64Bit { u64 fixed64; @@ -49,22 +59,22 @@ union _32Bit { }; struct LengthDelimited { - type::LEB128 length; + type::uLEB128 length; char data[length]; }; struct Entry { - Key key; + Key key; - if (key.wire_type == WireType::Varint) - type::LEB128 value; - else if (key.wire_type == WireType::_64Bit) + if (wire_type == WireType::Varint) + type::uLEB128 value; + else if (wire_type == WireType::_64Bit) _64Bit value; - else if (key.wire_type == WireType::LengthDelimited) + else if (wire_type == WireType::LengthDelimited) LengthDelimited value; - else if (key.wire_type == WireType::_32Bit) + else if (wire_type == WireType::_32Bit) _32Bit value; }; -Entry entries[while(!std::mem::eof())] @ 0x00; +Entry entries[while(!std::mem::eof())] @ 0x00; \ No newline at end of file diff --git a/patterns/quantized-mesh.hexpat b/patterns/quantized-mesh.hexpat new file mode 100644 index 00000000..f4b05c57 --- /dev/null +++ b/patterns/quantized-mesh.hexpat @@ -0,0 +1,169 @@ +#pragma author applecuckoo +#pragma description Cesium quantized-mesh terrain +#pragma endian little + +// based on https://github.com/CesiumGS/quantized-mesh +// potential improvements: figure out high water mark encoding for indices + +import std.math; +import std.io; + +u8 extensionCount; +extensionCount = 0; // NOTE: set this to the amount of extensions in your terrain. + +// ZigZag decoder based on protobuf.hexpat by WerWolv + +struct ZigZag16 { + u16 value; +} [[sealed, format("format_zigzag16")]]; + +fn format_zigzag16(ZigZag16 zigzag) { + return s16((s16(zigzag.value) << 1) ^ (s16(zigzag.value) >> 15)); +}; + +struct QuantizedMeshHeader { + double CenterX; + double CenterY; + double CenterZ; + + float MinimumHeight; + float MaximumHeight; + + double BoundingSphereCenterX; + double BoundingSphereCenterY; + double BoundingSphereCenterZ; + double BoundingSphereRadius; + + double HorizonOcclusionPointX; + double HorizonOcclusionPointY; + double HorizonOcclusionPointZ; +}; + +struct VertexData { + u32 vertexCount; + ZigZag16 u[vertexCount]; + ZigZag16 v[vertexCount]; + ZigZag16 height[vertexCount]; +}; + +struct IndexData16 { + u32 triangleCount; + u16 indices[triangleCount * 3]; +}; + +struct IndexData32 { + u32 triangleCount; + u32 indices[triangleCount * 3]; +}; + +struct EdgeIndices16 { + u32 westVertexCount; + u16 westIndices[westVertexCount]; + + u32 southVertexCount; + u16 southIndices[southVertexCount]; + + u32 eastVertexCount; + u16 eastIndices[eastVertexCount]; + + u32 northVertexCount; + u16 northIndices[northVertexCount]; +}; + +struct EdgeIndices32 { + u32 westVertexCount; + u32 westIndices[westVertexCount]; + + u32 southVertexCount; + u32 southIndices[southVertexCount]; + + u32 eastVertexCount; + u32 eastIndices[eastVertexCount]; + + u32 northVertexCount; + u32 northIndices[northVertexCount]; +}; + +enum ExtensionTypes : u8 { + OctEncodedVertexNormals = 0x1, + WaterMask, + Metadata = 0x4, +}; + +// Oct16 decode based on https://github.com/loicgasser/quantized-mesh-tile/blob/master/quantized_mesh_tile/utils.py + +fn signNotZero(float v) { + if (v < 0.0) + return -1.0; + else + return 1.0; +}; + +fn fromSnorm(u8 value) { + return float(std::math::clamp(value, 0.0, 255.0) / 255.0 * 2.0 - 1.0); +}; + +struct Oct16 { + u8 x; + u8 y; +}[[sealed, format("format_oct16")]]; + +fn format_oct16(Oct16 oct) { + float xOut; + float yOut; + float zOut; + + xOut = fromSnorm(oct.x); + yOut = fromSnorm(oct.y); + zOut = 1.0 - (std::math::abs(xOut) + std::math::abs(yOut)); + + if (zOut < 0.0) { + float oldX; + + oldX = xOut; + xOut = (1.0 - std::math::abs(yOut)) * signNotZero(oldX); + yOut = (1.0 - std::math::abs(oldX)) * signNotZero(yOut); + } + + return std::format("{}, {}, {}", xOut, yOut, zOut); +}; + +struct OctEncodedVertexNormals { + Oct16 xy[parent.parent.vertdata.vertexCount]; +}; + +struct WaterMask { + u8 mask[parent.extensionLength]; +}; + +struct Metadata { + u32 jsonLength; + char json[jsonLength]; +}; + +struct ExtensionHeader { + u8 extensionId; + u32 extensionLength; + match (extensionId) { + (ExtensionTypes::OctEncodedVertexNormals): OctEncodedVertexNormals octVertNormals; + (ExtensionTypes::WaterMask): WaterMask maskData; + (ExtensionTypes::Metadata): Metadata metadata; + } +}; + +struct QuantizedMesh { + QuantizedMeshHeader header; + VertexData vertdata; + + if (vertdata.vertexCount > 65536) { + IndexData32 indexdata; + EdgeIndices32 edgeindices; + } else { + IndexData16 indexdata; + EdgeIndices16 edgeindices; + } + + ExtensionHeader extensions[extensionCount]; +}; + +QuantizedMesh mesh @ 0x00; \ No newline at end of file diff --git a/patterns/uf2.hexpat b/patterns/uf2.hexpat index a710c8a4..c974cce9 100644 --- a/patterns/uf2.hexpat +++ b/patterns/uf2.hexpat @@ -98,6 +98,16 @@ enum UF2_FamilyID : u32 { NRF52832xxAB = 0x6f752678, AT32F415 = 0xa0c97b8e, CH32V = 0x699b62ec, + RA4M1 = 0x7be8976d, + RTL8710A = 0x9fffd543, + RTL8710B = 0x22e0d6fc, + RTL8720C = 0xe08f7564, + RTL8720D = 0x3379CFE2, + XR809 = 0x51e903a8, + BK7231U = 0x675a40b0, + BK7251 = 0x6a82cc42, + BK7231N = 0x7b3ef230, + BL602 = 0xde1270b7, }; fn formatTagType(UF2_TagType type) { diff --git a/patterns/vgm.hexpat b/patterns/vgm.hexpat new file mode 100644 index 00000000..5f8f59b2 --- /dev/null +++ b/patterns/vgm.hexpat @@ -0,0 +1,248 @@ +#pragma author applecuckoo +#pragma description VGM (Video Game Music) sound log + +#pragma endian little + +import type.magic; +import std.string; +import std.io; + +u32 versionValue; +u32 gd3TagPos; +u32 chpClkBase; +u32 chpVolBase; + +// note: the versionValue variable exists to help check which fields exist and which don't, otherwise the actual log data would show up as part of the header. + +bitfield VGMVersion { + bugfix : 4; + minor : 4; + major : 24; + + versionValue = major * 100 + minor * 10 + bugfix; + +} [[format("format_VGMVersion")]]; + +fn format_VGMVersion(auto version) { + return std::format("{}.{}{}", version.major, version.minor, version.bugfix); +}; + +bitfield SN76489Flags { + frequency : 1; + negateOutput : 1; + GameGearStereo : 1; + clockDiv : 1; + XNORNoiseMode : 1; + padding : 3; +}; + +bitfield AY8910Flags { + legacyOutput : 1; + singleOutput : 1; + discreteOutput : 1; + RAWOutput : 1; + YMclockDivLow : 1; + padding : 3; +}; + +bitfield OKIM6258Flags { + clockDiv : 2; + ADPCMsel : 1; + outputBitDepth : 1; + padding : 4; +}; + +bitfield K054539Flags { + reverseStereo : 1; + disableReverb : 1; + KeyOnUpdate : 1; + padding : 5; +}; + +enum AY8190Type : u8 { + AY8910, + AY8912, + AY8913, + AY8914, + YM2149 = 0x10, + YM3439, + YMZ284, + YMZ294, +}; + +enum C140Type : u8 { + C140_NamcoSystem2, + C140_NamcoSystem21, + ASIC219_NamcoNA, +}; + +struct Gd3 { + type::Magic<"Gd3 "> ident; + VGMVersion version; + u32 Gd3Length; + std::string::NullString16 trackNameEng; + std::string::NullString16 trackNameOriginal; + std::string::NullString16 gameNameEng; + std::string::NullString16 gameNameOriginal; + std::string::NullString16 sysNameEng; + std::string::NullString16 sysNameOriginal; + std::string::NullString16 trackAuthorEnglish; + std::string::NullString16 trackAuthorOriginal; + std::string::NullString16 gameReleaseDate; + std::string::NullString16 VGMConverter; + std::string::NullString16 Notes; +}; + +struct baseHeader { + type::Magic<"Vgm "> ident; + u32 eofOffset; + VGMVersion version; + u32 SN76489_clk; + u32 YM2413_clk; + gd3TagPos = $; + u32 Gd3Offset; + u32 sampleCount; + u32 loopOffset; + u32 loopSamples; +}; + +struct Header : baseHeader { + if (versionValue >= 101) { + u32 rate; + } if (versionValue >= 110) { + u16 SN76489_feedback; + u8 SN76489_shift_width; + } if (versionValue >= 151) { + SN76489Flags snflags; + } else { + padding[1]; + } if (versionValue >= 110) { + u32 YM2612_clk; + u32 YM2151_clk; + } if (versionValue >= 150) { + u32 VGMOffset; + } if (versionValue >= 151) { + u32 SegaPCM_clk; + u32 SegaPCM_reg; + u32 RF5C68_clk; + u32 YM2203_clk; + u32 YM2608_clk; + u32 YM2610_clk; + u32 YM3812_clk; + u32 YM3526_clk; + u32 Y8950_clk; + u32 YMF262_clk; + u32 YMF278B_clk; + u32 YMF271_clk; + u32 YMZ280B_clk; + u32 RF5C164_clk; + u32 PWM_clk; + u32 AY8910_clk; + AY8190Type AY8910_type; + AY8910Flags AY8910_flags; + u8 YM2203_flags; + u8 YM2608_flags; + } if (versionValue >= 160) { + u8 volumeMod; + padding[1]; + u8 loopBase; + } else { + padding[3]; + } if (versionValue >= 151) { + u8 loopMod; + } if (versionValue >= 161) { + u32 DMG_clk; + u32 APU_clk; + u32 MultiPCM_clk; + u32 uPD7759_clk; + u32 OKIM6258_clk; + OKIM6258Flags OKIM6258_flags; + K054539Flags K054539_flags; + C140Type C140_type; + padding[1]; + u32 OKIM6295_clk; + u32 K051649_clk; + u32 K054539_clk; + u32 HuC6280_clk; + u32 C140_clk; + u32 K053260_clk; + u32 Pokey_clk; + u32 QSound_clk; + } if (versionValue >= 171) { + u32 SCSP_clk; + } else { + padding[4]; + } if (versionValue >= 170) { + u32 extraHeaderOffset; + } if (versionValue >= 171) { + u32 WonderSwan_clk; + u32 VSU_clk; + u32 SAA1099_clk; + u32 ES5503_clk; + u32 ES5005_clk; + u8 ES5503_ch; + u8 ES5505_ch; + u8 C352_clockDiv; + padding[1]; + u32 X1_010_clk; + u32 C352_clk; + u32 GA20_clk; + } if (versionValue > 172) { + u32 Mikey_clk; + } +}; + +struct chpClkEntry { + u8 chpID; + u32 chpClk; +}; + +struct chpClkHeader { + u8 entryCount; + chpClkEntry entries[entryCount]; +}; + +bitfield chpVolume { + volume : 15; + absoluteRelative : 1; +}; + +struct chpVolEntry { + u8 chpID; + u8 flags; + chpVolume chpVol; +}; + +struct chpVolHeader { + u8 entryCount; + chpVolEntry entries[entryCount]; +}; + +struct ExtraHeader { + u32 headerSize; + chpClkBase = $; + u32 chpClkOffset; + chpVolBase = $; + u32 chpVolOffset; + + if (chpClkOffset > 0) { + $ = chpClkBase + chpClkOffset; + chpClkHeader chpClk; + } if (chpVolOffset > 0) { + $ = chpVolBase + chpVolOffset; + chpVolHeader chpVol; + } + +}; + +struct VGM { + Header header; + if (versionValue >= 170) { + if (header.extraHeaderOffset > 0) { + ExtraHeader extraHeader; + } + } + Gd3 tag @ (gd3TagPos + header.Gd3Offset); +}; + +VGM vgm @ 0x00; diff --git a/patterns/webp.hexpat b/patterns/webp.hexpat new file mode 100644 index 00000000..611b76b2 --- /dev/null +++ b/patterns/webp.hexpat @@ -0,0 +1,122 @@ +#pragma author applecuckoo +#pragma description Google WebP + +#pragma endian little +#pragma MIME image/webp + +// based off of ttf.hexpat by Rebuild and wav.hexpat by WerWolv + +import std.mem; +import std.core; +import type.magic; + +struct RiffHeader { + char ckID[4] [[comment("Container Signature"), name("RIFF Header Signature")]]; + u32 ckSize [[comment("Size of RIFF Header"), name("RIFF Chunk Size")]]; + char format[4] [[comment("RIFF format"), name("WAVE Header Signature")]]; +}; + +struct WebPChunk { + char chunkId[4]; + u32 chunkSize; +}; + +bitfield VP8XFlags { + padding : 1; + A : 1; + X : 1; + E : 1; + L : 1; + I : 1; + padding : 2; +}; + +struct OneBase { + u24 value; +} [[sealed, format("format_onebase")]]; + +fn format_onebase(OneBase onebase) { + return onebase.value + 1; +}; + +struct WebPVP8XData { + VP8XFlags flags; + padding[3]; + OneBase canvasWidth; + OneBase canvasHeight; +}; + +struct WebPANIMData { + u32 backgroundColor; + u16 loopCount; +}; + +bitfield ANMFFlags { + D : 1; + B : 1; + padding : 6; +}; + +bitfield WebPVP8LHeader { + widthMinusOne : 14; + heightMinusOne : 14; + alphaUsed : 1; + version : 3; +}; + +struct WebPVP8LData { + type::Magic<"\x2f"> id; + WebPVP8LHeader header; +}; + +u32 frameSize; + +struct WebPANMFData { + u24 frameX; + u24 frameY; + OneBase frameWidth; + OneBase frameHeight; + u24 frameDuration; + ANMFFlags flags; + u8 data[parent.chunkHeader.chunkSize - 16]; // lazy fix - can't be bothered implementing subchunks + +}; + +bitfield ALPHFlags { + C : 2; + F : 2; + P : 2; + padding : 2; +}; + +u32 paddedChunkSize; + +struct WebPData { + WebPChunk chunkHeader; + paddedChunkSize = (chunkHeader.chunkSize + 1) >> 1 << 1; + match (chunkHeader.chunkId) { + ("VP8X"): WebPVP8XData VP8XData; + ("ANIM"): WebPANIMData ANIMData; + ("ANMF"): { + WebPANMFData ANMFData; + padding[paddedChunkSize - sizeof(ANMFData)]; + } + ("VP8L"): { + WebPVP8LHeader VP8LData; + u8 image[chunkHeader.chunkSize-3]; + padding[paddedChunkSize - sizeof(VP8LData) - sizeof(image)]; + } + ("ALPH"): { + ALPHFlags flags; + u8 ALPHData[chunkHeader.chunkSize-1]; + padding[paddedChunkSize - sizeof(ALPHData) - sizeof(flags)]; + } + (_): { + u8 data[chunkHeader.chunkSize]; + padding[paddedChunkSize - sizeof(data)]; + } + } +} [[name(std::format("Chunk ({})", chunkHeader.chunkId))]];; + +RiffHeader header @0x00; +WebPData data[while (!std::mem::eof())] @ $; diff --git a/tests/patterns/test_data/webp.hexpat.webp b/tests/patterns/test_data/webp.hexpat.webp new file mode 100644 index 0000000000000000000000000000000000000000..510916df98e539f0f6d1738291b5acf11eb93789 GIT binary patch literal 934 zcmV;X16ll1Nk&GV0{{S5MM6+kP&il$0000G0000#002J#06|PpNM`{600EG+|DPc_ z^{;<*uWj4*?rCqfcVpYOlatMBp4^OTt-28=LagP5!opv5GP_3B4S_&(Qu*R*|Qkj<|e_YJ7n` z23>5#d(h4R$-%kx4UPB-Dz=@3>_sP%@d~t65!s>fNy+#QN|yq5pyM{4g*K89#Fp*y zI!X|@9UUP&9c>U_lG|%2-bI@Wfv2JM4z69q{YRo59hG=G+Tab;fsXs)*=Qq~Bh`;i zX5iImn?=T?{{(b>BR-FI1+gjOkeNv9!3aghLgyMe68#G#c&3qq5yph{A8%x5kQ5$X zMlpa?^!z1Dmo8nVap}^fi*haUq-vUTpTNYnkFR!Hk?=+`Dt>YS09H^qAP@oo0MHBo zodGI906+jfg*=rXY&tFk3r_7lqRO=9v&%*a=yhVQ~_+c>tL!TLf}NuOtRe2%m!fQ1Kk zaf7znUwh*rT0MGseFl|OG$a!3f)R%DM52ok*>zn578~n|K60H0hS)Bx?HSqIMxHT7 zyVy(P)4MPH&2^OUJB@h~8d>}0@2vV%#0;fAV&f_By%a30|8faUw^1B-Kmri=c94i9 zXYw(XzD3%{Ma@px?HBRV}d}0}YCgpjmNoMk3R Date: Sun, 17 Nov 2024 13:54:03 +0100 Subject: [PATCH 04/29] includes/std: Added checks to ends_with and starts_with to prevent failure (#295) Fixing cases where part is longer than string, this cases should both return false instead of failing. Co-authored-by: Nik --- includes/std/string.pat | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/includes/std/string.pat b/includes/std/string.pat index 0ce755df..3b3cb6a4 100644 --- a/includes/std/string.pat +++ b/includes/std/string.pat @@ -125,6 +125,8 @@ namespace auto std::string { @return True if the string starts with the substring, false otherwise. */ fn starts_with(str string, str part) { + if (std::string::length(string) < std::string::length(part)) + return false; return std::string::substr(string, 0, std::string::length(part)) == part; }; @@ -135,6 +137,8 @@ namespace auto std::string { @return True if the string ends with the substring, false otherwise. */ fn ends_with(str string, str part) { + if (std::string::length(string) < std::string::length(part)) + return false; return std::string::substr(string, std::string::length(string) - std::string::length(part), std::string::length(part)) == part; }; From abbd25e7f6e19afaf4c09a8ee9184c7bf71f3d81 Mon Sep 17 00:00:00 2001 From: Seth Hall Date: Sun, 17 Nov 2024 07:54:29 -0500 Subject: [PATCH 05/29] patterns: Added Windows Notepad cache file pattern (#297) * Added Windows Notepad Cache file parser. * Fixed Notepad windowstate link in readme * Added a test file for notepad-cache.hexpat --- README.md | 3 +- patterns/notepad-cache.hexpat | 82 ++++++++++++++++++ .../test_data/notepad-cache.hexpat.bin | Bin 0 -> 417 bytes 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 patterns/notepad-cache.hexpat create mode 100644 tests/patterns/test_data/notepad-cache.hexpat.bin diff --git a/README.md b/README.md index 4a6ff877..c651d1aa 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,8 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | NBT | | [`patterns/nbt.hexpat`](patterns/nbt.hexpat) | Minecraft NBT format | | NE | | [`patterns/ne.hexpat`](patterns/ne.hexpat) | NE header and Standard NE fields | | nes | | [`patterns/nes.hexpat`](patterns/nes.hexpat) | .nes file format | -| NotepadWindowState | | [`patterns/notepad-windowstate.hexpat`](patterns/notepad-windowstate.hexpat) | Windows 11 Notepad - Window State .bin file | +| NotepadCache | | [`patterns/notepad-cache.hexpat`](patterns/notepad-cache.hexpat) | Windows Notepad Cache | +| NotepadWindowState | | [`patterns/notepadwindowstate.hexpat`](patterns/notepadwindowstate.hexpat) | Windows 11 Notepad - Window State .bin file | | NRO | | [`patterns/nro.hexpat`](patterns/nro.hexpat) | Nintendo Switch NRO files | | NTAG | | [`patterns/ntag.hexpat`](patterns/ntag.hexpat) | NTAG213/NTAG215/NTAG216, NFC Forum Type 2 Tag compliant IC | | OGG | `audio/ogg` | [`patterns/ogg.hexpat`](patterns/ogg.hexpat) | OGG Audio format | diff --git a/patterns/notepad-cache.hexpat b/patterns/notepad-cache.hexpat new file mode 100644 index 00000000..cb7b0395 --- /dev/null +++ b/patterns/notepad-cache.hexpat @@ -0,0 +1,82 @@ +#pragma author sethhall +#pragma description Windows Notepad Cache Files + +// This was written based on the following blog post: +// https://u0041.co/posts/articals/exploring-windows-artifacts-notepad-files/ + +import type.leb128; +import type.time; +import type.magic; +import std.time; + +using uLEB128 = type::uLEB128; + +enum Encodings: u8 { + ANSI = 0x01, + UTF_16LE = 0x02, + UTF_16BE = 0x03, + UTF_8BOM = 0x04, + UTF_8 = 0x05, +}; + +enum LineEndings: u8 { + CRLF = 0x01, + CR = 0x02, + LF = 0x03, +}; + +struct ConfigBlock { + bool word_wrap; + bool rtl; + bool show_unicode; + uLEB128 version; + u16 unknown; +}; + +struct UnsavedChunk { + uLEB128 cursor_position; + uLEB128 deletion_number; + uLEB128 addition_number; + char16 chars[addition_number]; + char crc32[4] [[format("hash_format"), comment("CRC32 hash of the entire unsaved chunk structure")]]; +}; + +struct Notepad_File { + type::Magic<"NP\0"> signature [[comment("File signature")]]; + bool is_saved; + if ( is_saved ) { + uLEB128 path_length; + char16 path[path_length] [[comment("Path of saved file on disk")]]; + uLEB128 file_size; + Encodings encoding; + LineEndings line_endings; + uLEB128 last_write [[format("format_filetime")]]; + char sha256[32] [[format("hash_format")]]; + u8 unknown1; + } + u8 unknown2; + uLEB128 selection_start; + uLEB128 selection_end; + ConfigBlock config_block; + type::uLEB128 content_length; + char16 content[content_length]; + bool contain_unsaved_data; + char crc32[4] [[format("hash_format")]]; + UnsavedChunk unsaved_chunks[while(!std::mem::eof())]; +}; + +fn hash_format(auto bytes) { + str hash_hex; + for(u8 i=0, i < sizeof(bytes), i+=1) { + hash_hex = hash_hex + std::format("{:02X}",bytes[i]); + } + return hash_hex; +}; + +fn format_filetime(uLEB128 data) { + // We will only support dates back to the epoch + std::time::Time time64 = std::time::to_utc((data / 10000000.0) - 11644473600.0); + return std::time::format(time64, "%c"); +}; + +Notepad_File file @ $; \ No newline at end of file diff --git a/tests/patterns/test_data/notepad-cache.hexpat.bin b/tests/patterns/test_data/notepad-cache.hexpat.bin new file mode 100644 index 0000000000000000000000000000000000000000..9a3d62fd1c85ac8acf4ff5deb02dfc2c0cd4bedd GIT binary patch literal 417 zcmeYZU|>{pX0T$2VF+i)WXNMkVaR7FXD9~pLl{ySav2JMtURDt36NL7kO-8IXGjC8 z%mMO2szBn!KvEBgD}Y#?mGSzQ*O&e`?taU7hqp|^;F15cve!>Jwl(ow__^x3LEHz= z30Ji3UxY90S;N4nuFlB7z{JR?9>S0TG_;sO0ZJwUSump&;1=qDMPVvo&VcDD20E_{ z=$sS=2Av5(j3Ep!+7kC`GB9Q^EE6^SpvAzL$?#)cRiZWnV=+VN z>G$-tP%AZ{*eZN Date: Sun, 17 Nov 2024 23:25:00 +1030 Subject: [PATCH 06/29] patterns/lua5.1: Fixed a bug in the Lua 5.1 bytecode pattern (#298) * Fixed a bug in the Lua 5.1 bytecode pattern Lua 5.1 bytecode upvalue fields were incorrectly defined as a `Vector` instead of `Vector`. * Fixed another bug in the lua 5.1 bytecode pattern Lua 5.1 bytecode string sizes were incorrectly parsed as a 64-bit integer, instead of a 32-bit integer. * Updated the Lua 5.1 bytecode pattern for 32-bit compilers. Updated the pattern to allow for 32-bit and 64-bit integers for the size of the string length field. --- patterns/lua51.hexpat | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/patterns/lua51.hexpat b/patterns/lua51.hexpat index 03319bb3..b01e9311 100644 --- a/patterns/lua51.hexpat +++ b/patterns/lua51.hexpat @@ -36,8 +36,15 @@ struct LuaBinaryHeader { u8 lua_Number; u8 is_lua_Number_integral; }; + +LuaBinaryHeader header @ 0; + struct LuaString { - u64 size; + if (header.size_of_size_t == 4) { + u32 size; + } else { + u64 size; + } if (size > 0) { char data[size]; } @@ -72,7 +79,7 @@ struct LocalVar { struct LuaDebugInfo { Vector lineInfo; Vector localVar; - Vector upvalues; + Vector upvalues; }; struct LuaConstants{ @@ -96,9 +103,4 @@ struct LuaFunction { LuaDebugInfo debugInfo; }; -struct LuaFile { - LuaBinaryHeader header; - LuaFunction func; -}; - -LuaFile file @ 0; +LuaFunction toplevelFunction @ 12; // Lua header size is always 12 bytes \ No newline at end of file From c67dc849113af1e4b5eb31d083904dbb765c7e96 Mon Sep 17 00:00:00 2001 From: Valentin Lenhart <513541+vlenhart@users.noreply.github.com> Date: Sun, 17 Nov 2024 13:55:30 +0100 Subject: [PATCH 07/29] patterns/pe: Fixed off by one error in coff pattern (#299) --- patterns/coff.hexpat | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/patterns/coff.hexpat b/patterns/coff.hexpat index 9c9af1bf..dfbef130 100644 --- a/patterns/coff.hexpat +++ b/patterns/coff.hexpat @@ -164,7 +164,10 @@ bitfield SectionFlags { }; fn format_alignment(u8 alignment) { - return 1 << alignment; + if(alignment > 0) { + return 1 << (alignment - 1); + } + return alignment; }; struct Relocations { From 46e41db0a8a5584466fb06f669b9e1ad887bf684 Mon Sep 17 00:00:00 2001 From: Valentin Lenhart <513541+vlenhart@users.noreply.github.com> Date: Sun, 17 Nov 2024 13:56:01 +0100 Subject: [PATCH 08/29] patterns/pe: Use appropriate pointer for first section placement (#300) --- patterns/pe.hexpat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patterns/pe.hexpat b/patterns/pe.hexpat index 16bcff5a..d9510eab 100644 --- a/patterns/pe.hexpat +++ b/patterns/pe.hexpat @@ -1106,7 +1106,7 @@ struct Section { currentSectionIndex += 1; // Make the current section index the next section's index } [[name(sectionsTable[currentSectionIndex-1].name)]]; -Section sections[coffHeader.numberOfSections] @ coffHeader.optionalHeader.sizeOfHeaders; +Section sections[coffHeader.numberOfSections] @ sectionsTable[0].ptrRawData; // Symbol & String Tables enum SectionNumberType : s16 { From abc78d16442daff2be1fb78d7134b487a5e62758 Mon Sep 17 00:00:00 2001 From: Hikodroid <172511799+Hikodroid@users.noreply.github.com> Date: Sun, 17 Nov 2024 13:56:40 +0100 Subject: [PATCH 09/29] patterns/fbx: Improved the FBX pattern (#302) * patterns/fbx: Improved the FBX pattern -Added pragma magic -Fixed sizing of compressed contents array -Added references -Cleaned up whitespace * Added missing ] bracket in README.md --- patterns/fbx.hexpat | 59 ++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/patterns/fbx.hexpat b/patterns/fbx.hexpat index 13d8a8d4..0e898d6b 100644 --- a/patterns/fbx.hexpat +++ b/patterns/fbx.hexpat @@ -1,6 +1,12 @@ #pragma description Kaydara FBX Binary +#pragma magic [4B 61 79 64 61 72 61 20 46 42 58 20 42 69 6E 61 72 79 20 20 00 1A 00] @ 0x00 -// Based on Blenders implementation of FBX import/export +/* + * Based on Blenders implementation of FBX import/export, see: + * (incomplete) https://code.blender.org/2013/08/fbx-binary-file-format-specification/ + * https://projects.blender.org/blender/blender/src/branch/main/scripts/addons_core/io_scene_fbx/parse_fbx.py + * https://projects.blender.org/blender/blender/src/branch/main/scripts/addons_core/io_scene_fbx/encode_bin.py + */ #pragma endian little @@ -17,9 +23,9 @@ struct Array { u32 arrayLength; u32 encoding; u32 compressedLength; - + std::assert(encoding < 2, "Invalid array encoding!"); - + if (encoding == 0) { // Uncompressed E contents[arrayLength]; @@ -31,7 +37,11 @@ struct Array { #ifdef __IMHEX__ std::mem::Section contentsSection = std::mem::create_section(std::format("contentsSection @ {:#x}", pos)); hex::dec::zlib_decompress(compressedContents, contentsSection, 15); - E contents[] @ 0x00 in contentsSection; + auto contentsSectionSize = std::mem::get_section_size(contentsSection); + auto contentsElementSize = sizeof(E); + std::assert_warn((contentsSectionSize % contentsElementSize) == 0, + "The size of the contentsSection must be an integer multiple of sizeof(E) !"); + E contents[contentsSectionSize / contentsElementSize] @ 0x00 in contentsSection; #endif } }; @@ -57,7 +67,7 @@ enum PropertyTypeCode : char { struct PropertyRecord { PropertyTypeCode typeCode; - + match (typeCode) { (PropertyTypeCode::BYTE): s8 data; (PropertyTypeCode::SHORT): s16 data; @@ -90,15 +100,15 @@ struct NodeRecord32 { u32 numProperties; u32 propertyListLen; u8 nameLen; - + // Detect sentinel record which marks the end of a list of node records - if (endOffset == 0 - && numProperties == 0 - && propertyListLen == 0 + if (endOffset == 0 + && numProperties == 0 + && propertyListLen == 0 && nameLen == 0) { break; } - + char name[nameLen]; auto posBeforePropertyRecords = $; auto posAfterPropertyRecords = posBeforePropertyRecords + propertyListLen; @@ -113,15 +123,15 @@ struct NodeRecord64 { u64 numProperties; u64 propertyListLen; u8 nameLen; - + // Detect sentinel record which marks the end of a list of node records - if (endOffset == 0 - && numProperties == 0 - && propertyListLen == 0 + if (endOffset == 0 + && numProperties == 0 + && propertyListLen == 0 && nameLen == 0) { break; } - + char name[nameLen]; auto posBeforePropertyRecords = $; auto posAfterPropertyRecords = posBeforePropertyRecords + propertyListLen; @@ -133,27 +143,27 @@ struct NodeRecord64 { fn assertZero (auto array, u128 size, auto message) { bool nonzeroPadding = false; - + for (u8 i = 0, i < size, i = i + 1) { if (array[i] != 0) { nonzeroPadding = true; } } - + std::assert_warn(!nonzeroPadding, message); }; -struct Footer{ +struct Footer { type::Magic<"\xFA\xBC\xAB\x09\xD0\xC8\xD4\x66\xB1\x76\xFB\x83\x1C\xF7\x26\x7E"> footerId; char zeroes[4]; assertZero(zeroes, 4, "Found non-zero values in footer after footerId!"); u128 ofs = $; u8 alignmentPaddingSize = ((ofs + 15) & ~15) - ofs; - + if (alignmentPaddingSize == 0) { alignmentPaddingSize = 16; } - + char alignmentPadding[alignmentPaddingSize]; assertZero(alignmentPadding, alignmentPaddingSize, "Found non-zero bytes in alignmentPadding!"); u32 version; @@ -162,7 +172,6 @@ struct Footer{ type::Magic<"\xF8\x5A\x8C\x6A\xDE\xF5\xD9\x7E\xEC\xE9\x0C\xE3\x75\x8F\x29\x0B"> footerMagic; }; - struct Header { type::Magic<"Kaydara FBX Binary \x00\x1A\x00"> magic; u32 version; @@ -170,15 +179,15 @@ struct Header { struct FBX { Header header; - - if (header.version < 7500) { + + if (header.version < 7500) { NodeRecord32 rootRecords[while(true)]; } else { NodeRecord64 rootRecords[while(true)]; } - + Footer footer; std::assert_warn(header.version == footer.version, "Version numbers in header and footer do not match!"); }; -FBX fbx @ 0x00; +FBX fbx @ 0x00; \ No newline at end of file From e85645897e577c80fa605964cac538f5f6c95c56 Mon Sep 17 00:00:00 2001 From: Hikodroid <172511799+Hikodroid@users.noreply.github.com> Date: Sun, 17 Nov 2024 13:56:59 +0100 Subject: [PATCH 10/29] patterns: Added pattern for Blender project files (#303) * patterns: Added pattern for Blender project files * patterns/blend: Added pattern file and test data * patterns/blend: Fix the thumbnail bugs by passing the source data by reference * patterns/blend: Added ZSTD support and test data --- README.md | 1 + patterns/blend.hexpat | 196 ++++++++++++++++++ .../test_data/blend.hexpat/blend.hexpat.blend | Bin 0 -> 932364 bytes .../blend.hexpat/blend_zstd.hexpat.blend | Bin 0 -> 106904 bytes 4 files changed, 197 insertions(+) create mode 100644 patterns/blend.hexpat create mode 100644 tests/patterns/test_data/blend.hexpat/blend.hexpat.blend create mode 100644 tests/patterns/test_data/blend.hexpat/blend_zstd.hexpat.blend diff --git a/README.md b/README.md index c651d1aa..84114cfb 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | Bastion | | [`patterns/bastion/*`](https://gitlab.com/EvelynTSMG/imhex-bastion-pats) | Various [Bastion](https://en.wikipedia.org/wiki/Bastion_(video_game)) files | | Bencode | `application/x-bittorrent` | [`patterns/bencode.hexpat`](patterns/bencode.hexpat) | Bencode encoding, used by Torrent files | | Prusa BGCODE | | [`patterns/bgcode.hexpat`](patterns/bgcode.hexpat) | PrusaSlicer Binary G-Code files | +| BLEND | | [`patterns/blend.hexpat`](patterns/blend.hexpat) | Blender Project file | | BMP | `image/bmp` | [`patterns/bmp.hexpat`](patterns/bmp.hexpat) | OS2/Windows Bitmap files | | BIN | | [`patterns/selinux.hexpat`](patterns/selinux.pat) | SE Linux modules | | BSON | `application/bson` | [`patterns/bson.hexpat`](patterns/bson.hexpat) | BSON (Binary JSON) format | diff --git a/patterns/blend.hexpat b/patterns/blend.hexpat new file mode 100644 index 00000000..eac738a4 --- /dev/null +++ b/patterns/blend.hexpat @@ -0,0 +1,196 @@ +#pragma description Blender file +#pragma magic [42 4C 45 4E 44 45 52] @ 0x00 + +/* + * References: + * https://projects.blender.org/blender/blender + * https://github.com/facebook/zstd/blob/master/contrib/seekable_format/zstd_seekable_compression_format.md + * + * Refer to the following files/structs: + * source/blender/blenloader/intern/writefile.cc + * source/blender/blenkernel/BKE_main.hh BlendThumbnail + * source/blender/makesdna/DNA_sdna_types.h BHead + */ + +// Increased the pattern limit to be able to evaluate all pixels of the embedded thumbnail. +#pragma pattern_limit 1000000 + +#ifdef __IMHEX__ + import hex.dec; +#endif + +import std.core; +import std.io; +import std.mem; +import std.string; +import std.sys; +import type.color; +import type.magic; + +// Useful for extracting the thumbnail if the rest of the blend file is corrupted or truncated. +bool quitAfterThumbnailIsParsed in; +// Allow the pattern evaluator to skip the thumbnail e.g. if evaluation takes too long. +bool skipThumbnail in; + +struct BHead { + char code[4]; + s32 len; + Ptr old; + s32 SDNAnr; + s32 nr; + + // ENDB marks the last data block in the file. + if (code == "ENDB") { + break; + } +}; + +struct ThumbnailLine { + type::RGBA8 pixels[width]; +}; + +fn copyThumbnail(u32 height, ref auto lines, std::mem::Section target) { + for (s64 l = (height - 1), l >= 0, l = l - 1) { + u64 currentSectionSize = std::mem::get_section_size(target); + // Append the current line to section. + std::mem::copy_value_to_section(lines[l], target, currentSectionSize); + } +}; + +struct Thumbnail { + u32 width; + u32 height; + u128 size = width * height; + ThumbnailLine lines[height]; + + // Generate the thumbnail section. + std::mem::Section thumbnailFlipped = std::mem::create_section("thumbnail"); + copyThumbnail(height, lines, thumbnailFlipped); + type::RGBA8 image[size] @ 0x00 in thumbnailFlipped; +} +#ifdef __IMHEX__ +[[hex::visualize("bitmap", image, width, height)]] +#endif +; + +struct DataBlock { + BHead bHead; + + if (bHead.SDNAnr == 0 && bHead.code == "TEST") { + if (skipThumbnail) { + u8 thumbnail[bHead.len]; // Interpret as raw binary data. + } else { + Thumbnail thumbnail; + auto thumbnailSize = sizeof(thumbnail); + std::assert(thumbnailSize == bHead.len, + std::format("The thumbnail (size={:#x}) does not fit exactly into its DataBlock (len={:#x})!", + thumbnailSize, bHead.len)); + } + + if (quitAfterThumbnailIsParsed) { + break; + } + } else { + u8 data[bHead.len]; // Unknown. Interpret as raw binary data. + } +}; + +enum PointerSize : char { + POINTER_4BYTE = '_', + POINTER_8BYTE = '-' +}; + +enum Endianness : char { + BIG_ENDIAN = 'V', + LITTLE_ENDIAN = 'v' +}; + +struct Blend { + type::Magic<"BLENDER"> magic; + PointerSize pointerSize; + Endianness endianness; + char version[3]; + + match (endianness) { + (Endianness::LITTLE_ENDIAN): std::core::set_endian(std::mem::Endian::Little); + (Endianness::BIG_ENDIAN): std::core::set_endian(std::mem::Endian::Big); + (_): std::error("Invalid value for endianness!"); + } + + match (pointerSize) { + (PointerSize::POINTER_4BYTE): DataBlock dataBlock[while($ < inputSize)]; + (PointerSize::POINTER_8BYTE): DataBlock dataBlock[while($ < inputSize)]; + (_): std::error("Invalid pointer size!"); + } +}; + +struct BlendWrapper { + u128 currentPos = $; + char magic[4] @ currentPos [[hidden]]; + + if (magic != "\x28\xB5\x2F\xFD") { // ZSTD magic + // Assume the blend file is uncompressed. + Blend blend @ currentPos; + return; + } +} [[inline]]; + +BlendWrapper blendWrapper @ 0x00; + +// Assume the blend file is ZSTD compressed. + +struct SeekTableFooter { + u32 numFrames; + char flag; + type::Magic<"\xB1\xEA\x92\x8F"> footerMagic; +}; + +u128 seekTableFooterSize = 9; +SeekTableFooter seekTableFooter @ (sizeof($) - seekTableFooterSize); + +struct SeekTableEntry { + u32 compressedSize; + u32 uncompressedSize; +}; + +u128 seekTableEntrySize = 8; +SeekTableEntry seekTableEntries[seekTableFooter.numFrames] + @ (addressof(seekTableFooter) - seekTableFooter.numFrames * seekTableEntrySize); + +struct SeekTableHeader { + type::Magic<"\x5E\x2A\x4D\x18"> magic; + u32 frameSize; +}; + +u128 seekTableHeaderSize = 8; +std::assert(seekTableFooter.numFrames > 0, "The seek table must contain entries!"); +SeekTableHeader seekTableHeader @ (addressof(seekTableEntries[0]) - seekTableHeaderSize); + +u32 frameIndex = 0; + +struct ZSTDFrame { + u8 data[seekTableEntries[frameIndex].compressedSize]; + frameIndex = frameIndex + 1; +}; + +ZSTDFrame zstdFrames[seekTableFooter.numFrames] @ 0x00; + +#ifdef __IMHEX__ + std::mem::Section decompressedSection = std::mem::create_section("decompressedBlend"); + u128 previousSectionSize = 0; + + for (u32 i = 0, i < seekTableFooter.numFrames, i = i + 1) { + std::assert(hex::dec::zstd_decompress(zstdFrames[i].data, decompressedSection), + "Decompression failed!"); + u32 uncompressedSize = seekTableEntries[i].uncompressedSize; + u128 currentSectionSize = std::mem::get_section_size(decompressedSection) + - previousSectionSize; + std::assert_warn(uncompressedSize == currentSectionSize, + std::format("The uncompressedSize {} for ZSTDFrame #{} " + + "must be equal to its actual decompressed size{}!", + uncompressedSize, i, currentSectionSize)); + previousSectionSize += currentSectionSize; + }; + + Blend blend @ 0x00 in decompressedSection; +#endif \ No newline at end of file diff --git a/tests/patterns/test_data/blend.hexpat/blend.hexpat.blend b/tests/patterns/test_data/blend.hexpat/blend.hexpat.blend new file mode 100644 index 0000000000000000000000000000000000000000..da4aaeedd89d49d105aaee8b09ec73bc3ea9dc2f GIT binary patch literal 932364 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 R;h$J3S+PVh68P-j{{tqEhHd}= literal 0 HcmV?d00001 diff --git a/tests/patterns/test_data/blend.hexpat/blend_zstd.hexpat.blend b/tests/patterns/test_data/blend.hexpat/blend_zstd.hexpat.blend new file mode 100644 index 0000000000000000000000000000000000000000..62cb474434dd9f400fbf34b36fc54c0d544310a8 GIT binary patch literal 106904 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

v)P?PfpsvY;Y)&TWE*QWYc2Q2@ zov3Ib$u}W@94}vbzP72Zp&ZdhdwQ^rUUv0Ou2!e7*|sOq73i-N%mGOUn*AJHd1#O| z#H6oBZ3)Y9N6YJy4D>#*Q?fR==js{KXQc(m>;eg3HX7a_> z`*NH&c9e#G5t2bR6#C6Ilod`#X&a37c28}sL*$HxBAhGs!`^G|7Brr#=z;bea#CcR zsRup;X}ZYgm~dr19mwAq(R8CcJtgf0no9i>2BSZ~;=gSDjV>g7h^BIBp?L7M<1$6ju``P_J^I!$sguljmK&bABk9!@BHKu9jh z{nA_@uE{Ct%E!DDQcz-&W&JL{eK)w1V(<$-DP~+LDGYZx4U(0=3w2>o)&becjRw_+ z4Cfj%IS0QbzFasY9*iea>l7C>KuN9it9R2x*rnH0AIQrp3NkBNmXZ0D3$QCVHFvl_ znI^K3j+h{4fL~fV@{YEC%Uc9qxNUFx0ulR#X-MfH>>yy0d}{(pVyK|M5viULu`ai# zIwciI5bBzqKqd<&Y=k5Wd3|K8q2?UxK?gcP*Y%~YtaLk@?v>3m4iR04nSnzjS z3>Lo&Bpeo3ab~%5W$hS`-@?Z$mff}F>$f?osMF$gb3fj4=+@Z%#T}JQSiZgJKQS=t zM~JM(5BISjPn!n+adNtv8Ywz(%yEYiaF+umnt2P8xnH`gla;d+9#K4egn3;K;)a!o z9JO`5^`Ed^1Cn0o{EF8Ikx04VHWNN@Y5k+rkA5XUIunq+ACR~Gb&5OGTGQz&6*B$7 zp}dv`!H*x-4gD@{9i=H!f9cT3m_L3S;8f(bU0j;CST%o}B<2YKwA^CNsM4An$TnWY zdi~q+dy17Xn%!rvFblw-0VsL{XM78)|6t8xT|gMu2q4lsrBmfCc{@+~5gdYk#1n82 zr(vQV>!3e57^WC?&Fz)Rl&I)tsVeSolZt7~Wuc3boiZuZGCtv8a zOt=UPH;t_{#VWRh`PlG%oLPOgmUm?Tmb1>W3`OBl>d7jubUq@-PzJO;@@C#G$(Lwv zfa3aJzg|Y>6sc`%08nQFmq>IC1L*Ov8I(;3Bs zOwL&4rz68_U|>+-ce8v6uM9qqt>dv=KD}ZFG@Vwp0lL#}en{Lp%Ig3YhgydB+5!~C2k zz+C{e2*jI*S#FWICf<)$d?rgEa7DVTjxCTg5EEmQzg*^i^dAoy3AyT$;DR@a*JcpV zeZ>+bkTIe;^O5bkrW?%bie+@sAB~mUgDW$M*HF!Cuc%ui7 z`S-g#Osv<5;-9bb8>M(A-s08*kYRO#=Mx{AOH7+Dn7jr5C|qQ5i&LNe<*wk&^u3*L2@U%>4P!YC?{XSa zavHpI8jOJR8(?Ne%xiwAVFc9hHrFtS)}40jd-m~$wfCugArz()i)5UM5Gv#ZGK){sZekDH@Da3lq`+Q& zE_^d(vM5bWf!zlpJ<<0hF}_^~`z}K&VkCbdZlBC5m$D)*CF9ryYRC@Ywhbv{uW3?5 zo7fF?DM;tIo{AzQ>F3BRnB&hU)d<|Vz*)?>smE4v(pkn>x}mcoeai;7E)YZI_04^k zUY*iWlpLeL$iS86onDpF%0(*)1Rp&m`T4Pu>2=iUb^B>R$qr!|Z=nip@lARUp?$&E z(kS8AThgKVIoa4$t}}7_zEUI7WCI$FXURA3x|A$+~xMV^ffkH_7jc$O}8G1Q>2vvz4X| zH1ZYA8~$e6gc=${4L7VQR@0p1L;MXRq<u1Vrq8K&s95XnV`L!_S2g$V%Z3{aynmv9wtru#j3Z7#E=K@DzgJenr zbxOlKob`~0%x@PNqKnK*GrP}nS~Jwp7V}yLoLkWFZo=BYL#Cve{d-eP66bjn`*9Qd zK@&TniG5RneNBP=VR71PQ$saVbnmgIZ}{&0z6yIW=Jos&gOarS;9PHMS~J5+ zai_~p>pK{o*yc@L_p6Rx#-O0&U6LC(?AV3TCz$AmY=g4olVIT}$%{KjuJQk4#U zKN9IgD&gA+S}Kg%x=EA37G&)Htyx-^H|N5SAUr$+tywut?o0_xR02s8aeDjNoQeN} z`ct)O|LQ$>m)kxu0G6Zj^y?ZkLwN(8QD|4u^UKKB@_D?^Qdspr&&o# z)yt~80?QQIU60v-Ki`hMVbzSMG1i4MV};1n?*$3_$88v_-%D)@7WPYwt)wz+PGYLM zRrJOu@n)6xm#SNm6qz|f0@UopaAAIpT;+R-L4pP;+(P%%&faeU+h{VllNgZFCsIj? zd`h58q=1n>>LaTX$O#r^r#_D(E5B(f*h)^5_>3vuQgBI~Hl9h)E%9Gu3k{Ro8yBcW z#d9JDaeGR9Q3vZGV-?I+flV=2_VYtV$I*f(%KJt zI(iu}B2|v2`rRG+-PE5Uyst?)>t^E&FeE;@>6QX^iNwfhTG@Lr>>d_z)*-Btz_=Ei zY|q^627X{fb?desdzI47;kYy=`U=u2P6vIt4#a*3!aj&$DwqKc;kEBqdkgnB%JP%U z@~6)7m!w>M@QhT54XQ6;*(WX9N3mc@QxnQYDypIDO>rw~n}>z8TzF?hg$?fWj-H9$ z#yrRWU8W3mji@h)8mdYa^+wRpDD%~uM^GhQX5($1-+iiyBW?B@sX5$BIX7r$| zRiBM~ek`GJrbZhmq!@Q5KiVgQd2Xkv&*GiXA1e+zrDva=H5 zi}xHYSMqnu#$<p|i;r@zZ0XwbZr47w);ND7 z(p3tmJ~cHzBvq`#Y-Tyc{by}sKNb&d1mKh!a#Z-yd3W;EIkyn;QjLc|;_+><|gTCs*anb=& zTyaQEbm)h1cX=Wfs;>vtX=Y+twJbco#S_O==(nFNvp19+{c9HKV}w)77fbZu>0)VC zVt87-BDTO(;Nv}U$=XBn{fu8Xr>XPK)9n-{#?{%It2sAUEpJA=V;17WRPkbjq*P2L zX&G3X;}JA^RW3hffpFRf7v895LKz-W2GuviJohdtk4_m+MnmwPrHZUN zn$u#@AcuBUA@1<4%$v-4E_c`8F$r;_i1&LE-HTGyc?^sYRoF*@_Y|Y^ke+#2lPcSPZel`Wm&Rk1#$pt4e(xw(^iwE(#ahW4gx(Igpu(6@ zRcxp#QOQJL56#yw@n-`Zt?;@aZ3nWc#U#^8tBgt(=}jJC7vp^+Z^V6?Ox-t$29>uH z-2;jc>u0sSa{|bFFzWZ-H>>i$e>xZXr1oqjK$f}~Q#Bv1@FW=sU_avUH<1%ESV7!h z&GF`;HPaW&VcNf0`ZFo<&P?KwvdWX>TuXkW)Xwb(p|-TcLOWGziMKBimOY3LnRhpC zy}q@wZZ&$kp?}w#df|a>D~yqatbokdotE}qzMJ2~yrS ztoXGhnO5AH!^p-gd|vubC|BMU*cnaujfel*P-0Xka#-H15#%#1Hq|fICi7^nQe>MI zSU4_%U*h2bO_a+U7V&b$2surI!Z_b1%DK2{l8Jne1>oq{MkH)vnGO(ZRtJwYT@%2+V=_EB{uaaf8~YI4Ne>bj9MKujLpk)#*U z#7GOW&#Iz?%P5Vqyw>+=*+x_1Hf+<4ok^-8wD(B&DXem6^5cvZD03m-q~bZ`%4$e* zsz4ep{ci@%>o-#4XdgZ%$=slg+NNdcXWtnKW6x88q>)KdRH_M3z;AMgDp-oHJ~3z+ zTD%yf5s()39kAjU*EY#-vO`lFK8&0Yjv_=2?S6|7p+pB$Ht0PlLO)Baqh3@_nEW2d zWt{v$O@`yMim?jiV>dcuH@e?(fBV_Pq9%m>MjrR+dRsE923yDy@s`BCH@e^YIG&Bv zcu2n%IY~7Xys1s)K9ZoO>k&@&mXwLzW21-Hn>sNb^p1X(5+z7yOS_U~t1`X7ixE`9 zRMI-=J%sA9b5F0u8``)g$OF31aCO} z+>W|sGs`I`zkYKrs$n(Hge2@-Q10JNf|~JNb4$jx`u(mId(t?bAWHwL;jou(rNe58 z0_OK_xCB!=22n96nEi5y{Yjk6SZdWpPHrT72w2er>a^&EgqJ~RwJ&jbv}3D^fF!b{5u?)s$V zj7rIHvI6id3#0J=Zd3+e^zUVGHdf*}aR*Zx*6g|*eehw(LqL%g#7VL=-I8tf1Y&r20SGzMNonws7g@w zRhc5S*qhX^IXBozXtAmnk!rBtuPNV@VemXN!khH0i%F` zi0#jR&Yfg$_ReJY?43zw=6>JZQ)1@f;(P>(T}w+UW~XGPSp(;#+e^OCKY2G!3^q=*H%^p;hS!ZID#js2FkwIu zgtWDy?!Fo1krT8@LH0`Tvv3sJt`z>|{I}%qLC5A}2w8-0cj!8)|QNKTR zEWLfS=3rw^O*(2`uOHW7&)u*!J1!Ii>SR&2#-yZuQqnFd>3aL`cOAc}9lz%~eoy%Q z%o#N0Qj|vN9}oEbj2=9+A3vg`GD5HnPLq$7!u(&>yHW>_oBht(#GuYdqhnDBer8X< zMT5yN3d{SzL<`TS=33Z%>s!tNDN`u~<_J8x2yD zb9*`3R_%C(=g+sX1JzWtd>uRTJvGU${dafA?_|H9{_Fi?m+a)^=cg76Woa`qf1QTA z8(A`DxV<;h#%zx>6gt&(`MLh0tW65QGF#)FtIzJ_QTJ+tm`My z$-O-V@L-D-{2^ z>h2%0J|mghc>2U4IwRoAw-KNq3^3I|qwnyw6ZZK2MhEH2Nl@xB{VNho3>2;|~l zX61y~aA3GN>R3^>Y}Az@$g3kbEh&@+dwpa?6B+|tEm<>D)yd$w5A${+?`R=ltnUUM-N%R%l+cfgKqaUWbd!XlN+2+6?I^{%o zr%8^>=BiAc8gPTgi5fYMg~Rx4SdZT!Z5>gzrg?u*;+Lncr&J%$h%6S0tnXN^SP)FZ_OUX^w^T;9Df^3S)fPu}`KVl<^F~`EE>dq*h`TJwl)&^;d zAS&w7U=<8*{_lPJ9Wg3(I*Y>PJ0VjV7p>P$w*0|1;QaBBti9t;`k*>_qM|&pPL8Ok zy{MNF;mU9j)ZEYM%-HMjPb70s8P&;}>Ramm`Z#&CzCqcrh5qRZ`uWKJXXUD`;V;e1 zE$+;%5R=~n`oFa*laSOuijqK`1fVDZ)QJOiYKr23q8d$j`sgNG z@5DVQX8)Op+nck0;6wKusbf6D_K~&_VqGO8B)yP_R`}|uemTj5CLI3L0&Rh_soYS@^Su#nZ=6G>edvW z|IUzUXS{#fZhmmkO|o${`x&&*Lr456lCKiA7Z-uLn@qL&v2oW>(0y}4(o=W08W!GV7Jz)e?up?$nBb&zOT)VHI&VXd-J%}{u?~e@3w=?@?eF`{4oKz`Zjl#~I(n|6&{B&r9UUlk)0xPQo+r%V=z2%bX~`Q;Nxr&7Ah)e? zEnLP=DoPJs$F`zD*JC5fZ8KOe2F)yV!chcPz&n>v5D@W*x~vHREQpPm zj*5r|ymJVgPbK4ep0?e(!g#-YBLabV*}1Zy(ZN{ZqBExJhWHSo-rYTpC^YcJ)_eA! zRfY&6Vq`(GqsJep$R==h^;`ph>k(O0Uu9t7ZdnHJok=KI4fiKS3Q&hGvw#DAXi`6iGSj{G zUqL{;(;`&UYE}Fa7DvN6`WppQO=nwGi=C%WCjEwvx8Sr58#|FPMGtpYFRY2+1WepvtHH z9W%Yl6BB<)swhyAPO$FmF+x}kE#$WM82Yp&R43$hHno_zqQji4V$S1Y@}b2jd+OUm zF%+RWi5_l$KT7pR$3w=)a0X&VBRx!-`g&o3u=xTYR(L#f?Ue&=O~$EJpQ51bP|NAm zNm^5_a?}O+G1c@C(}0%?!;&L$!wZy&1+jEOR`HIm@G1N|C5m1~ONdCJO4goQ46Yb; z6~8oadgw!ASJp>vxUdR^iLLp(lzKUPn!8ZgR0*5~UN_i~fhqe4D}R;Q*pm7xvSDp~ z%mxCXtgWcqWU&b&+UFtDiA0vlgg>~1U$}%5T*5IfAtvgOPPgPsgjy#+t<5lFfoXbq zO*bU|njbU*+}TnD_0M&aTv_wrOH@OBhqe zv~p{jgv1kQ;Hc`jcd(jVd6=orO}TRT!&-AZ$Q)lQj=-bMJ z8Bp20Mid;ZN=^7Qw>OsPpnpi}5UV|fGMA4&>^1;_n6R|==))%+7Vc=%=8nULj>9{C z+vfwfQ+nYTt76C_LRKRR90jjk1>`F_8T2jkZEwts3`ppgU#8*f#8;#Q(_JW}NJzO5 zEK6+=SdNnJ*tB@NNb7ROMW|*8@4^lbmy>|us z(4m8G=o)K4;F`?(wn(7dSR!Z_qz{0&2R`O~#~2*Oc5DV{$qQzxbm{FG`_-rHcT@tH zLdma_0o#LX);Jx6;|?*9260W9dH-^;E{2!{(ZtS!D)P7?qbaHn3#=-f@W zVEx|Om}euxitK#~O=}Bcw1gqFCKp0jNC2c>!Iq&*P%fqb`^gAl)J{CQR~k%|I`eyN z`v+J@D|(R9pKy6mY!5?w&O+u&)zeGnW}!)foxo!hw3!7^%Yw2tL+GS@R~mFLUcP); znX_=(D+5mA>)hm6O2Cp5@<|D~RL^6SHf9KA?SQiO19I*xC4>XD$^eKdXA!VdOfPf- zpF?RqDZ=!ltdT&i;>!B)LRaukpMPORi{{IPH0Gh<;pNJt-dY`mBo<}tNmMvTG%46A zBQ#|ZvbbMPlDmc_dTQuz{WZZBNNaPHl@4h|U;OR$RLGPhO9O$UP@u3c=lcjfXCEX7 zs9_$?$&LV%NVD>$Of+93Bm3jz{QAarSj?e*UqS^60;c6>WsW2N6C3Vf`g%y_Thk#3 z*Z^r}MttP@|E5~Pkk$rB+ju`*f;SDzhnV3<6qXRiGYH%)$w+Ixfvk-0Pj)@8Yl3x9 ztzOmnniHUS3re^6$WbT@e20%8aqJ2j#zX4h3W)ANkeCGQ(g<<6%_7h z7BT!XXIVE2ztR2>#XkwaRUvbc7o z0CLUnG{Lz?gFU6=w<3SW?q2Rbg=jKLBgB27MnX<+Q#+R26#gq7>=8>tb92#N^8*S} zD63eMmENaU?pCLdU-SL>9MqZ`2Ej&XVk5Y*jDNHAOTtWrzmGh2<1-b09>aJ@U$z+R z2~3S>;#d^o1!_coJtR=#*Ox^G$FB4yFi6pYgYO>|un(#(eKMAN-}Tl_4sq(2BY>_# z(;^XB1#PGJ{R_~L0<>lU`Xf6+EjCYiVCeQ6QXF{q)Cb9-qoE#33i@L{T2(;^Ofd+g zEs=rx6f~EmG|v#^1$<0=G9PNBdi#3A&6E_fL0>K>QNfT{!-upI{{;10E+*@oHk{5w zCKRKKzB>3V4-T>VoV}>amJ$|{uYDzOStL+)th>wZBP&fvnucX3)|>?t&H^=O0EII^ z4c8kvz-+HZ4^YqpYEAqJl0^qjOZB8m*+Dc{>v77jRDFL+jwPkHGus(8o>`qh8SN%N&wmTs#|A~-qkKAcGQdqtn z^TOyS_OsQ;+po|~@8T+`aTDkeWGuW;tSAFIoItIJEBjL@aHPL$PU=AA8K%Ojem0Q+ECWhdjHow3`__}tF; z6vfzxV$gSM==!0aQlk<>&YSj|KeT(scs6YYW{c(%NDiNbl3FX69`3Tv8qp`pU+*1u$NsFsx?qT0+rw3i|ml zmZ6b~CIli}+r7AGvS|b*%IS$F&Q%<;afl9@lR~xeznI?A@!xQ;w3eayte+JTgu9@jub1Fc)gpQu1 zwWgq_$v}V~;$^cG&YK^qD?{;3X>1n1oNJkaE++#Q{SeCSOvL4a0pOL777RovvW%tV z>nqK(b|)DSu7csujti|nK8)GxVZGLS+&WtL%_I*!8i068_0|P?a?yKP z6~P8bu+kEe(KP`G!FGZNDnfjbZ?wl|uZQKQdrfm7u_q@Y__3&;FSkB{4d-pp;~lnG z^$l9{p4)6lsJXKZ)%>IRqlNUOIqteVblAN4!y{i%tBZW=#v@Bm|2L4=FD?@RWHR9W zAi`_ScQ7KuHI0FpTNG@Kkef>stU5lqi@u_}&V5+fP0LonMfXQ{2>vp2n)mn@w>NyNk-DW3S7Hdps2u^C&M+|6 zPS8i~gd?ssI|F5tK)-(DwL6^TMM)ue2ir(_;Q;g$MO>)-dX;lm)9eJxUjD~|`&)+P ztDWUP{B_rdy|2G~8Cp3NR5|5WIfbm8y4<2xsiRo@Xx|WCdww-NY@7GxoLEqTzJFEt z5Yps)W)|U&YKm^Yv|#K!AP;Q1gS7{|7$Y{OzW2)@a!KVkQA9fJhA1WoKT>t;2!6A@7Tm z-eVaA0T#ee4;6fw=_qHZQ%{uYhrBg12+$IO0N+#kP@HO7!_>r^Iguc@WYBpAB1wD#TEa3E@HQ?B8r9+J*bqONd7U-4*Gxw$q}WMHid zUz6nxC>98F-^#688^UGsuZv1$zWSu2%50dK*4g*(T!MIWn9jDZyWztNhII)g9fxY` z#ajc=nyDztbfU}&lrJmfqK7NRlhP1gvb62^!BW;q&lT>n=<6Z)s3gwL%MNF1=*8*Z z8W7aVHsskeaZox|?}{FzBtbgKcc_5=u``*1eJjS_^&l47@h|T89Qb zd&WH*$Gg)t1n49^6#R_T`3>!NB(-dAGQ{H;+ zQg353m7f04H|^Q^#xbw*F|Wgv5CEy_vYL9S_w=^7pNrnED%SvC!P$Az1y2w?`P}5C zTXequ*4Nabfn0XlXSTz&5c7+cm$F&XW&5XyHQ!nd*ISp#UX3Zb&;5&wcxMKpL$w}H zk-aif96W=6C8z6-D?jch|701FG29e#|A+3bA0+#_$m)SI|DZ^WXmltJNfz`ljCS!1 zLU~D0TeaH5*`Ah=#k*7W8gt3soGC5g`7O@*J{fzh+yi|1J{EMZYjbZBJG^c&Jz1DFiibHw106MZjs|=tc1H2^fos(d{oyNx%$=zDM zo#CVXNJk2AB{w z!lwL9S5NDR#`#xYp zpEFrQJrs>LzLas5GhzOt*O6B(q1DK1z9sir>nE?C=i4wrW%YEJNsKF}e^o$G72A+; z&xCQ~gmL+V@nIU8uQ+0gyUub0o_VLW2qv=Ma^6OL!gM|joy+Y~ygX>yn1~N5{)zQ^Rz)Y2rfo7Hhn=HL*nw1vjPQ!`ofo`c+Q_Ru2VMRSg>78aECe zH})DgR!x>OhBYN|q;SVlNAUBb5czJ!-y9TcDdo5>H8YWCeM@{CF(cyV$=RCAdUIbrW z#C9%vDmS7vHzJoQtc!DOL~UmJZQf&XHk(_?FxNe8_B+nG5jwfz;*>ctiYF^xTNB@6 zu;?jP7qABBIG$s%QBQ{D{P1MJyJ4Gm!jH6y>vU{;Mw%vI1^&cq$uG}OVnaMs zgz&3TQJYakxeNor-tlr;pk^JPZN@ikbsNqkzN41KCS+ZI-HFZoiTDnDrK#lD^Lq=+ zo$U{B$2(IW;E+30*)WN{7X7~hO_1E|2)_I+w#B*oZ8`Z7tsl7L4ATYEn+G#7OZZeO zKADQ|N5PRmi()8QyVLatbE^vlN9nqNR_^OdC`X;)3iImkFN5WhM@mC!XhOX!Rug4uy~vKAD+L(ziXegFyOY~ zJcXh|^|JAvr$D0<^~t-<7eI{@^`h}}!k~%(`oWKr(%V7@}gG~N9+ zcnS-d-_w!->u?pO4%T1Y)sh0|Vj(?r_nY8c&O+b8de2=g3y{QkJsso82g*o=)X?2E zFrK`i3#pI-y88{VGe=>}y1O#QQ?8J0u%3#UlP&aEcMk(^v(w(*hdkQT5(c-j7h0~n z+JSS~3k}!ZH<&LUVnKJm2A;};+}hJR1Da2Up!c-&K;@(L?U*@F(T^Iq^b6a(x5FQ@k*B%^e1AWhsnVa}NZ~kJRHZ;;bN(WC$x|G`P9A$OQ!QtzYD_hoX~k4Cn5q&}&0?PIGmjf0&?P{d z@tz;uy*Qul#`au@E___L3_7gK+|^Y6)lzKBxeCkw#mnvPflDh%w{Xt9GU$=# zBi;)bW;hFs)J!1@;q)gkT8``QK}EGA+15A+IQ4iyLRiIXtu$l zk+GxhvBL(g#?`X1ql1(k-h!P;_C+|i+dH`?BS2FK1-p)f$&n(VD3~m%)-cN2pPrzz z-k;-_&`%a`2u|!IA2^I1c&2RSuu&)+cuig?OoUpiq;FkdaD~H-L(_oRqz`C6Akyuk zepA?qn)+IT59BCOk>Is=QRFx*|KL_0V_{@6kI|C1lasgA!rG*V9|kn_h)t)7XcX)+ z3g+`h=9AQ7sRWSA$l-GyHZIF_d1zclPY56%btf~NSZQ#!>*mUSiI(Ie#lN+|EW_HU(-$ zlj**$*HExdN}qUIDB3%;POlbH(_Qw0;Tg_DGW3%1m>ma=B8o@|?!Qu5JFyk^ zpmtxxi=a^yL3Bz`?nWsT!{;z_6VJPKyN(XObe4C)%H8$iL%qhRMA^h04te3K$x zQxRy=qm@=%P(6lMZ1KK4P)&D}U)+*~yA?6ErWVphcw=)*C9nmumR>)^%0K4fYVpus zmq7`CTg~?9ZU4kI|HNhg#99BuDgVSF|HOX(#F9=X3uG;dioI%neyKK`T3d^PxdBZY zG_g`j2_q+yB0wt@puh$0rLNlN{R%s5lKRuIabC$5Y$h<8vBk?mf%8+YNau?2-*eMN z!gP=@?Wl+cB~cNcQnm3JVtjc=1EKm)qwMza4hy(UM4)i*SpI`hph*nS6mwHY!PHPN z2+&kX$QilfR#1IR;h~i><&{&9b>pA({#v(1AKiGX%2`~dOQ6{OF%OdP1LsAtEy3=z zUdkteaY}D#w*9(z!Uv9o%x~naZ~UET7n0G%p<8~kMD|3I5bB+bJpI+1A&1dPH(2dX zOPBRL0rl*zMYIo0UhQGjlM53sj~sfNrpqqyz2kEGH;l)2Z7|JT5SJchViIG*`KU@d zUhLFL_k@wgg<9=s8R?7&-kdgV5sI+BzF=00URFwDR4{Wg^bboh!RQCwHEUq9sHLT- zEvKm06cHjDv;$2AceQXU_NUvw4q&^@Dke;n)MwCLEsx*u($0knFI?ZbI8={hx~Ua@d^pD_PA%EMj`w~V-gK3y46+Bw z0IG_sTESI0=$rHz==l%k`17&ePHPeT!X?H-(ubnJfxhc9rLr@)SvrVp-7#^%`kG%% z7Ad76jO;bUzf)0<^21r~=5OVPnA4NP%(*|WzG(Jd2^x#7CvR}!H3SFJwb_wU2xcX& z70 z*zBrmYMXWzfj5h=l}T`#S`ZKZnI-3Higdk1#LEKAEN!KwgqEDp07DbK%7h?@#g z92b^(UPFe|o;ETsK3uUsxJm^PHKd97d4K3xq^mPgLyD-uf^^jz$jLAx>}p7m&Sr!_ zRpBKeP%ZE`fsiy_-lp`NO!g-ESDA^f!_U7&ou%;ll=8j{(OH!d&-gerY8x(jGLJDj7-l!nk!nMAJ8nn%X_lJHHi*lm-y zZDG|_waXx&QMoKVO3%cDZ>Yy%0x+HcMkWA`bcUn@p|iPr;&E}*f5z|3B)uN-Z-SRacD;~N#AE|$9!9bVd;`$}SJBBFL5_sBgQ6BVuQCxM9lz-OCmm$uh^ z*VcWf27A)es%Js8642tTAW_+*h>pUqqCS{c&q}-#x$!_mT`tO@EPVG>nQkfgE>i-0by(yn9(;IpaVRXx zgjLF+g#&rY(OjDn<>8i6>Y>7F)@Yo1L+g38UJl(iCXN5GckPuELV;p3eU`b}YTjoV3#dDDyJW_U3e!FU(z8b8VA@M(r0t$!DGR8ns?-;2SY|u!(P-z1nz}x! z@@)BqKmtDKrgEbyAjf1)X2!JfgUF@dh;k3 zdz6bE%H=B1s064h5gX4eir$ad%8HoGiU@nz(-17z8R(K5qZiy)(T=pgg>>-&)OBed z<>;I#z&IGO?T=XVM=bjzX8jRU{)nMRvBUQpP%iF(x*VXcwdf(ZI9nPqISI@KAp$z} z^p+|@sTJ2r6+xtm0Orp(wZeOGmVdFN1U;F7=4GLI@Y6*3X|sH^5LT4yuFi()^F3wD6_;qUVb8Jx|G(C2v)?#- z-|?F46_>|o?QXpkpmBx?GtV-Yb*@FXbm`5bbE;P*UO4D#4j;PE8kE6KJpWCUrtYd; z1ky;-j_0T)l1nAxOC_R9B}uz#4z(L;nhN`B_O+f9Wez;ODuOZ0Ovd0AZiJ{XNy^cR zURD%KW=$5Ks9+8o2jk@%<7IQ>rN1S2X~TQ*+0iQ-=R%)}W zW?Z|GqTRxdn3aL8kxO4;X1Qt`QZ!}u8U(<+>@b}`mws|Z5bGBXQ5hs(17DPrqd5JN zl54WPT?q?BwKLTyMV!@*Q{k%U>r;DO8XWi2q zM$FZAo!T{@|2fsmrp>Y#Onktnt=4D5C!aO);Vsarwix&&%2@6;+m=wqfpw+4DS*>~}^jQg(RFbbG(V?F4kAq68Y6{17 z=M$z!LS2!(wA_+RT+)7&T2Z<*f64vIhmN)4BiH&=7yrf*)s%pSGqi-)962wcMM*eb zMSO!Eyre=~o1d58Y)C!wy0Up?bZu;~OJ|I_m$G~OY}thd5XgSbpX1#~X&k8}mhPYx zt(wfXD|X`nk3Q7N0{PLrlF97-ar~0oc@x2=I9%c3a_RPFX4xa zveeEEm0rfo^1(d(U7VS+Uu#yIK2(;z;ruv$mKRaNi}2uuF>zOxhU(K$CspYAt57Eu zl#`)PNw|wM+NoJ@tNZ;Bc+%KjME z>bazY@xotJ>=TgE&O|F&#~~W;2)7sA!&;9bcivji?w+CRyz0Xs!S`! zs$0E`d^!^+RdpkmqB}29RTK$Py%gQ> zRxat))y0h7g zoV-@FwIe@e^Re4ZmR6u!R;^hYl5eSv-Vz_Z#lczf6ZgSJW7Xy&3~Z!Be#~}>hZ<)7 zGpmcq{yM??+DPzY-*EiJK%s`ne~iNetO5e8R9F^&eCn9=GiD)TIieJD`mo_;#UhMK z_t%BjDa9yDOUQ@j2iOW6c4EtySiff}`m~;UOZ=*?tm=beQJM(pJ(ap373F(QX3$jp z;?qa@NKM0jO~VsY%Zr4Ni%&u>g2oF<=p^^vP;nNl8>isVr%WOLbdKJFjdOU5BD8r? z@$}=jB84(C9SRa13K>heC~B_fuBr;4AfLo>-ST9=zG^F!XE}>W05D!K5Ue(G>EhK) z5B9z(t?UpcIl$l| z(5bk&;kc3ExbES&hT*uf;kfkSxY*%1wP35Zz|T4iuIsk zOHdFk6vPowt$NE#DduH*j3t;|hrmoSKt zVD;DW4QkESD%Sp<$>aez7}Pr%WGAb%5u3Z;WTs6yLerE+PZ>Hss+3*kVyQpHLMDqu z8!L(MU6<=%7iVE>oC=N75FBZSJ62Y*vaqtS@GxO^1OF5#l-_dUExxDwqB4%{<#9>p zj`zmRFYm8R2fVQV6q5P5$K~%h>b1QU_j6X!wOa8HUG=R0`5|d9i}lR^u8iKRSsND4 z@9NtaL1jcXz^oN{PC|{a^LO$JBGrYWO+J5>cCu(07Vdca7k+#2?m_8^^GB^t@n_oS zY_%ROQFAJ#dhLFnT|~}XYE=exntX9OvG|JAKed&@zCVeiZbl=-j2xr%DxTlZxd;hnSVkh-*wxw>dQO7MQon^X8nsrFD^!H#Gp_4?*q)D!Y>zl zsk-uiKiP1uWLVs-{^C?T9ZH6LnEZb8%qO@;wCWvo?85K|-u2gq*OvLEkGP+_^^Ux; z)@K#+oa1cC@n3_IPox^|1De{wzw~}|dHK(qV^^-)@`@0BF`$o|3o06aex|nJz>$f^ zlTm`^e@`9?fleKPz)UQk!Y-yXt6!8}WWS zy6?crtc;kP(1=Vp!dw9;>XCZQ!P6e9M%;m@z2u!C1uRI(AHk6N7r@M zq->lP{r6LDSZ^}y9`ARdS7!UFYaOkYWzDl+es}&#UD_C;#;!ft&wS9rt?u4`sMC$OHOX)kK4NJP|$0b2) zFUC|aqhS_9kTHf(Wfo3D! zvkdcwIXnf@ZUP`f|J3P-@g>3^p1G!{F8>I_{`_6Sl$#Vj$KEe`viOivCbyYNpZs^s z&Gz-keu=;QO$GSCrymUB=`&})!3)t-Pd`skaaTIGY~mlT#fZQ4B-mbTEIM7eS3{n`nE*<5J)%SKaNyL9f}3Qw9NNixTLP2*9*pM6i4 zmjky$XD5nG8AE8)%U?H z<38t;YWdIZ@?6#gq`x?}tykLx+?#)vwPv4c@#Y(cEhtm$*>{Kcq~3+=*o7OKcUlz> zPZ+LmAm6REqwd|9K7VIO6ytPn$rF1hJ&9T|d|M~k=lBA9-950oF!Yymk*&%JUt=v4 zyRbOS{wGP1Ao@H(AUh)JGr}i=S_+Q1eNB?qv|Ik0f`!S2w-1pO)->jAVM0gO`)NvvC>BQF)q}B|1ra5i8 zmmLW{`~*L#;{g}H6yrEXcPvx&UsU3HTpruQ7DIC$yd^096PK>Y_3>{)yOrb*)aTZN zSX9dMYMz|>v(*^a56{%BEnPL3069^Ors6QoobZoLRIiwWajwEN@LQp;Sex&+e-}z$ zOHj;4W1rQfuj`}<;&wQ}O^llcPtqES!?)9aU)h!ZCP4gl@XUxy<{Fo_)_!LcT7f9)HVzoZhBhd!?B1>N+kS|D-3FK8m~eJ_*%0w+S}Lq08j)gK%; z0G_c|60?0P_KhL_p7uw#4VHRsI%QUp9)GW~EA;hz8}{wJx8R2@9_0v_-fe~1>1LLP5<=`g6;iEAm3`koomnSbIaslzxmLRdU%7g zr~$_$gNMqhT#;Wh3(;CB46u0|F@(YD-GFRqj>AESx&o=VoDM%B=ceaJ3W1J_pYPeI zUU~A*U8gsTs!&R*m9CM}#B1^Ie}lOLtRWF?@7~u?-tU(hS4>i~ikd39 ztNgtG&cMEi|J{(Ew!MC`dXI77?ooKWgu6*mkbPr;I#4ChM=H}d`K9WlWWJZ*;%tjF zNc`;58?#(vxDQIEdy8Bay*?~YvDoRu)DcuJ`z)Nb-AMiEJpET}wb{>0NcdppXm9!2 zzd_rA+TR84)SY5{TPtvFuCZTv;zWG#d4;n6Q+Dwd7M%4l_{6gH$>Nk4*Mrx376rFI z_BH*vHSlr1^WGlYdq>Y-L*C!Q9(-$Iog7FZz=eHPCJKv!S*sF<-T^$QgJ79cY;~Uo zxTS8qXj%VO^O>tOeBEI4aJD^DFkunvI*vu#`i`8y8%)wsH_rf#2%CshBQm0};rDzH ztZsP+JZySveV#?LR$q9RsM&hs>eU{4vhwjYfqNPi&mXO9FNGHBH6C?-{x17^V20-- z`;Tk>1EM!J?Rf=$U1*yYVW|vSJhu7k?|3A-&^EmMBfSYa4C@*4y&z}`7NZ;|v)nC8 zKIzN}Jc$1)Uh9dGgZ=b1I@8f#aG*ZO3xU=n9im=XP1VZPG%5`L{b?%s-r$!{>VKU6 zmdNOT3qa!yF*j+_A*O4b{W63-s}Zc*H<_X-zqc4G?vsUyd)^{1=mE3ge&TE5 zO8G6mR(gE@?;K(NdkM#>hMcl@b`u6!rV&L1{+8ML^ zT9JPSIP*YFe-(P}ax4aX-4G=0=CNFQckRT+JH|%+&y?LS%z^mUj+HA4~WJQ2k;;LCFOv>96lw zP&W^qV&|HE6@Fd1Q_y?sU+X(}3lf(^{ret+mdZPl)-K$g{j*@Q9WU#eWf3^k<3@~X zJ#-G>ZV%1-*1S%Q(tWjMf~B&M9P~F;+qSgk4~BTb>P53@+>GXct=}-Ud81@v>S-5&F>b*a&2{4JeUrRs)ul)>I`NtWx zv-&>(Q9!Q0nHF~dq4sT=x$Fjg|JvL2!O|TRJ+xB#0w7rL_w=ib>g_pg1 zOV%CzcnxF2W^N|f1|Wx4=`r7a58_W~zzhLl`oBJiiSQSK z&D)Z=Y!GB`kja*kYu-EaKfeCRf1kd9GcK<#y}QIw`~&%=48IDvQtceBK{(jr`51fE z;x}3N;9<-aci`8Tb>Z*r_(V*P72o2G`UW*`Cr*?O5&a)1g!7?zRd8b?DRnkdB@9h? zWFC$ENHKcqrgzZ^9wb{`eNfCnoSkRvFFeTX(Znwy%TtQD?WV3#XXU%)HK+YVWBlpk z=NMg=bv?5|KPkslr2d`%H26HfP55nSb_BoVYMgM#ixpWh{se>uja#?g38cK%xi@nP%c^3F@_4-%)DgobGJLkY=K{ip-k?oiS>;y_-e+KckC>m2IPGYZBH@Uh;bB#m zC8!NN-Y%S{9qRyCvP+YvT=N3_ZF)I;1mOJlJA5{;Tf4?Iy`Y_{IP*faP^yh>>W_Wr?4{r=9IXygarJjSum^;COB>%V^glJG0Z%F3Fz77)db2qIOIYg)XCJ;aR_bfDZBZJsyB^jrJHFCqofeQu#{WK&^_4IUE<6gW< zpPRpzF!Y*maaK9xL=OOAuZY`+Ht`Qbavd5pZ{vU$<-HzzJ%!jWREiO@-lNV?V6k{0 z+lC!Ps9z)aj|cx9yCV_l>zfrdi9(Nfq9+qE>kOKj9ijCX#H{*evKIp$7>i>Xx9sFsO z=b3OPBXiM+Ph?>DRahA!NdyMiNB zd-?(FAZ}nko6xrhvDTwzF>Y*)-C2kK_y5~_7x493Zm#`T&XxLQ1(yna{B5##pND&u zo*4I|f^JopE!B?o<{Ibb)>d49?4bVX#7B4=?Usd~h<*mM`Rs^(WV7FM_2$H>Cf;17 ze?F(rCdkixr%*sQW-#?Bjg}|u2>>pVqn-s!o$a+X84rRDRl;ZMUY zxOl{6#KM0B>8KUw5HhPdI$b4ODvxxT%2n~|jD_pQpR-Tv*V$fqcM)g&4aLcAdK_@i z`*AK2mZPZaQ-`SSQB2GPJcR;1|@1FWDvYjL+* zS9kRBol3%&G|E}O3EMV(Oa7R5MD6AIkiX|Tg{xn#AK=tyJ`qBi6Rclhwk&=%br~$H zBPHN3%-h@>KuG;`PWSeJ1djC8w{(eGI!(g5Oo#!{o((gxXcOD;u*G|Qwpms_mg3r5 zk5RIouX8>jsqrf{vY=E~UT#0)+#WRN9-$Y4=`OLmq37|tF+$sjAu@s!dp%9Q17??l z3Vr?JR+l>Q<@{-`953xU=JtT=Zs>i^`Y2iYRm=5+>WXm6-|NTw9AA9GgXzJ8pPFi@ zW12IEORzK+uQz&t5S_;QM^fFhP_yt%{h7lG&0~Fp0ePzJqW$haIFTUI;W{^mDYX-AE?HAPE}`%i zpM2hz0WS28!S(Zy{Z&bs6V`)D$hT!pt&yfR`1k^$9DuBcpQGedV>YqG8^W+y%x-HW^?sY zx*uNe6Up`)O89|Yd^3w1g45fEZ~-J0z0#%o;Wgj>hm7vhd&}Kf$LKvUpN9UFsJK<8 zBUO1=ZVU1w-_put%9g4llhy&>OC~OrrCN;aYrfA+_@8LININTkL_LNg@orA5O2|@l z`~vuN<(>g4CtodgX2Sn(USayS=w49vJCwk@Yx*T<{|%fw;NL&1Sm#RY2#XuOyKYx* z-|#g)*DV&p(ztv@kGD1zeI8a3fbaAnUu^c*n82Gyy5Hi_ zJ6jsc(SOEy=?_1=sZJrP}Lj9N62qA3b;9NhRMA9tb4&$Bzo=4d=hp^}v3X%)qhp z)~C8}U^vBlf4t)FW4OdY8xD|gj-Q+gKmVBL0~dmfcX zZLV!mo2`24H4AlUpc2BMB9*^miurwK!VdVc=7a6rNNIk z%%|C0Mt+B=UmqPb9(@#rm07d|>vajSGp{Rgxh;De_o~3$^xJ{b{-ss9_j5eVb*iVE ziuv}tEqG!m`5^Arm%svCBZ29gMc9o3XugI!x8BxA!J+&NRu zgx9F1bSS(ZZ#M<*3NW06#_47>(vknZ^-%|UL^G?T7PkNhEknw~7*PO4F2|zPWD>7_ zvw8Y61biI$`B*+hMPsD{lrs@*wWD-qG;Tm6B0~x{2fubspH3T_MD%uCUtT7PR}{!c z!a`bsv!$?!02$1HkzNS^keO2KQ)RXppjy|y9gp;N#N7nF!S8qCN6XI*7;>xHz9Yx-8au${gQ#6^n@L?G0=< z>aQ8CvvX-v@iyZNbVE`2{hOj`O(}>P!706LqZr9&#%~kQ^w6hHr_ttk8O_hO8g6;T zJGmB(0kt^$Ppp3hk-IICf0AZg4!dFBI^G4j$@d}& z;O*AOa|xIVA3;EbLK#(gVO)`QROA27-!{%TB&q0b#|&M`y2=n%DSp{BUoS+G%j}7< z9r;;pf6#B+o9^Td%*PI_$Z|l$PZMU0LntFrMfMAmMF$B#QSjlz7ZX)6TeN$IivFS7 zRYnKk`|yu#XJkkNnVe&lL0zv&`un&@gcRn22}@yzRygz!{*}f>0kVFJqy{lt*?hG{yqT9-*x4|q2N{~zz8xLx>Vwbh zjyg_Q$Szzo77J2xajt){W!vKos7t)b1~LkD$9<49eXwC$0fij$m1>%ZN@O0v<3?QY zaAU~4N~@@T2mSyi7O|V~8`M)3IvNpiPO6Iei3TF|M>4yjjTW3uLDIl zf?SBwwnb7lvwVSR8mu%_@2NqUCK$p?NgIvqSsDk zJqiD`P9!Bq?$3Zy12&kpCG8rIl5AYAi{YiPx+HzJA?GOuBsN=4jU4!|Z}&jNPl1#g zy%=FU9jX)5Vl40HX?kdD6%YYUid)Ie&1&bR%w14Sw*1lmK;7xVXWjG7%d$vDQxk)V z?{X+YpLU`Et^62)W{>m>o6Dzco6BL~74JRW#~FdHxUeIKGnh{&h72)S^E^*tgSEV_ zbhL9|VcvbuCGI`(5hRyL$|IOxsZt1x`1fTILf$I`)ra@0@j+5dyd`pQVSFFJ z%VC>!f-i~eTbpOU2#3am#@>@}6g0zo{EUA5I2FH`Ax`EyEN=?skSWT4zcGUL21DQ_ z1BVJxo$L(hfg)w67IV?@j&-9GEXrFhsu(G274t|(XI}?g$5^Q1P2COvMOcY1*eb`N z??U}DJshJn*YBnkxgg4_6^EHBaWr=_Hx={rjfR+C*xb&%J|u*PxQEFr*&h|)|0Hp4 zhlV049IM*WZIh)n;!rdwYuZlEoDLzkQ+Vt8C4%j=C`K{8@}o=Gh_Rwb#G=E>sTBq0 zqrQiS5Xjn2uuW$ys^28F?(tkFuA6TkXu68F{ML6Zm;Ft~in~ zqa&cv-szTLo%AjDQSkLgaMdDhSUmM$G7~qeNRBUd zlJx$~qcZznw&y*Hj|B=PiQ+E6J`pJmQk=ZkxG5{vpPR9f+I+ru`J;>I6!NHoC$8AI zZVt%_tj(!Zr&PIjkgM~n!QWJ)a3_$X76h|Hw)c}ng7^?LbN~wO)>9KG*$Z`wG^AfR z4+(&p($PTn*54Yc+1dUgFP}FcEj8^n>161|K@|pQBY!s-PGr*7WNETd0!}T&Bw2U*iP=G!#O0mXWuX)oiFBoZ#2X{`AC3l6w$FYdEri1 zPtyR)Hur~s)^#-@#^avE_e+kw&1OajD&h#a- z98{4EYwGn8tsyJKVF&rUE;0H#`IdGr5N#k=D`-)n*AvIbyiSivq^=Oi@nkQecaeoj z!APOF$b9afS>N@Tv4?5sFP72ym38DOXq(8X$LFI{0U#I&f&vF0s`r@dmaaJaGO*}J zVoCaMYqtO6I^hnSj1Uu_w0`&-u)K&#)|rcV%6Q|Az_%A62pK+;^@C&=3hoj|u6^?N z_CxywQ5@t;{^N7@rpqs+qp0fcis6ynLh$cAIYY+K(8F9P|9YF}+P zXRO8%5i!Boq%8|51@V|pdFNFDU)s8I#AR+T15JeFfU{+6w+!?v7i<``2)dCGyUT`} z`!=Cx6IFrZQJ0+7<1Yql9`LAAs1$nwRQ6!xEAIp)u57%j z|1DbxCw%CS6wi&_{5UKve2k!vpAJ$OJbcH{>WiqPj%*Ng#(AV7UhIMQC5MK+$(VtG zwlMoAvaYmg|BkKY=rk%O-U((7(+8F*Ap$d)j`-2hD`0{ZIGJe>?(|ZmQx+hs*~Su) z8bouD<);B;s~c2SiEtH(MLS5abpsV= zkGF9$+%b_z^j0I7%heXh^wdKuB-Nj zDy2-^Kz`Ik`IMZ!lqHCGX(gZ-QPGw))-D4?bdd6G(;4M^#)jEunbQNJ5y4rr0Wdm>N-j!6g#nH_Fmjtg=6z zQB{;bPEAKnL<)2wp{So%!x`P_xoatAtRO_7q2vWHio1cTc?;R-6Kn1SDQ5WZ3{?h2 z>0qgSY6D9l8d#=$X1v${BK76@>`4>v>7;0_yc(&2qxf7l84NKxkf9ryrCR;U04Z@= zW-1FhcqL`1bdY*$+ubY?_U&J&tz7lj%7QT(pOqrl}hlV(m!&D0gMO>va?*o zBA(ej2*^swu@h==&mHCBtTqQPy0_ddzYo>bWtC_|L3b+}Kl7q_c1 z(5wAHVtjHh#Rv!g+B$w2Qz<8&{dnDCLa=PeK$qZrA0l!m2GQMhdL03AWs?_2u*SUe zef-u4p^ekM3_-I>ByzS4cCt6z1Jz;{3fwYVxR?F*8A8mSgE+#Q`CI(V z&L`kj+IxV2%E+8%Es;>=|u>kOe_1y)`dAiK0WZuAp+xF?91y zeEc2cJA%svCO~<>av|n9m%PH3-K-6+ON6_MCb+7jjbn8}Yo*pJdW3$BzwWv}+H zmgF@?z@@uq*HEe$5vo3#6_EKxZ;Um{_AbIJT8SGc-}3(BrhK&0UzQ)nqZL^MDYY;5 zQ(8VF1+zZ{Jrb)m#NdCO#eqHb)&zSVoN|>6 zflDyuY)=VPKviORiabNDp*MbqN!5`RDKzs(>77)VIjHP}CvyQX{jp;f5azq52>oWP=OnnzFI`JzJR2XiS9^Vf zB_+RA0_FxedWcaM`btvEs@HWOVc3r})>=8b{FSWq=`Mrm1Fs3HdLmvqH@a~OoRHr~ z;0NM6eFEOdhY*k|dlexbYHG6Lh&^tIkNM@PRVKTo5N<50Fk6J!ith!ZJ8%3i75plU z{9#I3-2I*LKyG|g=HDZu1WY2ej+XcRkyPFK%28yEh@?V~!U2F^+381+A*IreQWBY2 z%hR+VMFoCC`Sv8ngPa_g4sv$%!wP0ZT^XkHKq#l#M}lWC)| zyGu_fF81}q)3tDw^negOdb{d=xi4LrgpY~@sAqm{BD&X zH);G%3b`ql-#huqM}8{kp1*8Q8fesZPo*6s2c2!okeidFaOU4yDA54{z!QP1ea7cf zM>EGGiH_JI6{9hrj;vZ$$_m?wiVmMVInaHlbX%eO5 z(d4M9wl_v3D{;|Gld#J@_-SffiVMBesBIY;$t^`UMs;S&^_Z9RI=_U1SnMd4xVj@% zj1t#ne6>dM*~F}}cQB#Fk@!ay`tDUoK-w#nNfDCVrU#o^vP5D&Xr*%Nq)(AvI?W+C zK50kOCwqETC|8T(qM^xae&PuaI7D>?W4yA4Q2XDVSwe3WnHRz4F=3Jp&l$*(Ru`FH z@EBE2N=c?tQ?_jIP9#2&DB$MY+JQ^NW}>nrMzkQTwrmYEt&NJG+-EysbP`O!rzxcv zJgtRhgqB>6HkT?+eY9$0RI@Q1hiwF|)t1D#oR#)qO(U?B7CsN!y5fH@Eh|hY3#>9s zLHiE>?oobgJ`-YI^n?*SJjAY_#Sx|^&b^cdzLOriV85J@ zslOPwd+d%}AHJaPSWmdZ$kw7^#gW7=>dd5kZ)X4v@pyw_ zi`;Yuo6hTl_+Qc=B)ef#x*THe8BYGLT&Jj%Oqj*Wqa1pm+*Q;G;GAq@k-Q#%UvS+; z_5|rFx(>Jva$2yxjtiC37}b{>S!n@0TFD>P!KJh5u5zBf4CX2g^FH!5rax+G6lX{h zB6ClxoN$}18a*CXL?E9N?QvciMs6$bLYmx(F@cp%pEM$Qw=||39l{@wY{eb{Lo77) z31s)+RT8P*XFK-Cie$KilC+lmB-_`!gS)Gcq1eq>9{@eAthX!&Q4n3%R6mP4eI~=% zV}}KvT>~eiEm4AP^@}!AIoaBveJHyTfI_Ss^V4lSJ)p)wad&#vSseKnfsvvmrbvNN z;CW_*zyK1GB({-1RgdeW5&ypqg#ga>1?cs<3u4v?ku-Q*87!{oCnd*+!Wl17Z<-^f zp*(?>a7n3oD4{S^fczFMRe=iIRkoDWWlDJX1;wnz;Z$j_zDPW4@TUsDp>W|oTbFSN_1-EKj41Zu*oBef}GmS|qNN)2NH zkk^`WxN^W%N56gm%@7$u8Ekz+MxVPvzM_v243TMMO-)R#wV6WBnd~#RGZ9SR-!Nml zgSaa0p_Ew#tx}%0786?55v2YKh$Q9ed0jZzlzO|0?PRMwRFot&Y5AvRz=C^r`KO#4 zC}y2^GAV^7$_9L!u)g8Fx#Z|!lTzAqHxW69EWd<}YxYX2E6roF6-XsJ!WwoGoUmSC zRg?2Ml5m_B20;k7VF5_ZZTHX_jZQ;fffCYK(*mkwk+Q*KoBKGKlfrP22-p`9(wK5d zX}ME%=gOV-{KkNHvjI!`5yF@c?NN6F>nnv|;O7QNE1wi#`mP>h^091*-Y zc|eDOwpzwKMnyva#91N5Afz;C{HfF>t98#UjsG96Hp$I3ZNkSQsxTm>i?mx(9f4Hf zSH5DjF>N6)IGL>)a`s_FaYRqPsdtIc9~Knb%qWR8522bhvW8~Vbw~ZC(2Q8GQc4N= z?;NLJj&aH(Xpv0o@-mL~>_jw1`pSZs!fflqS^P3~N|@EvRK%qI?bw(yyA6DCh(U3S zT)vm4c)c?RTb@>7Ndl?Kl?mPRDLpTeRBrw;R8V@AU@f~p$p<0}=>|1!$&9NZuF8(t zx$I<#AZDo8S6c8|dpYSwd!nA+>nGfwgKfGk%@Ck~i*2|byzD;aq1Mk8YV(c5sM)J_ zn!xRuZ?u7XA#%R>k{d|%Ds@}}I=qt|Sd-k@#$k*#tOnvkE*+-?H_A)-BvcBqE)iVr z!>`Wk)9Paz1`-C!DlzY2gY=~c&s(^^hD21i$ZN=h(J3yP$Q;vx@<1w5-d-b`XrNB+ z!Xu`Kj!pj`IbjC3e=`Ci$gfC|hwMJMwDQd9NS@MvN*)~by5Z*t7F#8n3|!UVaKcJ+ zv+gT~9s9$}5Mj)3QnE@D8t64nM6`&Q6Z;XsVue2d8!8W-NP00ULV91oB5LQ%ZHx$| z11-CPXnJtYSZEpC$fB~M>Na#TmIz=W(X4hvRL9QhODI`T^h6`g@_{h8O$dV$r zC5?+TO3{LF2BY%5Tbx$NaX<^5lFiCu$u4)M7$afB%1{lNK>m8o)p(p#ifN)X9M@=A*Dfw4b5w0SJg zm>(XEbWbSogiirOKql|A13$!~PD>EqNL4&bVa=N)kVT~gSF1hW11bvTI?I%%H#zV? z^ov-91&6Y_Df3YAiS8TgA5v$DHyoU4D6}#IxP6Y zFO#J$q;h=WB_$DHL3$+H*ep%!sQfE5vWa=K^>)mYnZxTKGd5Qq=bcfP%oTdjclSv( zrHIjH0_R0L`=rb#iN_7tC^ANX=*ta{Jsn|1jw`@Dlc6xG;*KOreqU5aY=WnF>xj?y zu=7VDcUCnhL}hux*^66VjMEQQe6|op0J`f~a!|iq1kps( z-{AJIpyj`Up_mgi>;SC)Wy<=2^2XWv{Zd+G*C& z1h#L{$fKCL!@_jT$gn~FrRKDmz0e2f`}B=bGRfw?zSNz;G7TK#O4RT~Xn`?=rEr`V z)Ify8i)hw+fh2TU)YU$s{5@wS!_olxwJQ)2>jemkQH%?-@&>WC>;)`G5OO$L zvy(v&zAtm{%k@yEzg&!+@_<_iCh%B0V93U|#IL-u0b^Jyz3GF!KpCNr2H{S*7zOXo z!t<(8tViETCk;d#c1Wtl?I=u=-sHCxp+Bq0f;f9?I%NFfG$tTK$^>G-y+-Gw?Jx+98xN%* zn#{sL)TD+3gm%8Dd4d7TQ^q77Ju062}!%dO)b$g1tGU8I3(S!dL{`*(}^_b4b zo)f;{TR!$nWu49D{`9~G~65Z51;UzK(Z%G(&a&BR%SN9&%7(0RoC zKG^DUQclb+F~ZSnE+aj9obx>T%Lt#+WCimokFy=qn=T`)2O)vF*zgRwoMOaP7=Jtd zgPXkM_V@4~Y<-mrpp{QX;$6O#)}7)su&1@1vD={Be^S)Zg*I%OWo3$2uhH$?eiSnrL4jW zC(t@n;N=cAEuAMUPcZH&Kp0WGM&t0HVNiw>NzyA81_wy$7+OUXB&A*K_3z7xxilUmayy;zfEZv)D)=MjBe{iv*Aq)S*YQ4W9!qhDT4Z1#v?_(r z%@R-+*w?NUNz`;n{%K?yb7*8zJ(r}eOCiOTFpH?f&A_Vh1g0Td@pfAx;L#0Mxqfm@ z2EfVm8+Z7&jp>KNF3>12nYXV<(WA>#f6}L@H`OGL#jX;uzUmGLurpIu%ka}3n->+g zcQUC~T1Ig8sk9G5?O2}2<00Hgbc|<84-%E`Vb%DA{?H-;_mX?3iHS>{!jF$(utPdz z6JM8wz-e=PM2N;>0Cwb?8cFX^Gf~0gSt)TEQM6oCU-LbWd=#0wE!hLqRoi zhe_?2V(*6rq3*_n04s7ijPxkwXMaHjYO^8C&tc%)qBZm1`)$6TAm*TJ70N)9m~w;f zsxsbqs@047%Qr|(R_a1eaH^$IXSLIp-LD5smgdOHE^|G_x-CG$u7N55gpBR60$Aih zIO=`2?+Xw=zMPg1oh}mo6#=!SdY7AuVQeH)B1If41H%Y}IF9CD1PBvGt5S$qtpt%5 zo}YJyisBl?5936Y4TYF}VM$c1hH?U;zFRO7#)c#2CH=+KjyL_4)&WCy^R1cs0M<>7 z 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 literal 0 HcmV?d00001 From af957389c25d239f517a2d0e9f65a2d9ca6d8279 Mon Sep 17 00:00:00 2001 From: Hikodroid <172511799+Hikodroid@users.noreply.github.com> Date: Sun, 17 Nov 2024 13:57:23 +0100 Subject: [PATCH 11/29] patterns: Added pattern for LZNT1 compressed data (#304) --- README.md | 1 + patterns/lznt1.hexpat | 206 ++++++++++++++++++++ tests/patterns/test_data/lznt1.hexpat.lznt1 | Bin 0 -> 10940 bytes 3 files changed, 207 insertions(+) create mode 100644 patterns/lznt1.hexpat create mode 100644 tests/patterns/test_data/lznt1.hexpat.lznt1 diff --git a/README.md b/README.md index 84114cfb..e7f1d277 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | Lua 5.3 | | [`patterns/lua53.hexpat`](patterns/lua53.hexpat) | Lua 5.3 bytecode | | Lua 5.4 | | [`patterns/lua54.hexpat`](patterns/lua54.hexpat) | Lua 5.4 bytecode | | LCE Savefile | | [`patterns/lcesave.hexpat`](patterns/lcesave.hexpat) | Minecraft Legacy Console Edition save file | +| LZNT1 | | [`patterns/lznt1.hexpat`](patterns/lznt1.hexpat) | LZNT1 compressed data format | | Mach-O | `application/x-mach-binary` | [`patterns/macho.hexpat`](patterns/macho.hexpat) | Mach-O executable | | MIDI | `audio/midi` | [`patterns/midi.hexpat`](patterns/midi.hexpat) | MIDI header, event fields provided | | MiniDump | `application/x-dmp` | [`patterns/minidump.hexpat`](patterns/minidump.hexpat) | Windows MiniDump files | diff --git a/patterns/lznt1.hexpat b/patterns/lznt1.hexpat new file mode 100644 index 00000000..dfbfe441 --- /dev/null +++ b/patterns/lznt1.hexpat @@ -0,0 +1,206 @@ +#pragma description LZNT1 +#pragma endian little +#pragma pattern_limit 1000000 + +/* + * References: + * https://github.com/libyal/libfwnt/blob/main/documentation/Compression%20methods.asciidoc + * https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-xca/5655f4a3-6ba4-489b-959f-e1f407c52f15 + * https://github.com/tuxera/ntfs-3g/blob/edge/libntfs-3g/compress.c + */ + +import std.core; +import std.io; +import std.mem; +import std.sys; + +using BitfieldOrder = std::core::BitfieldOrder; +bool createDecompressedSection in; +std::mem::Section decompressedSection = 0; +u128 uncompressedDataSize = 0; +u128 maxUnitSize = 4096; + +fn appendData(ref auto data) { + if (createDecompressedSection) { + std::mem::copy_value_to_section(data, + decompressedSection, + std::mem::get_section_size(decompressedSection)); + } + + uncompressedDataSize += sizeof(data); +}; + +struct Value { + u8 value; +}; + +fn appendU8(u8 data) { + Value value; + value.value = data; + appendData(value); +}; + +bitfield Flag { + bool A : 1; + bool B : 1; + bool C : 1; + bool D : 1; + bool E : 1; + bool F : 1; + bool G : 1; + bool H : 1; +} [[bitfield_order(BitfieldOrder::LeastToMostSignificant, 8)]]; + +bitfield CompressedTuple { + std::assert(lengthSize >= 4 && lengthSize <= 12, + std::format("lengthSize has an invalid value {}. Must be between 4 and 12.", lengthSize)); + std::assert(displacementSize >= 4 && displacementSize <= 12, + std::format("displacementSizehas an invalid value {}. Must be between 4 and 12.", displacementSize)); + std::assert((lengthSize + displacementSize) == 16, + std::format("lengthSize {} and displacementSize {} must add up to 16.", lengthSize, displacementSize)); + unsigned length : lengthSize; + unsigned displacement : displacementSize; + u16 actualLength = u16(length) + 3 [[export]]; + s16 actualDisplacement = -1 * (s16(displacement) + 1) [[export]]; +} [[bitfield_order(BitfieldOrder::LeastToMostSignificant, 16)]]; + +fn calculateLengthSize() { + s8 lengthSize = 12; + + for (u128 i = uncompressedDataSize - 1, i >= 0x10, i = i >> 1) { + lengthSize = lengthSize - 1; + } + + if (lengthSize < 4) { + lengthSize = 4; + } + + return u8(lengthSize); +}; + +fn copySequentially(std::mem::Section section, u128 sourcePos, u128 destinationPos, u128 length) { + for (u128 i = 0, i < length, i = i + 1) { + std::mem::copy_section_to_section(section, + sourcePos + i, + section, + destinationPos + i, + 1); + } +}; + +struct Tuple { + std::assert(uncompressedDataSize > 0, + "uncompressedDataSize must be greater than zero" + + " because otherwise there would be no data to backrefrence!"); + u8 ls = calculateLengthSize(); + CompressedTuple ct[[inline]]; + std::assert((-1 * ct.actualDisplacement) <= uncompressedDataSize, + std::format("The actualDisplacement {} is referencing data before the beginning" + + " of the current decompressed chunk! Current decompressed size is {}.", + ct.actualDisplacement, + uncompressedDataSize)); + + if (createDecompressedSection) { + u128 destinationPos = std::mem::get_section_size(decompressedSection); + u128 destinationBackrefPos = destinationPos + ct.actualDisplacement; + u128 maxNonOverlap = destinationPos - destinationBackrefPos; + + if (ct.actualLength <= maxNonOverlap) { // Not overlapping + std::mem::copy_section_to_section(decompressedSection, + destinationBackrefPos, + decompressedSection, + destinationPos, + ct.actualLength); + } else { // Overlapping + // Copy non-overlapping part + std::mem::copy_section_to_section(decompressedSection, + destinationBackrefPos, + decompressedSection, + destinationPos, + maxNonOverlap); + // Copy overlapping part + destinationPos += maxNonOverlap; + destinationBackrefPos += maxNonOverlap; + copySequentially(decompressedSection, destinationBackrefPos, + destinationPos, ct.actualLength - maxNonOverlap); + } + } + + uncompressedDataSize += ct.actualLength; + std::assert(uncompressedDataSize <= maxUnitSize, + std::format("uncompressedDataSize {} is larger than the maximum allowed size of {}.", + uncompressedDataSize, + maxUnitSize)); +}; + +struct FlagGroup { + Flag flag [[comment("Each bit represents whether a data element in a group of 8 is compressed " + + "with the first value being the least significant bit.")]]; + // (up to) 8 data elements + if ($ >= endOffset) break; + if (flag.A) { Tuple A; } else { u8 A; appendU8(A); } + if ($ >= endOffset) break; + if (flag.B) { Tuple B; } else { u8 B; appendU8(B); } + if ($ >= endOffset) break; + if (flag.C) { Tuple C; } else { u8 C; appendU8(C); } + if ($ >= endOffset) break; + if (flag.D) { Tuple D; } else { u8 D; appendU8(D); } + if ($ >= endOffset) break; + if (flag.E) { Tuple E; } else { u8 E; appendU8(E); } + if ($ >= endOffset) break; + if (flag.F) { Tuple F; } else { u8 F; appendU8(F); } + if ($ >= endOffset) break; + if (flag.G) { Tuple G; } else { u8 G; appendU8(G); } + if ($ >= endOffset) break; + if (flag.H) { Tuple H; } else { u8 H; appendU8(H); } +}; + +bitfield ChunkHeader { + unsigned chunkDataSize : 12 [[comment("The actual value stored is the chunk data size - 1.")]]; + unsigned signatureValue : 3 [[comment("The value is always 3 except in an all-zero chunk header.")]]; + bool isCompressed : 1; +} [[bitfield_order(BitfieldOrder::LeastToMostSignificant, 16)]]; + +struct Chunk { + auto headerPos = $; + ChunkHeader header; + + /* An all-zero chunk header indicates the end of an LZNT1 compression stream. + Might not be present if there is not enough space to store it. */ + if (header.chunkDataSize == 0 + && header.signatureValue == 0 + && !header.isCompressed) { + break; + } + + std::assert_warn(header.signatureValue == 3, + std::format("ChunkHeader @ {:#x} has a signatureValue other than 3: {}", + headerPos, header.signatureValue)); + + u128 actualChunkDataSize = u128(header.chunkDataSize) + 1 [[export]]; + + if (header.isCompressed) { + auto currentEndOffset = $ + actualChunkDataSize; + uncompressedDataSize = 0; + FlagGroup data[while($ < currentEndOffset)]; + std::assert($ == currentEndOffset, + std::format("Invalid size of Chunk @ {:#x}.", headerPos)); + } else { + std::assert_warn(actualChunkDataSize == maxUnitSize, + std::format("actualChunkDataSize {} must be equal to maxUnitSize {}.", + actualChunkDataSize, + maxUnitSize)); + u8 data[actualChunkDataSize]; + appendData(data); + } +}; + +struct LZNT1 { + if (createDecompressedSection) { + decompressedSection = std::mem::create_section(std::format("decompressed @ {:#x}", $)); + } + + Chunk chunks[while($ < sizeof($))]; +}; + +LZNT1 lznt1 @ 0x00; \ No newline at end of file diff --git a/tests/patterns/test_data/lznt1.hexpat.lznt1 b/tests/patterns/test_data/lznt1.hexpat.lznt1 new file mode 100644 index 0000000000000000000000000000000000000000..07d4fa2cd761517b5918a3cf07c7927d553758b1 GIT binary patch literal 10940 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 literal 0 HcmV?d00001 From 61d3e110fb252902769e89ae0322aaf1bf46ca5f Mon Sep 17 00:00:00 2001 From: bbbbbr Date: Sun, 17 Nov 2024 04:57:45 -0800 Subject: [PATCH 12/29] patterns/gb: Add 16K and 32K arrays for ROM Banks (#305) Having arrays for dividing the GB ROM up into ROM Banks makes it much easier to jump to and inspect data at the start and end of banks. - 16K Banks are the typical standard - 32K banks aren't very common but are used in a couple unlicensed Games such as those using the Wisdom Tree MBC controller --- patterns/gb.hexpat | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/patterns/gb.hexpat b/patterns/gb.hexpat index 947ac984..79d0d33c 100644 --- a/patterns/gb.hexpat +++ b/patterns/gb.hexpat @@ -7,6 +7,9 @@ import type.size; import std.string; import std.mem; +const u16 ROMBANKSIZE_16K = 0x4000; +const u16 ROMBANKSIZE_32K = 0x8000; + bool uppercaseROMFeatures in; bool brandedROMFeatures in; @@ -357,5 +360,15 @@ struct JumpVectors { u8 int5[8] [[comment("joypad")]]; }; +struct romBank16K { + u8 Bank[ROMBANKSIZE_16K]; +}; +struct romBank32K { + u8 Bank[ROMBANKSIZE_32K]; +}; + +romBank16K romBanks16K[std::mem::size() / ROMBANKSIZE_16K] @ 0x00; +romBank32K romBanks32K[std::mem::size() / ROMBANKSIZE_32K] @ 0x00; + JumpVectors jumpVectors @ 0x00 [[comment("Instructions called on interrupts or RST instructions")]]; CartridgeStart cartridgeStart @ 0x100; From 85f554135063ca765c56e6224cac856a3eeff296 Mon Sep 17 00:00:00 2001 From: Dexrn ZacAttack <60078656+DexrnZacAttack@users.noreply.github.com> Date: Sun, 17 Nov 2024 04:58:02 -0800 Subject: [PATCH 13/29] patterns: Added Minecraft LCE ARC file format (#307) * Create arc.hexpat * add test file * mention arc * abide by contributing.md * use virtual filesystem * brainfart * ifdefs to fix github actions moment #2 --- README.md | 1 + patterns/arc.hexpat | 26 ++++++++++++++++++++++++ tests/patterns/test_data/arc.hexpat.arc | Bin 0 -> 7480 bytes 3 files changed, 27 insertions(+) create mode 100644 patterns/arc.hexpat create mode 100644 tests/patterns/test_data/arc.hexpat.arc diff --git a/README.md b/README.md index e7f1d277..336ade46 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | AFE2 | | [`patterns/afe2.hexpat`](patterns/afe2.hexpat) | Nintendo Switch Atmosphère CFW Fatal Error log | | ANI | `application/x-navi-animation` | [`patterns/ani.hexpat`](patterns/ani.hexpat) | Windows Animated Cursor file | | AR | `application/x-archive` | [`patterns/ar.hexpat`](patterns/ar.hexpat) | Static library archive files | +| ARC | | [`patterns/arc.hexpat`(patterns/arc.hexpat) | Minecraft Legacy Console Edition ARC files | | ARIA2 | | [`patterns/aria2.hexpat`](patterns/aria2.hexpat) | ARIA2 Download Manager Control files | | ARM VTOR | | [`patterns/arm_cm_vtor.hexpat`](patterns/arm_cm_vtor.hexpat) | ARM Cortex M Vector Table Layout | | Bastion | | [`patterns/bastion/*`](https://gitlab.com/EvelynTSMG/imhex-bastion-pats) | Various [Bastion](https://en.wikipedia.org/wiki/Bastion_(video_game)) files | diff --git a/patterns/arc.hexpat b/patterns/arc.hexpat new file mode 100644 index 00000000..02b89f43 --- /dev/null +++ b/patterns/arc.hexpat @@ -0,0 +1,26 @@ +#pragma author DexrnZacAttack +#pragma description Minecraft LCE ARC File +#pragma endian big + +import std.string; +#ifdef __IMHEX__ + import hex.core; +#endif + +struct Table { + u16 nameSize; + char fileName[nameSize]; + u32 offset; + u32 size; + u8 file[size] @ offset; + #ifdef __IMHEX__ + hex::core::add_virtual_file(fileName, file); + #endif +} [[name(std::string::to_string(fileName))]]; + +struct ARC { + u32 count; + Table table[count]; +}; + +ARC arc @ 0x00; diff --git a/tests/patterns/test_data/arc.hexpat.arc b/tests/patterns/test_data/arc.hexpat.arc new file mode 100644 index 0000000000000000000000000000000000000000..6678ee6313caebd077e205b5727afa43afef31cd GIT binary patch literal 7480 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 F{{c*Msqp{+ literal 0 HcmV?d00001 From bc5a55affecddab982a337e6352052be9b1a1b90 Mon Sep 17 00:00:00 2001 From: Dexrn ZacAttack <60078656+DexrnZacAttack@users.noreply.github.com> Date: Sun, 17 Nov 2024 04:58:56 -0800 Subject: [PATCH 14/29] patterns/lcesave: Use Virtual Filesystem + ZLib support + auto endian detection (#309) * use Virtual Filesystem + ZLib support + auto endian detection * fix builtin * smh how am I only noticing that I just duplicated a function for no reason * ifdefs to fix actions moment --- patterns/lcesave.hexpat | 45 ++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/patterns/lcesave.hexpat b/patterns/lcesave.hexpat index 3c940aab..7eeee9a3 100644 --- a/patterns/lcesave.hexpat +++ b/patterns/lcesave.hexpat @@ -1,17 +1,16 @@ #pragma author DexrnZacAttack -#pragma description Decompressed Minecraft LCE Save File +#pragma description Minecraft LCE Save File -#pragma endian big -/* ^ switch this to little for Switch, PS4, or Xbox One */ - -// NOTE: This pattern was only tested on Nightly@974c4ba, things may break/not work on older versions. -// ALSO NOTE: SPEGHETTI CODE! - -// TODO: re-add nbt pattern -// TODO: clean up getFileType +// NOTE: SPEGHETTI CODE! import std.string; +import std.mem; +import std.core; +import hex.dec; +#ifdef __IMHEX__ +import hex.core; +#endif #pragma pattern_limit 1000000 #pragma array_limit 1000000 @@ -51,8 +50,12 @@ struct LCEIndex { if (parent.header.curVersion > 1) u64 timestamp [[name("File Timestamp")]]; // useless as it writes weirdly, and differently on certain consoles. (e.g using time since last reset, etc) u8 file[filesize] @ offset [[name(this.filename),comment(getFileType(std::string::to_string(filename))),attribute_name(this.filename)]]; // files in the index + #ifdef __IMHEX__ + hex::core::add_virtual_file(std::string::to_string(filename), file); + #endif } [[name("(" + getFileType(std::string::to_string(filename)) + ") " + std::string::to_string(this.filename))]]; + struct LCEHeader { u32 offset [[name("Index Offset")]]; // where the index is located u32 count [[name("Index File Count")]]; // amount of files in the index @@ -60,7 +63,31 @@ struct LCEHeader { u16 curVersion [[name("Current LCE file version")]]; // Version that the file was written with }; +struct CompressedSave { + be u64 zlibSize; + u8 zlibData[std::mem::size() - 8]; + std::mem::Section zlib = std::mem::create_section(std::format("Compressed Save")); + hex::dec::zlib_decompress(zlibData, zlib, 15); + u8 decompressed[zlibSize] @ 0x00 in zlib; + #ifdef __IMHEX__ + hex::core::add_virtual_file("save", decompressed); + #endif + std::error("This save is ZLib-compressed, grab the decompressed save from the Virtual Filesystem tab and use this pattern on it."); +}; + struct LCESave { + u8 zlibMagic[2] @ 0x08 [[hidden]]; + // check if header matches + if (zlibMagic[0] == 0x78 && zlibMagic[1] == 0x9C) + CompressedSave compSave @ 0x00; + + // if not we will have continued. + le u16 endianCheck @ 0x0A [[hidden]]; + // check if version is bigger than 14 + if (endianCheck > 14) + std::core::set_endian(std::mem::Endian::Big); + + // rest of the processing LCEHeader header [[name("Header")]]; if (header.curVersion > 1) LCEIndex index[header.count] @ header.offset [[name("File Index")]]; // the index From 1d680fbf5e36f1554e9d7f8177030f8a411c5a0b Mon Sep 17 00:00:00 2001 From: Miku-666 <74728189+NessieHax@users.noreply.github.com> Date: Sun, 17 Nov 2024 13:59:20 +0100 Subject: [PATCH 15/29] patterns: Added Minecraft LCE .pck format pattern (#310) --- README.md | 1 + patterns/pck.hexpat | 187 ++++++++++++++++++++++++ tests/patterns/test_data/pck.hexpat.pck | Bin 0 -> 2772 bytes 3 files changed, 188 insertions(+) create mode 100644 patterns/pck.hexpat create mode 100644 tests/patterns/test_data/pck.hexpat.pck diff --git a/README.md b/README.md index 336ade46..f1b61b32 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | OGG | `audio/ogg` | [`patterns/ogg.hexpat`](patterns/ogg.hexpat) | OGG Audio format | | PAK | | [`patterns/xgspak.hexpat`](patterns/xgspak.hexpat) | Exient XGS Engine Pak files | | PCAP | `application/vnd.tcpdump.pcap` | [`patterns/pcap.hexpat`](patterns/pcap.hexpat) | pcap header and packets | +| PCK | | [`patterns/pck.hexpat`](patterns/pck.hexpat) | Minecraft Legacy Console Edition .pck file | | PCX | `application/x-pcx` | [`patterns/pcx.hexpat`](patterns/pcx.hexpat) | PCX Image format | | PE | `application/x-dosexec` `application/x-msdownload` | [`patterns/pe.hexpat`](patterns/pe.hexpat) | PE header, COFF header, Standard COFF fields and Windows Specific fields | | PP | | [`patterns/selinuxpp.hexpat`](patterns/selinuxpp.pat) | SE Linux package | diff --git a/patterns/pck.hexpat b/patterns/pck.hexpat new file mode 100644 index 00000000..08b15109 --- /dev/null +++ b/patterns/pck.hexpat @@ -0,0 +1,187 @@ +#pragma author nullptr +#pragma description Minecraft LCE .pck files + +import std.io; +import std.array; +import std.core; +import std.sys; +import std.string; +import std.mem; +import type.size; + +bool littleEndian in; + +if (littleEndian) + std::core::set_endian(std::mem::Endian::Little); +else + std::core::set_endian(std::mem::Endian::Big); + +namespace format +{ + // TODO: find a way to access look up table differently + fn look_up_str(s32 index) + { + return pck.lut.table[index].value; + }; + + fn property(ref auto p) + { + return std::format("{}: {}", format::look_up_str(p.key), p.value); + }; + + fn assets(ref auto assets) + { + return std::format("Assets: {}", assets.count); + }; + + fn properties(ref auto p) + { + return std::format("count: {}", p.propertyCount); + }; + + fn string(ref auto string) + { + return std::string::to_string(string.value); + }; + + fn asset_entry(ref auto e) + { + return std::string::to_string(e.type); + }; +} + +namespace transform +{ + fn assets(ref auto asstes) + { + std::unimplemented(); + }; +} + +struct string +{ + std::string::SizedString16 value; + padding[4]; +} [[sealed, format("format::string"), transform("format::string")]]; + +struct LookUpTableValue +{ + s32 index; + string value; +} [[sealed, name(value), value(index)]]; + +struct LookUpTable +{ + s32 count; + LookUpTableValue table[count]; +} [[sealed]]; + +fn lutContains(ref LookUpTable lut, str value) +{ + for (s32 i = 0, i < lut.count, i = i + 1) + { + if (std::string::contains(lut.table[i].value, value)) + return true; + } + return false; +}; + +enum AssetType : s32 +{ + SkinFile = 0, // *.png + CapeFile = 1, // *.png + TextureFile = 2, // *.png + //! Unused ! + UIDataFile = 3, + //! "0" file + InfoFile = 4, + //! (x16|x32|x64)Info.pck + TexturePackInfoFile = 5, + //! languages.loc/localisation.loc + LocalisationFile = 6, + //! GameRules.grf + GameRulesFile = 7, + //! audio.pck + AudioFile = 8, + //! colours.col + ColourTableFile = 9, + //! GameRules.grh + GameRulesHeader = 10, + //! Skins.pck + SkinDataFile = 11, + //! models.bin + ModelsFile = 12, + //! behaviours.bin + BehavioursFile = 13, + //! entityMaterials.bin + MaterialFile = 14, +}; + +struct Property +{ + s32 key [[format("format::look_up_str")]]; + string value; +} [[format("format::property")]]; + +struct AssetProperties +{ + s32 propertyCount[[hidden]]; + Property properties[propertyCount] [[inline]]; +} [[inline, format("format::properties")]]; + +struct AssetEntry +{ + type::Size32 size; + AssetType type; + string path; +} [[sealed, name(path), format("format::asset_entry")]]; + +struct AssetVisualizer +{ + u8 data[size] [[hidden]]; +} [[sealed, hex::visualize(visualizer_name, data)]]; + +using AssetTexture = AssetVisualizer; + +using Pck; +struct Asset +{ + u64 index = std::core::array_index(); + AssetProperties properties; + s32 size = parent.assetEntries[index].size; + AssetType type = parent.assetEntries[index].type; + match (parent.assetEntries[index].type) + { + (AssetType::SkinFile | AssetType::CapeFile | AssetType::TextureFile): AssetTexture data; + (AssetType::TexturePackInfoFile | AssetType::SkinDataFile): Pck data; + (_): u8 data[size]; // hex::visualize("hex_viewer", data); causes crash ._. + } +} [[name(std::string::to_string(parent.assetEntries[index].path)), format("format::asset_entry")]]; + +struct Assets +{ + s32 count [[hidden]]; + AssetEntry assetEntries[count] [[hidden]]; + Asset assetData[count] [[inline]]; +} [[format("format::assets"), transform_entities("transform::assets")]]; + +struct Pck +{ + s32 version; + if (version >> 24 & 0xff != 0) + { + if (!littleEndian) + std::warning("pck is likely to be little endian. Enable 'littleEndian' in the 'settings' tab"); + else + std::warning("pck is likely to be big endian. Disable 'littleEndian' in the 'settings' tab"); + break; + } + LookUpTable lut [[hidden]]; + if (lutContains(lut, "XMLVERSION")) + { + s32 xmlVersion; + } + Assets assets; +}; + +Pck pck @ 0x00; \ No newline at end of file diff --git a/tests/patterns/test_data/pck.hexpat.pck b/tests/patterns/test_data/pck.hexpat.pck new file mode 100644 index 0000000000000000000000000000000000000000..41f82a8d3afbb955ec262fcf91a506ab20a21a37 GIT binary patch literal 2772 zcmb7E2{_bS8$V-YnJh!H#}uVYF=G;y-4K}>;)akhn8H}HBoU+ADtqP9t`Y z*^)67LmEj$+ZdJTj(g+U@|{1`w>?<=_1tz#g~(cew8Ys9zakiwqLsvNN2QdUu0KYf~AQ~dV z#)2>q2?7BfVhD(V7z;5HL;%CDJBtC!)_#p!0fL}E5HgvN7XkH_J_KM4h+q?p+7G?^ zme^vg5RrPQ!h8%EV+7S1ARc}nSoHko8vpO{i?N6l1`vQL)WiA0GxZh~ z!1uKnz=VVcGvWHNm-h~&!9JLP51d0C!kMaybMy-ovG ze2wTfhAi`t+&W#8s~P^O`;yz$u+-nOeTTcpA6;G7(25s4^qj@9h=LssID|q;XK&Z_3@T`ft8X!S+0gw_a(?HasDY=KgnAj?__G- zb~jeThfN_X^W61wLmm0i1A>lmoZ<3-B=qrn#f<2IcbwQ#bozA5^oybMp@k*k4JOTC zA*PQngVyzPohmm;(RkIuXNO#fFoQ;lK^{V4@QvoiN;4<1V{#k(ENY7sSj zL20O~-Kf0AYIESS!vX_!?px*7nSCaAjG$wr6avo@Q5GY^26~yydPj!PRQg3a>WIK@ zR`Mh|{p=ZfpyI|SydJ?(4hJu9=_yv_aDp4m2=qgFCG`S zNm*5+HyV9+vLC$t(~e1fsga#4e{r3K*u0r?X@2jVp2T084O3du3SpQtPfXrKl6MJ{ z3@aXb-R7pOmnRI0sR_EbDv)}aewsM%H$gyTj;>Sf@IRV;uAE@~ppKsYyto(dQ9wGQ zqw7kT$WMD)fUERtH@?qb-znKDaaUd*pDFveB&lP$`z5cUXp+0k?;Nr7*Y;W0p(0aM z*5+7hS4OK{qh6Vb@+IUv>WEbQS+8Pe-H=4xdJEJ3{T zVfv9M#<=M83Q0x7FXvIKRw_puaJ)n8WLHBfoYA++yDJs`<8 z<-ZeGP-;?Eun`}c)$hLW;{KMWbCjo_HCL^Yotm7Cjf%RQl_f7ClgaPK#wbnxu9saM zFHyIbc{tA}(>k}5m6z)mb}<|-c{yNRZ&sY z@dqE1DoR+9%2_Qu@*;SXU~2aB%uML@g2FN^!M&u4ILf z{Sy;?j@#Pg<>j$N-er5YL_|gHe7)bx+1c6K$A_4q6rfM&Eb7+8JEUnwhrCmL*xY<{ zXMJrg(I}r_pS#1pC7}Dw Date: Sun, 17 Nov 2024 04:59:44 -0800 Subject: [PATCH 16/29] patterns: Added Minecraft LCE LOC file pattern (#311) * create LOC hexpat * loc to list --- README.md | 1 + patterns/loc.hexpat | 46 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 patterns/loc.hexpat diff --git a/README.md b/README.md index f1b61b32..f377f2f0 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | ISO | | [`patterns/iso.hexpat`](patterns/iso.hexpat) | ISO 9660 file system | | Java Class | `application/x-java-applet` | [`patterns/java_class.hexpat`](patterns/java_class.hexpat) | Java Class files | | JPEG | `image/jpeg` | [`patterns/jpeg.hexpat`](patterns/jpeg.hexpat) | JPEG Image Format | +| LOC | | [`patterns/loc.hexpat`](patterns/loc.hexpat) | Minecraft Legacy Console Edition Language file | | Lua 5.1 | | [`patterns/lua51.hexpat`](patterns/lua51.hexpat) | Lua 5.1 bytecode | | Lua 5.2 | | [`patterns/lua52.hexpat`](patterns/lua52.hexpat) | Lua 5.2 bytecode | | Lua 5.3 | | [`patterns/lua53.hexpat`](patterns/lua53.hexpat) | Lua 5.3 bytecode | diff --git a/patterns/loc.hexpat b/patterns/loc.hexpat new file mode 100644 index 00000000..3b5a8aef --- /dev/null +++ b/patterns/loc.hexpat @@ -0,0 +1,46 @@ +#pragma author DexrnZacAttack +#pragma description Minecraft LCE LOC file + +#pragma endian big +#pragma array_limit 739845729834 +#pragma pattern_limit 34893726894 + +import std.string; + +struct Keys { + bool useUniqueIDs; + u32 count; + u32 key[count]; +}; + +struct LangIds { + u16 len; + char id[len]; + u32 unk; +} [[name(id)]]; + +struct LocString { + u16 keySize; + char key[keySize]; +} [[name(std::string::to_string(key))]]; + +struct Language { + u32 read; + if (read > 0) + u8 unk; + u16 len; + char id[len]; + u32 count; + LocString strings[count]; +} [[name(id)]]; + +struct Loc { + u32 version; + u32 count; + if (version == 2) + Keys keys; + LangIds ids[count]; + Language language[count]; +}; + +Loc loc @ 0x00; From 79e25fdb732e73be98659fab61f17f4f37c672f3 Mon Sep 17 00:00:00 2001 From: Luca Corbatto Date: Sun, 17 Nov 2024 14:00:16 +0100 Subject: [PATCH 17/29] patterns/zip: Added parsing of general purpose bit flag (#314) * Add parsing of general purpose bit flag Add "GeneralPurposeBitFlags" bitfield definition. The names are based on the official ZIP format specification although I chose to name the fields closer to their purpose rather than their official name. This is intended to make reading the pattern and its output easier. Example: official name "Language encoding flag (EFS)" my name "filenameAndCommentAreUtf8" because this immediately explains what it does. I chose not to implement the specifics of the "compressionOptions" as their meanings are specific to the compression method used and it would make the pattern much more complicated for (in my opinion) little gain. I also chose to unify the names of the general purpose bit filed in the LocalFileHeader and the CentralDirectoryFileHeader. Previously, they had different names, suggesting they might have different meaning. According to the official docs though, these fields have the exact same meaning. Official docs: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT * Fix typo and remove redundant type declaration --- patterns/zip.hexpat | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/patterns/zip.hexpat b/patterns/zip.hexpat index c30c2e44..1800912f 100644 --- a/patterns/zip.hexpat +++ b/patterns/zip.hexpat @@ -156,10 +156,24 @@ enum CompressionMethod : u16 { AE_x = 99, // AE-x encryption marker (see APPENDIX E) }; +bitfield GeneralPurposeBitFlags { + encrypted : 1; + compressionOptions : 2; + crcAndSizesInCDAndDataDescriptor : 1; + enhancedDeflating : 1; + patchedData : 1; + strongEncryption : 1; + unused : 4; + filenameAndCommentAreUtf8 : 1; + reservedPKWARE_0 : 1; + centralDirectoryEncrypted : 1; + reservedPKWARE_1 : 2; +}; + struct LocalFileHeader { u32 headerSignature [[name("LCF PK\\3\\4")]]; u16 version [[ comment("The minimum supported ZIP specification version needed to extract the file") ]]; - u16 purposeBitflag [[ comment("General purpose bit flag") ]]; + GeneralPurposeBitFlags generalPurposeBitFlags [[ comment("General purpose bit flag") ]]; CompressionMethod compressionMethod [[ comment("Compression method") ]]; u16 lastModifyTime [[ comment("File last modification time") ]]; u16 lastModifyDate [[ comment("File last modification date") ]]; @@ -183,7 +197,7 @@ struct CentralDirectoryFileHeader { u32 headerSignature [[name("CDFH PK\\1\\2")]]; u16 versionMade [[comment("Version file made by")]]; u16 versionExtract [[comment("Minimum version needed to extract")]]; - u16 generalFlag [[comment("General purpose bit flag")]]; + GeneralPurposeBitFlags generalPurposeBitFlags [[comment("General purpose bit flag")]]; CompressionMethod compressionMethod; u16 fileLastModifyTime [[comment("File last modification time")]]; u16 fileLastModifyDate [[comment("File last modification date")]]; From 255116a58781a110720d1abf8a0d35d1e4c65b09 Mon Sep 17 00:00:00 2001 From: otakuxtom Date: Sun, 17 Nov 2024 21:00:43 +0800 Subject: [PATCH 18/29] patterns/pe: Fix error when ordinalTableRVA is zero (#315) ordinalTableRVA is not always has value. it might be zero. Whien this happened, it will failed. Signed-off-by: Chunhao Hung Co-authored-by: Chunhao Hung --- patterns/pe.hexpat | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/patterns/pe.hexpat b/patterns/pe.hexpat index d9510eab..192a701b 100644 --- a/patterns/pe.hexpat +++ b/patterns/pe.hexpat @@ -393,7 +393,9 @@ struct ExportsTable { ExportDirectoryTable directoryTable; ExportAddress exportAddressTable[directoryTable.addressesAmount] @ directoryTable.addressTableRVA - relativeVirtualDifference(); ExportNamePointer exportNamePointerTable[directoryTable.namePointersAmount] @ directoryTable.namePointerTableRVA - relativeVirtualDifference(); - u16 exportOrdinalTable[directoryTable.namePointersAmount] @ directoryTable.ordinalTableRVA - relativeVirtualDifference(); + if (directoryTable.ordinalTableRVA > relativeVirtualDifference()) { + u16 exportOrdinalTable[directoryTable.namePointersAmount] @ directoryTable.ordinalTableRVA - relativeVirtualDifference(); + } char imageName[] @ directoryTable.imageNameRVA - relativeVirtualDifference() [[format("formatNullTerminatedString")]]; $ = addressof(this)+coffHeader.optionalHeader.directories[0].size; }; From 55aca93a1876ecaf4b83460ba65779634fa29eb6 Mon Sep 17 00:00:00 2001 From: Maxim Savenko Date: Sun, 24 Nov 2024 13:38:31 +0300 Subject: [PATCH 19/29] patterns/mp4: Add 'stsz' box definition to MP4 pattern (#317) --- patterns/mp4.hexpat | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/patterns/mp4.hexpat b/patterns/mp4.hexpat index b687b9a8..cc208624 100644 --- a/patterns/mp4.hexpat +++ b/patterns/mp4.hexpat @@ -283,6 +283,14 @@ struct CompositionOffsetBox: FullBox { } }; +struct SampleSizeBox: FullBox { + u32 sample_size; + u32 sample_count; + if(this.sample_size==0) { + u32 entry_size[this.sample_count]; + } +}; + struct SubSampleBoxTable { u32 type = std::mem::read_unsigned($ + 4, 4, std::mem::Endian::Big); @@ -293,6 +301,7 @@ struct SubSampleBoxTable { ("stco"): ChunkOffsetBox box [[inline]]; ("stss"): SyncSampleBox box [[inline]]; ("ctts"): CompositionOffsetBox box [[inline]]; + ("stsz"): SampleSizeBox box [[inline]]; (_): UnknownBox box [[inline]]; } } [[name(std::format("SubSampleBoxTable({})", box.type))]]; From 7d0bbd1e24341afceec67a1b6302ef46e932ee39 Mon Sep 17 00:00:00 2001 From: Geky Date: Sun, 24 Nov 2024 11:38:54 +0100 Subject: [PATCH 20/29] patterns/GameBoy: Add support for cartridge size type $54 and missing licenses (#318) * add support for cartridge size type $54 Added support for cartridge size type $54, corresponding to 1.5 MiB (96 banks). * add missing license --- patterns/gb.hexpat | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/patterns/gb.hexpat b/patterns/gb.hexpat index 79d0d33c..0bce2312 100644 --- a/patterns/gb.hexpat +++ b/patterns/gb.hexpat @@ -33,6 +33,10 @@ namespace format { romSize = 1258291; romBanks = 80; } + if (cartSizeType == 0x54) { + romSize = 1572864; + romBanks = 96; + } return std::format("size: {}, banks: {}", type::impl::size_formatter(romSize), romBanks); }; @@ -131,6 +135,7 @@ namespace format { (0x46 | 0xCF): return "Angel"; (0x47): return "Spectrum Holoby"; (0x49): return "Irem"; + (0x4F): return "U.S. Gold"; (0x50): return "Absolute"; (0x51 | 0xB0): return "Acclaim"; (0x52): return "Activision"; @@ -217,6 +222,7 @@ namespace format { (0xE8): return "Asmik"; (0xE9): return "Natsume"; (0xEA): return "King Records"; + (0xEC): return "Epic/Sony Records"; (0xEE): return "IGS"; (0xF0): return "A Wave"; (0xF3): return "Extreme Entertainment"; @@ -287,7 +293,10 @@ namespace format { ("96"): return "Yonezawa/s’pal"; ("97"): return "Kaneko"; ("99"): return "Pack in soft"; + ("9H"): return "Bottom Up"; ("A4"): return "Konami (Yu-Gi-Oh!)"; + ("BL"): return "MTO"; + ("DK"): return "Kodansha"; } return "Unknown"; }; From 661e5b7081ba5fb600718980ee263d3e05781ea7 Mon Sep 17 00:00:00 2001 From: Dexrn ZacAttack <60078656+DexrnZacAttack@users.noreply.github.com> Date: Sun, 24 Nov 2024 02:39:22 -0800 Subject: [PATCH 21/29] patterns: Added RAD Game Tools BINKA hexpat (#319) * Binka readme * Binka hexpat --- README.md | 1 + patterns/binka.hexpat | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 patterns/binka.hexpat diff --git a/README.md b/README.md index f377f2f0..d4b67d4c 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | BLEND | | [`patterns/blend.hexpat`](patterns/blend.hexpat) | Blender Project file | | BMP | `image/bmp` | [`patterns/bmp.hexpat`](patterns/bmp.hexpat) | OS2/Windows Bitmap files | | BIN | | [`patterns/selinux.hexpat`](patterns/selinux.pat) | SE Linux modules | +| BINKA | | [`patterns/binka.hexpat`](patterns/binka.pat) | RAD Game Tools Bink Audio (BINKA) files | | BSON | `application/bson` | [`patterns/bson.hexpat`](patterns/bson.hexpat) | BSON (Binary JSON) format | | bplist | | [`patterns/bplist.hexpat`](patterns/bplist.hexpat) | Apple's binary property list format (bplist) | | BSP | | [`patterns/bsp_goldsrc.hexpat`](patterns/bsp_goldsrc.hexpat) | GoldSrc engine maps format (used in Half-Life 1) | diff --git a/patterns/binka.hexpat b/patterns/binka.hexpat new file mode 100644 index 00000000..fa11f389 --- /dev/null +++ b/patterns/binka.hexpat @@ -0,0 +1,24 @@ +#pragma author DexrnZacAttack +#pragma description RAD Game Tools BINKA (Bink Audio) +#pragma magic [31 46 43 42] @ 0x00 + +import std.string; +import std.math; +import type.magic; + +fn getDuration(u32 duration_ts, u32 sample_rate) { + return float(duration_ts) / float(sample_rate); +}; + +struct Binka { + type::Magic<"1FCB"> magic; + u8; // can't get this to change anything when using ffprobe + u8 channel_count [[name(std::format("Channel Count: {}", this))]]; + u16 sample_rate [[name(std::format("Sample Rate: {}", this))]]; + u32 duration_ts [[name(std::format("Duration: {}s", std::math::floor(getDuration(this, sample_rate))))]]; + u32; + u32 size [[name(std::format("File Size: {} bytes", this))]]; + u8 data[size - 20]; +}; + +Binka binka @ 0x00; From 221fa70a6767ca852d462b7970f1c8d17bd4df95 Mon Sep 17 00:00:00 2001 From: Dexrn ZacAttack <60078656+DexrnZacAttack@users.noreply.github.com> Date: Sun, 24 Nov 2024 02:39:43 -0800 Subject: [PATCH 22/29] patterns: Added Miles Sound System Compressed Archive (MSSCMP) (#320) * MSSCMP readme * Add files via upload --- README.md | 1 + patterns/msscmp.hexpat | 85 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 patterns/msscmp.hexpat diff --git a/README.md b/README.md index d4b67d4c..89ee9a1a 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | MiniDump | `application/x-dmp` | [`patterns/minidump.hexpat`](patterns/minidump.hexpat) | Windows MiniDump files | | mp4 | `video/mp4` | [`patterns/mp4.hexpat`](patterns/mp4.hexpat) | MPEG-4 Part 14 digital multimedia container format | | msgpack | `application/x-msgpack` | [`patterns/msgpack.hexpat`](patterns/msgpack.hexpat) | MessagePack binary serialization format | +| MSSCMP | | [`patterns/msscmp.hexpat`](patterns/msscmp.hexpat) | Miles Sound System Compressed Archive | | NACP | | [`patterns/nacp.hexpat`](patterns/nacp.hexpat) | Nintendo Switch NACP files | | NBT | | [`patterns/nbt.hexpat`](patterns/nbt.hexpat) | Minecraft NBT format | | NE | | [`patterns/ne.hexpat`](patterns/ne.hexpat) | NE header and Standard NE fields | diff --git a/patterns/msscmp.hexpat b/patterns/msscmp.hexpat new file mode 100644 index 00000000..81d354f0 --- /dev/null +++ b/patterns/msscmp.hexpat @@ -0,0 +1,85 @@ +#pragma author DexrnZacAttack +#pragma description MSSCMP (Miles Sound System Compressed Archive) +#pragma magic [ 42 41 4E 4B ] @ 0x00 +#pragma magic [ 4B 4E 41 42 ] @ 0x00 +#pragma array_limit 4294967295 +#pragma pattern_limit 4294967295 + +import type.magic; +import std.core; +#ifdef __IMHEX__ + import hex.core; +#endif + +struct FileDataEntry { + u32 nameOffset; + char name[] @ nameOffset; + u32 folderOffset; + char folder[] @ folderOffset; + le u32 fileDataOffset; + padding[8]; + u32 sampleRate; + u32 fileSize; + u8 data[fileSize] @ fileDataOffset; + #ifdef __IMHEX__ + hex::core::add_virtual_file(std::string::to_string(name) + ".binka", data); + #endif +}; + +struct Index2Entry { + u32 entryOffset; + u32 fStructureOffset; + FileDataEntry fileDataEntry @ entryOffset; +}; + +struct MsscmpEntry { + char name[12]; + Index2Entry entries[parent.header.index2EntryCount] @ parent.header.index1LastEntryOffset + 4; +}; + + +struct Header { + padding[4]; // unk + T fileDataOffset; + padding[8]; // unk + T index1Offset; + T index1LastEntryOffset; + if (sizeof(T) == 4) + padding[8]; + else + padding[16]; + + T unkOffset; + T index1EntryCount; + + if (sizeof(T) == 4) + padding[8]; + else + padding[4]; + + u32 index2EntryCount; +}; + +using OldGenHeader = Header; +using NewGenHeader = Header; + +struct MSSCMP { + char magic[4]; + if (magic == "BANK") + std::core::set_endian(std::mem::Endian::Big); + else if (magic == "KNAB") + std::core::set_endian(std::mem::Endian::Little); + else + std::error("Magic does not match BANK or KNAB"); + + u32 check1 @ 0x1c [[hidden]]; // use this to check if old gen or new gen + u32 check2 @ 0x20 [[hidden]]; + + if (check1 == check2) + OldGenHeader header; + else + NewGenHeader header; + MsscmpEntry entry; +}; + +MSSCMP msscmp @ 0x00; From c533017d0beb0b77335294683ac38ca0f5ac6b79 Mon Sep 17 00:00:00 2001 From: Mrmaxmeier <3913977+Mrmaxmeier@users.noreply.github.com> Date: Sun, 24 Nov 2024 11:41:26 +0100 Subject: [PATCH 23/29] git: Various style fixes everywhere, removing whitespaces (#321) * repo-wide: trim trailing spaces Note: This doesn't touch the .tbl files in encodings/ since they include meaningful trailing spaces (`20= `) * patterns: clean up duplicate semicolons * ELF: add header magic check * glTF: use type::Magic for magic value * glTF: check that the file size in the header matches * xgstexture: fix generics syntax for magic value * JPEG: define hex enum with 0x00 instead of 0X00 * CI: update deprecated actions --------- Co-authored-by: Nik --- .../PULL_REQUEST_TEMPLATE/pattern_template.md | 2 +- .github/workflows/dispatch.yml | 8 +- .github/workflows/tests.yml | 6 +- CONTRIBUTING.md | 2 +- constants/linux_errors.json | 2 +- includes/hex/provider.pat | 10 +- includes/std/bit.pat | 14 +- includes/std/file.pat | 10 +- includes/std/fxpt.pat | 10 +- includes/std/hash.pat | 8 +- includes/std/limits.pat | 38 ++-- includes/std/math.pat | 12 +- includes/std/mem.pat | 8 +- includes/std/ptr.pat | 2 +- includes/std/random.pat | 6 +- includes/std/string.pat | 24 +-- includes/type/byte.pat | 12 +- includes/type/color.pat | 10 +- includes/type/float16.pat | 16 +- includes/type/guid.pat | 2 +- includes/type/leb128.pat | 16 +- includes/type/time.pat | 4 +- patterns/3ds.hexpat | 194 ++++++++--------- patterns/7z.hexpat | 198 +++++++++--------- patterns/Crashlvl.hexpat | 2 +- patterns/afe2.hexpat | 4 +- patterns/ar.hexpat | 2 +- patterns/arm_cm_vtor.hexpat | 8 +- patterns/bencode.hexpat | 30 +-- patterns/bgcode.hexpat | 2 +- patterns/blend.hexpat | 4 +- patterns/bmp.hexpat | 4 +- patterns/bplist.hexpat | 44 ++-- patterns/bson.hexpat | 12 +- patterns/bsp_goldsrc.hexpat | 2 +- patterns/chm.hexpat | 32 +-- patterns/coff.hexpat | 12 +- patterns/cpio.hexpat | 4 +- patterns/dds.hexpat | 2 +- patterns/dex.hexpat | 4 +- patterns/dsstore.hexpat | 14 +- patterns/dted.hexpat | 6 +- patterns/elf.hexpat | 23 +- patterns/ext4.hexpat | 8 +- patterns/flac.hexpat | 24 +-- patterns/fs.hexpat | 24 +-- patterns/gb.hexpat | 40 ++-- patterns/gguf.hexpat | 6 +- patterns/gif.hexpat | 10 +- patterns/gltf.hexpat | 10 +- patterns/gzip.hexpat | 12 +- patterns/hinf_luas.hexpat | 16 +- patterns/hinf_module.hexpat | 4 +- patterns/ico.hexpat | 4 +- patterns/id3.hexpat | 12 +- patterns/intel_hex.hexpat | 22 +- patterns/ip.hexpat | 52 ++--- patterns/java_class.hexpat | 16 +- patterns/jpeg.hexpat | 8 +- patterns/lcesave.hexpat | 8 +- patterns/lnk.hexpat | 32 +-- patterns/lua51.hexpat | 4 +- patterns/lua52.hexpat | 4 +- patterns/lua53.hexpat | 4 +- patterns/lua54.hexpat | 10 +- patterns/lznt1.hexpat | 2 +- patterns/macho.hexpat | 18 +- patterns/max_v104.hexpat | 22 +- patterns/minidump.hexpat | 6 +- patterns/mp4.hexpat | 2 +- patterns/msgpack.hexpat | 2 +- patterns/nacp.hexpat | 2 +- patterns/nbt.hexpat | 4 +- patterns/ne.hexpat | 8 +- patterns/notepad-cache.hexpat | 2 +- patterns/ntag.hexpat | 26 +-- patterns/pck.hexpat | 40 ++-- patterns/pe.hexpat | 4 +- patterns/pfs0.hexpat | 6 +- patterns/pif.hexpat | 4 +- patterns/prodinfo.hexpat | 82 ++++---- patterns/protobuf.hexpat | 2 +- patterns/pyc.hexpat | 50 ++--- patterns/pyinstaller.hexpat | 16 +- patterns/quantized-mesh.hexpat | 16 +- patterns/ras.hexpat | 8 +- patterns/refs.hexpat | 10 +- patterns/rgbds.hexpat | 10 +- patterns/selinux.hexpat | 20 +- patterns/sit5.hexpat | 34 +-- patterns/stl.hexpat | 2 +- patterns/tar.hexpat | 4 +- patterns/tga.hexpat | 2 +- patterns/ttf.hexpat | 22 +- patterns/uefi.hexpat | 2 +- patterns/uefi_boot_entry.hexpat | 6 +- patterns/uf2.hexpat | 14 +- patterns/usb.hexpat | 12 +- patterns/vbmeta.hexpat | 4 +- patterns/vdf.hexpat | 4 +- patterns/vgm.hexpat | 8 +- patterns/vhdx.hexpat | 6 +- patterns/wad.hexpat | 2 +- patterns/wav.hexpat | 2 +- patterns/webp.hexpat | 6 +- patterns/wintec_tes.hexpat | 2 +- patterns/xbeh.hexpat | 22 +- patterns/xci.hexpat | 8 +- patterns/xgspak.hexpat | 6 +- patterns/xgstexture.hexpat | 28 +-- patterns/xilinx_bit.hexpat | 6 +- patterns/zip.hexpat | 32 +-- patterns/zlib.hexpat | 10 +- tests/includes/source/main.cpp | 2 +- tests/patterns/source/main.cpp | 4 +- yara/advanced_analysis/language.yar | 2 +- 116 files changed, 885 insertions(+), 884 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE/pattern_template.md b/.github/PULL_REQUEST_TEMPLATE/pattern_template.md index 49551fab..560ad846 100644 --- a/.github/PULL_REQUEST_TEMPLATE/pattern_template.md +++ b/.github/PULL_REQUEST_TEMPLATE/pattern_template.md @@ -9,4 +9,4 @@ - [ ] The pattern was associated with all relevant MIME types (using `#pragma MIME mime-type` in the source code) - Make sure to never use `application/octet-stream` here as that means "Unidentifiable binary data" - [ ] A test file for this pattern has been added to [/tests/patterns/test_data](/tests/patterns/test_data) - - Try to keep this file below ~ 1 MB + - Try to keep this file below ~ 1 MB diff --git a/.github/workflows/dispatch.yml b/.github/workflows/dispatch.yml index 024ef38f..bffdb172 100644 --- a/.github/workflows/dispatch.yml +++ b/.github/workflows/dispatch.yml @@ -8,7 +8,7 @@ on: repository_dispatch: types: [run_tests] workflow_dispatch: - inputs: + inputs: generate_docs: description: "Regenerate docs" required: false @@ -27,13 +27,13 @@ jobs: steps: - name: 🧰 Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: submodules: recursive - name: 📄 Check changed include files id: changed-includes - uses: tj-actions/changed-files@v35 + uses: tj-actions/changed-files@v45 with: files: includes/**/*.pat @@ -45,7 +45,7 @@ jobs: repo: Documentation owner: WerWolv event_type: update_pl_docs - + - name: ✉️ Update PatternLanguage Website if: ${{ env.DISPATCH_TOKEN != '' }} uses: mvasigh/dispatch-action@main diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 938a713f..a21995bb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,7 +19,7 @@ jobs: steps: - name: 🧰 Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: submodules: recursive @@ -39,7 +39,7 @@ jobs: python3-pip \ libmagic-dev \ lcov - + sudo pip install jsonschema - name: 📜 Setup ccache @@ -74,7 +74,7 @@ jobs: cd constants for file in ./[!_schema.json]*; do jsonschema -i $file _schema.json; done cd .. - + cd tips for file in ./[!_schema.json]*; do jsonschema -i $file _schema.json; done cd .. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e8606a68..6514d811 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,5 +8,5 @@ Thanks a lot for any additions or improvements :) ## Adding new Patterns When adding new patterns, if possible, please also add a test file named `.hexpat.` to the `/tests/patterns/test_data` directory. This allows our Unit Tests to be run against your code so we can make sure it stays up-to-date and doesn't break when changes are made to the PatternLanguage. -Please try to keep these files as small as possible (~100kiB at most) so cloning stays fast. +Please try to keep these files as small as possible (~100kiB at most) so cloning stays fast. Please also make sure to not submit any test files that are under copyright such as game files, ROMs or files extracted from other programs. We don't want a DMCA takedown on this repo. diff --git a/constants/linux_errors.json b/constants/linux_errors.json index ede8360c..5ad59be3 100644 --- a/constants/linux_errors.json +++ b/constants/linux_errors.json @@ -775,6 +775,6 @@ "name": "ENOTRECOVERABLE", "desc": "State not recoverable" } - + ] } \ No newline at end of file diff --git a/includes/hex/provider.pat b/includes/hex/provider.pat index ab046d5e..3eaf5c6d 100644 --- a/includes/hex/provider.pat +++ b/includes/hex/provider.pat @@ -7,11 +7,11 @@ import hex.impl.imhex_check; */ namespace auto hex::prv { - - + + /** Queries information from the currently loaded provider. The kind of information that's available depends on the provider that's loaded - + > **Available information** > - File Provider > - `file_path() -> str` @@ -32,12 +32,12 @@ namespace auto hex::prv { > - `region_size(regionName) -> u64` > - `process_id() -> u32` > - `process_name() -> str` - + @param category Information category @param argument Extra argument to pass along */ fn get_information(str category, str argument = "") { return builtin::hex::prv::get_information(category, argument); }; - + } diff --git a/includes/std/bit.pat b/includes/std/bit.pat index 934e6fcf..ab3e8875 100644 --- a/includes/std/bit.pat +++ b/includes/std/bit.pat @@ -21,7 +21,7 @@ namespace auto std::bit { x = (x & a) + ((x >> 1) & a); x = (x & b) + ((x >> 2) & b); x = (x & c) + ((x >> 4) & c); - + return x % 0xFF; }; @@ -33,19 +33,19 @@ namespace auto std::bit { fn has_single_bit(u128 x) { return x != 0 && (x & (x - 1)) == 0; }; - + /** Rounds the given number up to the next bigger power of two @param x The number @return Next bigger power of two that can fit `x` */ - fn bit_ceil(u128 x) { + fn bit_ceil(u128 x) { if (x == 0) return 0; - + u8 i; while ((1 << i) < x) i = i + 1; - + return 1 << i; }; @@ -56,11 +56,11 @@ namespace auto std::bit { */ fn bit_floor(u128 x) { if (x == 0) return 0; - + u8 i; while ((x >> i) > 0) i = i + 1; - + return 1 << (i - 1); }; diff --git a/includes/std/file.pat b/includes/std/file.pat index 48b78cf0..bc7d48a9 100644 --- a/includes/std/file.pat +++ b/includes/std/file.pat @@ -1,7 +1,7 @@ #pragma once /*! - The File library allows reading and writing from/to external files using + The File library allows reading and writing from/to external files using a C-like File IO API. **These functions are considered dangerous and require the user to manually permit them** @@ -13,7 +13,7 @@ namespace auto std::file { A handle representing a file that has been opened */ using Handle = s32; - + /** The mode to open a file in. Read opens the file in read-only mode @@ -45,7 +45,7 @@ namespace auto std::file { builtin::std::file::close(handle); }; - + /** Reads the content of a file into a string @param handle The file handle to read from @@ -85,14 +85,14 @@ namespace auto std::file { }; /** - Resizes a file + Resizes a file @param handle The handle of the file to resize */ fn resize(Handle handle, u64 size) { builtin::std::file::resize(handle, size); }; - /** + /** Flushes changes made to a file to disk @param handle The handle of the file to flush */ diff --git a/includes/std/fxpt.pat b/includes/std/fxpt.pat index ae02b60d..3c04a5c9 100644 --- a/includes/std/fxpt.pat +++ b/includes/std/fxpt.pat @@ -20,7 +20,7 @@ namespace auto std::fxpt { fn to_float(fixed fxt, u32 precision) { return double(fxt) / double((1 << precision)); }; - + /** Converts a floating point value into a fixed point value @param flt The floating point value to convert @@ -41,7 +41,7 @@ namespace auto std::fxpt { fn change_precision(fixed value, u32 start_precision, u32 end_precision) { return std::fxpt::to_fixed(std::fxpt::to_float(value, start_precision), end_precision); }; - + /** Adds two fixed point numbers with a given precision together @param a First fixed point number @@ -52,7 +52,7 @@ namespace auto std::fxpt { fn add(fixed a, fixed b, u32 precision) { return a + b; }; - + /** Subtracts two fixed point numbers with a given precision together @param a First fixed point number @@ -63,7 +63,7 @@ namespace auto std::fxpt { fn subtract(fixed a, fixed b, u32 precision) { return a - b; }; - + /** Multiplies two fixed point numbers with a given precision together @param a First fixed point number @@ -74,7 +74,7 @@ namespace auto std::fxpt { fn multiply(fixed a, fixed b, u32 precision) { return (a * b) / (1 << precision); }; - + /** Divides two fixed point numbers with a given precision together @param a First fixed point number diff --git a/includes/std/hash.pat b/includes/std/hash.pat index ca22da49..6600d143 100644 --- a/includes/std/hash.pat +++ b/includes/std/hash.pat @@ -14,7 +14,7 @@ namespace auto std::hash { @param xorout The CRC8 XOR-Out value @param reflect_in Whether or not the input bytes should be reflected @param reflect_out Whether or not the output should be reflected - @return Calculated CRC8 hash + @return Calculated CRC8 hash */ fn crc8(ref auto pattern, u8 init, u8 poly, u8 xorout, bool reflect_in, bool reflect_out) { return builtin::std::hash::crc8(pattern, init, poly, xorout, reflect_in, reflect_out); @@ -28,7 +28,7 @@ namespace auto std::hash { @param xorout The CRC16 XOR-Out value @param reflect_in Whether or not the input bytes should be reflected @param reflect_out Whether or not the output should be reflected - @return Calculated CRC16 hash + @return Calculated CRC16 hash */ fn crc16(ref auto pattern, u16 init, u16 poly, u16 xorout, bool reflect_in, bool reflect_out) { return builtin::std::hash::crc16(pattern, init, poly, xorout, reflect_in, reflect_out); @@ -42,7 +42,7 @@ namespace auto std::hash { @param xorout The CRC32 XOR-Out value @param reflect_in Whether or not the input bytes should be reflected @param reflect_out Whether or not the output should be reflected - @return Calculated CRC32 hash + @return Calculated CRC32 hash */ fn crc32(ref auto pattern, u32 init, u32 poly, u32 xorout, bool reflect_in, bool reflect_out) { return builtin::std::hash::crc32(pattern, init, poly, xorout, reflect_in, reflect_out); @@ -56,7 +56,7 @@ namespace auto std::hash { @param xorout The CRC64 XOR-Out value @param reflect_in Whether or not the input bytes should be reflected @param reflect_out Whether or not the output should be reflected - @return Calculated CRC64 hash + @return Calculated CRC64 hash */ fn crc64(ref auto pattern, u64 init, u64 poly, u64 xorout, bool reflect_in, bool reflect_out) { return builtin::std::hash::crc64(pattern, init, poly, xorout, reflect_in, reflect_out); diff --git a/includes/std/limits.pat b/includes/std/limits.pat index 5b8751b0..0e2d99d2 100644 --- a/includes/std/limits.pat +++ b/includes/std/limits.pat @@ -5,7 +5,7 @@ */ namespace auto std::limits { - + /** Returns the minimum value that can be stored in a `u8`. @return Minimum value @@ -13,7 +13,7 @@ namespace auto std::limits { fn u8_min() { return u8(0); }; - + /** Returns the maximum value that can be stored in a `u8`. @return Maximum value @@ -29,7 +29,7 @@ namespace auto std::limits { fn s8_min() { return -s8((std::limits::u8_max() / 2)) - 1; }; - + /** Returns the maximum value that can be stored in a `s8`. @return Maximum value @@ -45,7 +45,7 @@ namespace auto std::limits { fn u16_min() { return u16(0); }; - + /** Returns the maximum value that can be stored in a `u16`. @return Maximum value @@ -53,7 +53,7 @@ namespace auto std::limits { fn u16_max() { return u16(-1); }; - + /** Returns the minimum value that can be stored in a `s16`. @return Minimum value @@ -61,7 +61,7 @@ namespace auto std::limits { fn s16_min() { return -s16((std::limits::u16_max() / 2)) - 1; }; - + /** Returns the maximum value that can be stored in a `s16`. @return Maximum value @@ -69,7 +69,7 @@ namespace auto std::limits { fn s16_max() { return s16((std::limits::u16_max() / 2)); }; - + /** Returns the minimum value that can be stored in a `u32`. @return Minimum value @@ -77,7 +77,7 @@ namespace auto std::limits { fn u32_min() { return u32(0); }; - + /** Returns the maximum value that can be stored in a `u32`. @return Maximum value @@ -85,7 +85,7 @@ namespace auto std::limits { fn u32_max() { return u32(-1); }; - + /** Returns the minimum value that can be stored in a `s32`. @return Minimum value @@ -93,7 +93,7 @@ namespace auto std::limits { fn s32_min() { return -s32((std::limits::u32_max() / 2)) - 1; }; - + /** Returns the maximum value that can be stored in a `s32`. @return Maximum value @@ -101,7 +101,7 @@ namespace auto std::limits { fn s32_max() { return s32((std::limits::u32_max() / 2)); }; - + /** Returns the minimum value that can be stored in a `u64`. @return Minimum value @@ -109,7 +109,7 @@ namespace auto std::limits { fn u64_min() { return u64(0); }; - + /** Returns the maximum value that can be stored in a `u64`. @return Maximum value @@ -117,7 +117,7 @@ namespace auto std::limits { fn u64_max() { return u64(-1); }; - + /** Returns the minimum value that can be stored in a `s64`. @return Minimum value @@ -125,7 +125,7 @@ namespace auto std::limits { fn s64_min() { return -s64((std::limits::u64_max() / 2)) - 1; }; - + /** Returns the maximum value that can be stored in a `s64`. @return Maximum value @@ -133,7 +133,7 @@ namespace auto std::limits { fn s64_max() { return s64((std::limits::u64_max() / 2)); }; - + /** Returns the minimum value that can be stored in a `u128`. @return Minimum value @@ -141,7 +141,7 @@ namespace auto std::limits { fn u128_min() { return u128(0); }; - + /** Returns the maximum value that can be stored in a `u128`. @return Maximum value @@ -149,7 +149,7 @@ namespace auto std::limits { fn u128_max() { return u128(-1); }; - + /** Returns the minimum value that can be stored in a `s128`. @return Minimum value @@ -157,7 +157,7 @@ namespace auto std::limits { fn s128_min() { return -s128((std::limits::u128_max() / 2)) - 1; }; - + /** Returns the maximum value that can be stored in a `s128`. @return Maximum value @@ -165,5 +165,5 @@ namespace auto std::limits { fn s128_max() { return s128((std::limits::u128_max() / 2)); }; - + } diff --git a/includes/std/math.pat b/includes/std/math.pat index 5c1e8b08..5c608a06 100644 --- a/includes/std/math.pat +++ b/includes/std/math.pat @@ -20,7 +20,7 @@ namespace auto std::math { else return b; }; - + /** Compares the values `a` and `b` with each other and returns the bigger of the two @param a First value @@ -56,9 +56,9 @@ namespace auto std::math { @return `x` if `x` is positive, `-x` otherwise */ fn abs(auto x) { - if (x < 0) + if (x < 0) return -x; - else + else return x; }; @@ -96,13 +96,13 @@ namespace auto std::math { */ fn factorial(u128 x) { u128 result; - + result = x; while (x > 1) { x = x - 1; result = result * x; } - + return result; }; @@ -336,5 +336,5 @@ namespace auto std::math { fn accumulate(u128 start, u128 end, u128 valueSize, std::mem::Section section = 0, AccumulateOperation operation = AccumulateOperation::Add, std::mem::Endian endian = std::mem::Endian::Native) { return builtin::std::math::accumulate(start, end, valueSize, section, u128(operation), u128(endian)); }; - + } diff --git a/includes/std/mem.pat b/includes/std/mem.pat index b7359b77..2c223033 100644 --- a/includes/std/mem.pat +++ b/includes/std/mem.pat @@ -66,7 +66,7 @@ namespace auto std::mem { return remainder != 0 ? value + (alignment - remainder) : value; }; - + /** Gets the base address of the data @@ -261,7 +261,7 @@ namespace auto std::mem { From from_value; To to_value; }; - + /** Aligns the cursor to the given alignment @@ -280,11 +280,11 @@ namespace auto std::mem { } [[sealed, format("std::mem::impl::format_bytes")]]; namespace impl { - + fn format_bytes(auto bytes) { return ""; }; - + } } diff --git a/includes/std/ptr.pat b/includes/std/ptr.pat index 963ce4bd..22d4b1d4 100644 --- a/includes/std/ptr.pat +++ b/includes/std/ptr.pat @@ -57,7 +57,7 @@ namespace auto std::ptr { // `pointerValue` is `no_unique_address` because we don't want to advance // the current memory location after reading the value of the pointer itself; // we want to examine the value at this address to determine what should be - // displayed. It's also `hidden` so the editor only displays either thee + // displayed. It's also `hidden` so the editor only displays either thee // padding or the populated pointer/pointee field. PointerTy pointerValue [[no_unique_address, hidden]]; if (pointerValue == 0x0) { diff --git a/includes/std/random.pat b/includes/std/random.pat index 5dee3719..726e2256 100644 --- a/includes/std/random.pat +++ b/includes/std/random.pat @@ -7,7 +7,7 @@ import std.limits; */ namespace auto std::random { - + /** Represents the type of distribution to use to generate a random number */ @@ -68,7 +68,7 @@ namespace auto std::random { return builtin::std::random::generate(u32(distribution), param1, param2); }; - + /** Generates a uniformly distributed random number between `min` and `max` @param [min] Minimum number. Defaults to 0 @@ -77,5 +77,5 @@ namespace auto std::random { fn generate(u64 min = std::limits::u64_min(), u64 max = std::limits::u64_max()) { return std::random::generate_using(Distribution::Uniform, min, max); }; - + } diff --git a/includes/std/string.pat b/includes/std/string.pat index 3b3cb6a4..a9b63bed 100644 --- a/includes/std/string.pat +++ b/includes/std/string.pat @@ -165,16 +165,16 @@ namespace auto std::string { @param string The string to reverse. @return The reversed string. */ - fn reverse(str string) { + fn reverse(str string) { str result; - + s32 i; i = std::string::length(string); while (i > 0) { i = i - 1; result = result + std::string::at(string, i); } - + return result; }; @@ -185,23 +185,23 @@ namespace auto std::string { */ fn to_upper(str string) { str result; - + u32 i; char c; while (i < std::string::length(string)) { c = std::string::at(string, i); - + if (c >= 'a' && c <= 'z') result = result + char(c - 0x20); else result = result + c; - + i = i + 1; } - + return result; }; - + /** Converts a string to lower case. @param string The string to convert. @@ -209,20 +209,20 @@ namespace auto std::string { */ fn to_lower(str string) { str result; - + u32 i; char c; while (i < std::string::length(string)) { c = std::string::at(string, i); - + if (c >= 'A' && c <= 'Z') result = result + char(c + 0x20); else result = result + c; - + i = i + 1; } - + return result; }; diff --git a/includes/type/byte.pat b/includes/type/byte.pat index 895c8753..9cb21630 100644 --- a/includes/type/byte.pat +++ b/includes/type/byte.pat @@ -8,7 +8,7 @@ import std.core; */ namespace auto type { - + /** Type visualizing the value of each individual bit */ @@ -22,7 +22,7 @@ namespace auto type { bit6 : 1; bit7 : 1; } [[format("type::impl::format_bits"), bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 8)]]; - + /** Type visualizing the value of the two nibbles */ @@ -30,7 +30,7 @@ namespace auto type { low : 4; high : 4; } [[format("type::impl::format_nibbles")]]; - + /** Type representing a single Byte. Decodes the byte as it's hexadecimal value, individual bits and nibbles */ @@ -50,9 +50,9 @@ namespace auto type { byte.bits.bit0, byte.bits.bit7); }; - + fn format_bits(Bits bits) { - return std::format("0b{}{}{}{}{}{}{}{}", + return std::format("0b{}{}{}{}{}{}{}{}", bits.bit7, bits.bit6, bits.bit5, @@ -62,7 +62,7 @@ namespace auto type { bits.bit1, bits.bit0); }; - + fn format_nibbles(Nibbles nibbles) { return std::format("{{ {0:0X}, {1:0X} }}", nibbles.high, nibbles.low); }; diff --git a/includes/type/color.pat b/includes/type/color.pat index 35b2b17d..b2cebf74 100644 --- a/includes/type/color.pat +++ b/includes/type/color.pat @@ -14,7 +14,7 @@ namespace auto type { @tparam R Number of bits used for the red component @tparam G Number of bits used for the green component @tparam B Number of bits used for the blue component - @tparam A Number of bits used for the alpha component + @tparam A Number of bits used for the alpha component */ bitfield RGBA { r : R; @@ -22,7 +22,7 @@ namespace auto type { b : B; if (A > 0) a : A; } [[sealed, format("type::impl::format_color"), color(std::format("{0:02X}{1:02X}{2:02X}FF", r, g, b))]]; - + /** Type representing a generic RGB color with a variable number of bits for each color @tparam R Number of bits used for the red component @@ -30,7 +30,7 @@ namespace auto type { @tparam B Number of bits used for the blue component */ using RGB = RGBA; - + /** Type representing a RGBA color with 8 bits for the red component, 8 bits for green, 8 bits for blue and 8 bits for alpha @@ -56,7 +56,7 @@ namespace auto type { Type representing a RGBA color with 5 bits for the red component, 5 bits for green, 5 bits for blue and 1 bits for alpha */ using RGBA5551 = RGBA<5,5,5,1>; - + namespace impl { @@ -76,5 +76,5 @@ namespace auto type { }; } - + } \ No newline at end of file diff --git a/includes/type/float16.pat b/includes/type/float16.pat index 1d5008a2..d0fdc0e9 100644 --- a/includes/type/float16.pat +++ b/includes/type/float16.pat @@ -9,14 +9,14 @@ import std.mem; */ namespace auto type { - + /** Type representing a 16 bit half precision floating point number */ using float16 = u16 [[format("type::impl::format_float16")]]; namespace impl { - + union U32ToFloatConverter { u32 intValue; float floatValue; @@ -26,20 +26,20 @@ namespace auto type { u32 sign = value >> 15; u32 exponent = (value >> 10) & 0x1F; u32 mantissa = value & 0x3FF; - + u32 result = 0x00; - + if (exponent == 0) { if (mantissa == 0) { result = sign << 31; } else { exponent = 0x7F - 14; - + while ((mantissa & (1 << 10)) == 0) { exponent -= 1; mantissa <<= 1; } - + mantissa &= 0x3FF; result = (sign << 31) | (exponent << 23) | (mantissa << 13); } @@ -48,10 +48,10 @@ namespace auto type { } else { result = (sign << 31) | ((exponent + (0x7F - 15)) << 23) | (mantissa << 13); } - + std::mem::Reinterpreter converter; converter.from = result; - + return std::format("{}", converter.to); }; diff --git a/includes/type/guid.pat b/includes/type/guid.pat index b14de1a0..69205ddc 100644 --- a/includes/type/guid.pat +++ b/includes/type/guid.pat @@ -29,7 +29,7 @@ namespace auto type { fn format_guid(GUID guid) { bool valid = ((le u16(guid.time_high_and_version) >> 12) <= 5) && (((guid.clock_seq_and_reserved >> 4) >= 8) || ((guid.clock_seq_and_reserved >> 4) == 0)); - + return std::format("{}{{{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}}}", valid ? "" : "Invalid ", le u32(guid.time_low), diff --git a/includes/type/leb128.pat b/includes/type/leb128.pat index 2e95f855..04d857b6 100644 --- a/includes/type/leb128.pat +++ b/includes/type/leb128.pat @@ -8,7 +8,7 @@ import std.mem; */ namespace auto type { - + /** Base LEB128 type. Use `uLEB128` and `sLEB128` instead. */ @@ -30,9 +30,9 @@ namespace auto type { Legacy alias for uLEB128 */ using LEB128 = uLEB128; - + namespace impl { - + fn transform_uleb128_array(ref auto array) { u128 res = array[0] & 0x7f; for(u8 i = 1, array[i-1] & 0x80 != 0, i+=1) { @@ -41,19 +41,19 @@ namespace auto type { return res; }; - fn transform_sleb128_array(ref auto array) { + fn transform_sleb128_array(ref auto array) { s128 res = type::impl::transform_uleb128_array(array); if (res & 0x40 != 0) { res |= ~0 << (sizeof(array) / sizeof(u8)) * 7; } return res; }; - + fn format_uleb128(ref auto leb128) { u128 res = type::impl::transform_uleb128_array(leb128.array); return std::format("{} ({:#x})", res, res); }; - + fn transform_uleb128(ref auto leb128) { return type::impl::transform_uleb128_array(leb128.array); }; @@ -62,11 +62,11 @@ namespace auto type { s128 res = type::impl::transform_sleb128_array(leb128.array); return std::format("{} ({:#x})", res, res); }; - + fn transform_sleb128(ref auto leb128) { return type::impl::transform_sleb128_array(leb128.array); }; } - + } diff --git a/includes/type/time.pat b/includes/type/time.pat index 031cad29..71b6e954 100644 --- a/includes/type/time.pat +++ b/includes/type/time.pat @@ -13,9 +13,9 @@ namespace auto type { A 32 bit Unix time value */ using time32_t = u32 [[format("type::impl::format_time_t")]]; - + /** - Alias name for `time32_t` + Alias name for `time32_t` */ using time_t = time32_t; diff --git a/patterns/3ds.hexpat b/patterns/3ds.hexpat index acca6387..79d9aceb 100644 --- a/patterns/3ds.hexpat +++ b/patterns/3ds.hexpat @@ -7,7 +7,7 @@ import type.base; #pragma MIME image/x-3ds // Heavily based off of ZiZi's 010 Editor 3DS Template -enum ChunkIdentifier : u16 { +enum ChunkIdentifier : u16 { NULL_CHUNK = 0x0000, ChunkType = 0x0995, ChunkUnique = 0x0996, @@ -40,10 +40,10 @@ enum ChunkIdentifier : u16 { COLOR_24 = 0x0011, // r,g,b: Byte LIN_COLOR_24 = 0x0012, // r,g,b: Byte (gamma corrected) LIN_COLOR_F = 0x0013, // r,g,b: Single precision float (gamma corrected) - + INT_PERCENTAGE = 0x0030, // u16 integer FLOAT_PERCENTAGE = 0x0031, // Single precision float - + M3DMAGIC = 0x4D4D, SMAGIC = 0x2D2D, LMAGIC = 0x2D3D, @@ -72,7 +72,7 @@ enum ChunkIdentifier : u16 { RAY_BIAS = 0x1460, USE_RAYTRACE = 0x1470, O_CONSTS = 0x1500, - + AMBIENT_LIGHT = 0x2100, FOG = 0x2200, @@ -82,7 +82,7 @@ enum ChunkIdentifier : u16 { USE_DISTANCE_CUE = 0x2301, WORLD_LAYERED_FOG = 0x2302, WORLD_USE_LAYERED_FOG = 0x2303, - + DEFAULT_VIEW = 0x3000, VIEW_TOP = 0x3010, VIEW_BOTTOM = 0x3020, @@ -93,7 +93,7 @@ enum ChunkIdentifier : u16 { VIEW_USER = 0x3070, VIEW_CAMERA = 0x3080, VIEW_WINDOW = 0x3090, - + NAMED_OBJECT = 0x4000, OBJ_HIDDEN = 0x4010, OBJ_VIS_LOFTER = 0x4011, @@ -103,7 +103,7 @@ enum ChunkIdentifier : u16 { OBJ_PROCEDURAL = 0x4015, OBJ_FROZEN = 0x4016, OBJ_DONT_RCVSHADOW = 0x4017, - + N_TRI_OBJECT = 0x4100, POINT_ARRAY = 0x4110, POINT_FLAG_ARRAY = 0x4111, @@ -134,7 +134,7 @@ enum ChunkIdentifier : u16 { DL_SPOT_OVERSHOOT = 0x4652, DL_SPOT_PROJECTOR = 0x4653, DL_EXCLUDE = 0x4654, - DL_RANGE = 0x4655, /* Not used in R3 */ + DL_RANGE = 0x4655, /* Not used in R3 */ DL_SPOT_ROLL = 0x4656, DL_SPOT_ASPECT = 0x4657, DL_RAY_BIAS = 0x4658, @@ -143,22 +143,22 @@ enum ChunkIdentifier : u16 { DL_INNER_RANGE = 0x4659, DL_OUTER_RANGE = 0x465A, DL_MULTIPLIER = 0x465B, - + N_AMBIENT_LIGHT = 0x4680, - + N_CAMERA = 0x4700, CAM_SEE_CONE = 0x4710, CAM_RANGES = 0x4720, - + HIERARCHY = 0x4F00, PARENT_OBJECT = 0x4F10, PIVOT_OBJECT = 0x4F20, PIVOT_LIMITS = 0x4F30, PIVOT_ORDER = 0x4F40, XLATE_RANGE = 0x4F50, - + POLY_2D = 0x5000, - + /* Flags in shaper file that tell whether polys make up an ok shape */ SHAPE_OK = 0x5010, SHAPE_NOT_OK = 0x5011, @@ -177,8 +177,8 @@ enum ChunkIdentifier : u16 { YZ_CURVE = 0x6080, INTERPCT = 0x6090, DEFORM_LIMIT = 0x60A0, - - /* Flags for Modeler options */ + + /* Flags for Modeler options */ USE_CONTOUR = 0x6100, USE_TWEEN = 0x6110, USE_SCALE = 0x6120, @@ -187,7 +187,7 @@ enum ChunkIdentifier : u16 { USE_FIT = 0x6150, USE_BEVEL = 0x6160, - /* Viewport description chunks */ + /* Viewport description chunks */ VIEWPORT_LAYOUT_OLD = 0x7000, VIEWPORT_DATA_OLD = 0x7010, VIEWPORT_LAYOUT = 0x7001, @@ -196,7 +196,7 @@ enum ChunkIdentifier : u16 { VIEWPORT_SIZE = 0x7020, NETWORK_VIEW = 0x7030, - /* External Application Data */ + /* External Application Data */ XDATA_SECTION = 0x8000, XDATA_ENTRY = 0x8001, XDATA_APPNAME = 0x8002, @@ -215,8 +215,8 @@ enum ChunkIdentifier : u16 { XDATA_RFU1 = 0x800F, PARENT_NAME = 0x80F0, - - /* Material Chunk IDs */ + + /* Material Chunk IDs */ MAT_ENTRY = 0xAFFF, MAT_NAME = 0xA000, MAT_AMBIENT = 0xA010, @@ -228,7 +228,7 @@ enum ChunkIdentifier : u16 { MAT_TRANSPARENCY = 0xA050, MAT_XPFALL = 0xA052, MAT_REFBLUR = 0xA053, - + MAT_SELF_ILLUM = 0xA080, MAT_TWO_SIDE = 0xA081, MAT_DECAL = 0xA082, @@ -241,13 +241,13 @@ enum ChunkIdentifier : u16 { MAT_XPFALLIN = 0xA08A, MAT_PHONGSOFT = 0xA08C, MAT_WIREABS = 0xA08E, - + MAT_SHADING = 0xA100, - + MAT_TEXMAP = 0xA200, MAT_MAPNAME = 0xA300, MAT_ACUBIC = 0xA310, - + MAT_MAP_TILINGOLD = 0xA350, MAT_MAP_TILING = 0xA351, MAT_MAP_TEXBLUR_OLD = 0xA352, @@ -262,14 +262,14 @@ enum ChunkIdentifier : u16 { MAT_MAP_RCOL = 0xA364, MAT_MAP_GCOL = 0xA366, MAT_MAP_BCOL = 0xA368, - + MAT_SPECMAP = 0xA204, MAT_OPACMAP = 0xA210, MAT_REFLMAP = 0xA220, MAT_BUMPMAP = 0xA230, MAT_USE_REFBLUR = 0xA250, MAT_BUMP_PERCENT = 0xA252, - + MAT_SXP_TEXT_DATA = 0xA320, MAT_SXP_TEXT2_DATA = 0xA321, MAT_SXP_OPAC_DATA = 0xA322, @@ -285,7 +285,7 @@ enum ChunkIdentifier : u16 { MAT_SXP_SHIN_MASKDATA = 0xA334, MAT_SXP_SELFI_MASKDATA = 0xA336, MAT_SXP_REFL_MASKDATA = 0xA338, - + MAT_TEX2MAP = 0xA33A, MAT_SHINMAP = 0xA33C, MAT_SELFIMAP = 0xA33D, @@ -297,8 +297,8 @@ enum ChunkIdentifier : u16 { MAT_SPECMASK = 0xA348, MAT_SELFIMASK = 0xA34A, MAT_REFLMASK = 0xA34C, - - /* Keyframe Chunk IDs */ + + /* Keyframe Chunk IDs */ KFDATA = 0xB000, KFHDR = 0xB00A, AMBIENT_NODE_TAG = 0xB001, @@ -308,13 +308,13 @@ enum ChunkIdentifier : u16 { LIGHT_NODE_TAG = 0xB005, L_TARGET_NODE_TAG = 0xB006, SPOTLIGHT_NODE_TAG = 0xB007, - + KFSEG = 0xB008, KFCURTIME = 0xB009, - + NODE_HDR = 0xB010, INSTANCE_NAME = 0xB011, - PRESCALE = 0xB012, + PRESCALE = 0xB012, PIVOT = 0xB013, BOUNDBOX = 0xB014, MORPH_SMOOTH = 0xB015, @@ -329,10 +329,10 @@ enum ChunkIdentifier : u16 { FALL_TRACK_TAG = 0xB028, HIDE_TRACK_TAG = 0xB029, NODE_ID = 0xB030, - - + + CMAGIC = 0xC23D, - + C_MDRAWER = 0xC010, C_TDRAWER = 0xC020, C_SHPDRAWER = 0xC030, @@ -395,7 +395,7 @@ enum ChunkIdentifier : u16 { C_BITMAP_DRAWER = 0xC25B, C_RGB_FILE = 0xC260, C_RGB_OVASPECT = 0xC270, - + C_RGB_ANIMTYPE = 0xC271, C_RENDER_ALL = 0xC272, C_REND_FROM = 0xC273, @@ -513,16 +513,16 @@ enum ChunkIdentifier : u16 { C_VTR_IN = 0xC775, C_VTR_PK = 0xC780, C_VTR_SH = 0xC785, - -/* Material chunks */ - C_WORK_MTLS = 0xC790, /* Old-style -- now ignored */ - C_WORK_MTLS_2 = 0xC792, /* Old-style -- now ignored */ - C_WORK_MTLS_3 = 0xC793, /* Old-style -- now ignored */ - C_WORK_MTLS_4 = 0xC794, /* Old-style -- now ignored */ - C_WORK_MTLS_5 = 0xCB00, /* Old-style -- now ignored */ - C_WORK_MTLS_6 = 0xCB01, /* Old-style -- now ignored */ - C_WORK_MTLS_7 = 0xCB02, /* Old-style -- now ignored */ - C_WORK_MTLS_8 = 0xCB03, /* Old-style -- now ignored */ + +/* Material chunks */ + C_WORK_MTLS = 0xC790, /* Old-style -- now ignored */ + C_WORK_MTLS_2 = 0xC792, /* Old-style -- now ignored */ + C_WORK_MTLS_3 = 0xC793, /* Old-style -- now ignored */ + C_WORK_MTLS_4 = 0xC794, /* Old-style -- now ignored */ + C_WORK_MTLS_5 = 0xCB00, /* Old-style -- now ignored */ + C_WORK_MTLS_6 = 0xCB01, /* Old-style -- now ignored */ + C_WORK_MTLS_7 = 0xCB02, /* Old-style -- now ignored */ + C_WORK_MTLS_8 = 0xCB03, /* Old-style -- now ignored */ C_WORKMTL = 0xCB04, C_SXP_TEXT_DATA = 0xCB10, C_SXP_TEXT2_DATA = 0xCB20, @@ -542,15 +542,15 @@ enum ChunkIdentifier : u16 { C_BGTYPE = 0xC7A1, C_MEDTILE = 0xC7B0, - + /* Contrast */ C_LO_CONTRAST = 0xC7D0, C_HI_CONTRAST = 0xC7D1, - -/* 3d frozen display */ + +/* 3d frozen display */ C_FROZ_DISPLAY = 0xC7E0, - -/* Booleans */ + +/* Booleans */ C_BOOLWELD = 0xc7f0, C_BOOLTYPE = 0xc7f1, @@ -569,8 +569,8 @@ enum ChunkIdentifier : u16 { C_MAPDRAWER8 = 0xCA08, C_MAPDRAWER9 = 0xCA09, C_MAPDRAWER_ENTRY = 0xCA10, - -/* System Options */ + +/* System Options */ C_BACKUP_FILE = 0xCA20, C_DITHER_256 = 0xCA21, C_SAVE_LAST = 0xCA22, @@ -596,8 +596,8 @@ enum ChunkIdentifier : u16 { C_AUTO_SMOOTH = 0xCA80, C_DXF_SMOOTH_ANG = 0xCA90, C_SMOOTH_ANG = 0xCAA0, - -/* Special network-use chunks */ + +/* Special network-use chunks */ C_NET_USE_VPOST = 0xCC00, C_NET_USE_GAMMA = 0xCC10, C_NET_FIELD_ORDER = 0xCC20, @@ -628,8 +628,8 @@ enum ChunkIdentifier : u16 { C_VIEW_PRES_RATIO = 0xCE50, C_BGND_PRES_RATIO = 0xCE60, C_NTH_SERIAL_NUM = 0xCE70, - - /* Video Post */ + + /* Video Post */ VPDATA = 0xd000, P_QUEUE_ENTRY = 0xd100, @@ -648,7 +648,7 @@ enum ChunkIdentifier : u16 { P_QUEUE_ALIGN = 0xd190, P_CUSTOM_SIZE = 0xd1a0, - + P_ALPH_NONE = 0xd210, P_ALPH_PSEUDO = 0xd220, /* Old chunk */ P_ALPH_OP_PSEUDO = 0xd221, /* Old chunk */ @@ -732,7 +732,7 @@ bitfield Spline { Use_Bias : 1; Use_Ease_To : 1; Use_Ease_From : 1; - + padding : 3; if (Use_Tension == 1) @@ -745,7 +745,7 @@ bitfield Spline { float Ease_To; if (Use_Ease_From == 1) float Ease_From; - + padding : 8; }; @@ -795,7 +795,7 @@ struct Keys { ) : { RotationKey data[count]; } - + ( ChunkIdentifier::FOV_TRACK_TAG | ChunkIdentifier::ROLL_TRACK_TAG | @@ -804,13 +804,13 @@ struct Keys { ) : { AngleKey data[count]; } - + ( ChunkIdentifier::MORPH_TRACK_TAG ) : { MorphKey data[count]; } - + ( ChunkIdentifier::HIDE_TRACK_TAG ) : { @@ -842,7 +842,7 @@ struct Chunk { u32 chunkSize; u32 dataLength = chunkSize - 6 [[export]]; u32 chunkEnd = $ + dataLength; - + if (chunkSize > 0) { std::print("{}", identifier); match (identifier) { @@ -894,14 +894,14 @@ struct Chunk { ): { Chunk chunks[while($ < chunkEnd)]; } - + ( ChunkIdentifier::M3D_VERSION | ChunkIdentifier::MESH_VERSION ): { u32 version; } - + ( ChunkIdentifier::LO_SHADOW_BIAS | ChunkIdentifier::HI_SHADOW_BIAS | @@ -921,41 +921,41 @@ struct Chunk { ): { float value; } - + ( ChunkIdentifier::V_GRADIENT ) : { float position; Chunk chunks[while($ < chunkEnd)]; } - + ( ChunkIdentifier::NAMED_OBJECT ) : { char name[]; Chunk chunks[while($ < chunkEnd)]; } - + ( ChunkIdentifier::POINT_ARRAY ) : { u16 count; Vector3f vectices[count] [[hex::visualize("3d", this, null)]]; } - + ( ChunkIdentifier::TEX_VERTS ) : { u16 count; Vector2f coords[count]; } - + ( ChunkIdentifier::MESH_MATRIX ) : { Vector3f x, y, z, w; } - + ( ChunkIdentifier::FACE_ARRAY ) : { @@ -963,26 +963,26 @@ struct Chunk { Face faces[count]; Chunk chunks[while($ < chunkEnd)]; } - + ( ChunkIdentifier::POINT_FLAG_ARRAY ) : { u16 count; VertexFlags flags[count]; } - + ( ChunkIdentifier::SMOOTH_GROUP ) : { u32 groups[dataLength / sizeof(u32)]; } - + ( ChunkIdentifier::MESH_COLOR ) : { u8 value; } - + ( ChunkIdentifier::MSH_MAT_GROUP ) : { @@ -990,7 +990,7 @@ struct Chunk { u16 count; u16 groups[count]; } - + ( ChunkIdentifier::KFHDR ) : { @@ -998,25 +998,25 @@ struct Chunk { char name[]; u32 animationLength; } - + ( ChunkIdentifier::KFSEG ) : { u32 start, end; } - + ( ChunkIdentifier::KFCURTIME ) : { u32 frameIndex; } - + ( ChunkIdentifier::NODE_ID ) : { type::Hex id; } - + ( ChunkIdentifier::NODE_HDR ) : { @@ -1026,47 +1026,47 @@ struct Chunk { KeyFlags2 flags2; type::Hex parentId; } - + ( ChunkIdentifier::PIVOT ) : { Vector3f value; } - + ( ChunkIdentifier::BOUNDBOX ) : { Vector3f min, max; } - + ( ChunkIdentifier::COLOR_24 | ChunkIdentifier::LIN_COLOR_24 ) : { RGB color; } - + ( ChunkIdentifier::COLOR_F | ChunkIdentifier::LIN_COLOR_F ) : { RGB color; } - + ( ChunkIdentifier::INT_PERCENTAGE | ChunkIdentifier::MAT_BUMP_PERCENT ) : { u16 value; } - + ( ChunkIdentifier::SHADOW_MAP_SIZE | ChunkIdentifier::MAT_SHADING ) : { u16 value; } - + ( ChunkIdentifier::POS_TRACK_TAG | ChunkIdentifier::ROT_TRACK_TAG | @@ -1077,13 +1077,13 @@ struct Chunk { ChunkIdentifier::MORPH_TRACK_TAG | ChunkIdentifier::HOT_TRACK_TAG | ChunkIdentifier::FALL_TRACK_TAG | - ChunkIdentifier::HIDE_TRACK_TAG + ChunkIdentifier::HIDE_TRACK_TAG ) : { TrackFlags flags; u8 unknown[8]; Keys keys; } - + ( ChunkIdentifier::INSTANCE_NAME | ChunkIdentifier::BIT_MAP | @@ -1093,26 +1093,26 @@ struct Chunk { ) : { char name[]; } - + ( ChunkIdentifier::MAT_MAP_TILING ) : { u16 tiling; } - + ( ChunkIdentifier::WORLD_VIEWPORT_SIZE ) : { u16 x, y, w, h; } - + ( ChunkIdentifier::N_DIRECT_LIGHT ) : { Vector3f position; Chunk chunks[while($ < chunkEnd)]; } - + ( ChunkIdentifier::DL_SPOTLIGHT ) : { @@ -1120,16 +1120,16 @@ struct Chunk { float hotspot, falloff; Chunk chunks[while($ < chunkEnd)]; } - + ( ChunkIdentifier::N_CAMERA ) : { Vector3f position, target; float roll, fov; } - - - + + + (_) : { std::warning(std::format("Unhandled Chunk ID: {}, Skipping 0x{:04X} bytes", identifier, dataLength)); $ += dataLength; diff --git a/patterns/7z.hexpat b/patterns/7z.hexpat index 94cffc6e..bc9f095a 100644 --- a/patterns/7z.hexpat +++ b/patterns/7z.hexpat @@ -1,102 +1,102 @@ #pragma description 7z Archive #pragma MIME application/x-7z-compressed -import std.io; -import std.mem; -import std.math; - - -enum Type:u8{ - startPosition = 0x00, // Start position - sizeStartHeader = 0x20, // Size of start Header -}; - -enum TypeB:u48{ - sevenZipSignature = 0x1C27AFBC7A37, // Defining 7z signature -}; - -struct StartHeader { - // File signature - u48 signature [[color("FF0000")] ]; - // Version format - u16 formatVersion [[color("00FF00")]]; - // CRC start header - u32 crcOfTheFollowing20Bytes [[color("0000FF")]]; - // Relative offset of End Header - u64 relativeOffsetEndHeader [[color("FFFF00")]]; - // Length of End Header - u64 theLengthOfEndHeader [[color("00FFFF")]]; - // CRC of End Ender - u32 crcOftheEndHeader [[color("FF00FF")]]; - // File size calculation - u64 fileSize = relativeOffsetEndHeader + theLengthOfEndHeader + Type::sizeStartHeader; - // Absolute offset calculation End Header - u64 startEndHeader = relativeOffsetEndHeader + Type::sizeStartHeader; -}; - -StartHeader startheader @ Type::startPosition; - -struct CompressedData { - // Start of compressed data - u8 startOfCompressedData[4] [[color("C0C0C0")]]; -}; - -CompressedData compresseddata @ Type::sizeStartHeader; - - -struct EndHeader { - // Set padding to place End Header in right position - padding[startheader.relativeOffsetEndHeader]; - // Mark link to meta block - u8 linkToMetaBlock [[color("FF0000")]]; - // Mark all End Header - char fullEndHeader[startheader.theLengthOfEndHeader] [[color("63954A")]]; - // Detect LZMA signature - u64 lzmaSignaturePosition = std::mem::find_sequence_in_range(0, addressof(fullEndHeader), addressof(fullEndHeader) + sizeof(fullEndHeader), 0x23, 0x03, 0x01, 0x01, 0x05, 0x5D); - - // Mark positions if LZMA signature was detected - if(lzmaSignaturePosition != 0xFFFFFFFFFFFFFFFF){ - u48 lzmaSignature @ lzmaSignaturePosition [[color("0000FF")]]; - } -}; - -EndHeader endheader @ Type::sizeStartHeader; - -// Check 7z type -if(startheader.signature == TypeB::sevenZipSignature){ - std::print("It is a 7z File Type"); -}else{ - std::print("The file is not 7z type"); -} - -std::print("Format Version {} ",startheader.formatVersion); - -// Version verification -if(startheader.formatVersion == 0x0400){ - std::print("Major Version 0x00 || 0 - Minor Version 0x04 || 4"); -} - -// Verification of the compressed method is LZMA, Bzip2 or LZMA2 -if(compresseddata.startOfCompressedData[0] == Type::startPosition){ - std::print("Compressed Method LZMA"); -}else if(compresseddata.startOfCompressedData[0] == 0x42){ - std::print("Compressed Method Bzip2"); -}else{ - std::print("Compressed Method LZMA2"); -} - - - -std::print("CRC Start Header 0x{:X}",startheader.crcOfTheFollowing20Bytes); - -std::print("CRC End Header 0x{:X} ", startheader.crcOftheEndHeader); - -std::print("CompressedData length 0x{:X} || {} bytes ",startheader.relativeOffsetEndHeader, startheader.relativeOffsetEndHeader); - -std::print("Relative Offset of End Header 0x{:X} || {} bytes",startheader.relativeOffsetEndHeader, startheader.relativeOffsetEndHeader); - -std::print("Offset to start End Header 0x{:X} || {} bytes",startheader.startEndHeader,startheader.startEndHeader); - -std::print("End Header length 0x{:X} || {} bytes",startheader.theLengthOfEndHeader,startheader.theLengthOfEndHeader); - -std::print("File size 0x{:X} || {} bytes",startheader.fileSize, startheader.fileSize); +import std.io; +import std.mem; +import std.math; + + +enum Type:u8{ + startPosition = 0x00, // Start position + sizeStartHeader = 0x20, // Size of start Header +}; + +enum TypeB:u48{ + sevenZipSignature = 0x1C27AFBC7A37, // Defining 7z signature +}; + +struct StartHeader { + // File signature + u48 signature [[color("FF0000")] ]; + // Version format + u16 formatVersion [[color("00FF00")]]; + // CRC start header + u32 crcOfTheFollowing20Bytes [[color("0000FF")]]; + // Relative offset of End Header + u64 relativeOffsetEndHeader [[color("FFFF00")]]; + // Length of End Header + u64 theLengthOfEndHeader [[color("00FFFF")]]; + // CRC of End Ender + u32 crcOftheEndHeader [[color("FF00FF")]]; + // File size calculation + u64 fileSize = relativeOffsetEndHeader + theLengthOfEndHeader + Type::sizeStartHeader; + // Absolute offset calculation End Header + u64 startEndHeader = relativeOffsetEndHeader + Type::sizeStartHeader; +}; + +StartHeader startheader @ Type::startPosition; + +struct CompressedData { + // Start of compressed data + u8 startOfCompressedData[4] [[color("C0C0C0")]]; +}; + +CompressedData compresseddata @ Type::sizeStartHeader; + + +struct EndHeader { + // Set padding to place End Header in right position + padding[startheader.relativeOffsetEndHeader]; + // Mark link to meta block + u8 linkToMetaBlock [[color("FF0000")]]; + // Mark all End Header + char fullEndHeader[startheader.theLengthOfEndHeader] [[color("63954A")]]; + // Detect LZMA signature + u64 lzmaSignaturePosition = std::mem::find_sequence_in_range(0, addressof(fullEndHeader), addressof(fullEndHeader) + sizeof(fullEndHeader), 0x23, 0x03, 0x01, 0x01, 0x05, 0x5D); + + // Mark positions if LZMA signature was detected + if(lzmaSignaturePosition != 0xFFFFFFFFFFFFFFFF){ + u48 lzmaSignature @ lzmaSignaturePosition [[color("0000FF")]]; + } +}; + +EndHeader endheader @ Type::sizeStartHeader; + +// Check 7z type +if(startheader.signature == TypeB::sevenZipSignature){ + std::print("It is a 7z File Type"); +}else{ + std::print("The file is not 7z type"); +} + +std::print("Format Version {} ",startheader.formatVersion); + +// Version verification +if(startheader.formatVersion == 0x0400){ + std::print("Major Version 0x00 || 0 - Minor Version 0x04 || 4"); +} + +// Verification of the compressed method is LZMA, Bzip2 or LZMA2 +if(compresseddata.startOfCompressedData[0] == Type::startPosition){ + std::print("Compressed Method LZMA"); +}else if(compresseddata.startOfCompressedData[0] == 0x42){ + std::print("Compressed Method Bzip2"); +}else{ + std::print("Compressed Method LZMA2"); +} + + + +std::print("CRC Start Header 0x{:X}",startheader.crcOfTheFollowing20Bytes); + +std::print("CRC End Header 0x{:X} ", startheader.crcOftheEndHeader); + +std::print("CompressedData length 0x{:X} || {} bytes ",startheader.relativeOffsetEndHeader, startheader.relativeOffsetEndHeader); + +std::print("Relative Offset of End Header 0x{:X} || {} bytes",startheader.relativeOffsetEndHeader, startheader.relativeOffsetEndHeader); + +std::print("Offset to start End Header 0x{:X} || {} bytes",startheader.startEndHeader,startheader.startEndHeader); + +std::print("End Header length 0x{:X} || {} bytes",startheader.theLengthOfEndHeader,startheader.theLengthOfEndHeader); + +std::print("File size 0x{:X} || {} bytes",startheader.fileSize, startheader.fileSize); diff --git a/patterns/Crashlvl.hexpat b/patterns/Crashlvl.hexpat index 448ffaee..a3db7817 100644 --- a/patterns/Crashlvl.hexpat +++ b/patterns/Crashlvl.hexpat @@ -353,7 +353,7 @@ struct Object { u16 numOfMetafields; u8 metafields[numOfMetafields]; } - } + } else { u32 x; u32 y; diff --git a/patterns/afe2.hexpat b/patterns/afe2.hexpat index 9b70798e..76b293dd 100644 --- a/patterns/afe2.hexpat +++ b/patterns/afe2.hexpat @@ -52,8 +52,8 @@ std::assert(ctx.magic == ATMOSPHERE_REBOOT_TO_FATAL_MAGIC || ctx.magic == ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_1 || ctx.magic == ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_0, "File is not a valid Atmosphere fatal error binary!"); - -std::assert_warn(ctx.magic == ATMOSPHERE_REBOOT_TO_FATAL_MAGIC, + +std::assert_warn(ctx.magic == ATMOSPHERE_REBOOT_TO_FATAL_MAGIC, "Atmosphere fatal error binary is for an older version!"); diff --git a/patterns/ar.hexpat b/patterns/ar.hexpat index 4ae2e4fd..dbac3724 100644 --- a/patterns/ar.hexpat +++ b/patterns/ar.hexpat @@ -15,7 +15,7 @@ struct ARFile { char file_mode[8]; char file_size[10]; u16 end_marker; - + if (end_marker == 0x0A60) { u8 data[std::string::parse_int(this.file_size, 10)]; padding[sizeof(data) & 1]; diff --git a/patterns/arm_cm_vtor.hexpat b/patterns/arm_cm_vtor.hexpat index d7047d2b..fc4b281c 100644 --- a/patterns/arm_cm_vtor.hexpat +++ b/patterns/arm_cm_vtor.hexpat @@ -40,21 +40,21 @@ VectorTable vector_table @ VTOR; fn main() { u32 table_size = sizeof(vector_table); - + u32 default_handler_address = 0x00; - + for (u32 i = 4, i < table_size, i = i + 4) { u32 occurrences = 0; for (u32 j = 4, j < table_size, j = j + 4) { if (std::mem::read_unsigned(i, 4) == std::mem::read_unsigned(j, 4)) { occurrences = occurrences + 1; - + if (occurrences > 1) default_handler_address = std::mem::read_unsigned(i, 4); } } } - + if (default_handler_address != 0x00) std::print("Default Handler implementation at 0x{:08X}", default_handler_address); }; \ No newline at end of file diff --git a/patterns/bencode.hexpat b/patterns/bencode.hexpat index af1b4db0..070aab95 100644 --- a/patterns/bencode.hexpat +++ b/patterns/bencode.hexpat @@ -8,20 +8,20 @@ import std.mem; import std.string; namespace bencode { - + struct ASCIIDecimal { char value[while(std::ctype::isdigit(std::mem::read_unsigned($, 1)))]; } [[sealed, format("bencode::format_ascii_decimal"), transform("bencode::format_ascii_decimal")]]; - + fn format_ascii_decimal(ASCIIDecimal adsasd) { return std::string::parse_int(adsasd.value, 10); }; - + enum Type : u8 { Integer = 'i', Dictionary = 'd', List = 'l', - + String0 = '0', String1 = '1', String2 = '2', @@ -33,30 +33,30 @@ namespace bencode { String8 = '8', String9 = '9' }; - + struct String { ASCIIDecimal length; char separator [[hidden]]; char value[length]; } [[sealed, format("bencode::format_string"), transform("bencode::format_string")]]; - + fn format_string(String string) { - return string.value; + return string.value; }; - + using Bencode; using Value; - + struct DictionaryEntry { String key; Value value; }; - + struct Value { Type type; - + if (type == Type::Dictionary) { - DictionaryEntry entry[while(std::mem::read_unsigned($, 1) != 'e')]; + DictionaryEntry entry[while(std::mem::read_unsigned($, 1) != 'e')]; } else if (type == Type::Integer) { ASCIIDecimal value; char end; @@ -65,12 +65,12 @@ namespace bencode { String value; } }; - + struct Bencode { - Value value[while(!std::mem::eof())] [[inline]]; + Value value[while(!std::mem::eof())] [[inline]]; char end; }; - + } bencode::Bencode bencode @ 0x00; diff --git a/patterns/bgcode.hexpat b/patterns/bgcode.hexpat index d9f648cd..f86afc7b 100644 --- a/patterns/bgcode.hexpat +++ b/patterns/bgcode.hexpat @@ -43,7 +43,7 @@ struct Header { Header header @ 0; std::assert(header.version == 1, "only version 1 supported"); - + struct Block { BlockType type; Compression compression; diff --git a/patterns/blend.hexpat b/patterns/blend.hexpat index eac738a4..8d371151 100644 --- a/patterns/blend.hexpat +++ b/patterns/blend.hexpat @@ -36,7 +36,7 @@ struct BHead { char code[4]; s32 len; Ptr old; - s32 SDNAnr; + s32 SDNAnr; s32 nr; // ENDB marks the last data block in the file. @@ -137,7 +137,7 @@ struct BlendWrapper { BlendWrapper blendWrapper @ 0x00; -// Assume the blend file is ZSTD compressed. +// Assume the blend file is ZSTD compressed. struct SeekTableFooter { u32 numFrames; diff --git a/patterns/bmp.hexpat b/patterns/bmp.hexpat index bc54f0d4..e6200d85 100644 --- a/patterns/bmp.hexpat +++ b/patterns/bmp.hexpat @@ -97,7 +97,7 @@ struct Bitmap { (_): BitmapInfoHeaderV1 bmih; } padding[bmih.biSize - sizeof(bmih)]; - + if (bmih.biBitCount <= 8) { if (bmih.biClrUsed > 0 ) @@ -105,7 +105,7 @@ struct Bitmap { else Colors rgbq[1 << bmih.biBitCount]; } - + if (bmih.biSizeImage > 0 ) u8 lineData[bmih.biSizeImage]; else diff --git a/patterns/bplist.hexpat b/patterns/bplist.hexpat index 85a8038e..0a49f913 100644 --- a/patterns/bplist.hexpat +++ b/patterns/bplist.hexpat @@ -29,14 +29,14 @@ enum Marker : u8 { UNK_0xF0 = 0xF0 }; -fn get_marker_name(u8 marker) { +fn get_marker_name(u8 marker) { if (marker == Marker::Null){// null 0000 0000 return "Null "; }else if (marker == Marker::False){ //bool 0000 1000 // false return "False"; }else if (marker == Marker::True){//bool 0000 1001 // true return "True"; - }else if (marker == Marker::Fill){ //fill 0000 1111 // fill byte + }else if (marker == Marker::Fill){ //fill 0000 1111 // fill byte return "Fill"; }else if (marker & 0xF0 == Marker::Int){ //int 0001 nnnn ... // # of bytes is 2^nnnn, big-endian bytes return "Int"; @@ -45,10 +45,10 @@ fn get_marker_name(u8 marker) { }else if (marker == Marker::Date){ //date 0011 0011 ... // 8 byte float follows, big-endian bytes return "Date"; }else if (marker & 0xF0 == Marker::Data){ //data 0100 nnnn [int] ... // nnnn is number of bytes unless 1111 then int count follows, followed by bytes - return "Data"; + return "Data"; }else if (marker & 0xF0 == Marker::ASCIIString){ //string 0101 nnnn [int] ... // ASCII string, nnnn is # of chars, else 1111 then int count, then bytes return "ASCIIString"; - }else if (marker & 0xF0 == Marker::Unicode16String){ //string 0110 nnnn [int] ... // Unicode string, nnnn is # of chars, else 1111 then int count, then big-endian 2-byte + }else if (marker & 0xF0 == Marker::Unicode16String){ //string 0110 nnnn [int] ... // Unicode string, nnnn is # of chars, else 1111 then int count, then big-endian 2-byte return "Unicode16String"; }else if (marker & 0xF0 == Marker::UNK_0x70){ //0111 xxxx // unused return "UNK_0x70"; @@ -56,19 +56,19 @@ fn get_marker_name(u8 marker) { return "UID"; }else if (marker & 0xF0 == Marker::UNK_0x90){ // 1001 xxxx // unused return "UNK_0x90"; - }else if (marker & 0xF0 == Marker::Array){ //array 1010 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows + }else if (marker & 0xF0 == Marker::Array){ //array 1010 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows return "Array"; }else if (marker & 0xF0 == Marker::UNK_0xB0){ //1011 xxxx // unused return "UNK_0xB0"; }else if (marker & 0xF0 == Marker::Set){ //set 1100 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows return "Set"; - }else if (marker & 0xF0 == Marker::Dict){ //dict 1101 nnnn [int] keyref* objref* // nnnn is count, unless '1111', then int count follows + }else if (marker & 0xF0 == Marker::Dict){ //dict 1101 nnnn [int] keyref* objref* // nnnn is count, unless '1111', then int count follows return "Dict"; }else if (marker & 0xF0 == Marker::UNK_0xE0){ // 1110 xxxx // unused return "UNK_0xE0"; }else if (marker & 0xF0 == Marker::UNK_0xF0){ //1111 xxxx // unused return "UNK_0xF0"; - } + } }; fn format_tag(u8 marker) { @@ -79,12 +79,12 @@ fn coredata_to_date (double val){ return type::impl::format_time_t(978307200 + val); }; -struct DictElement { +struct DictElement { CFBinaryPlistObject key @ offsetTable[parent.objReference.key_refs[std::core::array_index()]].offset; CFBinaryPlistObject value @ offsetTable[parent.objReference.value_refs[std::core::array_index()]].offset; }; -struct ArrayElement { +struct ArrayElement { CFBinaryPlistObject value @ offsetTable[parent.objReference.value_refs[std::core::array_index()]].offset; }; @@ -142,23 +142,23 @@ struct CFBinaryPlistOffset{ struct CFBinaryPlistObject{ u8 marker [[format("get_marker_name")]]; - + u8 marker_msb = marker & 0xF0; u8 marker_lsb = marker & 0x0F; - match (marker_msb){ + match (marker_msb){ (0x0): { match (marker_lsb){ (Marker::Null): { u8 value = 0x00 [[export]]; } - (Marker::False): { + (Marker::False): { bool value = false [[export]]; } (Marker::True): { bool value = true [[export]]; } - (Marker::Fill): { + (Marker::Fill): { //I think the correct implementation is to do nothing here. The marker will be used as padding (Fill) ??? } (_): { @@ -167,7 +167,7 @@ struct CFBinaryPlistObject{ } } (Marker::Int): { - be u8 size = std::math::pow(2, marker_lsb); + be u8 size = std::math::pow(2, marker_lsb); // in format version '00', 1, 2, and 4-byte integers have to be interpreted as unsigned, // whereas 8-byte integers are signed (and 16-byte when available) // negative 1, 2, 4-byte integers are always emitted as 8 bytes in format '00' @@ -181,10 +181,10 @@ struct CFBinaryPlistObject{ (8): be s64 value; (16): be s128 value; (_): std::error(std::format("Invalid size detected for 'Int' marker. Got size: {}.", size)); - } + } } (Marker::Real): { - be u8 size = std::math::pow(2, marker_lsb); + be u8 size = std::math::pow(2, marker_lsb); match (size){ (4): be float value; (8): be double value; @@ -196,7 +196,7 @@ struct CFBinaryPlistObject{ } (Marker::Data): { ObjectLen ObjectLen; - u8 value[ObjectLen.size]; + u8 value[ObjectLen.size]; } (Marker::ASCIIString): { ObjectLen ObjectLen; @@ -216,7 +216,7 @@ struct CFBinaryPlistObject{ (8): be u64 value; (16): be u128 value; (_): std::error(std::format("Invalid size detected for 'UID' marker. Got size: {}.", size)); - } + } } (Marker::Set | Marker::Array): { ObjectLen ObjectLen; @@ -226,7 +226,7 @@ struct CFBinaryPlistObject{ } (Marker::Dict): { ObjectLen ObjectLen; - + ObjectReference objReference; DictElement value[ObjectLen.size]; } @@ -235,7 +235,7 @@ struct CFBinaryPlistObject{ } (_): { std::error(std::format("Got unknown marker 0x{:x}", marker)); - } + } } }; @@ -249,13 +249,13 @@ struct CFBinaryPlistHeader{ struct CFBinaryPlistTrailer { u8 unused[5]; - u8 sortVersion; + u8 sortVersion; be u8 offsetIntSize; match (offsetIntSize){ (1|2|4|8): {} (_): {std::error("Invalid offsetIntSize.");} } - be u8 objectRefSize; + be u8 objectRefSize; match (objectRefSize){ (1|2|4|8): {} (_): {std::error("Invalid objectRefSize.");} diff --git a/patterns/bson.hexpat b/patterns/bson.hexpat index 4f3d9b25..76260113 100644 --- a/patterns/bson.hexpat +++ b/patterns/bson.hexpat @@ -26,8 +26,8 @@ enum Type : u8 { Timestamp = 0x11, Int64 = 0x12, Decimal128 = 0x13, - - MinKey = 0xFF, + + MinKey = 0xFF, MaxKey = 0x7F }; @@ -78,9 +78,9 @@ using Document; struct Element { Type type; - + CString name; - + if (type == Type::Double) { double value; } else if (type == Type::String) { @@ -93,9 +93,9 @@ struct Element { Binary value; } else if (type == Type::Undefined) { /* undefined */ - } else if (type == Type::ObjectId) { + } else if (type == Type::ObjectId) { ObjectId value; - } else if (type == Type::Boolean) { + } else if (type == Type::Boolean) { bool value; } else if (type == Type::UTCDatetime) { type::time64_t value; diff --git a/patterns/bsp_goldsrc.hexpat b/patterns/bsp_goldsrc.hexpat index 918c0836..618eaee8 100644 --- a/patterns/bsp_goldsrc.hexpat +++ b/patterns/bsp_goldsrc.hexpat @@ -163,7 +163,7 @@ struct dmiptexlump_t MiptexPointer dataofs[nummiptex]; }; -struct VisibilityData +struct VisibilityData { u8 data[file_header.lumps[LumpIndex::Visibility].filelen]; u8 pad[std::mem::align_to(4, sizeof(this)) - sizeof(this)]; diff --git a/patterns/chm.hexpat b/patterns/chm.hexpat index 7d0b3fd5..6b29237c 100644 --- a/patterns/chm.hexpat +++ b/patterns/chm.hexpat @@ -285,26 +285,26 @@ struct DirectoryIndexEntry { struct ListingChunk { char magic[4]; - + if (magic == "PMGL") { type::Size freeSpaceLength; u32; u32 prevChunkNumber, nextChunkNumber; - + u16 directoryListingEntryCount @ addressof(this) + parent.directoryChunkSize - 2; u16 offsets[(freeSpaceLength - 2) / 2] @ addressof(directoryListingEntryCount) - (freeSpaceLength - 2); - + DirectoryListingEntry directories[directoryListingEntryCount]; - + $ = addressof(directoryListingEntryCount) + sizeof(directoryListingEntryCount); } else if (magic == "PMGI") { type::Size freeSpaceLength; - + u16 directoryIndexEntryCount @ addressof(this) + parent.directoryChunkSize - 2; u16 offsets[(freeSpaceLength - 2) / 2] @ addressof(directoryIndexEntryCount) - (freeSpaceLength - 2); - + DirectoryIndexEntry indexes[directoryIndexEntryCount]; - + $ = addressof(directoryIndexEntryCount) + sizeof(directoryIndexEntryCount); } else { std::error("Invalid chunk magic!"); @@ -313,7 +313,7 @@ struct ListingChunk { struct HeaderSection { char magic[4]; - + if (magic == "\xFE\x01\x00\x00") { u32; type::Size fileSize; @@ -337,7 +337,7 @@ struct HeaderSection { u32; u32; u32; - + ListingChunk chunk[directoryChunkCount]; } else { std::error("Invalid header section magic!"); @@ -347,22 +347,22 @@ struct HeaderSection { struct HeaderSectionTableEntry { u64 offset; type::Size size; - + HeaderSection headerSection @ offset; }; struct NameListEntry { type::Size nameLength; - char16 name[nameLength]; + char16 name[nameLength]; padding[2]; }; struct NameListFile { u16 fileLengthWords; u16 entriesInFile; - + NameListEntry nameList[entriesInFile]; - + padding[0x2E]; }; @@ -376,7 +376,7 @@ struct SectionData { u32; }; -struct Content { +struct Content { NameListFile nameListFile; SectionData sectionData; }; @@ -389,9 +389,9 @@ struct CHM { be u32 timeStamp; WindowsLanguageId languageId; type::GUID guids[2]; - + HeaderSectionTableEntry headerSectionTable[2]; - + Content *dataOffset : u64; }; diff --git a/patterns/coff.hexpat b/patterns/coff.hexpat index dfbef130..82d81fdc 100644 --- a/patterns/coff.hexpat +++ b/patterns/coff.hexpat @@ -116,11 +116,11 @@ struct SymbolTable { Type type; StorageClass storageClass; u8 numberOfAuxSymbols; - + countedSymbols += 1 + numberOfAuxSymbols; - + AuxSymbol auxSymbols[numberOfAuxSymbols]; - + if (countedSymbols >= parent.header.numberOfSymbols) break; }; @@ -187,7 +187,7 @@ struct Section { u16 numberOfRelocations; u16 numberOfLineNumbers; SectionFlags characteristics; - + u8 rawData[sizeOfRawData] @ pointerToRawData [[sealed]]; Relocations relocations[numberOfRelocations] @ pointerToRelocations; }; @@ -205,9 +205,9 @@ struct Header { struct COFF { Header header; - + Section sectionTable[header.numberOfSections]; - + SymbolTable symbolTable[header.numberOfSymbols] @ header.pointerToSymbolTable; StringTable stringTable @ addressof(symbolTable) + sizeof(symbolTable); }; diff --git a/patterns/cpio.hexpat b/patterns/cpio.hexpat index 191a2e5d..95ddd058 100644 --- a/patterns/cpio.hexpat +++ b/patterns/cpio.hexpat @@ -42,7 +42,7 @@ namespace old_binary { std::core::set_endian(std::mem::Endian::Little); else std::error("Invalid CPIO Magic!"); - + u16 dev; u16 ino; Mode mode; @@ -59,7 +59,7 @@ namespace old_binary { CpioHeader header; char pathname[header.namesize % 2 == 0 ? header.namesize : header.namesize + 1]; u8 data[header.filesize % 2 == 0 ? header.filesize : header.filesize + 1]; - + if (pathname == "TRAILER!!!\x00\x00") break; }; diff --git a/patterns/dds.hexpat b/patterns/dds.hexpat index 9cc60d76..a34d0825 100644 --- a/patterns/dds.hexpat +++ b/patterns/dds.hexpat @@ -203,7 +203,7 @@ enum DX10AlphaMode : u32 { Straight, PreMultiplied, Opaque, - Custom, + Custom, }; bitfield DX10MiscFlags { diff --git a/patterns/dex.hexpat b/patterns/dex.hexpat index ee6a041e..6598e0c2 100644 --- a/patterns/dex.hexpat +++ b/patterns/dex.hexpat @@ -99,7 +99,7 @@ enum access_flag : type::uLEB128{ static = 0x8, final = 0x10, synchronized = 0x20, - volatile = 0x40 + volatile = 0x40 }; struct encoded_field { @@ -134,7 +134,7 @@ struct class_def_item { u32 class_data_off; //class_data_item *class_data_off:u32; u32 static_values_off; - char class_name[] @ addressof(parent.type_ids[class_idx].type_name); + char class_name[] @ addressof(parent.type_ids[class_idx].type_name); }[[name(class_name)]]; struct type_item { diff --git a/patterns/dsstore.hexpat b/patterns/dsstore.hexpat index f79ee5e7..1df0a78b 100644 --- a/patterns/dsstore.hexpat +++ b/patterns/dsstore.hexpat @@ -7,11 +7,11 @@ import std.io; struct RecordEntry { u32 length; char16 filename[length]; - + char id[4]; // either blob or length char type[4]; - + if (type == "blob") { u32 blobCount; u8 blobData[blobCount]; @@ -67,15 +67,15 @@ struct BuddyBlock { u8 reserved[4]; // padding for next multiple of 256 entries (1024 bytes) u32 addresses[blockCount]; - + // u8 padding[paddingCount]; u32 directoryCount; - + // directory entries u8 count; u8 name[count]; u32 blockNumber; - + // free lists // 32 free lists BuddyRootBlockOffsets off[32]; @@ -96,7 +96,7 @@ struct BuddyAllocator { u32 offsetBookkeeping2; u32 offsetData; u8 reserved[12]; - + BuddyRootBlock root @ offsetBookkeeping + 4; std::print("TOC {} address 0x{:08x}", @@ -104,7 +104,7 @@ struct BuddyAllocator { root.offsets.addresses[root.toc.toc[0].value] >> 0x5 << 0x5); BlocksList blocks @ (root.offsets.addresses[root.toc.toc[0].value] >> 0x5 << 0x5) + 4; - + std::print("Blocks start at address 0x{:08x}, size 0x{:04x}", root.offsets.addresses[blocks.blockId] >> 0x5 << 0x5, 1 << (root.offsets.addresses[blocks.blockId] & 0x1f)); diff --git a/patterns/dted.hexpat b/patterns/dted.hexpat index 1f70aa58..9cc45ff9 100644 --- a/patterns/dted.hexpat +++ b/patterns/dted.hexpat @@ -7,11 +7,11 @@ import std.mem; import std.string; -enum Magic:u24 { +enum Magic:u24 { UHL = 0x55484C, DSI = 0x445349, ACC = 0x414343, -}; +}; struct UHL { Magic magic; @@ -95,7 +95,7 @@ struct ACC { ACCSub subs[9]; char reserved4[18]; char reserved5[69]; - + }; struct DataRecords { diff --git a/patterns/elf.hexpat b/patterns/elf.hexpat index 8b80bab2..71616a67 100644 --- a/patterns/elf.hexpat +++ b/patterns/elf.hexpat @@ -10,6 +10,7 @@ import std.core; import std.io; import std.mem; +import type.magic; using BitfieldOrder = std::core::BitfieldOrder; @@ -515,7 +516,7 @@ bitfield PF { }; struct E_IDENT { - char EI_MAG[4]; + type::Magic<"\x7fELF"> EI_MAG; EI_CLASS EI_CLASS; EI_DATA EI_DATA; EI_VERSION EI_VERSION; @@ -565,7 +566,7 @@ struct Elf32_Phdr { Elf32_Word p_memsz; PF p_flags; Elf32_Word p_align; - + if (p_offset >= 0 && p_filesz > 0 && (p_offset + p_filesz) <= std::mem::size() && p_filesz <= std::mem::size()) u8 p_data[p_filesz] @ p_offset [[sealed]]; }; @@ -579,7 +580,7 @@ struct Elf64_Phdr { Elf64_Xword p_filesz; Elf64_Xword p_memsz; Elf64_Xword p_align; - + if (p_offset >= 0 && p_filesz > 0 && (p_offset + p_filesz) <= std::mem::size() && p_filesz <= std::mem::size()) u8 p_data[p_filesz] @ p_offset [[sealed]]; }; @@ -636,13 +637,13 @@ struct Elf32_Shdr { Elf32_Word sh_info; Elf32_Word sh_addralign; Elf32_Word sh_entsize; - + if (sh_size > 0 && sh_offset + sh_size < std::mem::size()) { if (sh_type == SHT::NOBITS || sh_type == SHT::NULL) { // Section has no data } else if (sh_type == SHT::STRTAB) { String stringTable[while($ < (sh_offset + sh_size))] @ sh_offset; - stringTableIndex = std::core::array_index(); + stringTableIndex = std::core::array_index(); } else if (sh_type == SHT::SYMTAB || sh_type == SHT::DYNSYM) { Elf32_Sym symbolTable[sh_size / sh_entsize] @ sh_offset; } else if (sh_type == SHT::INIT_ARRAY || sh_type == SHT::FINI_ARRAY) { @@ -651,7 +652,7 @@ struct Elf32_Shdr { u8 data[sh_size] @ sh_offset [[sealed]]; } } -} [[format("format_section_header")]];; +} [[format("format_section_header")]]; struct Elf64_Chdr { u32 ch_type; @@ -696,13 +697,13 @@ struct Elf64_Shdr { Elf64_Word sh_info; Elf64_Xword sh_addralign; Elf64_Xword sh_entsize; - + if (sh_size > 0 && sh_offset + sh_size < std::mem::size()) { if (sh_type == SHT::NOBITS || sh_type == SHT::NULL) { // Section has no data } else if (sh_type == SHT::STRTAB) { String stringTable[while($ < (sh_offset + sh_size))] @ sh_offset; - stringTableIndex = std::core::array_index(); + stringTableIndex = std::core::array_index(); } else if (sh_type == SHT::SYMTAB || sh_type == SHT::DYNSYM) { Elf64_Sym symbolTable[sh_size / sh_entsize] @ sh_offset; } else if (sh_type == SHT::INIT_ARRAY || sh_type == SHT::FINI_ARRAY) { @@ -715,7 +716,7 @@ struct Elf64_Shdr { fn format_section_header(auto shdr) { u32 i = 0; - + u32 nameAddress = addressof(elf.shdr[stringTableIndex].stringTable) + shdr.sh_name; String string @ nameAddress; @@ -725,12 +726,12 @@ fn format_section_header(auto shdr) { struct ELF { E_IDENT e_ident; - + if (e_ident.EI_DATA == EI_DATA::ELFDATA2LSB) std::core::set_endian(std::mem::Endian::Little); else std::core::set_endian(std::mem::Endian::Big); - + if (e_ident.EI_CLASS == EI_CLASS::ELFCLASS32) { Elf32_Ehdr ehdr; Elf32_Phdr phdr[ehdr.e_phnum] @ ehdr.e_phoff; diff --git a/patterns/ext4.hexpat b/patterns/ext4.hexpat index f674ce1d..3e891a9c 100644 --- a/patterns/ext4.hexpat +++ b/patterns/ext4.hexpat @@ -211,7 +211,7 @@ struct ext4_super_block { u32 s_journal_dev [[comment("Device number of journal file.")]]; } else { padding[4]; - } + } } else { padding[24]; } @@ -277,7 +277,7 @@ struct ext4_super_block { } else { padding[10]; } - + u32 s_raid_stripe_width [[comment("RAID stripe width. This is the number of logical blocks read from or written to the disk before coming back to the current disk.")]]; if (s_feature_incompat.INCOMPAT_FLEX_BG) { @@ -304,7 +304,7 @@ struct ext4_super_block { } else { padding[20]; } - + u32 s_error_count [[comment("Number of errors seen.")]]; if (s_error_count > 0) { type::time32_t s_first_error_time [[comment("First time an error happened.")]]; @@ -329,7 +329,7 @@ struct ext4_super_block { } else { padding[8]; } - + u32 s_overhead_blocks [[comment("Overhead blocks/clusters in fs. (Huh? This field is always zero, which means that the linux kernel calculates it dynamically.)")]]; if (s_feature_compat.COMPAT_SPARSE_SUPER2) { diff --git a/patterns/flac.hexpat b/patterns/flac.hexpat index b974412a..7de7fe0a 100644 --- a/patterns/flac.hexpat +++ b/patterns/flac.hexpat @@ -22,7 +22,7 @@ enum BLOCK_TYPE : u8 { VORBIS_COMMENT = 4, CUESHEET = 5, PICTURE = 6, - + INVALID = 127 }; @@ -48,7 +48,7 @@ struct METADATA_BLOCK_STREAMINFO { u24 minFrameSize, maxFrameSize; STREAMINFO_FLAGS flags; u128 md5Signature; - + bitsPerSample = flags.bitsPerSample; }; @@ -79,7 +79,7 @@ struct VORBIS_USER_COMMENT { struct METADATA_BLOCK_VORBIS_COMMENT { le u32 vendorLength; u8 vendor[vendorLength]; - + le u32 userCommentListLength; VORBIS_USER_COMMENT userCommentList[userCommentListLength]; }; @@ -168,24 +168,24 @@ bitfield FRAME_HEADER_FLAGS { struct FRAME_HEADER { FRAME_HEADER_FLAGS flags; - + sampleSize = flags.sampleSize; - + if (flags.blockingStrategy) char16 sampleNumber[7]; else char16 frameNumber[6]; - + if (flags.blockSize == 0b0110) u8 blockSize; else if (flags.blockSize == 0b0111) u16 blockSize; - + if (flags.sampleRate == 0b1100) u8 sampleRate; else if (flags.sampleRate == 0b1101 || flags.sampleRate == 0b1110) u16 sampleRate; - + u8 crc8; }; @@ -248,8 +248,8 @@ struct RESIDUAL { RESIDUAL_CODING_METHOD_PARTITIONED_RICE rice; else if (parent.value.codingMethod == 0b01) RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 rice; - - + + if ((parent.parent.header.type & 0b111) == 0b000) u8 samples[(getBitsPerSample() * (parent.parent.parent.header.flags.blockSize - (parent.parent.header.type & 0b111))) / 8]; else if (std::core::array_index() != 0) @@ -278,7 +278,7 @@ struct SUBFRAME_LPC { struct SUBFRAME { SUBFRAME_HEADER header; - + if (header.type == 0b00000) SUBFRAME_CONSTANT constant; else if (header.type == 0b000001) @@ -300,7 +300,7 @@ struct METADATA_BLOCK { METADATA_BLOCK_HEADER header; if (header.lastMetadataBlock) break; - + if (header.blockType == BLOCK_TYPE::STREAMINFO) METADATA_BLOCK_STREAMINFO data; else if (header.blockType == BLOCK_TYPE::PADDING) diff --git a/patterns/fs.hexpat b/patterns/fs.hexpat index e787e368..0e3c7fc3 100644 --- a/patterns/fs.hexpat +++ b/patterns/fs.hexpat @@ -46,25 +46,25 @@ namespace fat32 { padding[12]; u32 trailSignature; }; - + bitfield SequenceNumber { padding : 1; lastLogical : 1; padding : 1; number : 5; } [[bitfield_order(BitfieldOrder::MostToLeastSignificant, 8)]]; - + enum EntryStatus : u8 { Regular = 0x00, DotEntry = 0x2E, DeletedEntry = 0xE5 }; - + union EntryStatusOrSequenceNumber { EntryStatus entryStatus; SequenceNumber sequenceNumber; }; - + bitfield Attributes { readOnly : 1; hidden : 1; @@ -74,7 +74,7 @@ namespace fat32 { archive : 1; padding : 2; } [[bitfield_order(BitfieldOrder::LeastToMostSignificant, 8)]]; - + struct DirEntry { char fileName[8]; char extension[3]; @@ -83,10 +83,10 @@ namespace fat32 { u16 time, date; u16 startingCluster; u32 fileSize; - + u8 data[fileSize] @ startingCluster * bytesPerCluster; }; - + struct VFATDirEntry { EntryStatusOrSequenceNumber entryStatusOrSequenceNumber; char16 name1[5]; @@ -96,7 +96,7 @@ namespace fat32 { char16 name2[6]; u16 startingCluster; char16 name3[2]; - + if (entryStatusOrSequenceNumber.sequenceNumber.number > 1) VFATDirEntry nextLogicalEntry; else @@ -133,9 +133,9 @@ namespace fat32 { char fsType[8]; u8 bootstrapCode[420]; u16 signature; - + bytesPerCluster = (sectorsPerCluster * 1024) * bytesPerSector; - + FSInfo fsInfo @ addressof(this) + fsInfoSector * bytesPerSector; VFATDirEntry rootDirEntry @ addressof(this) + rootCluster * bytesPerCluster; }; @@ -143,13 +143,13 @@ namespace fat32 { } struct PartitionEntry { - PartitionStatus status; + PartitionStatus status; CHS chsFirstSectorAddress; PartitionType type; CHS chsLastSectorAddress; u32 lbaFirstSectorAddress; u32 numSectors; - + if (type == PartitionType::EmptyPartitionEntry) continue; else if (type == PartitionType::FAT32_CHS || type == PartitionType::FAT32_LBA) diff --git a/patterns/gb.hexpat b/patterns/gb.hexpat index 0bce2312..7452b0b6 100644 --- a/patterns/gb.hexpat +++ b/patterns/gb.hexpat @@ -39,7 +39,7 @@ namespace format { } return std::format("size: {}, banks: {}", type::impl::size_formatter(romSize), romBanks); }; - + fn ram_type(u8 ramType) { u16 ramSize; u16 ramBanks; @@ -61,7 +61,7 @@ namespace format { } return std::format("size: {}, banks: {}", type::impl::size_formatter(ramSize), ramBanks); }; - + fn rom_features(u8 type) { str result = "( "; if (brandedROMFeatures) match(type) { @@ -71,37 +71,37 @@ namespace format { match(type) { (0x00): result += "no_mbc"; (0x01): result += "mbc1"; - (0x02): result += "mbc1 | ram"; + (0x02): result += "mbc1 | ram"; (0x03): result += "mbc1 | ram | battery"; - (0x05): result += "mbc2"; + (0x05): result += "mbc2"; (0x06): result += "mbc2 | battery"; - (0x08): result += "no_mbc | ram"; + (0x08): result += "no_mbc | ram"; (0x09): result += "no_mbc | ram | battery"; - (0x0B): result += "mmm01"; + (0x0B): result += "mmm01"; (0x0C): result += "mmm01 | ram"; - (0x0D): result += "mmm01 | ram | battery"; + (0x0D): result += "mmm01 | ram | battery"; (0x0F): result += "mbc3 | timer | battery"; - (0x10): result += "mbc3 | timer | ram | battery"; + (0x10): result += "mbc3 | timer | ram | battery"; (0x11): result += "mbc3"; - (0x12): result += "mbc3 | ram"; + (0x12): result += "mbc3 | ram"; (0x13): result += "mbc3 | ram | battery"; - (0x19): result += "mbc5"; + (0x19): result += "mbc5"; (0x1A): result += "mbc5 | ram"; - (0x1B): result += "mbc5 | ram | battery"; + (0x1B): result += "mbc5 | ram | battery"; (0x1C): result += "mbc5 | rumble"; - (0x1D): result += "mbc5 | rumble | ram"; + (0x1D): result += "mbc5 | rumble | ram"; (0x1E): result += "mbc5 | rumble | ram | battery"; - (0x20): result += "mbc6"; + (0x20): result += "mbc6"; (0x22): result += "mbc7 | sensor | rumble"; - (0xFC): result += "camera"; + (0xFC): result += "camera"; (0xFD): result += "tama5"; - (0xFE): result += "huc3"; + (0xFE): result += "huc3"; (0xFF): result += "huc1 | ram | battery"; } if (uppercaseROMFeatures) result = std::string::to_upper(result); - return result + " )"; + return result + " )"; }; - + fn licensee_code(u8 code) { match(code) { (0x00): return "None"; @@ -204,7 +204,7 @@ namespace format { (0xCA): return "Ultra"; (0xCB): return "Vap"; (0xCC): return "Use Corporation"; - (0xCD): return "Meldac"; + (0xCD): return "Meldac"; (0xD1): return "Sofel"; (0xD2): return "Quest"; (0xD3): return "Sigma Enterprises"; @@ -230,7 +230,7 @@ namespace format { return "Unknown Licensee"; }; - fn new_licensee_code(str a) { + fn new_licensee_code(str a) { if (std::mem::read_unsigned(0x14B, 1) != 0x33) return "See old licensee code"; match(a) { ("00"): return "None"; @@ -302,7 +302,7 @@ namespace format { }; using CGB; - + fn null_cgb_flags(CGB flags) { return "NO_CGB"; }; diff --git a/patterns/gguf.hexpat b/patterns/gguf.hexpat index 298473ba..975c679d 100644 --- a/patterns/gguf.hexpat +++ b/patterns/gguf.hexpat @@ -85,7 +85,7 @@ struct gguf_string_t { struct gguf_metadata_value_t { gguf_metadata_value_type type; u64 length; - + match(type) { (gguf_metadata_value_type::GGUF_METADATA_VALUE_TYPE_UINT8): u8 value[length]; (gguf_metadata_value_type::GGUF_METADATA_VALUE_TYPE_INT8): s8 value[length]; @@ -117,7 +117,7 @@ struct gguf_metadata_value { (gguf_metadata_value_type::GGUF_METADATA_VALUE_TYPE_STRING): gguf_string_t value; (gguf_metadata_value_type::GGUF_METADATA_VALUE_TYPE_UINT64): u64 value; (gguf_metadata_value_type::GGUF_METADATA_VALUE_TYPE_FLOAT64): double value; - (gguf_metadata_value_type::GGUF_METADATA_VALUE_TYPE_ARRAY): gguf_metadata_value_t value; + (gguf_metadata_value_type::GGUF_METADATA_VALUE_TYPE_ARRAY): gguf_metadata_value_t value; } }; @@ -132,7 +132,7 @@ struct gguf_metadata_kv_t { // The type of the value. // Must be one of the `gguf_metadata_value_type` values. // gguf_metadata_value_type value_type; - + // The value. gguf_metadata_value value; }; diff --git a/patterns/gif.hexpat b/patterns/gif.hexpat index ad0209d3..36dd028e 100644 --- a/patterns/gif.hexpat +++ b/patterns/gif.hexpat @@ -27,7 +27,7 @@ bitfield GCT_Flags { colorRes : 3 [[comment("Indicates the richness of the original pallet")]]; enabled : 1; }; - + bitfield ImageDescriptorFlags { lctSize: 3; reserved: 2; @@ -35,7 +35,7 @@ bitfield ImageDescriptorFlags { interlaceFlag: 1; lctEnable: 1; }; - + bitfield GCE_Flags { transparent : 1; userInput : 1; @@ -109,7 +109,7 @@ struct PlainTextExtension { u8 textForegroundColorIndex; u8 textBackgroundColorIndex; DataSubBlocks plainTextData; - + }; struct GraphicControlExtension { @@ -141,9 +141,9 @@ namespace format { if(value == 0x00) return "Do nothing"; if(value == 0x01) return "Do not remove pixels"; if(value == 0x02) return "Restore background pixels"; - if(value == 0x03) return "Restore previous pixels"; + if(value == 0x03) return "Restore previous pixels"; }; - + fn extension_name(u8 label) { if(label == LABEL_GC) return "Graphical Control Extension"; if(label == LABEL_COMMENT) return "Comment Extension"; diff --git a/patterns/gltf.hexpat b/patterns/gltf.hexpat index 34505934..85d94b8f 100644 --- a/patterns/gltf.hexpat +++ b/patterns/gltf.hexpat @@ -1,6 +1,6 @@ /** * @file ImHex Pattern for glTF binary files. - * + * * Copyright (c) 2023 H. Utku Maden * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -28,12 +28,13 @@ import std.mem; import std.io; +import type.magic; /** * @brief The glTF magic section. */ struct gltf_magic_t { - char magic[4]; /**< The magic value. Must be "glTF" */ + type::Magic<"glTF"> magic; /**< The magic value. Must be "glTF" */ u32 version; /**< The version. Must be 2 for glTF 2.0. */ u32 length; /**< Length of the file in bytes, including magic section. */ }; @@ -59,12 +60,11 @@ fn gltf_format(gltf_chunk_type_t x) { if (x == gltf_chunk_type_t::JSON) return "JSON"; else if (x == gltf_chunk_type_t::BIN) return "BIN"; - + return ""; }; gltf_magic_t magic @ 0x00; gltf_chunk_t chunks[while(!std::mem::eof())] @ $; -if (magic.magic != "glTF") - std::error("This file might not be a glTF file, expected \"glTF\", got %s", magic.magic); +std::assert_warn(std::mem::size() == magic.length, "file size mismatch"); diff --git a/patterns/gzip.hexpat b/patterns/gzip.hexpat index 0dd523c8..3389d83b 100644 --- a/patterns/gzip.hexpat +++ b/patterns/gzip.hexpat @@ -55,26 +55,26 @@ struct GZip { type::time32_t modificationTime; ExtraFlags extraFlags; OperatingSystemID operatingSystemId; - + if (flags.FEXTRA) { u16 extraLength; u8 extraField[extraLength]; } - + if (flags.FNAME) { char originalFileName[]; } - + if (flags.FCOMMENT) { char comment[]; } - + if (flags.FHCRC) { u16 crc16; } - + u8 data[while($ < std::mem::size() - 8)] [[sealed]]; - + u32 crc32; type::Size isize; }; diff --git a/patterns/hinf_luas.hexpat b/patterns/hinf_luas.hexpat index 5fe847cf..e9aa7d52 100644 --- a/patterns/hinf_luas.hexpat +++ b/patterns/hinf_luas.hexpat @@ -26,7 +26,7 @@ enum lua_endian : u8 { little_endian = 1, big_endian = 0, }; - + enum lua_numbertype : u8 { _float = 0, _int32 = 1, @@ -136,7 +136,7 @@ enum HksOpCode : s8 { GETSLOT_D, GETGLOBAL_MEM, }; - + enum HksType : u8 { TNIL, @@ -212,10 +212,10 @@ struct LuaConstant { } else if (type == HksType::TLIGHTUSERDATA) { s64 userdata; - } + } else { s64 data; - } + } }; struct HksLocal { @@ -226,7 +226,7 @@ struct HksLocal { struct LuaFunction { - + s32 upvaluecount; s32 paramcount; s8 isVarArg; @@ -238,7 +238,7 @@ struct LuaFunction { } LuaInstruction instructions[instruction_count]; s32 constantCount; - LuaConstant constants[constantCount];; + LuaConstant constants[constantCount]; s32 HasDebugInfo; if (HasDebugInfo != 0) { u32 LineCount; @@ -422,7 +422,7 @@ struct LuaScriptTagDefinition { LuaHeader lua_header; try { char pad[14]; - LuaReferencedTagContainer referencedTags[referencedTags_count.count]; + LuaReferencedTagContainer referencedTags[referencedTags_count.count]; } catch { std::print("This file does not support referenced Tags."); } @@ -438,4 +438,4 @@ Data_Reference Data_References[header.DataReferenceCount] @ header.DependencyCou Tag_Fixup_Reference tagfixupreference[header.TagReferenceCount] @ header.DependencyCount * 0x18 + 0x50 + header.DataBlockCount * 0x10 + header.TagStructCount * 0x20 + header.DataReferenceCount * 0x14; char ZoneSet[header.ZoneSetDataSize] @ header.HeaderSize - header.ZoneSetDataSize; InternalStruct internalstruct @ header.HeaderSize; -LuaScriptTagDefinition lua_script @ header.HeaderSize + 16; \ No newline at end of file +LuaScriptTagDefinition lua_script @ header.HeaderSize + 16; diff --git a/patterns/hinf_module.hexpat b/patterns/hinf_module.hexpat index ac18b112..c60fbe6f 100644 --- a/patterns/hinf_module.hexpat +++ b/patterns/hinf_module.hexpat @@ -5,7 +5,7 @@ import std.string; -struct ModuleHeader +struct ModuleHeader { char magic[4]; s32 version; @@ -68,7 +68,7 @@ struct Module ModuleFile files[module_header.fileCount]; $ = $ + 8; s32 resources[module_header.resourceCount]; - ModuleBlock blocks[module_header.blockCount]; + ModuleBlock blocks[module_header.blockCount]; padding[while($[$] == 0)]; std::print("Compressed Tag Data starts at: {}", $); }; diff --git a/patterns/ico.hexpat b/patterns/ico.hexpat index 436cf1e6..15ab4708 100644 --- a/patterns/ico.hexpat +++ b/patterns/ico.hexpat @@ -31,7 +31,7 @@ struct ICONDIRENTRY { u8 width, height; u8 num_colors; u8 reserved [[hidden]]; - + if (header.type == ImageType::Icon) { u16 color_planes; u16 bits_per_pixel; @@ -39,7 +39,7 @@ struct ICONDIRENTRY { u16 horizontal_hotspot_coordinate; u16 vertical_hotspot_coordinate; } - + u32 image_data_size; ImageData *image_data : u32; }; diff --git a/patterns/id3.hexpat b/patterns/id3.hexpat index 7c69cf48..3562eded 100644 --- a/patterns/id3.hexpat +++ b/patterns/id3.hexpat @@ -40,7 +40,7 @@ namespace v2 { u8 major; u8 revision; }; - + bitfield TagHeaderFlags { unsynchronized : 1; extended : 1; @@ -48,14 +48,14 @@ namespace v2 { footer : 1; padding : 4; }; - + struct TagHeader { char identifier[3]; TagVersion version; TagHeaderFlags flags; SyncSafeInt size; } [[static]]; - + bitfield ExtendedFlag { padding : 1; update : 1; @@ -70,14 +70,14 @@ namespace v2 { ExtendedFlag flags[nflagbytes]; u8 data[size]; }; - + struct TagFooter { char identifier[3]; TagVersion version; TagHeaderFlags flags; SyncSafeInt size; } [[static]]; - + bitfield FrameFlags { padding : 1; tagalterpreservation : 1; @@ -123,7 +123,7 @@ namespace v2 { }; } - + struct Frame { FrameId id; SyncSafeInt size; diff --git a/patterns/intel_hex.hexpat b/patterns/intel_hex.hexpat index dc283920..c4add243 100644 --- a/patterns/intel_hex.hexpat +++ b/patterns/intel_hex.hexpat @@ -1,9 +1,9 @@ #pragma description Intel hex -/* If you have no delimiters between data records then remove +/* If you have no delimiters between data records then remove * the null_bytes field in the data_packet struct. * Set the array at the bottom to the highest index + 1 in the Pattern Data view - * NOTE: these were ascii hex values for me, so use the calculator tool to convert + * NOTE: these were ascii hex values for me, so use the calculator tool to convert * values */ #pragma endian big @@ -29,25 +29,25 @@ struct data_packet { Bytes byte_count [[color("00A9CBB7")]]; u32 address [[color("00F7FF58")]]; FileType recordType [[color("00FF934F")]]; - + // both not A if (byte_count.HOB < 65 && byte_count.LOB < 65) { - u16 data[((byte_count.HOB - 48) * 16) - + (byte_count.LOB - 48)] [[color("0095B46A")]]; + u16 data[((byte_count.HOB - 48) * 16) + + (byte_count.LOB - 48)] [[color("0095B46A")]]; // HOB is A but LOB is not } else if (byte_count.HOB >= 65 && byte_count.LOB < 65) { - u16 data[((byte_count.HOB - 55) * 16) - + (byte_count.LOB - 48)] [[color("0095B46A")]]; + u16 data[((byte_count.HOB - 55) * 16) + + (byte_count.LOB - 48)] [[color("0095B46A")]]; // LOB is A but HOB is not } else if (byte_count.HOB < 65 && byte_count.LOB >= 65) { - u16 data[((byte_count.HOB - 48) * 16) + u16 data[((byte_count.HOB - 48) * 16) + (byte_count.LOB - 55)] [[color("0095B46A")]]; // both are A } else { - u16 data[((byte_count.HOB - 55) * 16) - + (byte_count.LOB - 55)] [[color("0095B46A")]]; + u16 data[((byte_count.HOB - 55) * 16) + + (byte_count.LOB - 55)] [[color("0095B46A")]]; } - + u16 checksum [[color("0045F0DF")]]; u8 line_ending_1 [[color("005E565A")]]; if (line_ending_1 == '\r') diff --git a/patterns/ip.hexpat b/patterns/ip.hexpat index 1b556cbe..ce2e1c76 100644 --- a/patterns/ip.hexpat +++ b/patterns/ip.hexpat @@ -89,9 +89,9 @@ namespace ip { OSPF = 89, SCTP = 132 }; - + namespace udp { - + struct Packet { u16 source_port; u16 destination_port; @@ -100,11 +100,11 @@ namespace ip { u16 checksum; u8 data[length - 8]; }; - + } - + namespace tcp { - + bitfield Flags { data_offset : 4; padding : 3; @@ -118,7 +118,7 @@ namespace ip { syn : 1; fin : 1; }; - + struct Packet { u16 source_port; u16 destination_port; @@ -128,15 +128,15 @@ namespace ip { u16 window_size; u16 checksum; u16 urgent_pointer; - + if (flags.data_offset > 5) u8 options[(flags.data_offset - 5) * sizeof(u32)]; - + u8 data[parent.parent.header.total_length - parent.parent.header.ihl * 4 - flags.data_offset * 4]; }; - + } - + struct Payload { if (parent.protocol == ip::Protocol::UDP) udp::Packet packet [[inline]]; @@ -153,7 +153,7 @@ namespace ipv4 { struct Address { u8 octets[4]; } [[format("ipv4::address_formatter")]]; - + fn address_formatter(Address addr) { return std::format("{0}.{1}.{2}.{3}", addr.octets[0], @@ -168,15 +168,15 @@ namespace ipv4 { tos : 8; total_length : 16; }; - + bitfield Flags { reserved : 1; df : 1; mf : 1; fragment_offset : 13; }; - - + + struct Packet { Header header [[inline]]; u16 identification; @@ -184,16 +184,16 @@ namespace ipv4 { u8 time_to_live; ip::Protocol protocol; u16 header_checksum; - + Address source_ip_address; Address destination_ip_address; - + if (header.ihl > 5) u8 options[(header.ihl - 5) * sizeof(u32)]; - + ip::Payload payload; }; - + } @@ -202,7 +202,7 @@ namespace ipv6 { struct Address { u16 segments[8]; } [[format("ipv6::address_formatter")]]; - + fn address_formatter(Address addr) { return std::format("{0:04X}:{1:04X}:{2:04X}:{3:04X}:{4:04X}:{5:04X}:{6:04X}:{7:04X}", addr.segments[0], @@ -221,16 +221,16 @@ namespace ipv6 { ecn : 2; flow_label : 20; }; - + struct Packet { Header header [[inline]]; u16 payload_length; ip::Protocol next_header; u8 hop_limit; - + Address source_address; Address destination_address; - + ip::Payload payload; }; @@ -254,18 +254,18 @@ bitfield TCI { struct EthernetIIFrame { MAC destination_address; MAC source_address; - + u16 possible_tpid [[no_unique_address, hidden]]; if (possible_tpid == EtherType::VLANTaggedFrame) { u16 tpid; TCI tci [[inline]]; } - + EtherType type; - + Payload payload; std::assert(sizeof(payload) >= 40 && sizeof(payload) <= 1500, std::format("Payload size out of range: {}", sizeof(payload))); - + u32 frame_check_sequence; }; diff --git a/patterns/java_class.hexpat b/patterns/java_class.hexpat index eeda8be4..5aec41d3 100644 --- a/patterns/java_class.hexpat +++ b/patterns/java_class.hexpat @@ -26,7 +26,7 @@ bool applyPadding = false; struct cp_info { bool isTop = false; - if(applyPadding) { + if(applyPadding) { isTop = true; applyPadding = false; } @@ -92,7 +92,7 @@ struct cp_info { } } } -} [[format("fmt::const_name")]]; +} [[format("fmt::const_name")]]; namespace fmt { fn const_name(auto tag) { @@ -115,7 +115,7 @@ namespace fmt { (_): return "Unknown"; } }; - + fn const_ref(u2 index) { cp_info info = file.constant_pool[index-1]; match(info.tag) { @@ -138,7 +138,7 @@ namespace fmt { (_): return std::format("{:d} [Unknown]", index); } }; - + fn const_ref_top(u2 index) { cp_info info = file.constant_pool[index-1]; match(info.tag) { @@ -357,7 +357,7 @@ namespace fmt { (_): return std::format("{:d} [Unknown]", value); } }; - + fn atype_fmt(auto atype) { match(atype) { (4): return "T_BOOLEAN"; @@ -370,11 +370,11 @@ namespace fmt { (11): return "T_LONG"; } }; - + fn instruction_code_fmt(ref auto code) { return fmt::byte_code_fmt(code.mnemonic); }; - + fn attribute(auto info) { return file.constant_pool[info.attribute_name_index-1].bytes; }; @@ -521,7 +521,7 @@ bitfield access_flags_module { padding : 1; // 0x4000 ACC_MANDATED : 1; // 0x8000 } [[bitfield_order(BitfieldOrder::LeastToMostSignificant, 16)]]; - + using attribute_code; struct attribute_constant_value { diff --git a/patterns/jpeg.hexpat b/patterns/jpeg.hexpat index c013093e..1e7269f6 100644 --- a/patterns/jpeg.hexpat +++ b/patterns/jpeg.hexpat @@ -42,7 +42,7 @@ enum Marker : u8 { APP14 = 0xEE, APP15 = 0xEF, COM = 0xFE, - UNKNOWN = 0X00 + UNKNOWN = 0x00 }; enum DensityUnit : u8 { @@ -107,7 +107,7 @@ fn sof0_component_read(SOF0Component c) { fn get_eoi_marker_position() { u32 pos = std::mem::find_sequence_in_range(0, $, std::mem::size(), 0xFF, 0xD9); return pos; - + }; struct SOF0 { @@ -138,9 +138,9 @@ struct Segment { } else { type::Magic<"\xff"> magic; Marker marker; - + if (marker == Marker::SOI || marker == Marker::EOI) { - + } else { u16 length; if (marker == Marker::APP0) { diff --git a/patterns/lcesave.hexpat b/patterns/lcesave.hexpat index 7eeee9a3..07673714 100644 --- a/patterns/lcesave.hexpat +++ b/patterns/lcesave.hexpat @@ -38,7 +38,7 @@ fn getFileType(str filename) { return "Level File"; else return "Unknown DAT"; - + (_): return "File"; } }; @@ -79,14 +79,14 @@ struct LCESave { u8 zlibMagic[2] @ 0x08 [[hidden]]; // check if header matches if (zlibMagic[0] == 0x78 && zlibMagic[1] == 0x9C) - CompressedSave compSave @ 0x00; - + CompressedSave compSave @ 0x00; + // if not we will have continued. le u16 endianCheck @ 0x0A [[hidden]]; // check if version is bigger than 14 if (endianCheck > 14) std::core::set_endian(std::mem::Endian::Big); - + // rest of the processing LCEHeader header [[name("Header")]]; if (header.curVersion > 1) diff --git a/patterns/lnk.hexpat b/patterns/lnk.hexpat index 27f11214..d8715542 100644 --- a/patterns/lnk.hexpat +++ b/patterns/lnk.hexpat @@ -104,7 +104,7 @@ enum Keys : u8 { KEY_X = 0x58, KEY_Y = 0x59, KEY_Z = 0x5A, - + VK_F1 = 0x70, VK_F2 = 0x71, VK_F3 = 0x72, @@ -161,12 +161,12 @@ struct ShellLinkHeader { HotKeyFlags HotKey; padding[2]; padding[4]; - padding[4]; + padding[4]; }; struct ItemID { type::Size16 ItemIDSize; - + if (ItemIDSize == 0x00) break; else @@ -264,15 +264,15 @@ struct CommonNetworkRelativeLink { u32 NetNameOffset; u32 DeviceNameOffset; NetworkProviderType NetworkProviderType; - + if (NetNameOffset > 0x14) { u32 NetNameOffsetUnicode; u32 DeviceNameOffsetUnicode; } - + char NetName[]; char DeviceName[]; - + if (NetNameOffset > 0x14) { char16 NetNameUnicode[]; char16 DeviceNameUnicode[]; @@ -287,22 +287,22 @@ struct LinkInfo { u32 LocalBasePathOffset; u32 CommonNetworkRelativeLinkOffset; u32 CommonPathSuffixOffset; - + if (LinkInfoHeaderSize >= 0x24) { u32 LocalBasePathOffsetUnicode; u32 CommonPathSuffixOffsetUnicode; } - + if (LinkInfoFlags.VolumeIDAndLocalBasePath) { VolumeID VolumeID; char LocalBasePath[]; - } - + } + if (LinkInfoFlags.CommonNetworkRelativeLinkAndPathSuffix) CommonNetworkRelativeLink CommonNetworkRelativeLink; - + char CommonPathSuffix[]; - + if (LinkInfoHeaderSize >= 0x24) { if (LinkInfoFlags.VolumeIDAndLocalBasePath) char16 LocalBasePathUnicode[]; @@ -333,12 +333,12 @@ bitfield FillAttributes { FOREGROUND_GREEN : 1; FOREGROUND_RED : 1; FOREGROUND_INTENSITY : 1; - + BACKGROUND_BLUE : 1; BACKGROUND_GREEN : 1; BACKGROUND_RED : 1; BACKGROUND_INTENSITY : 1; - + padding : 8; }; @@ -417,12 +417,12 @@ struct VistaAndAboveIDListDataBlock { struct ExtraDataBlock { type::Size32 BlockSize; - + if (BlockSize < 0x04) break; else { u32 BlockSignature; - + if (BlockSignature == 0xA000'0002) ConsoleDataBlock CONSOLE_PROPS; else if (BlockSignature == 0xA000'0004) diff --git a/patterns/lua51.hexpat b/patterns/lua51.hexpat index b01e9311..a1bbbdd1 100644 --- a/patterns/lua51.hexpat +++ b/patterns/lua51.hexpat @@ -9,11 +9,11 @@ namespace impl { } return std::format("\"{}\"", string.data); }; - + fn format_Constant(auto constant) { return constant.data; }; - + fn format_Version(auto ver) { return std::format("Ver. {}.{}", ver.major, ver.minor); }; diff --git a/patterns/lua52.hexpat b/patterns/lua52.hexpat index c25ad643..5fd3bb4d 100644 --- a/patterns/lua52.hexpat +++ b/patterns/lua52.hexpat @@ -10,11 +10,11 @@ namespace impl { } return std::format("\"{}\"", string.data); }; - + fn format_Constant(auto constant) { return constant.data; }; - + fn format_Version(auto ver) { return std::format("Ver. {}.{}", ver.major, ver.minor); }; diff --git a/patterns/lua53.hexpat b/patterns/lua53.hexpat index 4dc25d2e..f05d33a8 100644 --- a/patterns/lua53.hexpat +++ b/patterns/lua53.hexpat @@ -10,11 +10,11 @@ namespace impl { } return std::format("\"{}\"", string.data); }; - + fn format_Constant(auto constant) { return constant.data; }; - + fn format_Version(auto ver) { return std::format("Ver. {}.{}", ver.major, ver.minor); }; diff --git a/patterns/lua54.hexpat b/patterns/lua54.hexpat index 7b511f5c..0810decb 100644 --- a/patterns/lua54.hexpat +++ b/patterns/lua54.hexpat @@ -15,7 +15,7 @@ namespace impl { res |= array[sizeof(array)-1] & 0x7f; return res; }; - + fn format_Size(auto leb128) { u128 res = impl::transform_Size_array(leb128.array); return std::format("{} ({:#x})", res, res); @@ -24,18 +24,18 @@ namespace impl { fn transform_Size(auto leb128) { return impl::transform_Size_array(leb128.array); }; - + fn format_LuaString(auto string) { if (string.size == 0) { return "None"; } return std::format("\"{}\"", string.data); }; - + fn format_Constant(auto constant) { return constant.data; }; - + fn format_Version(auto ver) { return std::format("Ver. {}.{}", ver.major, ver.minor); }; @@ -47,7 +47,7 @@ using LuaFunction; struct Size { u8 array[while(addressof(this) == $ || ((addressof(this)-$ < 9) && (std::mem::read_unsigned($-1, 1) & 0x80 == 0)))] [[hidden]]; } [[sealed, format("impl::format_Size"), transform("impl::transform_Size")]]; - + bitfield Version { minor : 4; diff --git a/patterns/lznt1.hexpat b/patterns/lznt1.hexpat index dfbfe441..22cd44b6 100644 --- a/patterns/lznt1.hexpat +++ b/patterns/lznt1.hexpat @@ -104,7 +104,7 @@ struct Tuple { u128 destinationPos = std::mem::get_section_size(decompressedSection); u128 destinationBackrefPos = destinationPos + ct.actualDisplacement; u128 maxNonOverlap = destinationPos - destinationBackrefPos; - + if (ct.actualLength <= maxNonOverlap) { // Not overlapping std::mem::copy_section_to_section(decompressedSection, destinationBackrefPos, diff --git a/patterns/macho.hexpat b/patterns/macho.hexpat index b72911ea..f6a5f96a 100644 --- a/patterns/macho.hexpat +++ b/patterns/macho.hexpat @@ -230,7 +230,7 @@ struct Header { else if (cpuType == CpuType::BS32032 || cpuType == CpuType::BS32332 || cpuType == CpuType::NS32532) SubCpuType32XXX subCpuType; else if (cpuType == CpuType::I386 || cpuType == CpuType::X86_64) SubCpuTypeI386 subCpuType; else if (cpuType == CpuType::MIPS) SubCpuTypeMips subCpuType; - else if (cpuType == CpuType::HPPA) SubCpuTypeHPPA subCpuType; + else if (cpuType == CpuType::HPPA) SubCpuTypeHPPA subCpuType; else if (cpuType == CpuType::ARM) SubCpuTypeARM subCpuType; else if (cpuType == CpuType::MC88000) SubCpuTypeMC88000 subCpuType; else if (cpuType == CpuType::MC98000) SubCpuTypeMC98000 subCpuType; @@ -244,7 +244,7 @@ struct Header { u32 numCommands; type::Size sizeOfCommands; Flags flags; - + if (magic == Magic::_64BitMagic) padding[sizeof(u32)]; }; @@ -312,7 +312,7 @@ struct Section { u32 numRelocs; u32 flags; padding[8]; - + if (offset > 0) u8 data[size] @ offset [[sealed]]; }; @@ -327,9 +327,9 @@ struct CommandSegment { u32 initProtection; u32 numSections; u32 flags; - + Section sections[numSections]; - + if (fileOffset > 0) u8 data[fileSize] @ fileOffset [[sealed]]; }; @@ -345,7 +345,7 @@ struct Section64 { u32 numRelocs; u32 flags; padding[12]; - + if (offset > 0) u8 data[size] @ offset [[sealed]]; }; @@ -360,9 +360,9 @@ struct CommandSegment64 { u32 initProtection; u32 numSections; u32 flags; - + Section64 sections[numSections]; - + if (fileOffset > 0) u8 data[fileSize] @ fileOffset [[sealed]]; }; @@ -370,7 +370,7 @@ struct CommandSegment64 { struct LoadCommand { Command command; type::Size commandSize; - + if (command == Command::UUID) CommandUUID data; else if (command == Command::Segment) diff --git a/patterns/max_v104.hexpat b/patterns/max_v104.hexpat index 82eee8e8..8f5735dd 100644 --- a/patterns/max_v104.hexpat +++ b/patterns/max_v104.hexpat @@ -288,7 +288,7 @@ struct UnitValues struct Complex { u16 object_index; - + if(CheckObjectId(object_index, true) == true) { u16 class_type; @@ -461,11 +461,11 @@ struct PathStep struct Path { u16 object_index; - + if(CheckObjectId(object_index, true) == true) { u16 class_type; - + // Air path if (class_type == 1) { @@ -513,12 +513,12 @@ struct UnitInfo { u16 class_type; UnitType unit_type; - + if (unit_type == UnitType::UNIT_TYPE_DEAD_WALDO) { std::print("Found Waldo!"); } - + u16 hash_id; UnitFlags flags; Point pixel_position; @@ -678,19 +678,19 @@ struct AiPlayer TeamIndex16 target_team; AiMapList map_list; u16 has_info_map; - + if (has_info_map) { u8 info_map[12544]; } - + u16 has_mine_map; - + if (has_mine_map) { u8 mine_map[12544]; } - + Point target_location; }; @@ -722,7 +722,7 @@ fn CheckObjectId(u16 index, bool caller) last_object_index = index; result = true; } - + return result; }; @@ -803,7 +803,7 @@ struct SaveFile MessageLogList message_log_green; MessageLogList message_log_blue; MessageLogList message_log_gray; - + if (team_type_red == TeamType::Computer) { AiPlayer ai_player_red; diff --git a/patterns/minidump.hexpat b/patterns/minidump.hexpat index 0271b833..5c6b0886 100644 --- a/patterns/minidump.hexpat +++ b/patterns/minidump.hexpat @@ -263,7 +263,7 @@ struct MINIDUMP_UNLOADED_MODULE_LIST { if (SizeOfHeader > 12) padding[header.SizeOfHeader - 12]; - + MINIDUMP_UNLOADED_MODULE Modules[NumberOfEntries]; }; @@ -303,7 +303,7 @@ struct MINIDUMP_MEMORY_INFO_LIST { if (SizeOfHeader > 16) padding[SizeOfHeader - 16]; - + MINIDUMP_MEMORY_INFO Info[NumberOfEntries]; }; @@ -327,7 +327,7 @@ struct MINIDUMP_THREAD_INFO_LIST { if (SizeOfHeader > 12) padding[SizeOfHeader - 12]; - + MINIDUMP_THREAD_INFO Info[NumberOfEntries]; }; diff --git a/patterns/mp4.hexpat b/patterns/mp4.hexpat index cc208624..45cac945 100644 --- a/patterns/mp4.hexpat +++ b/patterns/mp4.hexpat @@ -56,7 +56,7 @@ struct BaseBox { u64 largeSize; boxSize = largeSize; endOffset = startOffset + boxSize; - } else if (this.size == 0) { + } else if (this.size == 0) { boxSize = std::mem::size() - startOffset; endOffset = std::mem::size(); } else { diff --git a/patterns/msgpack.hexpat b/patterns/msgpack.hexpat index cdfe6293..ca6e01a2 100644 --- a/patterns/msgpack.hexpat +++ b/patterns/msgpack.hexpat @@ -51,7 +51,7 @@ struct MapEntry { struct MessagePack { Type type; - + if (u8(type) <= 0x7F) { $ -= 1; u8 value; diff --git a/patterns/nacp.hexpat b/patterns/nacp.hexpat index e3d21b2d..18486b6a 100644 --- a/patterns/nacp.hexpat +++ b/patterns/nacp.hexpat @@ -145,7 +145,7 @@ fn main() { std::print("Application Name: {}", nacp.american_english_title.name); std::print("Application Publisher: {}", nacp.american_english_title.publisher); std::print("Application Version: {}", nacp.display_version); - + if (nacp.device_save_data_size_max > 0) std::print("This application has a device save file!"); }; \ No newline at end of file diff --git a/patterns/nbt.hexpat b/patterns/nbt.hexpat index 2ac09f09..9a8ff029 100644 --- a/patterns/nbt.hexpat +++ b/patterns/nbt.hexpat @@ -46,7 +46,7 @@ struct Value { Tag tag; s32 listLength; Value values[listLength] [[static]]; - } else if (parent.tag == Tag::Compound) { + } else if (parent.tag == Tag::Compound) { Element values[while(true)]; } else if (parent.tag == Tag::IntArray){ s32 arrayLength; @@ -67,7 +67,7 @@ struct Element { u16 nameLength; char name[nameLength]; - + Value value; } }; diff --git a/patterns/ne.hexpat b/patterns/ne.hexpat index 6e30fcdb..637ffe50 100644 --- a/patterns/ne.hexpat +++ b/patterns/ne.hexpat @@ -1,4 +1,4 @@ -#pragma description Microsoft DOS NE executable +#pragma description Microsoft DOS NE executable import std.mem; @@ -83,7 +83,7 @@ enum DGroupType : u8 { fn formatDGroupType(u8 value) { DGroupType dgroup = value; - + return dgroup; }; @@ -96,7 +96,7 @@ enum AppType : u8 { fn formatAppType(u8 value) { AppType app = value; - + return app; }; @@ -138,7 +138,7 @@ bitfield OS2EXEFlags { gangloadArea : 1; }; -struct NEHeader { +struct NEHeader { char signature[2]; u8 majorLinkerVersion; u8 minorLinkerVersion; diff --git a/patterns/notepad-cache.hexpat b/patterns/notepad-cache.hexpat index cb7b0395..d5a3cc2f 100644 --- a/patterns/notepad-cache.hexpat +++ b/patterns/notepad-cache.hexpat @@ -1,7 +1,7 @@ #pragma author sethhall #pragma description Windows Notepad Cache Files -// This was written based on the following blog post: +// This was written based on the following blog post: // https://u0041.co/posts/articals/exploring-windows-artifacts-notepad-files/ import type.leb128; diff --git a/patterns/ntag.hexpat b/patterns/ntag.hexpat index 831acd18..ace9056a 100644 --- a/patterns/ntag.hexpat +++ b/patterns/ntag.hexpat @@ -41,22 +41,22 @@ enum TNFType : u8 { struct NDEF { NDEFFlags flags; u8 typeLength; - + if (flags.SR) u8 payloadLength; else u32 payloadLength; - + if (flags.IL) u8 idLength; - + char type[typeLength]; - + if (flags.IL) u8 id[idLength]; - + u8 payload[payloadLength]; - + if (flags.ME) break; }; @@ -102,7 +102,7 @@ struct TLV { // Empty } else { Length length; - + if (length > 0) { if (tag == Tag::LockControl) { LockControl lockControl; @@ -154,25 +154,25 @@ fn validate_ntag_checksum(ManufacturerData mdata) { 0x88 is the CT/cascade digit. This is XOR'd with the first 3 bytes of the serial to calculate BCC0. */ - - u8 bcc0 = 0x88 ^ + + u8 bcc0 = 0x88 ^ mdata.serial1[0] ^ mdata.serial1[1] ^ mdata.serial1[2]; - + if (bcc0 == mdata.checkByte0) { std::print("NTAG BCC0 checksum OK. Value: {}", bcc0); } else { std::warning(std::format("NTAG BCC0 checksum failed! Value: {}", bcc0)); } - + // BCC1 is the XOR of the last 4 serial bytes. - + u8 bcc1 = mdata.serial2[0] ^ mdata.serial2[1] ^ mdata.serial2[2] ^ mdata.serial2[3]; - + if (bcc1 == mdata.checkByte1) { std::print("NTAG BCC1 checksum OK. Value: {}", bcc1); } else { diff --git a/patterns/pck.hexpat b/patterns/pck.hexpat index 08b15109..d2702601 100644 --- a/patterns/pck.hexpat +++ b/patterns/pck.hexpat @@ -23,12 +23,12 @@ namespace format { return pck.lut.table[index].value; }; - + fn property(ref auto p) { return std::format("{}: {}", format::look_up_str(p.key), p.value); }; - + fn assets(ref auto assets) { return std::format("Assets: {}", assets.count); @@ -38,12 +38,12 @@ namespace format { return std::format("count: {}", p.propertyCount); }; - + fn string(ref auto string) { return std::string::to_string(string.value); }; - + fn asset_entry(ref auto e) { return std::string::to_string(e.type); @@ -96,22 +96,22 @@ enum AssetType : s32 //! "0" file InfoFile = 4, //! (x16|x32|x64)Info.pck - TexturePackInfoFile = 5, + TexturePackInfoFile = 5, //! languages.loc/localisation.loc - LocalisationFile = 6, - //! GameRules.grf - GameRulesFile = 7, - //! audio.pck - AudioFile = 8, - //! colours.col - ColourTableFile = 9, - //! GameRules.grh - GameRulesHeader = 10, - //! Skins.pck - SkinDataFile = 11, - //! models.bin - ModelsFile = 12, - //! behaviours.bin + LocalisationFile = 6, + //! GameRules.grf + GameRulesFile = 7, + //! audio.pck + AudioFile = 8, + //! colours.col + ColourTableFile = 9, + //! GameRules.grh + GameRulesHeader = 10, + //! Skins.pck + SkinDataFile = 11, + //! models.bin + ModelsFile = 12, + //! behaviours.bin BehavioursFile = 13, //! entityMaterials.bin MaterialFile = 14, @@ -166,7 +166,7 @@ struct Assets } [[format("format::assets"), transform_entities("transform::assets")]]; struct Pck -{ +{ s32 version; if (version >> 24 & 0xff != 0) { diff --git a/patterns/pe.hexpat b/patterns/pe.hexpat index 192a701b..88174656 100644 --- a/patterns/pe.hexpat +++ b/patterns/pe.hexpat @@ -520,7 +520,7 @@ struct Bitmap { Colors rgbq[bmh.clrUsed*((1 << bmh.bitCount)*!bmh.clrUsed)]; imageDataSize = imageDataSize - sizeof(rgbq); } - + u8 imageData[imageDataSize-sizeof(bmh)]; }; @@ -1041,7 +1041,7 @@ struct LineNumber { bool dataDirectoryInSection[coffHeader.optionalHeader.numberOfRVAsAndSizes]; -fn checkForDataDirectory() { +fn checkForDataDirectory() { for (u32 i = 0, i < coffHeader.optionalHeader.numberOfRVAsAndSizes, i += 1) if (coffHeader.optionalHeader.directories[i].rva - relativeVirtualDifference() < sectionsTable[currentSectionIndex].ptrRawData+sectionsTable[currentSectionIndex].sizeOfRawData && coffHeader.optionalHeader.directories[i].rva - relativeVirtualDifference() >= $) diff --git a/patterns/pfs0.hexpat b/patterns/pfs0.hexpat index 8a584d6c..ca637004 100644 --- a/patterns/pfs0.hexpat +++ b/patterns/pfs0.hexpat @@ -22,7 +22,7 @@ struct Header { u32 numFiles; type::Size stringTableSize; padding[4]; - + FileEntry fileEntryTable[numFiles]; String strings[numFiles]; }; @@ -33,8 +33,8 @@ struct File { }; struct PFS0 { - Header header; - + Header header; + File files[header.numFiles]; }; diff --git a/patterns/pif.hexpat b/patterns/pif.hexpat index fb1bda63..622024fb 100644 --- a/patterns/pif.hexpat +++ b/patterns/pif.hexpat @@ -44,7 +44,7 @@ struct PIFInfoHeader { struct PIF { PIFFileHeader PIF_FileHeader; PIFInfoHeader PIF_ImageHeader; - + if (PIF_ImageHeader.ImageType == imageType_t::IND24) { u24 ColorTable[PIF_ImageHeader.ColorTableSize/3]; @@ -57,7 +57,7 @@ struct PIF { { u8 ColorTable[PIF_ImageHeader.ColorTableSize]; } - + if ((PIF_ImageHeader.ImageType == imageType_t::RGB888) || (PIF_ImageHeader.ImageType == imageType_t::IND24)) { diff --git a/patterns/prodinfo.hexpat b/patterns/prodinfo.hexpat index 20c66320..bb315c54 100644 --- a/patterns/prodinfo.hexpat +++ b/patterns/prodinfo.hexpat @@ -1,5 +1,5 @@ #pragma author WerWolv -#pragma description Nintendo Switch PRODINFO +#pragma description Nintendo Switch PRODINFO enum Model : u16 { NX = 1 @@ -42,15 +42,15 @@ struct PRODINFO { u32 BodySize; Model Model; u16 UpdateCount; - + padding[0x10]; - + u8 BodyHash[0x20]; char ConfigurationId1[0x1E]; - + padding[0x02]; padding[0x20]; - + u32 WlanCountryCodesNum; u32 WlanCountryCodesLastIndex; WlanCountryCode WlanCountryCodes[WlanCountryCodesNum]; @@ -59,9 +59,9 @@ struct PRODINFO { u8 WlanMacAddress[6]; padding[0x10 - 6]; u8 BdAddress[6]; - + padding[0x10 - 6]; - + u8 AccelerometerOffset[6]; padding[2]; u8 AccelerometerScale[6]; @@ -70,50 +70,50 @@ struct PRODINFO { padding[2]; u8 GyroscopeScale[6]; padding[2]; - + char SerialNumber[0x18]; - + padding[8]; - + Cert EccP256Certificate; Cert EccB233Certificate; - + Cert Ecc256ETicket; Cert Ecc233ETicket; - + u8 SslKey[0x110]; padding[0x10]; u32 SslCertificateSize; padding[0x0C]; u8 SslCertificate[0x800]; u8 SslCertificateHash[0x20]; - + u8 RandomNumber[0x1000]; u8 RandomNumberHash[0x20]; - + u8 GameCardKey[0x110]; u8 GameCardCertificate[0x400]; padding[0x10]; u8 GameCardCertificateHash[0x20]; - + u8 Rsa2048ETicketKey[0x220]; padding[0x10]; u8 Rsa2048ETicketCertificate[0x240]; - + padding[0x10]; - + char BatteryLot[0x18]; - + padding[0x08]; - + u8 SpeakerCalibrationValue[0x800]; - + padding[0x10]; - + RegionCode RegionCode; - + padding[0x0C]; - + u8 AmiiboKey[0x50]; padding[0x10]; u8 AmiiboEcqvCertificate[0x14]; @@ -125,21 +125,21 @@ struct PRODINFO { u8 AmiiboEcqvBlsCertificate[0x20]; padding[0x10]; u8 AmiiboEcqvBlsRootCertificate[0x90]; - + padding[0x10]; - + ProductModel ProductModel; - + padding[0x0C]; - + u8 ColorVariation[6]; - + padding[0x0A]; - + u8 LcdBacklightBrightnessMapping[0x0C]; - + padding[0x04]; - + u8 ExtendedEccB233DeviceKey[0x50]; padding[0x10]; u8 ExtendedEccP256ETicketKey[0x50]; @@ -153,19 +153,19 @@ struct PRODINFO { u8 ExtendedGameCardKey[0x130]; padding[0x10]; u32 LcdVendorId; - + padding[0x0C]; - + u8 ExtendedRsa2048DeviceKey[0x240]; padding[0x10]; u8 Rsa2048DeviceCertificate[0x240]; - + padding[0x10]; - + u8 UsbTypeCPowerSourceCircuitVersion; - + padding[0x0F]; - + u32 HousingSubColor; padding[0x0C]; u32 HousingBezelColor; @@ -175,9 +175,9 @@ struct PRODINFO { u32 HousingMainColor2; padding[0x0C]; u32 HousingMainColor3; - + padding[0x0C]; - + u8 AnalogStickModuleTypeL; padding[0x0F]; u8 AnalogStickModelParameterL[0x12]; @@ -189,9 +189,9 @@ struct PRODINFO { u8 AnalogStickModelParameterR[0x12]; padding[0x0E]; u8 AnalogStickFactoryCalibrationR[0x09]; - + padding[0x07]; - + u8 ConsoleSixAxisSensorModuleType; padding[0x0F]; u8 ConsoleSixAxisSensorHorizontalOffset[6]; diff --git a/patterns/protobuf.hexpat b/patterns/protobuf.hexpat index ce17cd7b..2624378d 100644 --- a/patterns/protobuf.hexpat +++ b/patterns/protobuf.hexpat @@ -66,7 +66,7 @@ struct LengthDelimited { struct Entry { Key key; - + if (wire_type == WireType::Varint) type::uLEB128 value; else if (wire_type == WireType::_64Bit) diff --git a/patterns/pyc.hexpat b/patterns/pyc.hexpat index 5cb90ba3..edefd857 100644 --- a/patterns/pyc.hexpat +++ b/patterns/pyc.hexpat @@ -59,7 +59,7 @@ struct MarshalString { u8 length; else u32 length; - + char data[length]; }; @@ -67,50 +67,50 @@ struct MarshalSequence { if(parent.type == TYPE_SMALL_TUPLE) u8 length; else - u32 length; - MarshalObject values[length]; + u32 length; + MarshalObject values[length]; }; struct MarshalCode { if(between(1, 3, 2, 3)) u16 numArgs; else if(above(2, 3)) u32 numArgs; - + if(above(3, 8)) u32 numPosOnlyArgs; - + if(_major >= 3) u32 numKwOnlyArgs; - + if(between(1, 3, 2, 3)) u16 numLocals; else if(between(2, 3, 3, 11)) u32 numLocals; - + if(between(1, 5, 2, 3)) u16 numStack; else if(above(2, 3)) u32 numStack; - + if(between(1, 3, 2, 3)) u16 flags; else if(above(2, 3)) u32 flags; - + MarshalObject code; MarshalObject constants; MarshalObject names; - + if(above(1, 3)) MarshalObject localNames; - + if(above(3, 11)) MarshalObject localKinds; - + if(between(2, 1, 3, 11)) { MarshalObject freeVars; MarshalObject cellVars; } - + MarshalObject fileName; MarshalObject name; - + if(above(3, 11)) MarshalObject qualifiedName; - + if(between(1, 5, 2, 3)) u16 firstLine; else if(above(2, 3)) u32 firstLine; - + if(above(1, 5)) MarshalObject lnTable; - + if(above(3, 11)) MarshalObject exceptionTable; }; @@ -120,7 +120,7 @@ struct DictEntry { MarshalObject value; }; -struct MarshalDict { +struct MarshalDict { DictEntry entries[while(true)]; }; @@ -156,11 +156,11 @@ struct MarshalObject { u8 _type [[hidden]]; u8 flag = _type & FLAG_REF; u8 type = _type & ~FLAG_REF; - + if(flag) { refMember($-1); } - + match(type) { (TYPE_NULL | TYPE_NONE | TYPE_STOPITER | TYPE_ELLIPSIS | TYPE_FALSE | TYPE_TRUE): continue; (TYPE_CODE): { @@ -210,7 +210,7 @@ struct MarshalObject { std::print("Unknown marshal object type: {:c}", type); } } - + } [[format("format_marshal")]]; fn format_marshal(ref auto obj) { @@ -313,23 +313,23 @@ struct PYCHeader { _minor = getMinor(magic); u8 major = _major [[export]]; u8 minor = _minor [[export]]; - + if(above(3, 7)) { u32 flags; } else { u32 flags; } - + if(flags & 0x1) { u64 checksum; } else { type::time32_t timestamp; - + if(above(3, 3)) { u32 size; } } - + MarshalObject object; }; diff --git a/patterns/pyinstaller.hexpat b/patterns/pyinstaller.hexpat index 0c33c631..e49ec2ab 100644 --- a/patterns/pyinstaller.hexpat +++ b/patterns/pyinstaller.hexpat @@ -1,10 +1,10 @@ -#pragma description PyInstaller binray +#pragma description PyInstaller binray #pragma endian big import std.mem; -// Reference: +// Reference: // https://pyinstaller.org/en/stable/advanced-topics.html // typedef struct _cookie { @@ -59,22 +59,22 @@ enum TypeCode : u8 { Module = 109, Script = 115, Data = 120, - RuntimeOption = 111, + RuntimeOption = 111, }; struct TOC { s32 structLen; u32 pos; - u32 len[[comment("len of the data (compressed)")]];; - u32 uLen[[comment("len of data (uncompressed)")]];; - bool cFlag[[comment("is it compressed")]];; + u32 len[[comment("len of the data (compressed)")]]; + u32 uLen[[comment("len of data (uncompressed)")]]; + bool cFlag[[comment("is it compressed")]]; TypeCode typcd; char name[this.structLen - tocStructLength]; - + if(typcd == TypeCode::Zlib) { ZlibArchive zlibArchive @ startOffset + this.pos; } }; -TOC toc[while( $ < cookie.TOClen + tocOffset)] @ tocOffset; \ No newline at end of file +TOC toc[while( $ < cookie.TOClen + tocOffset)] @ tocOffset; diff --git a/patterns/quantized-mesh.hexpat b/patterns/quantized-mesh.hexpat index f4b05c57..c9856dba 100644 --- a/patterns/quantized-mesh.hexpat +++ b/patterns/quantized-mesh.hexpat @@ -25,15 +25,15 @@ struct QuantizedMeshHeader { double CenterX; double CenterY; double CenterZ; - + float MinimumHeight; float MaximumHeight; - + double BoundingSphereCenterX; double BoundingSphereCenterY; double BoundingSphereCenterZ; double BoundingSphereRadius; - + double HorizonOcclusionPointX; double HorizonOcclusionPointY; double HorizonOcclusionPointZ; @@ -112,11 +112,11 @@ fn format_oct16(Oct16 oct) { float xOut; float yOut; float zOut; - + xOut = fromSnorm(oct.x); yOut = fromSnorm(oct.y); zOut = 1.0 - (std::math::abs(xOut) + std::math::abs(yOut)); - + if (zOut < 0.0) { float oldX; @@ -124,7 +124,7 @@ fn format_oct16(Oct16 oct) { xOut = (1.0 - std::math::abs(yOut)) * signNotZero(oldX); yOut = (1.0 - std::math::abs(oldX)) * signNotZero(yOut); } - + return std::format("{}, {}, {}", xOut, yOut, zOut); }; @@ -154,7 +154,7 @@ struct ExtensionHeader { struct QuantizedMesh { QuantizedMeshHeader header; VertexData vertdata; - + if (vertdata.vertexCount > 65536) { IndexData32 indexdata; EdgeIndices32 edgeindices; @@ -162,7 +162,7 @@ struct QuantizedMesh { IndexData16 indexdata; EdgeIndices16 edgeindices; } - + ExtensionHeader extensions[extensionCount]; }; diff --git a/patterns/ras.hexpat b/patterns/ras.hexpat index ef8a5cfc..e78c11d7 100644 --- a/patterns/ras.hexpat +++ b/patterns/ras.hexpat @@ -12,20 +12,20 @@ struct RAS{ //Magic Bytes type::Magic<"\x59\xa6\x6a\x95"> signature; - + //BE -> px -> mm u32 width [[name("Width")]]; u32 height [[name("Height")]]; - + //BE u32 depth; u32 length; u32 type; u32 colorMapType; u32 colorMapLenght; - + //Data Viewer - u8 image_data[length]; + u8 image_data[length]; }; RAS header @0x00; diff --git a/patterns/refs.hexpat b/patterns/refs.hexpat index fa7aff3e..086fc267 100644 --- a/patterns/refs.hexpat +++ b/patterns/refs.hexpat @@ -1,6 +1,6 @@ #pragma author 5h4rrK // https://www.github.com/5h4rrK #pragma organization temabi0s // (https://bi0s.in) -#pragma description ReFS-File-System +#pragma description ReFS-File-System #pragma array_limit 10000 @@ -97,7 +97,7 @@ struct INDEX_HEADER { u64 unused2[[name("UnUsed")]]; u32 end[[name("KeyIndexEnd")]]; u32 align[[name("Alignment")]]; - $ = this.parent.start_pos + 0x50 + this.parent.rootsize + keyindxoff; + $ = this.parent.start_pos + 0x50 + this.parent.rootsize + keyindxoff; OFFSET_ENTRIES entries[keycount][[name("KeyEntries")]]; }; @@ -142,7 +142,7 @@ struct ROOT_NODE{ char comps[rootsize - ($ - start_pos) + 0x50][[name("Components")]]; $ = (start_pos + 0x50 + rootsize); INDEX_HEADER indxhdr[[name("IndexHeader")]]; - // $ = start_pos + 0x50 + rootsize + indxhdr.keyindxoff; + // $ = start_pos + 0x50 + rootsize + indxhdr.keyindxoff; $ = (start_pos + 0x50 + rootsize + indxhdr.size); INDEX_ENTRY_STRUCTURE indxentrystruct[indxhdr.keycount][[name("IndexEntry")]]; }; @@ -214,7 +214,7 @@ struct CHECKPOINT { u32 lenselfdescriptor[[comment("Self Descriptor Size"),name("SelfDescriptorSz")]]; u64 blockno[[comment("Most Recent CheckPoint") , name("BlockNumber")]]; u32 prev_pos = $; - $ = ($ / clustersz) *clustersz + offsetselfdescriptor; + $ = ($ / clustersz) *clustersz + offsetselfdescriptor; SELF_DESCRIPTOR selfdes[[name("SelfDescriptor")]]; $ = prev_pos; $ += (0x28); @@ -238,7 +238,7 @@ struct SUPERBLOCK { }; struct VOLUME_BOOT_RECORD { - BYTE jmpInstruction[3] [[comment("Jump Instruction"), name("JumpInstruction")]];; + BYTE jmpInstruction[3] [[comment("Jump Instruction"), name("JumpInstruction")]]; FILESYSTEM FileSystem[[comment("FileSystemName"), name("FileSystem")]]; BYTE UnKnown[5]; char Identifier[4][[comment("File System Recognition Structure, allows OS to recognise the structure"), name("FSRSIdentifier")]]; diff --git a/patterns/rgbds.hexpat b/patterns/rgbds.hexpat index 5752824c..d00473cb 100644 --- a/patterns/rgbds.hexpat +++ b/patterns/rgbds.hexpat @@ -33,7 +33,7 @@ namespace fstack { LONG iters[depth]; } }; - + LONG nbNodes @ $; Node nodes[nbNodes] @ $; } @@ -54,7 +54,7 @@ namespace sym { LONG value; } }; - + Symbol symbols[nbSym] @ $; } @@ -75,7 +75,7 @@ namespace sect { LONG rpnSize; BYTE rpn[rpnSize]; }; - + enum Type : BYTE { WRAM0, VRAM, @@ -105,7 +105,7 @@ namespace sect { Patch patches[nbPatches]; } }; - + Section sections[nbSect] @ $; } @@ -126,7 +126,7 @@ namespace assert { BYTE rpn[rpnSize]; STRING msg; }; - + LONG nbAsserts @ $; Assertion assertions[nbAsserts] @ $; } diff --git a/patterns/selinux.hexpat b/patterns/selinux.hexpat index 09f5df0d..16b887b2 100644 --- a/patterns/selinux.hexpat +++ b/patterns/selinux.hexpat @@ -99,7 +99,7 @@ struct expression { if ((type_g == policy_types::kernel && version >= 29) || (type_g != policy_types::kernel)) type_set type_names; - } + } }; struct constraint { @@ -235,12 +235,12 @@ struct user_s { if (boundary_feature) u32 bounds; char key[length]; - + if (type_g == policy_types::kernel) extensible_bitmap roles; else role_set roles; - + if ((type_g == policy_types::kernel && version >= 19) || (type_g == policy_types::module && version >= 5 && version < 6) || (type_g == policy_types::base && version >= 5 && version < 6)) { mls_range exp_range; mls_level exp_dftlevel; @@ -258,7 +258,7 @@ struct bool_ { // conditional boolean u32 state; u32 length; char key[length]; - if ((type_g != policy_types::kernel && version >= 14)) + if ((type_g != policy_types::kernel && version >= 14)) u32 flags; }; using bools = symbols_list; @@ -289,7 +289,7 @@ struct symbols { if (symbols_count >= 6) bools bools; if (symbols_count >= 7) - sensitivity_levels levels; + sensitivity_levels levels; if (symbols_count >= 8) categories cats; }; @@ -429,7 +429,7 @@ struct filename_trans_item { struct role_trans_rule_item { role_set roles; role_set types; - if (version >= 12) + if (version >= 12) extensible_bitmap classes; u32 new_role; }; @@ -468,7 +468,7 @@ struct filename_trans_rule_item { u32 tclass; u32 otype; - if (version >= 21) + if (version >= 21) u32 flags; }; using filename_trans_rule = list; @@ -668,10 +668,10 @@ struct Header { if (magic == magics::kernel) type_g = policy_types::kernel; else - if (policy_subtype == policy_types::module) + if (policy_subtype == policy_types::module) type_g = policy_types::module; else - type_g = policy_types::base; + type_g = policy_types::base; u32 __policyvers; version = __policyvers; boundary_feature = (type_g == policy_types::kernel && version >= 24) || (type_g != policy_types::kernel && version >= 9); @@ -680,7 +680,7 @@ struct Header { u32 symbols_count_; symbols_count = symbols_count_; std::assert(0 <= symbols_count && symbols_count <= 9, "Invalid 'symbols_count' value."); - u32 object_contexts_count_; + u32 object_contexts_count_; object_contexts_count = object_contexts_count_; std::assert(0 <= object_contexts_count && object_contexts_count <= 9, "Invalid 'object_contexts_count' value."); if (magic == magics::module) diff --git a/patterns/sit5.hexpat b/patterns/sit5.hexpat index 105b84e6..6eaaa898 100644 --- a/patterns/sit5.hexpat +++ b/patterns/sit5.hexpat @@ -17,19 +17,19 @@ namespace v5 { encrypted : 1; padding : 5; }; - + bitfield Flags2 { padding : 7; resource_fork : 1; padding : 8; }; - + using MacOSHFSPlusDate = u32 [[format("v5::format_macos_date")]]; - + fn format_macos_date(MacOSHFSPlusDate date) { return std::time::format(std::time::to_utc(date - 2082844800)); }; - + struct Header { char magic[0x50]; u32 unknown1; @@ -37,7 +37,7 @@ namespace v5 { u32 entriesOffset; u32 unknown2; }; - + struct EntryHeader { u32 magic; u8 version; @@ -49,12 +49,12 @@ namespace v5 { u32 prevEntryOffset, nextEntryOffset, parentEntryOffset; u16 nameSize; u16 headerChecksum; - + u32 dataForkUncompressedLength, dataForkCompressedLength; u16 dataForkChecksum; u16 unknown3; }; - + enum CompressionMethod : u8 { None = 0x00, // No compression Rle90 = 0x01, // Run length encoding @@ -66,25 +66,25 @@ namespace v5 { Stuffit14 = 0x0E, // Unknown StuffItArsenic = 0x0F // BWT and arithmetic coding }; - + struct Entry { EntryHeader header; if (header.flags.folder) { u16 numFiles; - + if (header.dataForkUncompressedLength) std::print("Folder entry {} is special!", std::core::array_index()); } else { CompressionMethod compressionMethod; } - + u8 passwordDataLength; u8 passwordInformation[passwordDataLength]; char fileName[header.nameSize]; u16 commentSize; u16 unknown2; char comment[commentSize]; - + if (!header.flags.folder) { Flags2 flags; u16 unknown3; @@ -94,25 +94,25 @@ namespace v5 { u32 unknown4; u32 unknown5; u8 unknown6[6]; - + if (header.version == 1) u32 unknown7; - + u8 compressedData[header.dataForkCompressedLength] [[sealed]]; - + if (header.nextEntryOffset == 0x00) break; else $ = header.nextEntryOffset; } }; - + struct StuffIt { Header header; - + Entry entries[while(true)] @ header.entriesOffset; }; - + } v5::StuffIt stuffIt @ 0x00; \ No newline at end of file diff --git a/patterns/stl.hexpat b/patterns/stl.hexpat index 508e21a9..48bf5ae5 100644 --- a/patterns/stl.hexpat +++ b/patterns/stl.hexpat @@ -30,7 +30,7 @@ struct BinarySTLHeader { u32 triangleCount; }; -struct STL { +struct STL { if (std::mem::read_string(0, 6) == "solid ") std::warning("ASCII STL file!"); else { diff --git a/patterns/tar.hexpat b/patterns/tar.hexpat index 3418c156..a9101358 100644 --- a/patterns/tar.hexpat +++ b/patterns/tar.hexpat @@ -61,7 +61,7 @@ struct posix_header { struct tar { posix_header header_raw; char file[octal_to_decimal(header_raw.size)]; - + std::print("-" * 50); std::print("File Name: {}", header_raw.name); std::print("File Permissions: {}" , header_raw.mode); @@ -78,7 +78,7 @@ struct tar { std::print("devmajor: {}", header_raw.devmajor); std::print("devminor: {}", header_raw.devminor); std::print("prefix: {}", header_raw.prefix); - + char empty[while (std::mem::read_string($, 1) == NULL && !std::mem::eof())]; if (std::mem::eof()) { break; diff --git a/patterns/tga.hexpat b/patterns/tga.hexpat index 93c6e8ac..70eff541 100644 --- a/patterns/tga.hexpat +++ b/patterns/tga.hexpat @@ -61,7 +61,7 @@ struct Header { ColorMapSpec colorMapSpec; ImageSpec imageSpec; char imageId[idLength]; - + if (colorMapType == ColorMapType::ColorPalette) { u8 colorMapData[GetColorMapDataSize(colorMapSpec)]; } diff --git a/patterns/ttf.hexpat b/patterns/ttf.hexpat index 13825b1c..1386a76a 100644 --- a/patterns/ttf.hexpat +++ b/patterns/ttf.hexpat @@ -159,7 +159,7 @@ struct CmapSubtable12 : BaseCmapSubtable { struct CmapSubtable { u16 format = std::mem::read_unsigned($, 2, std::mem::Endian::Big); - + match (format) { (0): CmapSubtable0 table [[inline]]; (2): CmapSubtable2 table [[inline]]; @@ -184,12 +184,12 @@ struct EncodingRecord { CmapPlatform platformId; u16 encodingId; u32 offset; - + u64 endEncodingRecord = $; $ = startOfCmapTable + offset; - + CmapSubtable subtable; - + $ = endEncodingRecord; }; @@ -334,7 +334,7 @@ struct CompositeGlyphTable : GlyphTableHeader { struct GlyfTable { s16 type = std::mem::read_signed($, 2, std::mem::Endian::Big); - + if (type >= 0) { SimpleGlyphTable table [[inline]]; } else { @@ -553,7 +553,7 @@ struct NameTableV1 : NameTableV0 { struct NameTable { u16 version = std::mem::read_unsigned($, 2, std::mem::Endian::Big); - + match (version) { (0): NameTableV0 table [[inline]]; (_): NameTableV1 table [[inline]]; @@ -613,7 +613,7 @@ struct OS2TableV5 : OS2TableV4 { struct OS2Table { u16 version = std::mem::read_unsigned($, 2, std::mem::Endian::Big); - + match (version) { (0): OS2TableV0 table [[inline]]; (1): OS2TableV1 table [[inline]]; @@ -648,10 +648,10 @@ struct PostTableV2 : PostTableV1 { struct PostTable { u16 major = std::mem::read_unsigned($, 2, std::mem::Endian::Big); u16 minor = std::mem::read_unsigned($ + 2, 2, std::mem::Endian::Big); - + match (major, minor) { (2, 0): PostTableV2 table [[inline]]; - (_, _): PostTableV1 table [[inline]]; + (_, _): PostTableV1 table [[inline]]; } }; @@ -715,11 +715,11 @@ struct TTF { u16 searchRange; u16 entrySelector; u16 rangeShift; - + u64 start = $; HiddenForPreprocessing hidden[numTables] [[hidden]]; $ = start; - + Table tables[numTables]; }; diff --git a/patterns/uefi.hexpat b/patterns/uefi.hexpat index a520b398..cb7dc633 100644 --- a/patterns/uefi.hexpat +++ b/patterns/uefi.hexpat @@ -2,7 +2,7 @@ #define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002 #define WIN_CERT_TYPE_EFI_PKCS115 0x0EF0 -#define WIN_CERT_TYPE_EFI_GUID 0x0EF1 +#define WIN_CERT_TYPE_EFI_GUID 0x0EF1 struct EFI_TIME { u16 Year; // 1900 – 9999 diff --git a/patterns/uefi_boot_entry.hexpat b/patterns/uefi_boot_entry.hexpat index 59756cc1..926ad73f 100644 --- a/patterns/uefi_boot_entry.hexpat +++ b/patterns/uefi_boot_entry.hexpat @@ -25,7 +25,7 @@ struct FILE_PATH { // subsection 10.3.1 enum MEDIA_DEVICE_PATH_SUBTYPE : u8 { HARD_DRIVE = 0x01, - FILE_PATH = 0x04, + FILE_PATH = 0x04, }; enum DEVICE_PATH_TYPE : u8 { @@ -42,10 +42,10 @@ struct DEVICE_PATH { // the size of the data is the size of the structure minus the fields we know. // not always used u16 dataSize = length-1-1-2; - + match (type, subtype) { (DEVICE_PATH_TYPE::MEDIA_DEVICE_PATH, MEDIA_DEVICE_PATH_SUBTYPE::HARD_DRIVE): HARD_DRIVE data; - (DEVICE_PATH_TYPE::MEDIA_DEVICE_PATH, MEDIA_DEVICE_PATH_SUBTYPE::FILE_PATH): FILE_PATH data; + (DEVICE_PATH_TYPE::MEDIA_DEVICE_PATH, MEDIA_DEVICE_PATH_SUBTYPE::FILE_PATH): FILE_PATH data; (_, _): u8 data[dataSize]; } }; diff --git a/patterns/uf2.hexpat b/patterns/uf2.hexpat index c974cce9..bfdd86bb 100644 --- a/patterns/uf2.hexpat +++ b/patterns/uf2.hexpat @@ -131,7 +131,7 @@ struct UF2_ExtensionTag { u8 size; UF2_TagType type; u8 data[size - 4]; - + if (size == 0 && type.bytes[0] == 0x00 && type.bytes[1] == 0x00 && type.bytes[2] == 0x00) break; }; @@ -144,26 +144,26 @@ struct UF2_Block { u32 payloadSize; u32 blockNo; u32 numBlocks; - + if (flags.FamilyIDPresent) UF2_FamilyID familyId; else u32 fileSize; - + if (flags.MD5ChecksumPresent) { u8 data[452]; - UF2_Hash hash; + UF2_Hash hash; } else { u8 data[476]; } - + if (flags.ExtensionTagsPresent) { UF2_ExtensionTag extensionTags[0xFF]; } - + u32 magicEnd; - + std::assert(magicStart0 == "UF2\n", "Invalid magicStart0 value!"); std::assert(magicStart1 == 0x9E5D5157, "Invalid magicStart1 value!"); std::assert(magicEnd == 0x0AB16F30, "Invalid magicEnd value!"); diff --git a/patterns/usb.hexpat b/patterns/usb.hexpat index ee60bdd8..428707c2 100644 --- a/patterns/usb.hexpat +++ b/patterns/usb.hexpat @@ -19,7 +19,7 @@ enum DescriptorType : u8 { OTGDescriptor = 0x09, DebugDescriptor = 0x0A, InterfaceAssociationDescriptor = 0x0B, - + HIDDescriptor = 0x21, ReportDescriptor = 0x22, PhysicalDescriptor = 0x23 @@ -130,7 +130,7 @@ fn format_bcd(ref auto bcd) { str result; for (s8 i = sizeof(bcd.bytes) - 1, i >= 0, i -= 1) result += std::format("{:X}.", bcd.bytes[i]); - + return std::string::substr(result, 0, std::string::length(result) - 1); }; @@ -168,7 +168,7 @@ struct InterfaceDescriptor { u8 bInterfaceNumber; u8 bAlternateSetting; u8 bNumEndpoints; - + InterfaceClass bInterfaceClass; if (bInterfaceClass == InterfaceClass::Hub) { HubInterfaceSubClass bInterfaceSubClass; @@ -183,7 +183,7 @@ struct InterfaceDescriptor { u8 bInterfaceSubClass; u8 bInterfaceProtocol; } - + u8 iInterface; }; @@ -248,7 +248,7 @@ struct HIDDescriptor { struct USBDescriptor { u8 bLength; DescriptorType bDescriptorType; - + if (bDescriptorType == DescriptorType::DeviceDescriptor) DeviceDescriptor deviceDescriptor [[inline]]; else if (bDescriptorType == DescriptorType::ConfigDescriptor) @@ -267,7 +267,7 @@ struct USBDescriptor { OTGDescriptor otgDescriptor [[inline]]; else if (bDescriptorType == DescriptorType::HIDDescriptor) HIDDescriptor hidDescriptor [[inline]]; - + padding[bLength - ($ - addressof(this))]; }; diff --git a/patterns/vbmeta.hexpat b/patterns/vbmeta.hexpat index 69f6d7c1..9681b08b 100644 --- a/patterns/vbmeta.hexpat +++ b/patterns/vbmeta.hexpat @@ -157,7 +157,7 @@ struct AvbDescriptor { AvbDescriptorTag tag; uint64_t num_bytes_following; u8 data[num_bytes_following] [[hidden]]; - + match (tag) { (AvbDescriptorTag::AVB_DESCRIPTOR_TAG_PROPERTY) : AvbPropertyDescriptor descriptor @ addressof(data); (AvbDescriptorTag::AVB_DESCRIPTOR_TAG_HASHTREE) : AvbHashtreeDescriptor descriptor @ addressof(data); @@ -204,7 +204,7 @@ struct AvbVBMetaImage header.hash_offset, header.hash_size, header.signature_offset, header.signature_size> authentication_data; - + AuxiliaryData 0) { $ = chpClkBase + chpClkOffset; chpClkHeader chpClk; @@ -232,7 +232,7 @@ struct ExtraHeader { $ = chpVolBase + chpVolOffset; chpVolHeader chpVol; } - + }; struct VGM { diff --git a/patterns/vhdx.hexpat b/patterns/vhdx.hexpat index 0c9bac9f..7831d7c8 100644 --- a/patterns/vhdx.hexpat +++ b/patterns/vhdx.hexpat @@ -38,7 +38,7 @@ struct DataDescriptor { struct LogDescriptor { char signature[4]; - + if (signature == "zero") ZeroDescriptor descriptor [[inline]]; else if (signature == "desc") @@ -103,7 +103,7 @@ struct ParentLocatorEntry { u32 valueOffset; u16 keyLength; u16 valueLength; - + char16 key[keyLength / 2] @ addressof(parent) + keyOffset; char16 value[valueLength / 2] @ addressof(parent) + valueOffset; }; @@ -161,7 +161,7 @@ struct RegionTable { u32 checksum; u32 entryCount; padding[4]; - + RegionTableEntry entries[entryCount]; }; diff --git a/patterns/wad.hexpat b/patterns/wad.hexpat index be3661da..42fb54bd 100644 --- a/patterns/wad.hexpat +++ b/patterns/wad.hexpat @@ -13,7 +13,7 @@ struct FileLump { u32 filePos; type::Size size; char name[8]; - + u8 data[size] @ filePos [[sealed]]; }; diff --git a/patterns/wav.hexpat b/patterns/wav.hexpat index a52125dc..e8595726 100644 --- a/patterns/wav.hexpat +++ b/patterns/wav.hexpat @@ -53,7 +53,7 @@ struct WaveFormatPCM { }; struct WaveMSADPCMCoefSet { - s16 coef1; + s16 coef1; s16 coef2; }; diff --git a/patterns/webp.hexpat b/patterns/webp.hexpat index 611b76b2..9dd10a8d 100644 --- a/patterns/webp.hexpat +++ b/patterns/webp.hexpat @@ -79,7 +79,7 @@ struct WebPANMFData { u24 frameDuration; ANMFFlags flags; u8 data[parent.chunkHeader.chunkSize - 16]; // lazy fix - can't be bothered implementing subchunks - + }; bitfield ALPHFlags { @@ -97,7 +97,7 @@ struct WebPData { match (chunkHeader.chunkId) { ("VP8X"): WebPVP8XData VP8XData; ("ANIM"): WebPANIMData ANIMData; - ("ANMF"): { + ("ANMF"): { WebPANMFData ANMFData; padding[paddedChunkSize - sizeof(ANMFData)]; } @@ -116,7 +116,7 @@ struct WebPData { padding[paddedChunkSize - sizeof(data)]; } } -} [[name(std::format("Chunk ({})", chunkHeader.chunkId))]];; +} [[name(std::format("Chunk ({})", chunkHeader.chunkId))]]; RiffHeader header @0x00; WebPData data[while (!std::mem::eof())] @ $; diff --git a/patterns/wintec_tes.hexpat b/patterns/wintec_tes.hexpat index cc902764..ca2bb8b0 100644 --- a/patterns/wintec_tes.hexpat +++ b/patterns/wintec_tes.hexpat @@ -1,6 +1,6 @@ // Wintec TES file // -// Wintec produced small GPS-Loggers that created TES files. Each file +// Wintec produced small GPS-Loggers that created TES files. Each file // is a series of timestamps and coordinates. The format is exceedingly // simple. The same 16 byte struct is repeated from start to finish. // diff --git a/patterns/xbeh.hexpat b/patterns/xbeh.hexpat index 2ba70b26..93e4a1f8 100644 --- a/patterns/xbeh.hexpat +++ b/patterns/xbeh.hexpat @@ -61,11 +61,11 @@ union EntryPoint { u32 betaAddress [[format("format_beta_entrypoint")]]; u32 debugAddress [[format("format_debug_entrypoint")]]; u32 retailAddress [[format("format_retail_entrypoint")]]; - + if ((betaAddress ^ 0xE682F45B) - parent.baseAddress < std::mem::size()) - u8 beta @ (betaAddress ^ 0xE682F45B) - parent.baseAddress;; - if ((debugAddress ^ 0x94859D4B) - parent.baseAddress < std::mem::size()) - u8 debug @ (debugAddress ^ 0x94859D4B) - parent.baseAddress;; + u8 beta @ (betaAddress ^ 0xE682F45B) - parent.baseAddress; + if ((debugAddress ^ 0x94859D4B) - parent.baseAddress < std::mem::size()) + u8 debug @ (debugAddress ^ 0x94859D4B) - parent.baseAddress; if ((retailAddress ^ 0xA8FC57AB) - parent.baseAddress < std::mem::size()) u8 retail @ (retailAddress ^ 0xA8FC57AB) - parent.baseAddress; }; @@ -301,9 +301,9 @@ struct KernelImageThunk { union KernelImageThunkAddress { u32 debugAddress [[format("format_debug_kernel_image_thunk_address")]]; u32 retailAddress [[format("format_retail_kernel_image_thunk_address")]]; - - if ((debugAddress ^ 0xEFB1F152) - parent.baseAddress < std::mem::size()) - KernelImageThunk debug @ (debugAddress ^ 0xEFB1F152) - parent.baseAddress;; + + if ((debugAddress ^ 0xEFB1F152) - parent.baseAddress < std::mem::size()) + KernelImageThunk debug @ (debugAddress ^ 0xEFB1F152) - parent.baseAddress; if ((retailAddress ^ 0x5B6D40B6) - parent.baseAddress < std::mem::size()) KernelImageThunk retail @ (retailAddress ^ 0x5B6D40B6) - parent.baseAddress; }; @@ -365,7 +365,7 @@ struct SectionHeader { u16 *headSharedPageReferenceCount : u32 [[pointer_base("relative_to_base_section")]]; u16 *tailSharedPageReferenceCount : u32 [[pointer_base("relative_to_base_section")]]; u8 sectionDigest[20]; - + u8 data[rawSize] @ rawAddress [[sealed]]; }; @@ -381,7 +381,7 @@ struct XBEH { InitializationFlags initializationFlags; EntryPoint entrypoint; TLS *tls : u32 [[pointer_base("relative_to_base")]]; - + type::Size stackSize; u32 peHeapReserve, peHeapCommit, peBaseAddress; type::Size peImageSize; @@ -399,8 +399,8 @@ struct XBEH { u32 logoBitMapAddress, logoBitmapSize; u64 unknown1; u32 unknown2; - + u8 logoBitmap[logoBitmapSize] @ logoBitMapAddress - baseAddress; }; -XBEH xbeh @ 0x00; \ No newline at end of file +XBEH xbeh @ 0x00; diff --git a/patterns/xci.hexpat b/patterns/xci.hexpat index 80d4d8c3..6c24f7a0 100644 --- a/patterns/xci.hexpat +++ b/patterns/xci.hexpat @@ -57,7 +57,7 @@ struct CardHeader { u32 selT1Key; u32 selKey; u32 limArea; - + u8 cardHeaderEncryptedData[0x70]; }; @@ -86,15 +86,15 @@ struct PartitionFs { padding[4]; FileEntry fileEntryTable[fileCount]; String stringTable[while($ < (addressof(fileEntryTable) + sizeof(fileEntryTable) + stringTableSize))]; - + File files[fileCount]; }; struct XCI { CardHeader header; - + PartitionFs x @ header.romAreaStartPageAddress * PAGE_SIZE; - + }; XCI xci @ 0x00; \ No newline at end of file diff --git a/patterns/xgspak.hexpat b/patterns/xgspak.hexpat index 45cd4541..264a4a7a 100644 --- a/patterns/xgspak.hexpat +++ b/patterns/xgspak.hexpat @@ -32,7 +32,7 @@ enum PakVersion : u8 { Version0, //NFS Wii/Trilogy 3DS/DLS Version1, //ABGO V1/ABTF, AB Console Ports - Version2 //ABGO V2+ + Version2 //ABGO V2+ }; enum CompressionType : u32 @@ -67,7 +67,7 @@ struct XGSPakHeader struct XGSPakFolderBase { if (Head.ver == PakVersion::Version0) - { + { char *name[] : u32[[pointer_base("offToStringTable")]]; u32 filesInFolder; u32 subfolders; @@ -102,7 +102,7 @@ struct XGSPakFolderBase struct XGSPakContentMeta { if (Head.ver == PakVersion::Version0) - { + { char *name[] : u32 [[pointer_base("offToStringTable")]]; u32 decompFileSize; u32 fileOff; diff --git a/patterns/xgstexture.hexpat b/patterns/xgstexture.hexpat index b176cab4..2e2bfb20 100644 --- a/patterns/xgstexture.hexpat +++ b/patterns/xgstexture.hexpat @@ -24,7 +24,7 @@ enum XGSPlatform : u8 DURANGO, //Xbox One WINPHONE, //PCOGL WINPHONE_LEGACY, //WP8 - APPLE_TV, //Apple TV + APPLE_TV, //Apple TV }; enum XGSTextureFormat : u16 @@ -43,17 +43,17 @@ enum XGSTextureFormat : u16 PVR_UNK, //2BPP no alpha? Crashes in ABGO v101 RGB888, //Greyscale? unused, crashes in EOP but works in ABGO v101 AL88, - //14, //8BPP something? Crash on EOP and PS3, on ABGO v101, nothing - PVRTC_2BPP_RGBA = 15, //Crash on PS3, on ABGO v101, nothing - PVRTC_4BPP_RGBA, //Crash on PS3 or is this 2bppRGBA, on ABGO v101, nothing - PVRTC_4BPP_RGBA_2, //May need to swap these two, crash on PS3, on ABGO v101, nothing - PVRTC_4BPP_RGBA_3, //Crashes on EOP and PS3, on ABGO v101, nothing - //19, //Crash on PS3 and EOP, on ABGO v101, nothing - //20, //Crash on PS3 and EOP, on ABGO v101, nothing - //21, //Crash on PS3 and EOP, on ABGO v101, nothing - RG88_OR_GR88 = 22, //Unused. On PS3, it's unswizzled GR88, crashes on EOP, on ABGO v101, nothing - L4, //Unused, crashes on EOP, on ABGO v101, nothing - DXT1_OR_ETC1, //On Android this is usually DXT1, old Android has this DXT1 swizzled, null on PS3, crash on EOP, on ABGO v101, nothing + //14, //8BPP something? Crash on EOP and PS3, on ABGO v101, nothing + PVRTC_2BPP_RGBA = 15, //Crash on PS3, on ABGO v101, nothing + PVRTC_4BPP_RGBA, //Crash on PS3 or is this 2bppRGBA, on ABGO v101, nothing + PVRTC_4BPP_RGBA_2, //May need to swap these two, crash on PS3, on ABGO v101, nothing + PVRTC_4BPP_RGBA_3, //Crashes on EOP and PS3, on ABGO v101, nothing + //19, //Crash on PS3 and EOP, on ABGO v101, nothing + //20, //Crash on PS3 and EOP, on ABGO v101, nothing + //21, //Crash on PS3 and EOP, on ABGO v101, nothing + RG88_OR_GR88 = 22, //Unused. On PS3, it's unswizzled GR88, crashes on EOP, on ABGO v101, nothing + L4, //Unused, crashes on EOP, on ABGO v101, nothing + DXT1_OR_ETC1, //On Android this is usually DXT1, old Android has this DXT1 swizzled, null on PS3, crash on EOP, on ABGO v101, nothing LA44, //Crash on EOP, on ABGO v101, nothing DXT5, //Crash on EOP, on ABGO v101, nothing //?, //Crash on EOP, on ABGO v101, nothing, note starting here PS3 crashes @@ -92,7 +92,7 @@ bitfield XGSTextureFlags struct XGSTextureHeader { - type::Magic"XGST"> magic; + type::Magic<"XGST"> magic; u8 dataOffset; XGSPlatform compilationPlatform; //Definitely platform, this has no harm when modifying (except in Edge of Perception) but does help with knowing platform specific compressions, usually u16 headerSize; //? @@ -113,4 +113,4 @@ struct XGSTextureHeader }; XGSTextureHeader Head @ $; -u8 Texture[Head.dataSize] @ $; \ No newline at end of file +u8 Texture[Head.dataSize] @ $; diff --git a/patterns/xilinx_bit.hexpat b/patterns/xilinx_bit.hexpat index c92122ef..05bf7100 100644 --- a/patterns/xilinx_bit.hexpat +++ b/patterns/xilinx_bit.hexpat @@ -21,7 +21,7 @@ struct Command { fn format_command(Command command) { u32 x = command.value; - + if (x == 0x20000000) return "NOP"; if (x == 0xAA995566) return "SYNC"; if (x == 0x000000BB) return "Bus Width Sync"; @@ -32,10 +32,10 @@ fn format_command(Command command) { if (x == 0x30018001) return "Write to IDCODE"; if (x == 0x30004000) return "Write to FDRI"; if (x == 0x30008001) return "Write to CMD"; - + if ((x & 0xF0000000) == 0x30000000) return std::format("Write to Register {}", (x & 0x0003E000) >> 13); - + return std::format("0x{:08X}", x); }; diff --git a/patterns/zip.hexpat b/patterns/zip.hexpat index 1800912f..c4df97d7 100644 --- a/patterns/zip.hexpat +++ b/patterns/zip.hexpat @@ -20,15 +20,15 @@ struct EndOfCentralDirectory { namespace extra { - + bitfield Flags { bool modification_time_set : 1; - bool access_time_set : 1; + bool access_time_set : 1; bool creation_time_set : 1; reserved: 5; //reserved for additional timestamps; not set }; - - struct X5455_ExtendedTimestamp { + + struct X5455_ExtendedTimestamp { Flags Flags; if (Flags.modification_time_set){ u32 ModTime; @@ -40,7 +40,7 @@ namespace extra { u32 CrTime; } }; - + struct X000A_NTFS { u32 reserved; u16 tag; @@ -50,8 +50,8 @@ namespace extra { type::FILETIME AcTime; type::FILETIME CrTime; }; - - struct X7875_NewUnix { + + struct X7875_NewUnix { u16 tag; u16 TSize; u8 version; @@ -60,7 +60,7 @@ namespace extra { u8 GIDSize; u8 GID[GIDSize]; }; - + struct X5855_InfoZipUnix { u32 AcTime; u32 ModTime; @@ -71,7 +71,7 @@ namespace extra { u16 GID; } }; - + struct ExtraField { u16 tag; u16 TSize; @@ -83,7 +83,7 @@ namespace extra { extra::X7875_NewUnix x7875_NewUnix; }else if (tag == 0x5855){ extra::X5855_InfoZipUnix x5855_InfoZipUnix; - }else{ + }else{ std::print("Unsupported tag 0x{:02X}", tag); padding[TSize]; } @@ -98,25 +98,25 @@ fn find_eocd() { return std::mem::size()-22; } else { // If it's not there, then there's probably a zip comment; - // search the last 64KB of the file for the signature. + // search the last 64KB of the file for the signature. u128 offset_search_from = std::math::max(0, std::mem::size()-65536-22); u128 prev_address; while(1){ - u128 current_address = std::mem::find_sequence_in_range(0, offset_search_from, std::mem::size(), 0x50,0x4B,0x05,0x06); - + u128 current_address = std::mem::find_sequence_in_range(0, offset_search_from, std::mem::size(), 0x50,0x4B,0x05,0x06); + //Reached EOF and did not find valid eocd. if (current_address == 340282366920938463463374607431768211455){ std::error("Could not find EOCD."); } - + //Potential eocd found. Create a eocd struct EndOfCentralDirectory EOCD @ current_address; - + //If central directory file header is valid, then we know the eocd offset is valid. if (std::mem::read_unsigned(EOCD.CDOffset, 4, std::mem::Endian::Little) == 0x2014B50){ return current_address; } - + offset_search_from = current_address + 1; prev_address = current_address; } diff --git a/patterns/zlib.hexpat b/patterns/zlib.hexpat index 385ad503..4c7ad5ec 100644 --- a/patterns/zlib.hexpat +++ b/patterns/zlib.hexpat @@ -22,7 +22,7 @@ if (data_len > (HEADER_SIZE + CSUM_SIZE)) { /// Compression method; only Deflate is used enum CM: u8 { Deflate = 8, - Reserved = 15, + Reserved = 15, }; /// Compression level @@ -43,7 +43,7 @@ bitfield Header { } [[bitfield_order(BitfieldOrder::LeastToMostSignificant, 16)]]; -/// Validate the header's checksum +/// Validate the header's checksum fn validate_hdr_checksum(Header hdr) { // Reassemble as a 2-byte big endian value u16 value = ( @@ -53,7 +53,7 @@ fn validate_hdr_checksum(Header hdr) { (hdr.fdict << 5) + (u8(hdr.flevel) << 6) ); - + if (value % 31 == 0) { std::print("Zlib header checksum OK. Value: {}", value); } else { @@ -65,12 +65,12 @@ fn validate_hdr_checksum(Header hdr) { struct Zlib { /// Configuration Header header; - + if (header.fdict) { /// Adler checksum of the optional dictionary u32 dict; } - + /// Compressed data u8 data[data_len]; /// Adler-32 checksum of uncompressed data diff --git a/tests/includes/source/main.cpp b/tests/includes/source/main.cpp index 94b2e04e..244f0f40 100644 --- a/tests/includes/source/main.cpp +++ b/tests/includes/source/main.cpp @@ -31,7 +31,7 @@ int main(int argc, char **argv) { return false; }; - runtime.setDataSource(0x00, 0x100000, + runtime.setDataSource(0x00, 0x100000, [&](pl::u64 address, pl::u8 *data, size_t size) { pl::core::err::E0011.throwError("Include files should never read from memory directly!"); } diff --git a/tests/patterns/source/main.cpp b/tests/patterns/source/main.cpp index ade89dd4..295e3f1f 100644 --- a/tests/patterns/source/main.cpp +++ b/tests/patterns/source/main.cpp @@ -47,7 +47,7 @@ int main(int argc, char **argv) { return true; }; - runtime.setDataSource(0x00, testFile.getSize(), + runtime.setDataSource(0x00, testFile.getSize(), [&](pl::u64 address, pl::u8 *data, size_t size) { testFile.seek(address); testFile.readBuffer(data, size); @@ -58,7 +58,7 @@ int main(int argc, char **argv) { runtime.addPragma("MIME", DummyPragmaHandler); runtime.addPragma("description", DescPragmaHandler); runtime.addDefine("__PL_UNIT_TESTS__"); - + runtime.setLogCallback([](auto level, const std::string &message) { switch (level) { using enum pl::core::LogConsole::Level; diff --git a/yara/advanced_analysis/language.yar b/yara/advanced_analysis/language.yar index 0fbc0379..ebc16846 100644 --- a/yara/advanced_analysis/language.yar +++ b/yara/advanced_analysis/language.yar @@ -6,7 +6,7 @@ rule LanguageCpp { strings: $exception_windows = "_CxxThrowException" ascii fullword $iostreams = "iostream" ascii - + condition: any of them } From 6f9b05b853131a75fbccfa0b51f78792ad4dcb7e Mon Sep 17 00:00:00 2001 From: Bananchiki Date: Sun, 24 Nov 2024 16:51:06 +0300 Subject: [PATCH 24/29] themes: Added One Dark theme (#322) * Added One Dark theme * theme: fixed spacing --- themes/one_dark.json | 343 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 343 insertions(+) create mode 100644 themes/one_dark.json diff --git a/themes/one_dark.json b/themes/one_dark.json new file mode 100644 index 00000000..545c904c --- /dev/null +++ b/themes/one_dark.json @@ -0,0 +1,343 @@ +{ + "base": "Dark", + "colors": { + "imgui": { + "border": "#6969777F", + "border-shadow": "#00000000", + "button": "#66748466", + "button-active": "#1D90FCFF", + "button-hovered": "#4296F9FF", + "check-mark": "#4296F9FF", + "child-background": "#282C34FF", + "docking-empty-background": "#0F0F0FEF", + "docking-preview": "#4296F9B2", + "drag-drop-target": "#FFFF00E5", + "frame-background": "#4F545889", + "frame-background-active": "#646A6F89", + "frame-background-hovered": "#77818889", + "header": "#4296F94F", + "header-active": "#4296F9FF", + "header-hovered": "#4185CFFF", + "menu-bar-background": "#1F2228FF", + "modal-window-dim-background": "#CCCCCC59", + "nav-highlight": "#4296F9FF", + "nav-windowing-background": "#CCCCCC33", + "nav-windowing-highlight": "#FFFFFFB2", + "plot-histogram": "#E5B200FF", + "plot-histogram-hovered": "#FF9900FF", + "plot-lines": "#9B9B9BFF", + "plot-lines-hovered": "#FF6D59FF", + "popup-background": "#24262FFF", + "resize-grip": "#3B414BFF", + "resize-grip-active": "#4B525DFF", + "resize-grip-hovered": "#4B525DFF", + "scrollbar-background": "#12161F00", + "scrollbar-grab": "#3B414BFF", + "scrollbar-grab-active": "#4B525DFF", + "scrollbar-grab-hovered": "#4B525DFF", + "separator": "#6565807F", + "separator-active": "#446BBBFF", + "separator-hovered": "#1966BFC6", + "slider-grab": "#3D84E0FF", + "slider-grab-active": "#4296F9FF", + "tab": "#212227FF", + "tab-active": "#42454FFF", + "tab-hovered": "#636673FF", + "tab-unfocused": "#1A1B1FFF", + "tab-unfocused-active": "#3F4148FF", + "table-border-light": "#3A3A3FFF", + "table-border-strong": "#3F3F47FF", + "table-header-background": "#272A31FF", + "table-row-background": "#00000000", + "table-row-background-alt": "#10101040", + "text": "#FFFFFFFF", + "text-disabled": "#808080FF", + "text-selected-background": "#4296F959", + "title-background": "#1F2228FF", + "title-background-active": "#202125FF", + "title-background-collapse": "#202125FF", + "window-background": "#292B34FF", + "window-shadow": "#000000FF" + }, + "imhex": { + "IEEE-tool-exp": "#5D7F5DFF", + "IEEE-tool-mantissa": "#7F5D5DFF", + "IEEE-tool-sign": "#5D5D7FFF", + "achievement-unlocked": "#F1C40FFF", + "advanced-encoding-ascii": "#06539BFF", + "advanced-encoding-multi": "#F1C40FFF", + "advanced-encoding-single": "#E74C3CFF", + "advanced-encoding-unknown": "#E74C3CFF", + "blur-background": "#00000000", + "desc-button": "#2A2C38FF", + "desc-button-active": "#3C3C3CFF", + "desc-button-hovered": "#242424FF", + "diff-added": "#388B42FF", + "diff-changed": "#F1C40FFF", + "diff-removed": "#E74C3CFF", + "find-highlight": "#672A78FF", + "highlight": "#4DC69BFF", + "logger-debug": "#388B42FF", + "logger-error": "#E74C3CFF", + "logger-fatal": "#672A78FF", + "logger-info": "#FFFFFFFF", + "logger-warning": "#F1C40FFF", + "patches": "#E74C3CFF", + "pattern-selected": "#67B5FFFF", + "toolbar-blue": "#06539BFF", + "toolbar-brown": "#DBB377FF", + "toolbar-gray": "#E6E6E6FF", + "toolbar-green": "#388B42FF", + "toolbar-purple": "#672A78FF", + "toolbar-red": "#E74C3CFF", + "toolbar-yellow": "#F1C40FFF" + }, + "imnodes": { + "box-selector": "#3D85E01E", + "box-selector-outline": "#3D85E096", + "grid-background": "#282832C8", + "grid-line": "#C8C8C828", + "grid-line-primary": "#F0F0F03C", + "link": "#3D85E0C8", + "link-hovered": "#4296FAFF", + "link-selected": "#4296FAFF", + "mini-map-background": "#19191996", + "mini-map-background-hovered": "#191919C8", + "mini-map-canvas": "#C8C8C819", + "mini-map-canvas-outline": "#C8C8C8C8", + "mini-map-link": "#3D85E0C8", + "mini-map-link-selected": "#4296FAFF", + "mini-map-node-background": "#C8C8C864", + "mini-map-node-background-hovered": "#C8C8C8FF", + "mini-map-node-background-selected": "#C8C8C8FF", + "mini-map-node-outline": "#C8C8C864", + "mini-map-outline": "#96969664", + "mini-map-outline-hovered": "#969696C8", + "node-background": "#323232FF", + "node-background-hovered": "#4B4B4BFF", + "node-background-selected": "#4B4B4BFF", + "node-outline": "#646464FF", + "pin": "#F5CB25FF", + "pin-hovered": "#FA8335FF", + "title-bar": "#294A7AFF", + "title-bar-hovered": "#4296FAFF", + "title-bar-selected": "#4296FAFF" + }, + "implot": { + "axis-bg": "#00000000", + "axis-bg-active": "#00000000", + "axis-bg-hovered": "#00000000", + "axis-grid": "#FFFFFF3F", + "axis-text": "#FFFFFFFF", + "axis-tick": "#00000000", + "crosshairs": "#FFFFFF7F", + "error-bar": "#00000000", + "fill": "#00000000", + "frame-bg": "#FFFFFF11", + "inlay-text": "#FFFFFFFF", + "legend-bg": "#141414EF", + "legend-border": "#6D6D7F7F", + "legend-text": "#FFFFFFFF", + "line": "#00000000", + "marker-fill": "#00000000", + "marker-outline": "#00000000", + "plot-bg": "#0000007F", + "plot-border": "#6D6D7F7F", + "selection": "#FF9900FF", + "title-text": "#FFFFFFFF" + }, + "text-editor": { + "background": "#282C34FF", + "breakpoint": "#FF200040", + "char-literal": "#E0A070FF", + "comment": "#646E7FFF", + "current-line-edge": "#00000000", + "current-line-fill": "#353C4CC3", + "current-line-fill-inactive": "#80808040", + "cursor": "#446BBBFF", + "default": "#DADADAFF", + "doc-comment": "#206858FF", + "error-marker": "#C01A2BFF", + "global-doc-comment": "#208070FF", + "identifier": "#E4E4E4FB", + "keyword": "#C46DE6FF", + "known-identifier": "#C46DE6FF", + "line-number": "#777E8EFF", + "multi-line-comment": "#206040FF", + "number": "#FF8E8ED7", + "preproc-identifier": "#A63BCAFF", + "preprocessor": "#646464FF", + "preprocessor-deactivated": "#353535FF", + "punctuation": "#EFEFEFFF", + "selection": "#3E4451FF", + "string": "#60CA51FF" + } + }, + "image_theme": "dark", + "name": "One Dark", + "styles": { + "imgui": { + "alpha": 1.0, + "button-text-align": [ + 0.5, + 0.5 + ], + "cell-padding": [ + 4.0, + 2.0 + ], + "child-border-size": 1.0, + "child-rounding": 0.0, + "disabled-alpha": 0.6000000238418579, + "frame-border-size": 0.0, + "frame-padding": [ + 4.0, + 3.0 + ], + "frame-rounding": 0.0, + "grab-min-size": 12.0, + "grab-rounding": 0.0, + "indent-spacing": 10.0, + "item-inner-spacing": [ + 4.0, + 4.0 + ], + "item-spacing": [ + 8.0, + 4.0 + ], + "popup-border-size": 1.0, + "popup-rounding": 0.0, + "scrollbar-rounding": 9.0, + "scrollbar-size": 14.0, + "selectable-text-align": [ + 0.0, + 0.0 + ], + "tab-rounding": 4.0, + "window-border-size": 1.0, + "window-min-size": [ + 32.0, + 32.0 + ], + "window-padding": [ + 8.0, + 8.0 + ], + "window-rounding": 0.0, + "window-shadow-angle": 0.0, + "window-shadow-offset": 0.0, + "window-shadow-size": 64.0, + "window-title-align": [ + 0.0, + 0.5 + ] + }, + "imhex": { + "popup-alpha": 0.6000000238418579, + "window-blur": 0.0 + }, + "imnodes": { + "grid-spacing": 24.0, + "link-hover-distance": 10.0, + "link-line-segments-per-length": 0.10000000149011612, + "link-thickness": 3.0, + "mini-map-offset": [ + 4.0, + 4.0 + ], + "mini-map-padding": [ + 8.0, + 8.0 + ], + "node-border-thickness": 1.0, + "node-corner-rounding": 4.0, + "node-padding": [ + 8.0, + 8.0 + ], + "pin-circle-radius": 4.0, + "pin-hover-radius": 10.0, + "pin-line-thickness": 1.0, + "pin-offset": 0.0, + "pin-quad-side-length": 7.0, + "pin-triangle-side-length": 9.5 + }, + "implot": { + "annotation-padding": [ + 2.0, + 2.0 + ], + "digital-bit-gap": 4.0, + "digital-bit-height": 8.0, + "error-bar-size": 5.0, + "error-bar-weight": 1.5, + "fill-alpha": 1.0, + "fit-padding": [ + 0.0, + 0.0 + ], + "label-padding": [ + 5.0, + 5.0 + ], + "legend-inner-padding": [ + 5.0, + 5.0 + ], + "legend-padding": [ + 10.0, + 10.0 + ], + "legend-spacing": [ + 5.0, + 0.0 + ], + "line-weight": 1.0, + "major-grid-size": [ + 1.0, + 1.0 + ], + "major-tick-len": [ + 10.0, + 10.0 + ], + "major-tick-size": [ + 1.0, + 1.0 + ], + "marker-size": 4.0, + "marker-weight": 1.0, + "minor-alpha": 0.25, + "minor-grid-size": [ + 1.0, + 1.0 + ], + "minor-tick-len": [ + 5.0, + 5.0 + ], + "minor-tick-size": [ + 1.0, + 1.0 + ], + "mouse-pos-padding": [ + 10.0, + 10.0 + ], + "plot-border-size": 1.0, + "plot-default-size": [ + 400.0, + 300.0 + ], + "plot-min-size": [ + 200.0, + 150.0 + ], + "plot-padding": [ + 10.0, + 10.0 + ] + } + } +} From 4c22f28a6790162d7784bc520b2fd6ebd6a59d67 Mon Sep 17 00:00:00 2001 From: Nik Date: Sun, 24 Nov 2024 14:53:02 +0100 Subject: [PATCH 25/29] pattern/rgbds: Fixed bad include --- patterns/rgbds.hexpat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patterns/rgbds.hexpat b/patterns/rgbds.hexpat index d00473cb..16791897 100644 --- a/patterns/rgbds.hexpat +++ b/patterns/rgbds.hexpat @@ -5,7 +5,7 @@ #pragma magic [ 52 47 42 39 ] @ 0x00 #pragma endian little -#include +#include using LONG = u32; using BYTE = u8; From b9f5f1668bff1651189c5cefd9cf1bbc355a50a5 Mon Sep 17 00:00:00 2001 From: Nik Date: Sun, 24 Nov 2024 14:53:30 +0100 Subject: [PATCH 26/29] includes/type: Fixed use of Reinterpreter type --- includes/type/float16.pat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/type/float16.pat b/includes/type/float16.pat index d0fdc0e9..57f9490d 100644 --- a/includes/type/float16.pat +++ b/includes/type/float16.pat @@ -50,7 +50,7 @@ namespace auto type { } std::mem::Reinterpreter converter; - converter.from = result; + converter.from_value = result; return std::format("{}", converter.to); }; From 5ffc58364063cf4c9d4f0175fdffdacc42491b13 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Wed, 27 Nov 2024 20:44:56 +0100 Subject: [PATCH 27/29] pattern/tiff: Fixed function parameter types --- patterns/tiff.hexpat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patterns/tiff.hexpat b/patterns/tiff.hexpat index 8a72011a..9f866d60 100644 --- a/patterns/tiff.hexpat +++ b/patterns/tiff.hexpat @@ -30,13 +30,13 @@ struct Big { (_): std::error(std::format("Unrecognized version: {}", Version)); } } [[sealed, format_read("format_read_big"), format_write("format_write_big"), transform("transform_big")]]; -fn format_read_big(Big v) { +fn format_read_big(auto v) { return std::format("{} (0x{:X})", v, v); }; fn format_write_big(str v) { return std::string::parse_int(v, 0); }; -fn transform_big(Big v) { +fn transform_big(auto v) { return v.V; }; From 500a3fe26e2bb6fadb277c700fb1d0e5e4e7e454 Mon Sep 17 00:00:00 2001 From: Geky Date: Thu, 28 Nov 2024 17:59:45 +0100 Subject: [PATCH 28/29] patterns: Added GBA ROM pattern (#323) * add support for cartridge size type $54 Added support for cartridge size type $54, corresponding to 1.5 MiB (96 banks). * add missing license * Add GBA Cartridge Header * Update README.md Added GBA information to README.md and corrected a typo. --- README.md | 3 +- patterns/gba.hexpat | 175 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 patterns/gba.hexpat diff --git a/README.md b/README.md index 89ee9a1a..f5786eef 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,8 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | File System | | [`patterns/fs.hexpat`](patterns/fs.hexpat) | Drive File System | | FLAC | `audio/flac` | [`patterns/flac.hexpat`](patterns/flac.hexpat) | Free Lossless Audio Codec, FLAC Audio Format | | Flipper Zero Settings | | [`patterns/flipper_settings.hexpat`](patterns/flipper_settings.hexpat) | Flipper Zero Settings Files | -| GB | `application/x-gameboy-rom` | [`patterns/gb.hexpat`](patterns/gb.hexpat) | Gameboy ROM | +| GB | `application/x-gameboy-rom` | [`patterns/gb.hexpat`](patterns/gb.hexpat) | Game Boy ROM | +| GBA | `application/x-gameboy-advance-rom` | [`patterns/gba.hexpat`](patterns/gba.hexpat) | Game Boy Advance ROM header | | GGUF | | [`patterns/gguf.hexpat`](patterns/gguf.hexpat) | GGML Inference Models | | GIF | `image/gif` | [`patterns/gif.hexpat`](patterns/gif.hexpat) | GIF image files | | GLTF | `model/gltf-binary` | [`patterns/gltf.hexpat`](patterns/gltf.hexpat) | GL Transmission Format binary 3D model file | diff --git a/patterns/gba.hexpat b/patterns/gba.hexpat new file mode 100644 index 00000000..3e6cb7ad --- /dev/null +++ b/patterns/gba.hexpat @@ -0,0 +1,175 @@ +#pragma author GekySan +#pragma description Game Boy Advance ROM Header + +#pragma MIME application/x-gameboy-advance-rom +#pragma MIME application/x-agb-rom +#pragma MIME application/x-gba-rom + +import std.string; +import std.mem; +import std.sys; + +// In gb.hexpat +namespace format { + fn licensee_code(str code) { + match(code) { + ("00"): return "None"; + ("01" | "31"): return "Nintendo"; + ("08" | "38"): return "Capcom"; + ("09"): return "Hot-B"; + ("0A" | "E0"): return "Jaleco"; + ("0B"): return "Coconuts Japan"; + ("0C" | "6E"): return "Elite Systems"; + ("13" | "69"): return "EA (Electronic Arts)"; + ("18"): return "Hudsonsoft"; + ("19"): return "ITC Entertainment"; + ("1A"): return "Yanoman"; + ("1D"): return "Japan Clary"; + ("1F" | "4A" | "61"): return "Virgin Interactive"; + ("24"): return "PCM Complete"; + ("25"): return "San-X"; + ("28"): return "Kotobuki Systems"; + ("29"): return "Seta"; + ("30" | "70"): return "Infogrames"; + ("32" | "A2" | "B2" | "C4"): return "Bandai"; + ("33"): return "See new licensee code"; + ("34" | "A4"): return "Konami"; + ("35"): return "HectorSoft"; + ("39" | "9D"): return "Banpresto"; + ("3C"): return ".Entertainment i"; + ("3E"): return "Gremlin"; + ("41"): return "Ubisoft"; + ("42" | "EB"): return "Atlus"; + ("44" | "4D"): return "Malibu"; + ("46" | "CF"): return "Angel"; + ("47"): return "Spectrum Holoby"; + ("49"): return "Irem"; + ("4F"): return "U.S. Gold"; + ("50"): return "Absolute"; + ("51" | "B0"): return "Acclaim"; + ("52"): return "Activision"; + ("53"): return "American Sammy"; + ("54"): return "GameTek"; + ("55"): return "Park Place"; + ("56" | "DB" | "FF"): return "LJN"; + ("57"): return "Matchbox"; + ("59"): return "Milton Bradley"; + ("5A"): return "Mindscape"; + ("5B"): return "Romstar"; + ("5C" | "D6"): return "Naxat Soft"; + ("5D"): return "Tradewest"; + ("60"): return "Titus"; + ("67"): return "Ocean Interactive"; + ("6F"): return "Electro Brain"; + ("71"): return "Interplay"; + ("72" | "AA"): return "Broderbund"; + ("73"): return "Sculptered Soft"; + ("75"): return "The Sales Curve"; + ("78"): return "t.hq"; + ("79"): return "Accolade"; + ("7A"): return "Triffix Entertainment"; + ("7C"): return "Microprose"; + ("7F" | "C2"): return "Kemco"; + ("80"): return "Misawa Entertainment"; + ("83"): return "Lozc"; + ("86" | "C4"): return "Tokuma Shoten Intermedia"; + ("8B"): return "Bullet-Proof Software"; + ("8C"): return "Vic Tokai"; + ("8E"): return "Ape"; + ("8F"): return "I'Max"; + ("91"): return "Chunksoft Co."; + ("92"): return "Video System"; + ("93"): return "Tsubaraya Productions Co."; + ("95"): return "Varie Corporation"; + ("96"): return "Yonezawa/S’Pal"; + ("97"): return "Kaneko"; + ("99"): return "Arc"; + ("9A"): return "Nihon Bussan"; + ("9B"): return "Tecmo"; + ("9C"): return "Imagineer"; + ("9F"): return "Nova"; + ("A1"): return "Hori Electric"; + ("A6"): return "Kawada"; + ("A7"): return "Takara"; + ("A9"): return "Technos Japan"; + ("AC"): return "Toei Animation"; + ("AD"): return "Toho"; + ("AF"): return "Namco"; + ("B1"): return "ASCII or Nexsoft"; + ("B4"): return "Square Enix"; + ("B6"): return "HAL Laboratory"; + ("B7"): return "SNK"; + ("B9" | "CE"): return "Pony Canyon"; + ("BA"): return "Culture Brain"; + ("BB"): return "Sunsoft"; + ("BD"): return "Sony Imagesoft"; + ("BF"): return "Sammy"; + ("C0" | "D0"): return "Taito"; + ("C3"): return "Squaresoft"; + ("C5"): return "Data East"; + ("C6"): return "Tonkinhouse"; + ("C8"): return "Koei"; + ("C9"): return "UFL"; + ("CA"): return "Ultra"; + ("CB"): return "Vap"; + ("CC"): return "Use Corporation"; + ("CD"): return "Meldac"; + ("D1"): return "Sofel"; + ("D2"): return "Quest"; + ("D3"): return "Sigma Enterprises"; + ("D4"): return "ASK Kodansha Co."; + ("D7"): return "Copya System"; + ("DA"): return "Tomy"; + ("DD"): return "NCS"; + ("DE"): return "Human"; + ("DF"): return "Altron"; + ("E1"): return "Towa Chiki"; + ("E2"): return "Yutaka"; + ("E3"): return "Varie"; + ("E5"): return "Epoch"; + ("E7"): return "Athena"; + ("E8"): return "Asmik"; + ("E9"): return "Natsume"; + ("EA"): return "King Records"; + ("EC"): return "Epic/Sony Records"; + ("EE"): return "IGS"; + ("F0"): return "A Wave"; + ("F3"): return "Extreme Entertainment"; + } + return "Unknown Licensee"; + }; +} + +fn calcChecksum() { + u8 sum = 0; + u8 offset = 0xA0; + + while (offset <= 0xBC) { + sum += std::mem::read_unsigned(offset, 1); + offset += 1; + } + + return ((-((0x19 + sum) & 0xFF)) & 0xFF) == std::mem::read_unsigned(0xBD, 1); +}; + +struct GBAHeader { + u8 entryPoint[4] [[comment("ARM entry point code, typically a 'B rom_start' instruction")]]; + u8 nintendoLogo[156] [[comment("Nintendo logo")]]; + char gameTitle[12] [[comment("Game title, uppercase ASCII, max 12 characters")]]; + char gameCode[4] [[comment("Game code, uppercase ASCII, 4 characters")]]; + char makerCode[2] [[format("format::licensee_code"), comment("Maker code, uppercase ASCII, 2 characters")]]; + u8 fixedValue [[comment("Fixed value, must be 0x96")]]; + u8 unitCode [[comment("Main unit code, identifies required hardware (00h for GBA)")]]; + u8 deviceType [[comment("Device type, usually 00h. Bit 7 relates to DACS/debug features")]]; + u8 reserved1[7] [[comment("Reserved area, must be zero-filled")]]; + u8 softwareVersion [[comment("Software version number, usually 00h")]]; + u8 complementCheck [[comment("Header checksum, required for validation")]]; + u8 reserved2[2] [[comment("Reserved area, must be zero-filled")]]; +}; + + +if (!calcChecksum()) { + std::error("Checksum validation failed: Calculated value does not match the expected checksum in the header."); +} + +GBAHeader gbaHeader @ 0x0000; From 4c96bfbeb331efbcd0747aaaf17535798cc276dc Mon Sep 17 00:00:00 2001 From: WerWolv Date: Thu, 28 Nov 2024 21:35:25 +0100 Subject: [PATCH 29/29] patterns/fs: Fixed pattern and uploaded test data --- patterns/fs.hexpat | 5 +++-- tests/patterns/test_data/fs.hexpat.img | Bin 0 -> 1474560 bytes 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 tests/patterns/test_data/fs.hexpat.img diff --git a/patterns/fs.hexpat b/patterns/fs.hexpat index 0e3c7fc3..bed31018 100644 --- a/patterns/fs.hexpat +++ b/patterns/fs.hexpat @@ -2,6 +2,7 @@ #pragma description Drive File System import std.io; +import std.core; struct DiskTimeStamp { u8 seconds, minutes, hours; @@ -52,7 +53,7 @@ namespace fat32 { lastLogical : 1; padding : 1; number : 5; - } [[bitfield_order(BitfieldOrder::MostToLeastSignificant, 8)]]; + } [[bitfield_order(std::core::BitfieldOrder::MostToLeastSignificant, 8)]]; enum EntryStatus : u8 { Regular = 0x00, @@ -73,7 +74,7 @@ namespace fat32 { subdirectory : 1; archive : 1; padding : 2; - } [[bitfield_order(BitfieldOrder::LeastToMostSignificant, 8)]]; + } [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 8)]]; struct DirEntry { char fileName[8]; diff --git a/tests/patterns/test_data/fs.hexpat.img b/tests/patterns/test_data/fs.hexpat.img new file mode 100644 index 0000000000000000000000000000000000000000..79d6f0c388c20d86bc1fda34f15830fe7b12d80e GIT binary patch literal 1474560 zcmeI$AqoOf5C-7c*TiPCZM6t)Fc}m?5R1rqFVEpAJivMf!D!iJU2QLKl9lgIGw_Gu zi}PAt_Qz9QF6ULGqKNc}^|&3yBvRzewujrZ%-L<~MfF|p)27KS)6fLY*S711cC>p5 z5FkK+009C72oNAZfB*pk1bPVg|Mb98B|v}x0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+zz+r9LVy4P0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ p009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5Fqf^0w2D>={*1d literal 0 HcmV?d00001