diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 62bc2f23..1f12f44d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,6 +7,12 @@ on: branches: [ '*' ] repository_dispatch: types: [run_tests] + workflow_call: + inputs: + pattern_language_git_repo: + type: string + pattern_language_git_hash: + type: string jobs: tests: @@ -21,6 +27,7 @@ jobs: - name: 🧰 Checkout uses: actions/checkout@v4 with: + repository: ${{ github.repository_owner }}/ImHex-Patterns submodules: recursive - name: ⬇️ Install dependencies @@ -61,6 +68,8 @@ jobs: -DIMHEX_PATTERNS_ENABLE_UNIT_TESTS=ON \ -DLIBPL_ENABLE_TESTS=OFF \ -DLIBPL_ENABLE_CLI=OFF \ + -DIMHEX_PATTERNS_LIBPL_GIT_REPO="${{ inputs.pattern_language_git_repo }}" \ + -DIMHEX_PATTERNS_LIBPL_GIT_HASH="${{ inputs.pattern_language_git_hash }}" \ -G Ninja \ .. ninja unit_tests @@ -73,9 +82,9 @@ jobs: - name: 📎 Validate JSON Files run: | cd constants - for file in ./[!_schema.json]*; do jsonschema -i $file _schema.json; done + for file in ./[!_]*; do jsonschema -i $file _schema.json; done cd .. cd tips - for file in ./[!_schema.json]*; do jsonschema -i $file _schema.json; done + for file in ./[!_]*; do jsonschema -i $file _schema.json; done cd .. diff --git a/.gitignore b/.gitignore index 9a0898a8..8698987f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ tests/cmake*/ tests/build*/ build/ +cmake-build-*/ .vscode/ .devcontainer/ diff --git a/CMakeLists.txt b/CMakeLists.txt index cbbb75e5..17a3e362 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,10 +16,22 @@ endif() if(NOT TARGET libpl) include(FetchContent) + if (NOT DEFINED IMHEX_PATTERNS_LIBPL_GIT_REPO OR IMHEX_PATTERNS_LIBPL_GIT_REPO STREQUAL "") + set(LIBPL_GIT_REPO "https://github.com/WerWolv/PatternLanguage") + else() + set(LIBPL_GIT_REPO ${IMHEX_PATTERNS_LIBPL_GIT_REPO}) + endif() + + if (NOT DEFINED IMHEX_PATTERNS_LIBPL_GIT_HASH OR IMHEX_PATTERNS_LIBPL_GIT_HASH STREQUAL "") + set(LIBPL_GIT_TAG "master") + else() + set(LIBPL_GIT_TAG ${IMHEX_PATTERNS_LIBPL_GIT_HASH}) + endif() + FetchContent_Declare( pattern_language - GIT_REPOSITORY https://github.com/WerWolv/PatternLanguage - GIT_TAG master + GIT_REPOSITORY ${LIBPL_GIT_REPO} + GIT_TAG ${LIBPL_GIT_TAG} ) FetchContent_MakeAvailable(pattern_language) @@ -27,4 +39,4 @@ endif() if(IMHEX_PATTERNS_ENABLE_UNIT_TESTS) add_subdirectory(tests) -endif() \ No newline at end of file +endif() diff --git a/README.md b/README.md index 55e004aa..243ac793 100644 --- a/README.md +++ b/README.md @@ -29,10 +29,14 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | ADTS | `audio/x-hx-aac-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 | +| APFS | | [`patterns/fs/apfs.hexpat`](patterns/fs/apfs.hexpat) | Apple File Ssytem (APFS) | +| AppleSingle | `application/applefile` | [`patterns/apple_single_double.hexpat`](patterns/apple_single_double.hexpat) | AppleSingle Dual Fork file | +| AppleDouble | `multipart/appledouble` | [`patterns/apple_single_double.hexpat`](patterns/apple_single_double.hexpat) | AppleDouble Resource Fork/Finder Metadata 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 | +| Assassin's Creed: Unity | | [`patterns/Assassin's Creed: Unity`](patterns/Assassin's Creed: Unity) | Assassin's Creed: Unity archive files -- .forge & .data (compressed and decompressed) -- | | Bastion | | [`patterns/bastion/*`](https://gitlab.com/EvelynTSMG/imhex-bastion-pats) | Various [Bastion](https://en.wikipedia.org/wiki/Bastion_(video_game)) files | | BeyondCompare BCSS | | [`patterns/bcss.hexpat`](patterns/bcss.hexpat) | BeyondCompare Snapshot (BCSS) file | | Bencode | `application/x-bittorrent` | [`patterns/bencode.hexpat`](patterns/bencode.hexpat) | Bencode encoding, used by Torrent files | @@ -50,6 +54,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | CCVXL | | [`patterns/ccvxl.hexpat`](patterns/ccvxl.hexpat) | Command and Conquer Voxel Model | | CCPAL | | [`patterns/ccpal.hexpat`](patterns/ccpal.hexpat) | Command and Conquer Voxel Palette | | CDA | | [`patterns/cda.hexpat`](patterns/cda.hexpat) | Compact Disc Audio track | +| CHD | | [`patterns/chd.hexpat`](patterns/chd.hexpat) | MAME Compressed Hunks of Data file | | CHM | `application/vnd.ms-htmlhelp` | [`patterns/chm.hexpat`](patterns/chm.hexpat) | Windows HtmlHelp Data (ITSF / CHM) | | COFF | `application/x-coff` | [`patterns/coff.hexpat`](patterns/coff.hexpat) | Common Object File Format (COFF) executable | | CPIO | `application/x-cpio` | [`patterns/cpio.hexpat`](patterns/cpio.hexpat) | Old Binary CPIO Format | @@ -57,20 +62,27 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | CREDHIST | | [`patterns/credhist.hexpat`](patterns/credhist.hexpat) | CREDHIST Format | | DDS | `image/vnd-ms.dds` | [`patterns/dds.hexpat`](patterns/dds.hexpat) | DirectDraw Surface | | DEX | | [`patterns/dex.hexpat`](patterns/dex.hexpat) | Dalvik EXecutable Format | +| Devil May Cry HD Collection | | [`patterns/Devil May Cry HD Collection`](patterns/Devil May Cry HD Collection) | 3D Model files used in Devil May Cry 3 HD Collection | | DICOM | `application/dicom` | [`patterns/dicom.hexpat`](patterns/dicom.hexpat) | DICOM image format | +| DISK_PARSER (DFIR) | `application/x-ima` | [`patterns/DFIR/DISK_PARSER.hexpat`](patterns/DFIR/DISK_PARSER.hexpat) | Recursive Disk/Volume/Filesystem parsing | | DMG | | [`patterns/dmg.hexpat`](patterns/dmg.hexpat) | Apple Disk Image Trailer (DMG) | | DMP | | [`patterns/dmp64.hexpat`](patterns/dmp64.hexpat) | Windows Kernel Dump(DMP64) | +| DOS | `application/x-dosexec` | [`patterns/dos.hexpat`](patterns/dos.hexpat) | 16-bit real mode DOS EXE files | +| DOTNET_BinaryFormatter | | [`patterns/dotnet_binaryformatter.hexpat`](patterns/dotnet_binaryformatter.hexpat) | .NET BinaryFormatter | | DPAPI_Blob | | [`patterns/dpapblob.hexpat`](patterns/dpapiblob.hexpat) | Data protection API Blob File Format | | DPAPI_MasterKey | | [`patterns/dpapimasterkey.hexpat`](patterns/dpapimasterkey.hexpat) | Data protection API MasterKey | | DS_Store | | [`patterns/dsstore.hexpat`](patterns/dsstore.hexpat) | .DS_Store file format | | DTA | | [`patterns/max_v104.hexpat`](patterns/max_v104.hexpat) | Mechanized Assault and Exploration v1.04 (strategy game) save file format | | DTED | | [`patterns/dted.hexpat`](patterns/dted.hexpat) | Digital Terrain Elevation Data (DTED) | | ELF | `application/x-executable` | [`patterns/elf.hexpat`](patterns/elf.hexpat) | ELF header in elf binaries | +| ESP32 Image | | [`patterns/esp32_image.hexpat`](patterns/esp32_image.hexpat) | Firmware image format for the ESP32 chip family | | EVTX | `application/x-ms-evtx` | [`patterns/evtx.hexpat`](patterns/evtx.hexpat) | MS Windows Vista Event Log | | EXFAT | | [`patterns/fs/exfat.hexpat`](patterns/fs/exfat.hexpat) | Extensible File Allocation Table (exFAT) | +| EXFAT (DFIR) | | [`patterns/DFIR/exFAT.hexpat`](patterns/DFIR/exFAT.hexpat) | Imported by DISK_PARSER.hexpat | | EXT4 | | [`patterns/fs/ext4.hexpat`](patterns/fs/ext4.hexpat) | Ext4 File System | | FAS | | [`patterns/fas_oskasoftware.hexpat`](patterns/fas_oskasoftware.hexpat) [`patterns/fas_oskasoftware_old.hexpat`](patterns/fas_oskasoftware_old.hexpat) (Old versions of Oska DeskMate) | Oska Software DeskMates FAS (Frames and Sequences) file | | FAT32 | | [`patterns/fs/fat32.hexpat`](patterns/fs/fat32.hexpat) | FAT32 File System | +| FAT32 (DFIR) | | [`patterns/DFIR/FAT32.hexpat`](patterns/DFIR/FAT32.hexpat) | Imported by DISK_PARSER.hexpat | | FBX | | [`patterns/fbx.hexpat`](patterns/fbx.hexpat) | Kaydara FBX Binary | | FDT | | [`patterns/fdt.hexpat`](patterns/fdt.hexpat) | Flat Linux Device Tree blob | | FFX | | [`patterns/ffx/*`](https://gitlab.com/EvelynTSMG/imhex-ffx-pats) | Various Final Fantasy X files | @@ -99,7 +111,10 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | ISO | `application/x-iso9660-image` | [`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 | +| KTX | `image/ktx` | [`patterns/ktx.hexpat`](patterns/ktx.hexpat) | Khronos TeXture 1.0 | | LOC | | [`patterns/loc.hexpat`](patterns/loc.hexpat) | Minecraft Legacy Console Edition Language file | +| Lua 4.0 | | [`patterns/lua40.hexpat`](patterns/lua40.hexpat) | Lua 4.0 bytecode | +| LUC | | [`patterns/popcap_luc.hexpat`](patterns/popcap_luc.hexpat) | PopCap's proprietary Lua bytecode | | 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 | @@ -117,14 +132,19 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | NBT | | [`patterns/nbt.hexpat`](patterns/nbt.hexpat) | Minecraft NBT format | | NDS | `application/x-nintendo-ds-rom` | [`patterns/nds.hexpat`](patterns/nds.hexpat) | DS Cartridge Header | | NE | `application/x-ms-ne-executable` | [`patterns/ne.hexpat`](patterns/ne.hexpat) | NE header and Standard NE fields | -| nes | | [`patterns/nes.hexpat`](patterns/nes.hexpat) | .nes file format | +| nes | | [`patterns/nes.hexpat`](patterns/nes.hexpat) | Nintendo Entertainment System ROM | +| NSF | | [`patterns/nsf.hexpat`](patterns/nsf.hexpat) | NES Sound Format | +| NSFe | | [`patterns/nsfe.hexpat`](patterns/nsfe.hexpat) | NES Sound Format extended | | NotepadCache | | [`patterns/notepad-cache.hexpat`](patterns/notepad-cache.hexpat) | Windows Notepad Cache | +| NotepadStateFile | | [`patterns/notepad-state.hexpat`](patterns/notepad-state.hexpat) | Windows Notepad .bin State files | | 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 | | NTFS | | [`patterns/fs/ntfs.hexpat`](patterns/fs/ntfs.hexpat) | NTFS (NT File System) | +| NTFS (DFIR) | | [`patterns/DFIR/NTFS.hexpat`](patterns/DFIR/NTFS.hexpat) | Imported by DISK_PARSER.hexpat | | OGG | `audio/ogg` | [`patterns/ogg.hexpat`](patterns/ogg.hexpat) | OGG Audio format | | ORP / ORS | | [`patterns/orp.hexpat`](patterns/orp.hexpat) | OpenRGB profile format | +| PACK | | [`patterns/roblox_pack.hexpat`](patterns/roblox_pack.hexpat) | Roblox shader archive 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 | | PcapNG | `application/vnd.tcpdump.pcap` | [`patterns/pcapng.hexpat`](patterns/pcapng.hexpat) | pcapng header and packets | @@ -136,6 +156,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | PP | | [`patterns/selinuxpp.hexpat`](patterns/selinuxpp.pat) | SE Linux package | | PFS0 | | [`patterns/pfs0.hexpat`](patterns/pfs0.hexpat) | Nintendo Switch PFS0 archive (NSP files) | | PF | | [`patterns/pf.hexpat`](patterns/pf.hexpat) | Microsoft uncompressed prefetch files (.pf) | +| Pickle | | [`patterns/pickle.hexpat`](patterns/pickle.hexpat) | Python Pickle Protocol | | PIF | `image/pif` | [`patterns/pif.hexpat`](patterns/pif.hexpat) | PIF Image Format | | PKM | | [`patterns/pkm.hexpat`](patterns/pkm.hexpat) | PKM texture format | | PNG | `image/png` | [`patterns/png.hexpat`](patterns/png.hexpat) | PNG image files | @@ -154,10 +175,13 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | ReFS | | [`patterns/refs.hexpat`](patterns/fs/refs.hexpat) | Microsoft Resilient File System | | RGBDS | | [`patterns/rgbds.hexpat`](patterns/rgbds.hexpat) | [RGBDS](https://rgbds.gbdev.io) object file format | | RPM | | [`patterns/rpm.hexpat`](patterns/rpm.hexpat) | [RPM](http://ftp.rpm.org/max-rpm/s1-rpm-file-format-rpm-file-format.html) package file format | +| SDB | | [`patterns/sdb.hexpat`](patterns/sdb.hexpat) | [Shim DataBase](https://learn.microsoft.com/en-us/windows/win32/devnotes/application-compatibility-database) file format | | Shell Link | `application/x-ms-shortcut` | [`patterns/lnk.hexpat`](patterns/lnk.hexpat) | Windows Shell Link file format | | shp | | [`patterns/shp.hexpat`](patterns/shp.hexpat) | ESRI shape file | +| SHR | | [`patterns/SHR.hexpat`](patterns/SHR.hexpat) | Apple IIgs Super Hi-Res (SHR) + PaintWorks Animation (ANI) | | shx | | [`patterns/shx.hexpat`](patterns/shx.hexpat) | ESRI index file | | smk | | [`patterns/smk.hexpat`](patterns/smk.hexpat) | Smacker video file | +| SNES | | [`patterns/snes.hexpat`](patterns/snes.hexpat) | Super Nintendo Entertainment System ROM header | | sup | | [`patterns/sup.hexpat`](patterns/sup.hexpat) | PGS Subtitle | | SPIRV | | [`patterns/spirv.hexpat`](patterns/spirv.hexpat) | SPIR-V header and instructions | | STDF | | [`patterns/stdfv4.hexpat`](patterns/stdfv4.hexpat) | Standard test data format for IC testers | @@ -167,6 +191,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | SWF | `application/x-shockwave-flash` |[`patterns/swf.hexpat`](patterns/swf.hexpat) | Shockwave Flash file format | | TA | | [`patterns/optee_ta.hexpat`](patterns/optee_ta.hexpat) | OPTEE Trusted Application Executable | | TAR | `application/x-tar` | [`patterns/tar.hexpat`](patterns/tar.hexpat) | Tar file format | +| TARC | | [`patterns/tarc.hexpat`](patterns/tarc.hexpat) | KEX Engine TARC file format | | TES | | [`patterns/wintec_tes.hexpat`](patterns/wintec_tes.hexpat) | Wintec TES GPS log | | Thumbcache | | [`patterns/thumbcache.hexpat`](patterns/thumbcache.hexpat) | Windows thumbcache_*.db | | TIFF | `image/tiff` | [`patterns/tiff.hexpat`](patterns/tiff.hexpat) | Tag Image File Format | @@ -176,7 +201,9 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | UPK | | [`patterns/upk-ue3.hexpat`](patterns/upk-ue3.hexpat) | Unreal Engine 3 UPK file | | UEFI | | [`patterns/uefi.hexpat`](patterns/uefi.hexpat)` | UEFI structs for parsing efivars | | UEFI Boot Entry | | [`patterns/uefi_boot_entry.hexpat`](patterns/uefi_boot_entry.hexpat) | UEFI Boot Entry (Load option) | +| UEFI Variable Store | | [`patterns/uefi_fv_varstore.hexpat`](patterns/uefi_fv_varstore.hexpat) | UEFI Firmware Volume Variable Store | | UF2 | | [`patterns/uf2.hexpat`](patterns/uf2.hexpat) | [USB Flashing Format](https://github.com/microsoft/uf2) | +| Unity Asset Bundle | | [`patterns/unity-asset-bundle.hexpat`](patterns/unity-asset-bundle.hexpat) | Unity Asset Bundle | | Valve VPK | | [`patterns/valve_vpk.hexpat`](valve_vpk.hexpat) | Valve Package File | | VBMeta | | [`patterns/vbmeta.hexpat`](patterns/vbmeta.hexpat) | Android VBMeta image | | VDF | | [`patterns/vdf.hexpat`](patterns/vdf.hexpat) | Binary Value Data Format (.vdf) files | @@ -185,7 +212,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | VHDX | | [`patterns/vhdx.hexpat`](patterns/vhdx.hexpat) | Microsoft Hyper-V Virtual Hard Disk format | | VOX | | [`patterns/vox.hexpat`](patterns/vox.hexpat) | MagicaVoxel scene description 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 +| 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 | @@ -197,6 +224,11 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | ZIP | `application/zip` | [`patterns/zip.hexpat`](patterns/zip.hexpat) | End of Central Directory Header, Central Directory File Headers | | ZLIB | `application/zlib` | [`patterns/zlib.hexpat`](patterns/zlib.hexpat) | ZLIB compressed data format | | ZSTD | `application/zstd` | [`patterns/zstd.hexpat`](patterns/zstd.hexpat) | Zstandard compressed data format | +| MOD | `3d-model/mod` | [`patterns/DMC3HD-Mod.hexpat`](patterns/dmc3_hd_mod.hexpat) | 3D Model files used in Devil May Cry 3 HD Collection | +| CBM BASIC | | [`commodore_basic.hexpat`](patterns/commodore_basic.hexpat) | Commodore BASIC | +| Terminfo | `application/x-terminfo` and `application/x-terminfo2` | [`patterns/terminfo.hexpat`](patterns/terminfo.hexpat) | Compiled *(legacy and extended)* term info entry | + + ### Scripts @@ -292,6 +324,8 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | Catppuccin Frappe | [`themes/catppuccin-frappe.json`](themes/catppuccin-frappe.json) | Catppuccin Frappe Flavor (Dark Theme) | | Catppuccin Macchiato | [`themes/catppuccin-macchiato.json`](themes/catppuccin-macchiato.json) | Catppuccin Macchiato Flavor (Dark Theme) | | Catppuccin Mocha | [`themes/catppuccin-mocha.json`](themes/catppuccin-mocha.json) | Catppuccin Mocha Flavor (Dark Theme) | +| Theme Lion | [`themes/theme_lion.json`](themes/theme_lion.json) | Semantic CLion inspired theme (Dark Theme) | +| Retina Dark | [`themes/retina_dark.json`](themes/retina_dark.json) | Semantic theme based on Dark Theme | ### Disassemblers diff --git a/constants/_schema.json b/constants/_schema.json index 9cd1cc58..d2b4238c 100644 --- a/constants/_schema.json +++ b/constants/_schema.json @@ -29,23 +29,10 @@ "title": "Items", "type": "object", "required": [ - "type", "value", "name" ], "properties": { - "type": { - "$id": "#root/values/items/type", - "title": "Type", - "type": "string", - "default": "", - "examples": [ - "int16be", - "int16le", - "int10" - ], - "pattern": "^(int10|int16le|int16be)$" - }, "value": { "$id": "#root/values/items/value", "title": "Value", @@ -54,7 +41,7 @@ "examples": [ "ACDC" ], - "pattern": "^([0-9a-fA-F]+)$" + "pattern": "^.*$" }, "name": { "$id": "#root/values/items/name", diff --git a/constants/crc16.json b/constants/crc16.json index 2e7fa22d..aa75c4f8 100644 --- a/constants/crc16.json +++ b/constants/crc16.json @@ -2,118 +2,95 @@ "name": "CRC16 Constants", "values": [ { - "type": "int16be", - "value": "1021", + "value": "10 21", "name": "CRC-16/CCITT-FALSE Polynomial" }, { - "type": "int16be", - "value": "8005", + "value": "80 05", "name": "CRC-16/ARC Polynomial" }, { - "type": "int16be", - "value": "1021", + "value": "10 21", "name": "CRC-16/AUG-CCITT Polynomial" }, { - "type": "int16be", - "value": "8005", + "value": "80 05", "name": "CRC-16/BUYPASS Polynomial" }, { - "type": "int16be", - "value": "C867", + "value": "C8 67", "name": "CRC-16/CDMA2000 Polynomial" }, { - "type": "int16be", - "value": "8005", + "value": "80 05", "name": "CRC-16/DDS-110 Polynomial" }, { - "type": "int16be", - "value": "0589", + "value": "05 89", "name": "CRC-16/DECT-R Polynomial" }, { - "type": "int16be", - "value": "0589", + "value": "05 89", "name": "CRC-16/DECT-X Polynomial" }, { - "type": "int16be", - "value": "3D65", + "value": "3D 65", "name": "CRC-16/DNP Polynomial" }, { - "type": "int16be", - "value": "3D65", + "value": "3D 65", "name": "CRC-16/EN-13757 Polynomial" }, { - "type": "int16be", - "value": "1021", + "value": "10 21", "name": "CRC-16/GENIBUS Polynomial" }, { - "type": "int16be", - "value": "8005", + "value": "80 05", "name": "CRC-16/MAXIM Polynomial" }, { - "type": "int16be", - "value": "1021", + "value": "10 21", "name": "CRC-16/MCRF4XX Polynomial" }, { - "type": "int16be", - "value": "1021", + "value": "10 21", "name": "CRC-16/RIELLO Polynomial" }, { - "type": "int16be", - "value": "8BB7", + "value": "8B B7", "name": "CRC-16/T10-DIF Polynomial" }, { - "type": "int16be", - "value": "A097", + "value": "A0 97", "name": "CRC-16/TELEDISK Polynomial" }, { - "type": "int16be", - "value": "1021", + "value": "10 21", "name": "CRC-16/TMS37157 Polynomial" }, { - "type": "int16be", - "value": "8005", + "value": "80 05", "name": "CRC-16/USB Polynomial" }, { - "type": "int16be", - "value": "1021", + "value": "10 21", "name": "CRC-A Polynomial" }, { - "type": "int16be", - "value": "1021", + "value": "10 21", "name": "CRC-16/KERMIT Polynomial" }, { - "type": "int16be", - "value": "8005", + "value": "80 05", "name": "CRC-16/MODBUS Polynomial" }, { - "type": "int16be", - "value": "1021", + "value": "10 21", "name": "CRC-16/X-25 Polynomial" }, { - "type": "int16be", - "value": "1021", + "value": "10 21", "name": "CRC-16/XMODEM Polynomial" } ] diff --git a/constants/crc32.json b/constants/crc32.json index d4ea31e0..151d9d2b 100644 --- a/constants/crc32.json +++ b/constants/crc32.json @@ -2,48 +2,39 @@ "name": "CRC32 Constants", "values": [ { - "type": "int16be", - "value": "04C11DB7", + "value": "04 C1 1D B7", "name": "CRC-32 Polynomial" }, { - "type": "int16be", - "value": "04C11DB7", + "value": "04 C1 1D B7", "name": "CRC-32/BZIP2 Polynomial" }, { - "type": "int16be", - "value": "1EDC6F41", + "value": "1E DC 6F 41", "name": "CRC-32C Polynomial" }, { - "type": "int16be", - "value": "A833982B", + "value": "A8 33 98 2B", "name": "CRC-32D Polynomial" }, { - "type": "int16be", - "value": "04C11DB7", + "value": "04 C1 1D B7", "name": "CRC-32/MPEG-2 Polynomial" }, { - "type": "int16be", - "value": "04C11DB7", + "value": "04 C1 1D B7", "name": "CRC-32/POSIX Polynomial" }, { - "type": "int16be", - "value": "814141AB", + "value": "81 41 41 AB", "name": "CRC-32Q Polynomial" }, { - "type": "int16be", - "value": "04C11DB7", + "value": "04 C1 1D B7", "name": "CRC-32/JAMCRC Polynomial" }, { - "type": "int16be", - "value": "000000AF", + "value": "00 00 00 AF", "name": "CRC-32/XFER Polynomial" } ] diff --git a/constants/http_status.json b/constants/http_status.json deleted file mode 100644 index e5d4a8c9..00000000 --- a/constants/http_status.json +++ /dev/null @@ -1,315 +0,0 @@ -{ - "name": "HTTP Status Codes", - "values": [ - { - "type": "int10", - "value": "100", - "name": "Continue" - }, - { - "type": "int10", - "value": "101", - "name": "Switching Protocols" - }, - { - "type": "int10", - "value": "102", - "name": "Processing (WebDAV; RFC 2518)" - }, - { - "type": "int10", - "value": "103", - "name": "Early Hints (RFC 8297)" - }, - { - "type": "int10", - "value": "200", - "name": "OK" - }, - { - "type": "int10", - "value": "201", - "name": "Created" - }, - { - "type": "int10", - "value": "202", - "name": "Accepted" - }, - { - "type": "int10", - "value": "203", - "name": "Non-Authoritative Information (since HTTP/1.1)" - }, - { - "type": "int10", - "value": "204", - "name": "No Content" - }, - { - "type": "int10", - "value": "205", - "name": "Reset Content" - }, - { - "type": "int10", - "value": "206", - "name": "Partial Content (RFC 7233)" - }, - { - "type": "int10", - "value": "207", - "name": "Multi-Status (WebDAV; RFC 4918)" - }, - { - "type": "int10", - "value": "208", - "name": "Already Reported (WebDAV; RFC 5842)" - }, - { - "type": "int10", - "value": "226", - "name": "IM Used (RFC 3229)" - }, - { - "type": "int10", - "value": "300", - "name": "Multiple Choices" - }, - { - "type": "int10", - "value": "301", - "name": "Moved Permanently" - }, - { - "type": "int10", - "value": "302", - "name": "Found (Previously \"Moved temporarily\")" - }, - { - "type": "int10", - "value": "303", - "name": "See Other (since HTTP/1.1)" - }, - { - "type": "int10", - "value": "304", - "name": "Not Modified (RFC 7232)" - }, - { - "type": "int10", - "value": "305", - "name": "Use Proxy (since HTTP/1.1)" - }, - { - "type": "int10", - "value": "306", - "name": "Switch Proxy" - }, - { - "type": "int10", - "value": "307", - "name": "Temporary Redirect (since HTTP/1.1)" - }, - { - "type": "int10", - "value": "308", - "name": "Permanent Redirect (RFC 7538)" - }, - { - "type": "int10", - "value": "400", - "name": "Bad Request" - }, - { - "type": "int10", - "value": "401", - "name": "Unauthorized (RFC 7235)" - }, - { - "type": "int10", - "value": "402", - "name": "Payment Required" - }, - { - "type": "int10", - "value": "403", - "name": "Forbidden" - }, - { - "type": "int10", - "value": "404", - "name": "Not Found" - }, - { - "type": "int10", - "value": "405", - "name": "Method Not Allowed" - }, - { - "type": "int10", - "value": "406", - "name": "Not Acceptable" - }, - { - "type": "int10", - "value": "407", - "name": "Proxy Authentication Required (RFC 7235)" - }, - { - "type": "int10", - "value": "408", - "name": "Request Timeout" - }, - { - "type": "int10", - "value": "409", - "name": "Conflict" - }, - { - "type": "int10", - "value": "410", - "name": "Gone" - }, - { - "type": "int10", - "value": "411", - "name": "Length Required" - }, - { - "type": "int10", - "value": "412", - "name": "Precondition Failed (RFC 7232)" - }, - { - "type": "int10", - "value": "413", - "name": "Payload Too Large (RFC 7231)" - }, - { - "type": "int10", - "value": "414", - "name": "URI Too Long (RFC 7231)" - }, - { - "type": "int10", - "value": "415", - "name": "Unsupported Media Type (RFC 7231)" - }, - { - "type": "int10", - "value": "416", - "name": "Range Not Satisfiable (RFC 7233)" - }, - { - "type": "int10", - "value": "417", - "name": "Expectation Failed" - }, - { - "type": "int10", - "value": "418", - "name": "I'm a teapot (RFC 2324, RFC 7168)" - }, - { - "type": "int10", - "value": "421", - "name": "Misdirected Request (RFC 7540)" - }, - { - "type": "int10", - "value": "422", - "name": "Unprocessable Entity (WebDAV; RFC 4918)" - }, - { - "type": "int10", - "value": "424", - "name": "Failed Dependency (WebDAV; RFC 4918)" - }, - { - "type": "int10", - "value": "425", - "name": "Too Early (RFC 8470)" - }, - { - "type": "int10", - "value": "426", - "name": "Upgrade Required" - }, - { - "type": "int10", - "value": "428", - "name": "Precondition Required (RFC 6585)" - }, - { - "type": "int10", - "value": "429", - "name": "Too Many Requests (RFC 6585)" - }, - { - "type": "int10", - "value": "431", - "name": "Request Header Fields Too Large (RFC 6585)" - }, - { - "type": "int10", - "value": "451", - "name": "Unavailable For Legal Reasons (RFC 7725)" - }, - { - "type": "int10", - "value": "500", - "name": "Internal Server Error" - }, - { - "type": "int10", - "value": "501", - "name": "Not Implemented" - }, - { - "type": "int10", - "value": "502", - "name": "Bad Gateway" - }, - { - "type": "int10", - "value": "503", - "name": "Service Unavailable" - }, - { - "type": "int10", - "value": "504", - "name": "Gateway Timeout" - }, - { - "type": "int10", - "value": "505", - "name": "HTTP Version Not Supported" - }, - { - "type": "int10", - "value": "506", - "name": "Variant Also Negotiates (RFC 2295)" - }, - { - "type": "int10", - "value": "507", - "name": "Insufficient Storage (WebDAV; RFC 4918)" - }, - { - "type": "int10", - "value": "508", - "name": "Loop Detected (WebDAV; RFC 5842)" - }, - { - "type": "int10", - "value": "510", - "name": "Not Extended (RFC 2774)" - }, - { - "type": "int10", - "value": "511", - "name": "Network Authentication Required (RFC 6585)" - } - ] -} \ No newline at end of file diff --git a/constants/linux_errors.json b/constants/linux_errors.json deleted file mode 100644 index 5ad59be3..00000000 --- a/constants/linux_errors.json +++ /dev/null @@ -1,780 +0,0 @@ -{ - "name": "Linux Error Codes", - "values": [ - { - "type": "int10", - "value": "1", - "name": "EPERM", - "desc": "Operation not permitted" - }, - { - "type": "int10", - "value": "2", - "name": "ENOENT", - "desc": "No such file or directory" - }, - { - "type": "int10", - "value": "3", - "name": "ESRCH", - "desc": "No such process" - }, - { - "type": "int10", - "value": "4", - "name": "EINTR", - "desc": "Interrupted system call" - }, - { - "type": "int10", - "value": "5", - "name": "EIO", - "desc": "I/O error" - }, - { - "type": "int10", - "value": "6", - "name": "ENXIO", - "desc": "No such device or address" - }, - { - "type": "int10", - "value": "7", - "name": "E2BIG", - "desc": "Argument list too long" - }, - { - "type": "int10", - "value": "8", - "name": "ENOEXEC", - "desc": "Exec format error" - }, - { - "type": "int10", - "value": "9", - "name": "EBADF", - "desc": "Bad file value" - }, - { - "type": "int10", - "value": "10", - "name": "ECHILD", - "desc": "No child processes" - }, - { - "type": "int10", - "value": "11", - "name": "EAGAIN", - "desc": "Try again" - }, - { - "type": "int10", - "value": "12", - "name": "ENOMEM", - "desc": "Out of memory" - }, - { - "type": "int10", - "value": "13", - "name": "EACCES", - "desc": "Permission denied" - }, - { - "type": "int10", - "value": "14", - "name": "EFAULT", - "desc": "Bad address" - }, - { - "type": "int10", - "value": "15", - "name": "ENOTBLK", - "desc": "Block device required" - }, - { - "type": "int10", - "value": "16", - "name": "EBUSY", - "desc": "Device or resource busy" - }, - { - "type": "int10", - "value": "17", - "name": "EEXIST", - "desc": "File exists" - }, - { - "type": "int10", - "value": "18", - "name": "EXDEV", - "desc": "Cross-device link" - }, - { - "type": "int10", - "value": "19", - "name": "ENODEV", - "desc": "No such device" - }, - { - "type": "int10", - "value": "20", - "name": "ENOTDIR", - "desc": "Not a directory" - }, - { - "type": "int10", - "value": "21", - "name": "EISDIR", - "desc": "Is a directory" - }, - { - "type": "int10", - "value": "22", - "name": "EINVAL", - "desc": "Invalid argument" - }, - { - "type": "int10", - "value": "23", - "name": "ENFILE", - "desc": "File table overflow" - }, - { - "type": "int10", - "value": "24", - "name": "EMFILE", - "desc": "Too many open files" - }, - { - "type": "int10", - "value": "25", - "name": "ENOTTY", - "desc": "Not a typewriter" - }, - { - "type": "int10", - "value": "26", - "name": "ETXTBSY", - "desc": "Text file busy" - }, - { - "type": "int10", - "value": "27", - "name": "EFBIG", - "desc": "File too large" - }, - { - "type": "int10", - "value": "28", - "name": "ENOSPC", - "desc": "No space left on device" - }, - { - "type": "int10", - "value": "29", - "name": "ESPIPE", - "desc": "Illegal seek" - }, - { - "type": "int10", - "value": "30", - "name": "EROFS", - "desc": "Read-only file system" - }, - { - "type": "int10", - "value": "31", - "name": "EMLINK", - "desc": "Too many links" - }, - { - "type": "int10", - "value": "32", - "name": "EPIPE", - "desc": "Broken pipe" - }, - { - "type": "int10", - "value": "33", - "name": "EDOM", - "desc": "Math argument out of domain of func" - }, - { - "type": "int10", - "value": "34", - "name": "ERANGE", - "desc": "Math result not representable" - }, - { - "type": "int10", - "value": "35", - "name": "EDEADLK", - "desc": "Resource deadlock would occur" - }, - { - "type": "int10", - "value": "36", - "name": "ENAMETOOLONG", - "desc": "File name too long" - }, - { - "type": "int10", - "value": "37", - "name": "ENOLCK", - "desc": "No record locks available" - }, - { - "type": "int10", - "value": "38", - "name": "ENOSYS", - "desc": "Function not implemented" - }, - { - "type": "int10", - "value": "39", - "name": "ENOTEMPTY", - "desc": "Directory not empty" - }, - { - "type": "int10", - "value": "40", - "name": "ELOOP", - "desc": "Too many symbolic links encountered" - }, - { - "type": "int10", - "value": "42", - "name": "ENOMSG", - "desc": "No message of desired type" - }, - { - "type": "int10", - "value": "43", - "name": "EIDRM", - "desc": "Identifier removed" - }, - { - "type": "int10", - "value": "44", - "name": "ECHRNG", - "desc": "Channel value out of range" - }, - { - "type": "int10", - "value": "45", - "name": "EL2NSYNC", - "desc": "Level 2 not synchronized" - }, - { - "type": "int10", - "value": "46", - "name": "EL3HLT", - "desc": "Level 3 halted" - }, - { - "type": "int10", - "value": "47", - "name": "EL3RST", - "desc": "Level 3 reset" - }, - { - "type": "int10", - "value": "48", - "name": "ELNRNG", - "desc": "Link value out of range" - }, - { - "type": "int10", - "value": "49", - "name": "EUNATCH", - "desc": "Protocol driver not attached" - }, - { - "type": "int10", - "value": "50", - "name": "ENOCSI", - "desc": "No CSI structure available" - }, - { - "type": "int10", - "value": "51", - "name": "EL2HLT", - "desc": "Level 2 halted" - }, - { - "type": "int10", - "value": "52", - "name": "EBADE", - "desc": "Invalid exchange" - }, - { - "type": "int10", - "value": "53", - "name": "EBADR", - "desc": "Invalid request descriptor" - }, - { - "type": "int10", - "value": "54", - "name": "EXFULL", - "desc": "Exchange full" - }, - { - "type": "int10", - "value": "55", - "name": "ENOANO", - "desc": "No anode" - }, - { - "type": "int10", - "value": "56", - "name": "EBADRQC", - "desc": "Invalid request code" - }, - { - "type": "int10", - "value": "57", - "name": "EBADSLT", - "desc": "Invalid slot" - }, - { - "type": "int10", - "value": "59", - "name": "EBFONT", - "desc": "Bad font file format" - }, - { - "type": "int10", - "value": "60", - "name": "ENOSTR", - "desc": "Device not a stream" - }, - { - "type": "int10", - "value": "61", - "name": "ENODATA", - "desc": "No data available" - }, - { - "type": "int10", - "value": "62", - "name": "ETIME", - "desc": "Timer expired" - }, - { - "type": "int10", - "value": "63", - "name": "ENOSR", - "desc": "Out of streams resources" - }, - { - "type": "int10", - "value": "64", - "name": "ENONET", - "desc": "Machine is not on the network" - }, - { - "type": "int10", - "value": "65", - "name": "ENOPKG", - "desc": "Package not installed" - }, - { - "type": "int10", - "value": "66", - "name": "EREMOTE", - "desc": "Object is remote" - }, - { - "type": "int10", - "value": "67", - "name": "ENOLINK", - "desc": "Link has been severed" - }, - { - "type": "int10", - "value": "68", - "name": "EADV", - "desc": "Advertise error" - }, - { - "type": "int10", - "value": "69", - "name": "ESRMNT", - "desc": "Srmount error" - }, - { - "type": "int10", - "value": "70", - "name": "ECOMM", - "desc": "Communication error on send" - }, - { - "type": "int10", - "value": "71", - "name": "EPROTO", - "desc": "Protocol error" - }, - { - "type": "int10", - "value": "72", - "name": "EMULTIHOP", - "desc": "Multihop attempted" - }, - { - "type": "int10", - "value": "73", - "name": "EDOTDOT", - "desc": "RFS specific error" - }, - { - "type": "int10", - "value": "74", - "name": "EBADMSG", - "desc": "Not a data message" - }, - { - "type": "int10", - "value": "75", - "name": "EOVERFLOW", - "desc": "Value too large for defined data type" - }, - { - "type": "int10", - "value": "76", - "name": "ENOTUNIQ", - "desc": "Name not unique on network" - }, - { - "type": "int10", - "value": "77", - "name": "EBADFD", - "desc": "File descriptor in bad state" - }, - { - "type": "int10", - "value": "78", - "name": "EREMCHG", - "desc": "Remote address changed" - }, - { - "type": "int10", - "value": "79", - "name": "ELIBACC", - "desc": "Can not access a needed shared library" - }, - { - "type": "int10", - "value": "80", - "name": "ELIBBAD", - "desc": "Accessing a corrupted shared library" - }, - { - "type": "int10", - "value": "81", - "name": "ELIBSCN", - "desc": ".lib section in a.out corrupted" - }, - { - "type": "int10", - "value": "82", - "name": "ELIBMAX", - "desc": "Attempting to link in too many shared libraries" - }, - { - "type": "int10", - "value": "83", - "name": "ELIBEXEC", - "desc": "Cannot exec a shared library directly" - }, - { - "type": "int10", - "value": "84", - "name": "EILSEQ", - "desc": "Illegal byte sequence" - }, - { - "type": "int10", - "value": "85", - "name": "ERESTART", - "desc": "Interrupted system call should be restarted" - }, - { - "type": "int10", - "value": "86", - "name": "ESTRPIPE", - "desc": "Streams pipe error" - }, - { - "type": "int10", - "value": "87", - "name": "EUSERS", - "desc": "Too many users" - }, - { - "type": "int10", - "value": "88", - "name": "ENOTSOCK", - "desc": "Socket operation on non-socket" - }, - { - "type": "int10", - "value": "89", - "name": "EDESTADDRREQ", - "desc": "Destination address required" - }, - { - "type": "int10", - "value": "90", - "name": "EMSGSIZE", - "desc": "Message too long" - }, - { - "type": "int10", - "value": "91", - "name": "EPROTOTYPE", - "desc": "Protocol wrong type for socket" - }, - { - "type": "int10", - "value": "92", - "name": "ENOPROTOOPT", - "desc": "Protocol not available" - }, - { - "type": "int10", - "value": "93", - "name": "EPROTONOSUPPORT", - "desc": "Protocol not supported" - }, - { - "type": "int10", - "value": "94", - "name": "ESOCKTNOSUPPORT", - "desc": "Socket type not supported" - }, - { - "type": "int10", - "value": "95", - "name": "EOPNOTSUPP", - "desc": "Operation not supported on transport endpoint" - }, - { - "type": "int10", - "value": "96", - "name": "EPFNOSUPPORT", - "desc": "Protocol family not supported" - }, - { - "type": "int10", - "value": "97", - "name": "EAFNOSUPPORT", - "desc": "Address family not supported by protocol" - }, - { - "type": "int10", - "value": "98", - "name": "EADDRINUSE", - "desc": "Address already in use" - }, - { - "type": "int10", - "value": "99", - "name": "EADDRNOTAVAIL", - "desc": "Cannot assign requested address" - }, - { - "type": "int10", - "value": "100", - "name": "ENETDOWN", - "desc": "Network is down" - }, - { - "type": "int10", - "value": "101", - "name": "ENETUNREACH", - "desc": "Network is unreachable" - }, - { - "type": "int10", - "value": "102", - "name": "ENETRESET", - "desc": "Network dropped connection because of reset" - }, - { - "type": "int10", - "value": "103", - "name": "ECONNABORTED", - "desc": "Software caused connection abort" - }, - { - "type": "int10", - "value": "104", - "name": "ECONNRESET", - "desc": "Connection reset by peer" - }, - { - "type": "int10", - "value": "105", - "name": "ENOBUFS", - "desc": "No buffer space available" - }, - { - "type": "int10", - "value": "106", - "name": "EISCONN", - "desc": "Transport endpoint is already connected" - }, - { - "type": "int10", - "value": "107", - "name": "ENOTCONN", - "desc": "Transport endpoint is not connected" - }, - { - "type": "int10", - "value": "108", - "name": "ESHUTDOWN", - "desc": "Cannot send after transport endpoint shutdown" - }, - { - "type": "int10", - "value": "109", - "name": "ETOOMANYREFS", - "desc": "Too many references: cannot splice" - }, - { - "type": "int10", - "value": "110", - "name": "ETIMEDOUT", - "desc": "Connection timed out" - }, - { - "type": "int10", - "value": "111", - "name": "ECONNREFUSED", - "desc": "Connection refused" - }, - { - "type": "int10", - "value": "112", - "name": "EHOSTDOWN", - "desc": "Host is down" - }, - { - "type": "int10", - "value": "113", - "name": "EHOSTUNREACH", - "desc": "No route to host" - }, - { - "type": "int10", - "value": "114", - "name": "EALREADY", - "desc": "Operation already in progress" - }, - { - "type": "int10", - "value": "115", - "name": "EINPROGRESS", - "desc": "Operation now in progress" - }, - { - "type": "int10", - "value": "116", - "name": "ESTALE", - "desc": "Stale NFS file handle" - }, - { - "type": "int10", - "value": "117", - "name": "EUCLEAN", - "desc": "Structure needs cleaning" - }, - { - "type": "int10", - "value": "118", - "name": "ENOTNAM", - "desc": "Not a XENIX named type file" - }, - { - "type": "int10", - "value": "119", - "name": "ENAVAIL", - "desc": "No XENIX semaphores available" - }, - { - "type": "int10", - "value": "120", - "name": "EISNAM", - "desc": "Is a named type file" - }, - { - "type": "int10", - "value": "121", - "name": "EREMOTEIO", - "desc": "Remote I/O error" - }, - { - "type": "int10", - "value": "122", - "name": "EDQUOT", - "desc": "Quota exceeded" - }, - { - "type": "int10", - "value": "123", - "name": "ENOMEDIUM", - "desc": "No medium found" - }, - { - "type": "int10", - "value": "124", - "name": "EMEDIUMTYPE", - "desc": "Wrong medium type" - }, - { - "type": "int10", - "value": "125", - "name": "ECANCELED", - "desc": "Operation Canceled" - }, - { - "type": "int10", - "value": "126", - "name": "ENOKEY", - "desc": "Required key not available" - }, - { - "type": "int10", - "value": "127", - "name": "EKEYEXPIRED", - "desc": "Key has expired" - }, - { - "type": "int10", - "value": "128", - "name": "EKEYREVOKED", - "desc": "Key has been revoked" - }, - { - "type": "int10", - "value": "129", - "name": "EKEYREJECTED", - "desc": "Key was rejected by service" - }, - { - "type": "int10", - "value": "130", - "name": "EOWNERDEAD", - "desc": "Owner died" - }, - { - "type": "int10", - "value": "131", - "name": "ENOTRECOVERABLE", - "desc": "State not recoverable" - } - - ] -} \ No newline at end of file diff --git a/includes/std/mem.pat b/includes/std/mem.pat index 2c223033..835505e9 100644 --- a/includes/std/mem.pat +++ b/includes/std/mem.pat @@ -6,23 +6,6 @@ namespace auto std::mem { - namespace impl { - - struct MagicSearchImpl { - s128 address = builtin::std::mem::find_string_in_range(0, $, builtin::std::mem::size(), Magic); - if (address < 0) - break; - - $ = address; - try { - T data [[inline]]; - } catch { - T data; - } - }; - - } - /** A Handle for a custom Section */ @@ -91,8 +74,8 @@ namespace auto std::mem { @return The address of the sequence */ fn find_sequence(u128 occurrence_index, auto ... bytes) { - const u128 address = builtin::std::mem::base_address(); - return builtin::std::mem::find_sequence_in_range(occurrence_index, address, address + builtin::std::mem::size(), bytes); + const u128 address = std::mem::base_address(); + return builtin::std::mem::find_sequence_in_range(occurrence_index, address, address + std::mem::size(), bytes); }; /** @@ -115,8 +98,8 @@ namespace auto std::mem { @return The address of the sequence */ fn find_string(u128 occurrence_index, str string) { - const u128 address = builtin::std::mem::base_address(); - return builtin::std::mem::find_string_in_range(occurrence_index, address, address + builtin::std::mem::size(), string); + const u128 address = std::mem::base_address(); + return builtin::std::mem::find_string_in_range(occurrence_index, address, address + std::mem::size(), string); }; /** @@ -242,16 +225,6 @@ namespace auto std::mem { return builtin::std::mem::current_bit_offset(); }; - - /** - Searches for a sequence of bytes and places the given type at that address - @tparam Magic The magic sequence to search for - @tparam T The type to place at the address - */ - struct MagicSearch { - std::mem::impl::MagicSearchImpl impl[while(!std::mem::eof())] [[inline]]; - }; - /** Reinterprets a value as a different one @tparam From The type to reinterpret from @@ -285,6 +258,28 @@ namespace auto std::mem { return ""; }; + struct MagicSearchImpl { + s128 address = builtin::std::mem::find_string_in_range(0, $, std::mem::size(), Magic); + if (address < 0) + break; + + $ = address; + try { + T data [[inline]]; + } catch { + T data; + } + }; + } + /** + Searches for a sequence of bytes and places the given type at that address + @tparam Magic The magic sequence to search for + @tparam T The type to place at the address + */ + struct MagicSearch { + std::mem::impl::MagicSearchImpl impl[while(!std::mem::eof())] [[inline]]; + }; + } diff --git a/patterns/Assassin's Creed: Unity/acu_data_compressed.hexpat b/patterns/Assassin's Creed: Unity/acu_data_compressed.hexpat new file mode 100644 index 00000000..cab12712 --- /dev/null +++ b/patterns/Assassin's Creed: Unity/acu_data_compressed.hexpat @@ -0,0 +1,47 @@ +#pragma description Assassin's Creed: Unity's Compressed .data file +#pragma author haru233 + +// many thanks to AxCut +// ImHex Hex Pattern File for Assassin's Creed: Unity's Compressed .data files + + +import std.core; +import std.mem; + +enum CompressionType : u8 { + LZO1X_ = 0x00, // Both 0x00 and 0x01 mean LZO1X + LZO1X = 0x01, + LZO2A = 0x02, + xmemdecompress = 0x03, + LZO1C = 0x05 +}; + +struct CHUNK { + u16 Uncompressed_Size; + u16 Compressed_Size; +}; + +struct CHUNK_Data { + u32 Hash; + + u64 i = std::core::array_index(); + u8 data[parent.chunk[i].Compressed_Size]; +}; + +struct PACK { + u64 ID; + padding[2]; + CompressionType Compression_Type; + padding[3]; + u8 Version; + u16 CHUNK_Count; + + CHUNK chunk[CHUNK_Count]; + CHUNK_Data data[CHUNK_Count]; + + +}; + + + +PACK pack[while(!std::mem::eof())] @0x00; diff --git a/patterns/Assassin's Creed: Unity/acu_data_decompressed.hexpat b/patterns/Assassin's Creed: Unity/acu_data_decompressed.hexpat new file mode 100644 index 00000000..84e34e78 --- /dev/null +++ b/patterns/Assassin's Creed: Unity/acu_data_decompressed.hexpat @@ -0,0 +1,93 @@ +#pragma description Assassin's Creed: Unity's Decompressed .data file +#pragma author haru233 + +// Thanks to yretenai on GitHub for helping with the Block Allocator part + +import std.core; +import std.mem; + +struct Block_Allocator_Type0 { + padding[4]; + u32 Class_ID; + u32 Size; +}; + +struct Block_Allocator_Type1 { + padding[4]; + u32 Type_ID; + u32 Size; +}; + +struct Block_Allocator { + u16 Version; + + if (Version == 0) { + u32 Block_Allocator_Number; + Block_Allocator_Type0 block_allocator_type0[Block_Allocator_Number]; + } + + else if (Version == 1) { + u32 Block_Allocator_Number; + Block_Allocator_Type1 block_allocator_type1_[Block_Allocator_Number]; + } + + else if (Version == 2) { + bool Has_Secondary_Block_Allocator; + u32 Main_Block_Allocator_Number; + + Block_Allocator_Type1 block_allocator_type1__[Main_Block_Allocator_Number]; + + if (Has_Secondary_Block_Allocator) { + u32 Secondary_Block_Allocator_Number; + Block_Allocator_Type1 block_allocator_type1___[Secondary_Block_Allocator_Number+1]; + } + } + + +}; + +struct File { + u32 Object_Hash; + u32 File_Size; + u32 Filename_Length; + + if (File_Size > 0) { + if (Filename_Length == 0) { + bool HasBlockAllocator; + + if (HasBlockAllocator) { + Block_Allocator block_allocator; + u8 File_Data[File_Size]; + } + + else + u8 File_Data[File_Size]; + } + + else { + char Filename[Filename_Length]; + + bool HasBlockAllocator; + + if (HasBlockAllocator) { + Block_Allocator block_allocator; + u8 File_Data[File_Size]; + } + + else + u8 File_Data[File_Size]; + + + } + + } + + else + continue; + + + +}; + + +File file[while(!std::mem::eof())] @0x00; diff --git a/patterns/Assassin's Creed: Unity/acu_forge.hexpat b/patterns/Assassin's Creed: Unity/acu_forge.hexpat new file mode 100644 index 00000000..e000a492 --- /dev/null +++ b/patterns/Assassin's Creed: Unity/acu_forge.hexpat @@ -0,0 +1,64 @@ +#pragma description Assassin's Creed: Unity's .forge archive file +#pragma author haru233 + +// many thanks to AxCut +// ImHex Hex Pattern File for Assassin's Creed: Unity's .forge files + +import std.core; + +struct Forge_Header { + char MAGIC[8]; + padding[1]; + u32 Version; + u32 File_Data_Header_Offset; +}; + +struct File_Data_Header { + u32 File_Count; + padding[32]; + u64 File_Data_Header2_Offset; +}; + +struct File_Data_Header2 { + u32 File_Count2; + padding[4]; + u64 File_Table_Offset; + padding[12]; + u32 File_Count3; + u64 File_Name_Table_Offset; + padding[8]; +}; + + +struct File_Table { + u64 Raw_Data_Offset; + u64 File_ID; + u32 Raw_Data_Size; +}; + +struct File_Name_Table { + u32 Raw_Data_Size; + padding[40]; + char Filename[128]; + padding[20]; +}; + + +Forge_Header forge_header @0x00; + +File_Data_Header file_data_header @(forge_header.File_Data_Header_Offset); + +File_Data_Header2 file_data_header2 @(file_data_header.File_Data_Header2_Offset); + +File_Table file_table[file_data_header.File_Count] @(file_data_header2.File_Table_Offset); + +File_Name_Table file_name_table[file_data_header.File_Count] @(file_data_header2.File_Name_Table_Offset); + + +struct Raw_Data_Table { + u64 i = std::core::array_index(); + u8 Raw_Data[file_table[i].Raw_Data_Size] @ file_table[i].Raw_Data_Offset; +}; + + +Raw_Data_Table raw_data_table[file_data_header.File_Count] @0x00; diff --git a/patterns/DFIR/DFIR_README.md b/patterns/DFIR/DFIR_README.md new file mode 100644 index 00000000..6c518ddb --- /dev/null +++ b/patterns/DFIR/DFIR_README.md @@ -0,0 +1,81 @@ +ImHex Pattern Files - Digital Forensics: + + - [ImHex-DFIR-Patterns](https://github.com/Xtreme-Liberty/ImHex-DFIR-Patterns) + +Enhanced features of the stock Disk/Filesystem pattern files for forensic review of disk content. + - [ImHex](https://github.com/WerWolv/ImHex) + - [ImHex Patterns](https://github.com/WerWolv/ImHex-Patterns) + +Use: + - Open a physical disk via Raw Provider (read-only) + - EXAMPLE: /dev/disk6 + - Import Pattern File + - EXAMPLE: DISK_PARSER.hexpat + - [Pattern_Selection (screenshot)](https://github.com/Xtreme-Liberty/ImHex-DFIR-Patterns/blob/main/screenshots/2-DISK_PARSER-Pattern.png) + + - DISK_PARSER.hexpat + - Recognize MBR/GPT Disks and parse MPT/GPT + - Including Logical Volumes in an Extended Partition (container) + - Auto load file system patterns for FAT32, exFAT, NTFS formatted volumes + - Optional Disk Report + + - [DISK > MBR/GPT (screenshot)](https://github.com/Xtreme-Liberty/ImHex-DFIR-Patterns/blob/main/screenshots/3-DISK-HYBRID.png) + - [DISK > MBR > MPT > 3 Primaries | 2 Logicals in an Extended (screenshot)](https://github.com/Xtreme-Liberty/ImHex-DFIR-Patterns/blob/main/screenshots/3a-DISK-MBR.png) + + - FAT32.hexpat + - Auto loaded by DISK_PARSER.hexpat + - Parse VBR, FAT1, FAT2, Root Dir, and 1 level of SubDirs + - FAT1/FAT2 Cluster chaining with SFN resolution + - LFN/SFN Alias grouping in Root Dir + - Recognize deleted entries (xE5) + - File Content pointer + - D/T Conversions + - Optional FAT32 Volume Report + + - [VOLUME > FAT32 > FAT1 (screenshot)](https://github.com/Xtreme-Liberty/ImHex-DFIR-Patterns/blob/main/screenshots/4-FAT32-1_SMALL_TXT.png) + - [VOLUME > FAT32 > Root Dir (screenshot)](https://github.com/Xtreme-Liberty/ImHex-DFIR-Patterns/blob/main/screenshots/5-FAT32_ROOT_DIR.png) + - [VOLUME > FAT32 > Data Pointer (screenshot)](https://github.com/Xtreme-Liberty/ImHex-DFIR-Patterns/blob/main/screenshots/6-FAT32_SFN_POINTER.png) + + - exFAT.hexpat + - Auto loaded by DISK_PARSER.hexpat + - Parse VBR/Boot Sector/Extended Sectors, FAT1, Root Dir + - Recognize active directory entries (x85, xC0, xC1) + - Recognize inactive directory entries (x05, x40, x41) + - xC0/x40 File Content pointer + - D/T Conversions + - Optional exFAT Volume Report + + - [VOLUME > exFAT (screenshot)](https://github.com/Xtreme-Liberty/ImHex-DFIR-Patterns/blob/main/screenshots/7-exFAT-1.png) + - [VOLUME > exFAT > Root Dir > xC0 (screenshot)](https://github.com/Xtreme-Liberty/ImHex-DFIR-Patterns/blob/main/screenshots/8-exFAT_xC0.png) + - [VOLUME > exFAT > Data Pointer (screenshot)](https://github.com/Xtreme-Liberty/ImHex-DFIR-Patterns/blob/main/screenshots/9-exFAT-Data_Pointer.png) + + - NTFS.hexpat + - Auto loaded by DISK_PARSER.hexpat + - Parse VBR (Boot Sector), $MFT, Root Dir, and Indexes + - Recursively parse the $Metadata files, $Attributes, and user files/dirs + - Added file record | parent [MFT#] [SEQ#] indicators + - Parse x80/xB0 Data Runs + - File Content pointer + - D/T Conversions + - Optional NTFS Volume Report + + - [VOLUME > NTFS > $MFT > D/T Conversion (screenshot)](https://github.com/Xtreme-Liberty/ImHex-DFIR-Patterns/blob/main/screenshots/10-NTFS-DT.png) + - [VOLUME > NTFS > $MFT > x80 Run List (screenshot)](https://github.com/Xtreme-Liberty/ImHex-DFIR-Patterns/blob/main/screenshots/11-NTFS-DATA_RUN.png) + - [VOLUME > NTFS > Data Pointer (screenshot)](https://github.com/Xtreme-Liberty/ImHex-DFIR-Patterns/blob/main/screenshots/12-NTFS-DATA_POINTER.png) + + - Optional Reports + - Simply copy the console output to a file... + + - To enable/disable the reports: + - Open each DFIR related .hexpat + - Find the report constant (near the top) + - "true" = enabled + - "false" = disabled + + Example Report: GPT > FAT32|exFAT + - [exFAT_Report](https://github.com/Xtreme-Liberty/ImHex-DFIR-Patterns/blob/main/reports/exFAT_Report.txt) + + Example Report: MBR > 5 Logical Volumes (2 in an Extended) > All FAT32 Volumes + - [MBR_5_VOLs](https://github.com/Xtreme-Liberty/ImHex-DFIR-Patterns/blob/main/reports/MBR_5_VOLs.txt) + + diff --git a/patterns/DFIR/DISK_PARSER.hexpat b/patterns/DFIR/DISK_PARSER.hexpat new file mode 100644 index 00000000..aa2222fe --- /dev/null +++ b/patterns/DFIR/DISK_PARSER.hexpat @@ -0,0 +1,677 @@ +#pragma author Formula Zero One Technologies +#pragma description DFIR_DISK_PARSER_v2.0 +#pragma MIME application/x-ima +#pragma endian little + + +// ----------------------------------------------------------------------------- +// CREDIT +// ----------------------------------------------------------------------------- +// Based on /fs/pattern.hexpat by WerWolv +// ----------------------------------------------------------------------------- +// TODO +// ----------------------------------------------------------------------------- +// Refine File System Detection/Match +// ----------------------------------------------------------------------------- +// IMPORTS +// ----------------------------------------------------------------------------- +import std.core; +import std.io; +import std.time; +import type.guid; +import type.magic; +import type.time; +import type.base; +import hex.provider; + +// WORKING IMPORTS +import * from DFIR.FAT32 as FAT32Pat; +import * from DFIR.exFAT as EXFATPat; +import * from DFIR.NTFS as NTFSPat; + +// ------------------------------------ +// DISABLED IMPORTS +// REFS - UNTESTED +// EXT4 - GROUP DESC ERRORS +// APFS - PARTIALLY WORKS + // Comment out "using uuid_t = type::GUID" + // Replace all instances of "uuid_t" with "type::GUID" + // Comment out line 1456-EOF +// JPEG/PNG - OFFSET ERRORS +// ------------------------------------ +//import * from fs.apfs as APFSPat; +//import * from fs.ext4 as EXT4Pat; +//import * from fs.refs as REFSPat; +//import * from jpeg as JPEGPat; +//import * from png as PNGPat; + +// ----------------------------------------------------------------------------- +// FWD DECs - GLOBAL +// ----------------------------------------------------------------------------- +bool has_ext = false; +bool has_gpt = false; +u64 partitionOffset = 0; +u64 containerStartOffset = 0; + +u32 mptIndex = 0; +u32 extIndex = 0; +str entryName = ""; + +u32 MPT_Count = 0; +u32 EXT_VolCount = 0; +u32 GPT_Count = 0; + +u32 memory_size = std::mem::size(); +str disk_path = hex::prv::get_information("file_path",""); +u128 sector_size = hex::prv::get_information("sector_size",""); + +// ----------------------------------------------------------------------------- +// REPORT HEADER ** ATTENTION ** +// ----------------------------------------------------------------------------- + +// ---******---*******---vvvv--- | +const bool DISK_REPORT = true; +// ---******---*******---^^^^--- | + +if (DISK_REPORT) { + std::print(" # # # # # # "); + std::print(" # # # "); + std::print(" # # # "); + std::print(" # # # # # # # "); + std::print(" I m H e x "); + std::print(" "); + std::print("-----------------------------------------"); + std::print(" "); + std::print(" ENTITY: _____________________"); + std::print(" "); + std::print("EXAMINER: _____________________"); + std::print(" "); + u128 timestamp = std::time::epoch(); + std::time::Time local_ts = std::time::to_local(timestamp); + std::time::Time utc_ts = std::time::to_utc(timestamp); + std::print("-----------------------------------------"); + std::print(" LOCAL: {}", + std::format("{:02}/{:02}/{:04} @ {:02}:{:02}:{:02}", + local_ts.mon + 1, + local_ts.mday, + local_ts.year + 1900, + local_ts.hour, + local_ts.min, + local_ts.sec + )); + std::print(" UTC: {}", + std::format("{:02}/{:02}/{:04} @ {:02}:{:02}:{:02}", + utc_ts.mon + 1, + utc_ts.mday, + utc_ts.year + 1900, + utc_ts.hour, + utc_ts.min, + utc_ts.sec + )); + std::print("-----------------------------------------"); + std::print(" "); +} + +// ----------------------------------------------------------------------------- +// SIGNATURE HELPER +// ----------------------------------------------------------------------------- +enum MBRSignature : u16 { + MBR_SIG = 0xAA55 // 0x55AA -> Read LE +}; + +// ----------------------------------------------------------------------------- +// CHS HELPER +// ----------------------------------------------------------------------------- +bitfield CHS_Decoder { + head : 8; + sector : 6; + cylinder : 10; +} [[format("chs_formatter")]]; + +fn chs_formatter(CHS_Decoder CHS) { + return std::format("({:X}, {:X}, {:X}) | 0x{:X}", CHS.cylinder, CHS.head, CHS.sector, (CHS.cylinder * 16 + CHS.head) * 63 + (CHS.sector - 1)); +}; + +// ----------------------------------------------------------------------------- +// TIMESTAMP HELPER +// ----------------------------------------------------------------------------- +struct DiskTimeStamp { + u8 seconds, minutes, hours; +}; + +// ----------------------------------------------------------------------------- +// DISK PROTECTION HELPER +// ----------------------------------------------------------------------------- +enum DiskProtection : u16 { + NotProtected = 0x0000, + CopyProtected = 0x5A5A +}; + +// ----------------------------------------------------------------------------- +// PARTITION STATUS HELPER +// ----------------------------------------------------------------------------- +enum PartitionStatus : u8 { + Not_Active = 0x00, // not_bootable + Active = 0x80 // bootable +}; + +enum MPTPartLabel : u8 { + UNUSED_OR_HIDDEN_ENTRY = 0x00, + PRIMARY = 0x07, + PRIMARY_F32_SMALL = 0x0B, + PRIMARY_0C_BIG = 0x0C, + EXTENDED_CONT_SMALL = 0x05, + EXTENDED_CONT_BIG = 0x0F, + LEGACY_MBR = 0xEE +}; + +// ----------------------------------------------------------------------------- +// PARTITION TYPE HELPER +// ----------------------------------------------------------------------------- +enum PartitionTypeCode : u8 { + UNUSED_ENTRY = 0x00, + FAT12_HDD = 0x01, + FAT12_HIDDEN = 0x11, + FAT16_SMALL = 0x04, + FAT16_SMALL_HIDDEN = 0x14, + FAT16_BIG = 0x06, + FAT16_BIG_HIDDEN = 0x16, + FAT32_SMALL = 0x0B, + FAT32_SMALL_HIDDEN = 0x1B, + FAT32_BIG = 0x0C, + FAT32_BIG_HIDDEN = 0x1C, + EXT_PART_SMALL = 0x05, + EXT_PART_SMALL_HIDDEN = 0x15, + EXT_PART_BIG = 0x0F, + EXT_PART_BIG_HIDDEN = 0x1F, + NTFS_EXFAT = 0x07, + NTFS_EXFAT_HIDDEN = 0x17, + WINDOWS_RECOVERY = 0x27, + NTFS_VOL_SET_1 = 0x86, + NTFS_VOL_SET_2 = 0x87, + macOSX = 0xA8, + OS2_HIDDEN_CDRIVE = 0x84, + LINUX_EXT = 0x83, + LINUX_EXT2 = 0x85, + LINUX_LVM = 0x8E, + LINUX_PA_RISC = 0xF0, + LINUX_RAID = 0xFD, + FREE_BSD = 0xA5, + OPEN_BSD = 0xA6, + QNX_1 = 0x4D, + QNX_2 = 0x4E, + QNX_3 = 0x4F, + GPT_DISK_STD = 0xEE, + GPT_DISK_SYS = 0xEF, + UNKNOWN = 0xFF, +}; + +// ----------------------------------------------------------------------------- +// GUID PARTITION TABLE (GPT) PARTIONING SCHEME RELATED +// ----------------------------------------------------------------------------- +// V V V V V V V V V V +// ----------------------------------------------------------------------------- +// GPT PARTITION LABEL HELPER +// ----------------------------------------------------------------------------- +enum GUIDPartLabel : u128 { + // ---------------- COMMON ---------------- + UNUSED_ENTRY = 0x00000000000000000000000000000000, + EFI_SYSTEM_PART = 0x3BC93EC9A0004BBA11D2F81FC12A7328, + APPLE_APFS_CONT = 0xACEC4365300011AA11AA00007C3457EF, + APPLE_HFS_PLUS_PART = 0xACEC4365300011AA11AA000048465300, + MICROSOFT_RESERVED_PART = 0xAE1502F92DF97D81B84D5C0BE3E3C9E3, + WINDOWS_REC_ENVIRONMENT = 0xACD67901D5BF6AA1404DD106A4BB94DE, + BASIC_DATA_PART = 0xC79926B7B668C0874433B9E5EBD0A0A2, + + // ---------------- LINUX ---------------- + LINUX_FILE_SYSTEM = 0xE47D47D8693D798E477284830FC63DAF, + RAID_PART = 0x1E91840F3F7406A04D3B05FCA19D880F, + ROOT_PART_X86 = 0x8A45F0D531D1F79A41B2F29744479540, + ROOT_PART_X86_64 = 0x09B784F9CAFBE7964DB1E8CD4F68BCE3, + ROOT_PART_ARM = 0xD3BE9AD4A1216CB14E3C2CE469DAD710, + ROOT_PART_ARM_64 = 0xAE3F0D286F4C44AF41C31DF0B921B045, + BOOT_PART = 0x72716FFD75B252A3426259E6BC13C2FF, + SWAP_PART = 0x4F4F4BC83309E58443C4A4AB0657FD6D, + LOGICAL_VOLUME_MGR_PART = 0x28F93D2A8F233CA244C2F507E6D6D379, + HOME_PART = 0x15F9AEE2140E44B84F132EB4933AC7E1, + SRV_SERVER_DATA_PART = 0xE8986FA7251A7F904F3B20E03B8F8425, + PLAIN_DMCRYPT_PART = 0xB786550AA13E418949B72D007FFEC5C9, + LUKS_PART = 0xCC59605342171C864C5363EDCA7D7CCB, + + // ---------------- APPLE ---------------- + APPLE_UFS_CONT = 0xACEC4365300011AA11AA000055465300, + APPLE_ZFS = 0x316673200008A69911B21DD26A898CC3, + APPLE_RAID_PART = 0xACEC4365300011AA11AA000052414944, + APPLE_RAID_PART_OFFLINE = 0xACEC4365300011AA11AA5F4F52414944, + APPLE_BOOT_PART_REC_HD = 0xACEC4365300011AA11AA0000426F6F74, + APPLE_LABEL = 0xACEC4365300011AA11AA6C004C616265, + APPLE_TV_RECOVERY_PART = 0xACEC4365300011AA11AA76655265636F, + APPLE_CORE_STORAGE_CONT = 0xACEC4365300011AA11AA616753746F72, + HFS_FILEVAULT_VOLUME_CONT = 0xACEC4365300011AA11AA616753746F72, + APPLE_APFS_PREBOOT_PART = 0xACEC4365300011AA11AA006769646961, + APPLE_APFS_RECOVERY_PART = 0xACEC4365300011AA11AA007972637652, + + // ---------------- WINDOWS ---------------- + LOGICAL_DISK_MGR_META_PART = 0xB3CF34E104E1D28542E08F7EAAC80858, + LOGICAL_DISK_MGR_DATA_PART = 0xAD694A71113368BC4F621431A0609BAF, + IBM_GENERAL_PARALLEL_FILE_SYS_PART = 0x74B155E07A2DC3914E4EEF7D90FFAA37, + STORAGE_SPACES_PART = 0x2DECF6E501B0A3AFEE4CF6808FAF5CE7, + STORAGE_REPLICA_PART = 0xD123292BD147C8AAC043A1ACC58D4355, +}; +// ----------------------------------------------------------------------------- +// BASIC DATA PARTITION ATTRIBUTES +// ----------------------------------------------------------------------------- +bitfield GPT_BDP_Attributes { + bool platform_required : 1 [[comment("Bit 0: RequiredPartition - Volume must be preserved")]]; + bool io_ignore : 1 [[comment("Bit 1: NoBlockIOProtocol - EFI ignores this Volume, no FS Mapping")]]; + bool legacy_flag : 1 [[comment("Bit 2: LegacyBIOSBootable - Active/Bootable under BIOS")]]; + reserved_UEFI : 45 [[comment("Bits 3–47: Reserved for UEFI")]]; + reserved_MS : 12 [[comment("Bits 48–59: Reserved for Microsoft")]]; + bool read_only : 1 [[comment("Bit 60: BasicDataPart - Read-Only Volume")]]; + bool shadow_copy : 1 [[comment("Bit 61: BasicDataPart - Shadow Copy Volume")]]; + bool hidden : 1 [[comment("Bit 62: BasicDataPart - Hidden Volume")]]; + bool no_drive_letter : 1 [[comment("Bit 63: BasicDataPart - Do not Auto-Assign Drive Letter")]]; +} [[bitfield_order( + std::core::BitfieldOrder::LeastToMostSignificant, 64)]]; + +// ----------------------------------------------------------------------------- +// GPT ENTRIES PARSER +// LBA2-LBA33 +// EACH ENTRY IS 128 BYTES (DESCRIBES A VOLUME) +// ----------------------------------------------------------------------------- +union PartitionUnion { + le type::GUID PartTypeGUID; // HUMAN READABLE GUID + GUIDPartLabel PartTypeLabel [[name(std::format("PartTypeLabel (DERIVED)"))]]; // OBJECT LABEL +}; + +struct GPT_PartitionEntry { + PartitionUnion Type [[comment("Known Partition Type GUID: Global Identifier")]]; + le type::GUID Unique_GUID [[comment("Unique Partition GUID: Every Volume has its own Unique GUID")]]; + u64 Start_LBA [[comment("The first Sector of the Volume (Offset by 1)")]]; + u64 End_LBA [[comment("The last Sector of the Volume (Offset by 1)")]]; + GPT_BDP_Attributes ATTR [[comment("ATTRs for a Basic Data Partition may not be the same as a Microsoft Reserved Partition")]]; + char16 PartName[36] [[comment("Partition Name: Based on Known Partition Type GUID, except for Disk Images")]]; + + if (Type.PartTypeLabel != GUIDPartLabel::UNUSED_ENTRY) { + GPT_Count += 1; + } + + u64 GPTpartitionOffset = Start_LBA * sector_size + [[name(std::format("VOL_OFFSET {} | 0x{:02X} (DERIVED)", Start_LBA * sector_size, Start_LBA * sector_size)), + export]]; + + match (Type.PartTypeLabel) { + (GUIDPartLabel::UNUSED_ENTRY): + continue; + + (GUIDPartLabel::EFI_SYSTEM_PART): + FAT32Pat EFI_SYS_VOL @ GPTpartitionOffset; + + (GUIDPartLabel::BASIC_DATA_PART | + GUIDPartLabel::WINDOWS_REC_ENVIRONMENT): { + char gpt_fat32_magic[8] @ GPTpartitionOffset + 82 [[hidden]]; + char gpt_ntfs_magic[8] @ GPTpartitionOffset + 3 [[hidden]]; + char gpt_exfat_magic[8] @ GPTpartitionOffset + 3 [[hidden]]; + + if (gpt_fat32_magic == "FAT32 ") + FAT32Pat FAT32_VOL @ GPTpartitionOffset; + if (gpt_ntfs_magic == "NTFS ") + NTFSPat NTFS_VOL @ GPTpartitionOffset; + else if (gpt_exfat_magic == "EXFAT ") + EXFATPat EXFAT_VOL @ GPTpartitionOffset; + } + // --------- DISABLED ----------------- + // EXT4 PATTERN WAS INOP WHEN TESTED + //(GUIDPartLabel::LINUX_FILE_SYSTEM): + //EXT4Pat EXT4_VOL @ GPTpartitionOffset; + //(GUIDPartLabel::APPLE_APFS_CONT): + // APFSPat APFS_VOL @ GPTpartitionOffset; + } +} [[name(std::format("GPT_ENTRY [{}]", std::core::array_index()))]]; + +// ----------------------------------------------------------------------------- +// GPT HEADER PARSER +// LBA1 OFFSETS 0-91 (92 bytes of 512 bytes used) +// ----------------------------------------------------------------------------- +struct GPT_Header { + type::Magic<"EFI PART"> signature [[comment("Signature (EFI PART)")]]; + u32 revision [[comment("Header Revision Value")]]; + u32 header_size [[comment("Size of Header - 92 Bytes")]]; + type::Hex header_crc32 [[comment("GPT Header Checksum")]]; + u32 reserved [[comment("Zeros")]]; + u64 current_lba [[comment("Current LBA - GPT Header Location")]]; + u64 backup_lba [[comment("Location of Backup - Header & GPT")]]; + u64 first_usable_lba [[comment("1st Sector Available for Logical VOL")]]; + u64 last_usable_lba [[comment("Last Sector Available for Logical VOL")]]; + type::GUID disk_guid [[comment("Unique Disk GUID")]]; + u64 partition_entries_lba [[comment("1st Sector of GPT")]]; + u32 num_partition_entries [[comment("Total Number of Partition Entries Available - 128 on Windows")]]; + u32 size_of_partition_entry [[comment("Size in Bytes of each GPT Entry")]]; + type::Hex partition_entries_crc32 [[comment("GPT Array Checksum")]]; +}; + +// ----------------------------------------------------------------------------- +// MASTER BOOT RECORD (MBR) PARTIONING SCHEME RELATED +// ----------------------------------------------------------------------------- +// V V V V V V V V V V +// ----------------------------------------------------------------------------- +// MASTER PARTITION TABLE (MPT) +// LBA0 > 0FFSETS 446-509 +// Each Entry Describes a Logical Volume (type/start_loc/size) +// ----------------------------------------------------------------------------- +union MBRPartitionUnion { + PartitionTypeCode Part_Type; + MPTPartLabel PartTypeLabel; // overlay for 0x00 +}; + +struct PartitionTableEntry { + // partition table fields + PartitionStatus ActiveFlag; + CHS_Decoder Starting_CHS; + MBRPartitionUnion Type; + CHS_Decoder Ending_CHS; + u32 Start_LBA; + u32 Total_Sectors; + + if (Type.PartTypeLabel != MPTPartLabel::UNUSED_OR_HIDDEN_ENTRY) { + // Track Count of Logical Volumes in the Extended Container + //MPT_Count += 1; + if (containerStartOffset == 0) { + // top-level MBR entry + MPT_Count = MPT_Count + 1; + } else { + // a logical inside an extended container + EXT_VolCount = EXT_VolCount + 1; + } + } + + partitionOffset = containerStartOffset + (Start_LBA * sector_size); + + match (Type.PartTypeLabel) { + (PartitionTypeCode::UNUSED_ENTRY): continue; + (PartitionTypeCode::FAT32_SMALL | PartitionTypeCode::FAT32_BIG): { + FAT32Pat FAT32_VOL @ partitionOffset; + } + (PartitionTypeCode::NTFS_EXFAT): { + char magic[8] @ partitionOffset + 3; + if (magic == "NTFS ") + NTFSPat NTFS_VOL @ partitionOffset; + else + EXFATPat EXFAT_VOL @ partitionOffset; + } + (PartitionTypeCode::EXT_PART_SMALL | PartitionTypeCode::EXT_PART_BIG): { + // Save parent state + bool parent_has_ext = has_ext; + has_ext = true; + + containerStartOffset = partitionOffset; + + // Parse first two entries of the extended partition + PartitionTableEntry EXTENDED_PARTITION[2] @ partitionOffset + 446; + + has_ext = parent_has_ext; + } + (PartitionTypeCode::GPT_DISK_STD | PartitionTypeCode::GPT_DISK_SYS): + // Set global flag + has_gpt = true; + } + if (!has_ext) { + entryName = std::format("MPT_ENTRY [{}]", mptIndex); + mptIndex += 1; + } else { + if (std::core::array_index() <= 0) { + entryName = std::format("LOGICAL_VOL (EXT) [{}]", extIndex); + } else if (std::core::array_index() == 1) { + entryName = "NEXT VOL POINTER (EXT)"; + } else { + entryName = std::format("LOGICAL_VOL (EXT) [{}]", extIndex); + } + extIndex += 1; + } +} [[name(entryName)]]; + +// ----------------------------------------------------------------------------- +// MBR PARSER +// LBA0 > OFFSETS 0-511 (512 bytes) +// ----------------------------------------------------------------------------- +struct MasterBootRecord { + u8 bootstrapCodeArea1[218] [[comment("Boot Strapping Code")]]; + padding[2] [[comment("Zeros")]]; + u8 originalPhysicalDrive [[comment("???")]]; + DiskTimeStamp diskTimeStamp [[comment("Timestamp of Disk OG Partitioning")]]; + u8 bootstrapCodeArea2[216] [[comment("Boot Strapping Code")]]; + u32 diskSignature [[comment("Disk Signature")]]; + DiskProtection diskProtection [[comment("Disk Protection - 0x0000=Not | 0x5A5A=Prot")]]; + PartitionTableEntry PT[4] [[comment("Master Partition Table (MPT) Offset 446-509")]]; + MBRSignature MBR_SIG [[comment("End of MBR - 0x55AA")]]; +}; + +// ----------------------------------------------------------------------------- +// DISK PARSER +// ----------------------------------------------------------------------------- +struct DiskRoot { + // Master Boot Record at LBA 0 (1st physical sector) + MasterBootRecord MBR @ 0x00; + + if (has_gpt) { + // GPT Header at LBA 1 (2nd physical sector) + GPT_Header GPT_HEADER @ 0x200; + // The GPT (table) at LBA 2 (3rd physical sector) to LBA 33 (34th physical sector) + // 32 sectors total (Windows) that can define up to 128 - (primary) logical volumes + GPT_PartitionEntry GPT_ENTRIES[GPT_HEADER.num_partition_entries] @ (GPT_HEADER.partition_entries_lba * 512); + } +}; + +// ----------------------------------------------------------------------------- +// ROOT OBJECT +// ----------------------------------------------------------------------------- +// --- +DiskRoot DISK @ 0x0; +// --- + + +// ------------------------------ +// DISK REPORT +// ------------------------------ + +if (DISK_REPORT) { + std::print("-----------------------------------------"); + std::print("-------------- DISK_REPORT --------------"); + std::print("-----------------------------------------"); + + // Disk Basics + std::print("DISK_PATH = {}", disk_path); + std::print("SECTOR_SIZE = {} BYTES", sector_size); + std::print("DISK_SIZE = {} SECTORS", memory_size / sector_size); + std::print("DISK_SIZE = {:.4f} GB @ 1000", memory_size / 1000.0 / 1000.0 / 1000.0); + std::print("DISK_SIZE = {:.4f} GiB @ 1024", memory_size / 1024.0 / 1024.0 / 1024.0); + + // Disk Protection + str diskProtectionStr; + if (DISK.MBR.diskProtection == DiskProtection::NotProtected) { + diskProtectionStr = "NOT_COPY_PROTECTED"; + } else if (DISK.MBR.diskProtection == DiskProtection::CopyProtected) { + diskProtectionStr = "COPY_PROTECTED"; + } else { + diskProtectionStr = "UNKNOWN"; + } + std::print("DISK_PROTECT = {}", diskProtectionStr); + + // Partition Scheme + if (MPT_Count >= 1 && GPT_Count == 0) { + std::print("PART_SCHEME = MBR"); + } else if (GPT_Count >= 1 && MPT_Count == 0) { + std::print("PART_SCHEME = GPT"); + } else if (GPT_Count >= 1 && MPT_Count >= 1) { + std::print("PART_SCHEME = HYBRID (MBR + GPT)"); + } else { + std::print("PART_SCHEME = UNKNOWN"); + } + + // MBR MPT Partitions + for (u32 i = 0, i < MPT_Count, i = i + 1) { + std::print("-----------------------------------------"); + std::print("-------------- MBR_MPT [{}] --------------", i); + std::print("-----------------------------------------"); + + // STATUS + str statusStr; + if (DISK.MBR.PT[i].ActiveFlag == PartitionStatus::Active) { + statusStr = "ACTIVE/BOOTABLE"; + } else if (DISK.MBR.PT[i].ActiveFlag == PartitionStatus::Not_Active) { + statusStr = "INACTIVE/NOT_BOOTABLE"; + } else { + statusStr = "UNKNOWN"; + } + std::print(" STATUS = {}", statusStr); + + // TYPE_CODE + str typeStr; + if (DISK.MBR.PT[i].Type.Part_Type == PartitionTypeCode::FAT32_SMALL) { + typeStr = "FAT32 (CHS) (0x0B)"; + } else if (DISK.MBR.PT[i].Type.Part_Type == PartitionTypeCode::FAT32_BIG) { + typeStr = "FAT32 (LBA) (0x0C)"; + } else if (DISK.MBR.PT[i].Type.Part_Type == PartitionTypeCode::NTFS_EXFAT) { + typeStr = "NTFS/EXFAT (0x07)"; + } else if (DISK.MBR.PT[i].Type.Part_Type == PartitionTypeCode::GPT_DISK_STD) { + typeStr = "GPT_PROTECTIVE (0xEE)"; + } else if (DISK.MBR.PT[i].Type.Part_Type == PartitionTypeCode::EXT_PART_BIG) { + typeStr = "EXTENDED (0x0F)"; + } else { + typeStr = "OTHER/UNKNOWN"; + } + std::print(" TYPE_CODE = {}", typeStr); + + // LBA and size + std::print(" FIRST_LBA = {:02}", DISK.MBR.PT[i].Start_LBA); + std::print(" LAST_LBA = {:02}", DISK.MBR.PT[i].Start_LBA + DISK.MBR.PT[i].Total_Sectors - 1); + std::print(" VOL_SIZE = {:02} SECTORS", DISK.MBR.PT[i].Total_Sectors); + std::print(" VOL_SIZE = {:.4f} GB", (DISK.MBR.PT[i].Total_Sectors * sector_size) / 1000.0 / 1000.0 / 1000.0); + std::print(" VOL_SIZE = {:.4f} GiB", (DISK.MBR.PT[i].Total_Sectors * sector_size) / 1024.0 / 1024.0 / 1024.0); + + if (DISK.MBR.PT[i].Type.PartTypeLabel == MPTPartLabel::EXTENDED_CONT_SMALL || + DISK.MBR.PT[i].Type.PartTypeLabel == MPTPartLabel::EXTENDED_CONT_BIG) { + + u32 logicalCount = std::core::member_count(DISK.MBR.PT[i].EXTENDED_PARTITION); + //u32 logicalCount = std::mem::size(DISK.MBR.PT[i].EXTENDED_PARTITION); + + for (u32 e = 0, e < logicalCount, e = e + 1) { + if (DISK.MBR.PT[i].EXTENDED_PARTITION[e].Type.PartTypeLabel == MPTPartLabel::UNUSED_OR_HIDDEN_ENTRY) + continue; + + std::print("-----------------------------------------"); + std::print("---------- LOGICAL (EXT) [{}] ------------", e); + std::print("-----------------------------------------"); + + // STATUS + str EXTstatusStr; + if (DISK.MBR.PT[i].EXTENDED_PARTITION[e].ActiveFlag == PartitionStatus::Active) { + EXTstatusStr = "ACTIVE/BOOTABLE"; + } else if (DISK.MBR.PT[i].EXTENDED_PARTITION[e].ActiveFlag == PartitionStatus::Not_Active) { + EXTstatusStr = "INACTIVE/NOT_BOOTABLE"; + } else { + EXTstatusStr = "UNKNOWN"; + } + std::print(" STATUS = {}", EXTstatusStr); + + // TYPE_CODE + str EXTtypeStr; + if (DISK.MBR.PT[i].EXTENDED_PARTITION[e].Type.Part_Type == PartitionTypeCode::FAT32_SMALL) { + EXTtypeStr = "FAT32 (CHS) (0x0B)"; + } else if (DISK.MBR.PT[i].EXTENDED_PARTITION[e].Type.Part_Type == PartitionTypeCode::FAT32_BIG) { + EXTtypeStr = "FAT32 (LBA) (0x0C)"; + } else if (DISK.MBR.PT[i].EXTENDED_PARTITION[e].Type.Part_Type == PartitionTypeCode::NTFS_EXFAT) { + EXTtypeStr = "NTFS/EXFAT (0x07)"; + } else if (DISK.MBR.PT[i].EXTENDED_PARTITION[e].Type.Part_Type == PartitionTypeCode::GPT_DISK_STD) { + EXTtypeStr = "GPT_PROTECTIVE (0xEE)"; + } else if (DISK.MBR.PT[i].EXTENDED_PARTITION[e].Type.Part_Type == PartitionTypeCode::EXT_PART_BIG) { + EXTtypeStr = "EXTENDED (0x0F)"; + } else { + EXTtypeStr = "OTHER/UNKNOWN"; + } + std::print(" TYPE_CODE = {}", EXTtypeStr); + + std::print(" FIRST_LBA = {}", DISK.MBR.PT[i].EXTENDED_PARTITION[e].Start_LBA); + std::print(" LAST_LBA = {}", DISK.MBR.PT[i].EXTENDED_PARTITION[e].Start_LBA + + DISK.MBR.PT[i].EXTENDED_PARTITION[e].Total_Sectors - 1); + std::print(" VOL_SIZE = {} SECTORS", DISK.MBR.PT[i].EXTENDED_PARTITION[e].Total_Sectors); + std::print(" VOL_SIZE = {:.4f} GB", + (DISK.MBR.PT[i].EXTENDED_PARTITION[e].Total_Sectors * sector_size) / 1000.0 / 1000.0 / 1000.0); + std::print(" VOL_SIZE = {:.4f} GiB", + (DISK.MBR.PT[i].EXTENDED_PARTITION[e].Total_Sectors * sector_size) / 1024.0 / 1024.0 / 1024.0); + } + } + } + + // GPT Header + if (GPT_Count >= 1) { + std::print("-----------------------------------------"); + std::print("-------------- GPT_HEADER ---------------"); + std::print("-----------------------------------------"); + std::print("SIGNATURE = {}", DISK.GPT_HEADER.signature); + std::print("REVISION = 0x{:02X}", DISK.GPT_HEADER.revision); + std::print("GPT_HDR_CRC = 0x{:02X}", DISK.GPT_HEADER.header_crc32); + std::print("GPT_HDR_BACKUP_LBA = {}", DISK.GPT_HEADER.backup_lba); + std::print("DISK_GUID = {}", DISK.GPT_HEADER.disk_guid); + std::print("FIRST_USABLE_LBA = {}", DISK.GPT_HEADER.first_usable_lba); + std::print("LAST_USABLE_LBA = {}", DISK.GPT_HEADER.last_usable_lba); + std::print("MAX_GPT_ENTRIES = {:02}", DISK.GPT_HEADER.num_partition_entries); + std::print("GPT_ENTRY_SIZE = {:02} BYTES", DISK.GPT_HEADER.size_of_partition_entry); + std::print("GPT_ARRAY_CRC = 0x{:02X}", DISK.GPT_HEADER.partition_entries_crc32); + + // GPT Partitions + for (u32 j = 0, j < GPT_Count, j = j + 1) { + std::print("-----------------------------------------"); + std::print("------------- GPT_PART [{}] --------------", j); + std::print("-----------------------------------------"); + std::print(" PART_TYPE_LABEL = {}", DISK.GPT_ENTRIES[j].Type.PartTypeLabel); + std::print(" PART_TYPE_GUID = {}", DISK.GPT_ENTRIES[j].Type.PartTypeGUID); + std::print(" UNIQUE_PART_GUID = {}", DISK.GPT_ENTRIES[j].Unique_GUID); + std::print(" FIRST_LBA = {:02}", DISK.GPT_ENTRIES[j].Start_LBA); + std::print(" LAST_LBA = {:02}", DISK.GPT_ENTRIES[j].End_LBA); + + bool _any = false; + std::print(" ATTR_FLAGS |"); + if(DISK.GPT_ENTRIES[j].ATTR.platform_required) { + std::print(" |- - - - > PlatformRequired"); + _any = true; + } + if(DISK.GPT_ENTRIES[j].ATTR.io_ignore) { + std::print(" |- - - - > NO_FS_MAP"); + _any = true; + } + if(DISK.GPT_ENTRIES[j].ATTR.legacy_flag) { + std::print(" |- - - - > LEGACY_BOOT"); + _any = true; + } + if(DISK.GPT_ENTRIES[j].Type.PartTypeLabel == GUIDPartLabel::BASIC_DATA_PART) { + if(DISK.GPT_ENTRIES[j].ATTR.read_only) { + std::print(" |- - - - > READ_ONLY"); + _any = true; + } + if(DISK.GPT_ENTRIES[j].ATTR.shadow_copy) { + std::print(" |- - - - > SHADOW_COPY"); + _any = true; + } + if(DISK.GPT_ENTRIES[j].ATTR.hidden) { + std::print(" |- - - - > HIDDEN"); + _any = true; + } + if(DISK.GPT_ENTRIES[j].ATTR.no_drive_letter) { + std::print(" |- - - - > NO_AUTO_MOUNT"); + _any = true; + } + } + // if nothing was printed, say "NONE" + if (!_any) { + //std::print(" |> NONE"); + std::print(" |- - - - > NONE"); + } + std::print(" PART_TYPE_NAME = {}", DISK.GPT_ENTRIES[j].PartName); + } + } + std::print("-----------------------------------------"); + std::print("------------------ END ------------------"); + std::print("-----------------------------------------"); + std::print(" "); +} diff --git a/patterns/DFIR/FAT32.hexpat b/patterns/DFIR/FAT32.hexpat new file mode 100644 index 00000000..c43b2797 --- /dev/null +++ b/patterns/DFIR/FAT32.hexpat @@ -0,0 +1,789 @@ +#pragma author Formula Zero One Technologies +#pragma description FAT32 File System (FAT32_v2.0) +#pragma MIME application/x-ima +#pragma endian little + + +// ----------------------------------------------------------------------------- +// CREDIT +// ----------------------------------------------------------------------------- +// OG AUTHOR: WerWolv +// OG DESC: fs/fat32.hexpat_v1.0 +// ----------------------------------------------------------------------------- +// NOTES FOR v2.0 ** GLOBALS NEED YOUR INPUT ** +// ----------------------------------------------------------------------------- +// Imported by DISK_PARSER.hexpat +// Added section separators for organization +// Added recursive parsing for Root Dir and a next level +// Added D/T conversions +// Show filenames on hover +// Added comments to DFIR fields of interest +// Changed pattern output naming/structure. +// Parse FAT1/FAT2 +// Show SFN <-> Starting Cluster Relation Overlay +// ----------------------------------------------------------------------------- +// TODO +// ----------------------------------------------------------------------------- +// Parse all SFN/LFN entries, not just Root + 1 +// ----------------------------------------------------------------------------- +// IMPORTS +// ----------------------------------------------------------------------------- +import std.core; +import std.io; +import std.mem; +import std.time; +import std.string; +import type.time; + +// ----------------------------------------------------------------------------- +// FORWARD DECS/GLOBALS +// ----------------------------------------------------------------------------- +// *** ATTENTION *** +// SET MAXIMUM NUMBER OF 4 BYTE CHUNKS TO PARSE FROM FAT1 +// DEFAULT IS 4096 +// Choose a value greater than 1 and less than 65536 OR increase the Array size limit with "#define... " + +// -------**************---vvvv--- | +const u64 MAX_FAT_CHUNKS = 4096; +// -------**************---^^^^--- | + +// *** ATTENTION *** +// SET MAXIMUM NUMBER OF SFN = STARTING CLUSTER TO PROCESS +// DEFAULT IS 100 (2 LEVELS DEEP | ROOT DIR + 1) +// Choose a value greater than 1 and less than 65536 OR increase the Array size limit with "#define... " + +// ---**************---************---vvv--- | +const u64 MAX_SFN_CLUSTER_RELATIONS = 100; +// ---**************---************---^^^--- | + +// ---*******---*******----vvvv--- | +const bool VOLUME_REPORT = true; +// ---*******---*******----^^^^--- | + +u64 bytesPerCluster = 0; +u64 rootDirSectors = 0; +u64 firstDataSector = 0; +u64 dataRegionStart = 0; +u64 sfn_count = 0; +u64 sfn_del_count = 0; +u64 lfn_count = 0; +u64 lfn_del_count = 0; +u64 start_index = 0; +u64 root_dir_start = 0; +u64 allocated_file_count = 0; + +u64 VBR_OFFSET = 0; +u64 FAT1_start_offset = 0; +u64 FAT2_start_offset = 0; +u64 FAT_ClusterHeap_Count = 0; + +u64 abs_FAT1_start_offset = 0; +u64 abs_FAT2_start_offset = 0; +u64 abs_rootDirStart_offset = 0; + +// ----------------------------------------------------------------------------- +// FILE ALLOCATION TABLE RELATED +// ----------------------------------------------------------------------------- +// V V V V V V V V V V +// ----------------------------------------------------------------------------- + +// ------------------------------ +// SFN <-> CLUSTER OVERLAY +// ------------------------------ +struct INFO_Overlay { + u64 index = std::core::array_index(); + u64 start_location = FAT1_start_offset + 8 + (index * 4); + u32 current_cluster = 2 + index; + str filename = overlay_func_name(current_cluster); + + if (filename != "") { + char hover_label[4] @ start_location [[ + name(std::format( + "SFN: {} | CLUSTER {}", + filename, + current_cluster + ))]]; + } +} [[inline]]; + +fn overlay_func_name(u32 cluster_num) { + str fname = ""; + str ext = ""; + str combo = ""; + + // Loop through all ROOT_DIR_ENTRIES + for (u32 i = 0, i < std::core::member_count(ROOT_DIR_ENTRIES), i = i + 1) { + + // Check SFN_ALIAS and SFN_ENTRY in root entries + if (std::core::has_member(ROOT_DIR_ENTRIES[i], "SFN_ALIAS")) { + if (ROOT_DIR_ENTRIES[i].SFN_ALIAS.first_cluster == cluster_num) { + combo = std::format("{}.{}", + ROOT_DIR_ENTRIES[i].SFN_ALIAS.fileName, + ROOT_DIR_ENTRIES[i].SFN_ALIAS.extension); + return combo; + } + } else if (std::core::has_member(ROOT_DIR_ENTRIES[i], "SFN_ENTRY")) { + if (ROOT_DIR_ENTRIES[i].SFN_ENTRY.first_cluster == cluster_num) { + combo = std::format("{}.{}", + ROOT_DIR_ENTRIES[i].SFN_ENTRY.fileName, + ROOT_DIR_ENTRIES[i].SFN_ENTRY.extension); + return combo; + } + } + + // Loop through all SUB_DIR_INDEX arrays for this root entry + if (std::core::has_member(ROOT_DIR_ENTRIES[i], "SUB_DIR_INDEX")) { + for (u32 j = 0, j < std::core::member_count(ROOT_DIR_ENTRIES[i].SUB_DIR_INDEX), j = j + 1) { + + if (std::core::has_member(ROOT_DIR_ENTRIES[i].SUB_DIR_INDEX[j], "SFN_ALIAS")) { + if (ROOT_DIR_ENTRIES[i].SUB_DIR_INDEX[j].SFN_ALIAS.first_cluster == cluster_num) { + combo = std::format("{}.{}", + ROOT_DIR_ENTRIES[i].SUB_DIR_INDEX[j].SFN_ALIAS.fileName, + ROOT_DIR_ENTRIES[i].SUB_DIR_INDEX[j].SFN_ALIAS.extension); + return combo; + } + + } else if (std::core::has_member(ROOT_DIR_ENTRIES[i].SUB_DIR_INDEX[j], "SFN_ENTRY")) { + if (ROOT_DIR_ENTRIES[i].SUB_DIR_INDEX[j].SFN_ENTRY.first_cluster == cluster_num) { + combo = std::format("{}.{}", + ROOT_DIR_ENTRIES[i].SUB_DIR_INDEX[j].SFN_ENTRY.fileName, + ROOT_DIR_ENTRIES[i].SUB_DIR_INDEX[j].SFN_ENTRY.extension); + return combo; + } + } + } + } + } + return ""; // no match found +}; + +// ----------------------------------------------------------------------------- +// FAT32 FILE ALLOCATION TABLE (FAT) PARSER +// ----------------------------------------------------------------------------- + +const u32 CLUSTER_SIZE_BYTES = 4; // Each FAT32 entry = 4 bytes +const u32 FAT32_EOF = 0x0FFFFFFF; // End-of-file marker +const u32 FAT32_BAD = 0x0FFFFFF7; // Bad cluster marker +const u32 FIRST_ALLOC_CLUSTER = 2; // First usable cluster after reserved + +enum FAT_Flags : u32 { + UNALLOCATED = 0x00000000, + END_OF_FILE = 0x0FFFFFFF, // L.END + BAD_CLUSTER = 0xFFFFFFF7, // L.END + //POINTER = Num >= 1 // INFO +}; + +union FAT_Union { + u32 DECIMAL [[hidden]]; + FAT_Flags FAT_FLAG; +}; + +// ------------------------------ +// Helper function for pointer label +// ------------------------------ +fn cluster_label(u32 val) { + if (val == FAT_Flags::UNALLOCATED) + return "UNALLOCATED"; + if (val == FAT_Flags::BAD_CLUSTER) + return "BAD"; + if (val >= 0x0FFFFFF8) + return "EOF"; + return std::format("{}", val); +}; + +// ------------------------------ +// FAT1/FAT2 HEAPS/CHAINS +// ------------------------------ +struct FAT_Entry { + FAT_Union FAT [[inline]]; + + u32 cluster_num = (FIRST_ALLOC_CLUSTER) + (std::core::array_index()); + + u32 next_cluster = FAT.DECIMAL & 0x0FFFFFFF; + + char hover_label[4] @ $ - 4 [[ + name(std::format( + "Cluster: {} → {}", + cluster_num, + cluster_label(next_cluster) + )) + ]]; + + bool is_eof = next_cluster >= 0x0FFFFFF8; + bool is_bad = next_cluster == FAT32_BAD; + bool is_free = next_cluster == 0; + + if (is_eof) { + allocated_file_count += 1; + } +} [[name(format_fat_entry(FAT.DECIMAL, std::core::array_index(), FIRST_ALLOC_CLUSTER))]]; + +// ------------------------------ +// FAT FORMATTER FUNC +// ------------------------------ +fn format_fat_entry(u32 raw_value, u32 cluster_index, u32 first_alloc_cluster) { + u32 next_cluster = raw_value & 0x0FFFFFFF; + + str next_label; + + if (next_cluster == 0) + next_label = "UNALLOCATED"; + + else if (next_cluster == FAT32_BAD) + next_label = "BAD"; + + else if (next_cluster == 0x0FFFFFFF) + next_label = "EOF"; + + else + next_label = std::format("{}", next_cluster); + + u32 logical_cluster = first_alloc_cluster + cluster_index; + + if (next_label == "UNALLOCATED" || next_label == "BAD" || next_label == "EOF") + return std::format("Cluster {}: {}", logical_cluster, next_label); + else + return std::format("Cluster {} → {}", logical_cluster, next_label); +}; + +// ------------------------------ +// MEDIA DESCRIPTOR HELPER +// ------------------------------ +enum Media_Descriptor : u8 { + SINGLE_SIDE_FLOPPY = 0xF0, + DOUBLE_SIDE_FLOPPY = 0xF9, + HARD_DISK_DRIVE = 0xF8, +}; + +// ------------------------------ +// FAT1/FAT2 HEADER PARSER +// ------------------------------ +struct FAT_Header { + Media_Descriptor mediaDescriptor [[comment("0xF8=FIXED DISK | 0xF0=REMOVABLE")]];; + u8 FAT32_FAT_HEADER[7] [[comment("8 BYTES TOTAL: 4 BYTES REPRESENT PSUEDO CLUSTER 0 (SYSTEM) | 4 BYTES REPRESENT PSUEDO CLUSTER 1 (SYSTEM)(EOF)")]]; + char root_dir_label[4] @ $ [[ + name(std::format( + "ROOT_DIRECTORY" + )) + ]]; + // WHICH IS WHY THE ROOT DIRECTORY (FIRST DATA AREA ITEM) STARTS IN CLUSTER 2 +}; + +// ----------------------------------------------------------------------------- +// ROOT DIRECTORY RELATED +// ----------------------------------------------------------------------------- +// V V V V V V V V V V +// ----------------------------------------------------------------------------- +// ------------------------------ +// ACTIVE LFN SEQUENCE NUMBER BITFIELD +// * EXCEPT DELETED ENTRIES - 0xE5 * +// ------------------------------ +bitfield LFN_Sequence { + padding : 1; + IS_LAST_ENTRY : 1 [[name("IS_LAST_ENTRY: [0=NO | 1=YES] ==")]]; + padding : 1; + LFN_SEQ_NUM : 5; +} [[bitfield_order( + std::core::BitfieldOrder::MostToLeastSignificant, 8)]]; + +// ------------------------------ +// DIRECTORY ENTRY STATUS/SEQUENCE HELPERS +// ------------------------------ +enum Entry_Status : u8 { + EMPTY_ENTRY = 0x00, + DOT_ENTRY = 0x2E, + DELETED_ENTRY = 0xE5, + + ACTIVE_1ST_ENTRY = 0x01, + ACTIVE_2ND_ENTRY = 0x02, + ACTIVE_3RD_ENTRY = 0x03, + ACTIVE_4TH_ENTRY = 0x04, + ACTIVE_5TH_ENTRY = 0x05, + ACTIVE_6TH_ENTRY = 0x06, + ACTIVE_7TH_ENTRY = 0x07, + ACTIVE_8TH_ENTRY = 0x08, + ACTIVE_9TH_ENTRY = 0x09, + ACTIVE_10TH_ENTRY = 0x0A, + ACTIVE_11TH_ENTRY = 0x0B, + ACTIVE_12TH_ENTRY = 0x0C, + ACTIVE_13TH_ENTRY = 0x0D, + ACTIVE_14TH_ENTRY = 0x0E, + ACTIVE_15TH_ENTRY = 0x0F, + ACTIVE_16TH_ENTRY = 0x10, + ACTIVE_17TH_ENTRY = 0x11, + ACTIVE_18TH_ENTRY = 0x12, + ACTIVE_19TH_ENTRY = 0x13, + ACTIVE_20TH_ENTRY = 0x14, + ACTIVE_1ST_ENTRY_LAST = 0x41, + ACTIVE_2ND_ENTRY_LAST = 0x42, + ACTIVE_3RD_ENTRY_LAST = 0x43, + ACTIVE_4TH_ENTRY_LAST = 0x44, + ACTIVE_5TH_ENTRY_LAST = 0x45, + ACTIVE_6TH_ENTRY_LAST = 0x46, + ACTIVE_7TH_ENTRY_LAST = 0x47, + ACTIVE_8TH_ENTRY_LAST = 0x48, + ACTIVE_9TH_ENTRY_LAST = 0x49, + ACTIVE_10TH_ENTRY_LAST = 0x4A, + ACTIVE_11TH_ENTRY_LAST = 0x4B, + ACTIVE_12TH_ENTRY_LAST = 0x4C, + ACTIVE_13TH_ENTRY_LAST = 0x4D, + ACTIVE_14TH_ENTRY_LAST = 0x4E, + ACTIVE_15TH_ENTRY_LAST = 0x4F, + ACTIVE_16TH_ENTRY_LAST = 0x50, + ACTIVE_17TH_ENTRY_LAST = 0x51, + ACTIVE_18TH_ENTRY_LAST = 0x52, + ACTIVE_19TH_ENTRY_LAST = 0x53, + ACTIVE_20TH_ENTRY_LAST = 0x54, +}; + +// ------------------------------ +// HELPER FOR LFN FIRST BYTE +// ------------------------------ +union LFNEntry_FirstByte { + Entry_Status status; + LFN_Sequence seq_num; +}; + +// ------------------------------ +// SFN ATTRIBUTE HELPER +// ------------------------------ +bitfield Attributes { + readOnly : 1; + hidden : 1; + systemFile : 1; + volumeLabel : 1; + subDirectory : 1; + archive : 1; + padding : 2; +} [[bitfield_order( + std::core::BitfieldOrder::LeastToMostSignificant, 8)]]; + +// ------------------------------ +// ROOT DIRECTORY ENTRY FUNC +// ------------------------------ +fn dir_entry_marker(u64 abs_off) { + u8 first @ abs_off; + return first; +}; + +// ------------------------------ +// ROOT DIRECTORY ENTRY FUNC +// ------------------------------ +fn dir_entry_attr(u64 abs_off) { + u8 attr @ abs_off + 0x0B; + return attr; +}; + +// ------------------------------ +// DATES AND TIMES FUNC +// ------------------------------ +fn format_dos_time_field(std::time::DOSTime t) { + return std::time::format_dos_time(t, "{:02}:{:02}:{:02}"); +}; + +fn format_dos_date_field(std::time::DOSDate d) { + return std::time::format_dos_date(d, "{1:02}-{0:02}-{2:04}"); +}; + +// ------------------------------ +// SHORT FILE NAME ALIAS PARSER +// ------------------------------ +struct SFN_Entry_Alias { + char fileName[8] [[name("SFN"), comment("Short File Name (8dot3)")]]; + char extension[3] [[name("EXT"), comment("File Extension (8dot3)")]]; + Attributes attributes [[name("RASH ATTR"), comment("Read-Only | Archive | System | Hidden | SubDir...")]]; + u8 reserved [[comment("Zeros")]]; + u8 milliseconds [[comment("Add to Times for Refinement")]]; + std::time::DOSTime Created_Time [[format("format_dos_time_field")]]; + std::time::DOSDate Created_Date [[format("format_dos_date_field")]]; + std::time::DOSDate Accessed_Date [[format("format_dos_date_field")]]; + u16 Cluster_Hi [[comment("High Cluster if Needed")]]; + std::time::DOSTime Modified_Time [[format("format_dos_time_field")]]; + std::time::DOSDate Modified_Date [[format("format_dos_date_field")]]; + u16 Cluster_Lo [[comment("Starting Cluster or Combine with Cluster_Hi")]]; + u32 fileSize [[name("FILE_SIZE"), comment("File Size in Bytes")]]; + + u32 first_cluster = (Cluster_Hi << 16) | Cluster_Lo; + + u8 FILE_DATA[fileSize] @ dataRegionStart + (first_cluster -2) * bytesPerCluster [[comment("Pointer to the Files Content")]]; + + sfn_count += 1; + + if (fileName[0] == 0xE5) { + sfn_del_count += 1; + } +}; + +// ------------------------------ +// LOOOONG FILE NAME PARSER +// ------------------------------ +struct LFN_Entry { + u64 curr_first_byte = $; + u8 curr_attr = dir_entry_attr(curr_first_byte); + + LFNEntry_FirstByte SeqByte [[name("SEQUENCE_NUM"), comment("0x01-0x20 | Add 0x40 to Last LFN Entry")]]; + char16 NAME_1[5] [[comment("First 5 Characters of LFN")]]; + Attributes attributes [[name("LFN_ATTR"), comment("0x0F = LFN")]]; + padding[1] [[comment("Zeros")]]; + u8 nameChecksum [[name("Checksum"), comment("Checksum Calculated on SFN_ALIAS")]]; + char16 NAME_2[6] [[comment("Next 6 Characters of LFN")]]; + padding[2] [[comment("Zeros")]]; + char16 NAME_3[2] [[comment("Next 2 Characters of LFN")]]; + + // ATTEMPT TO CLEANUP UNICODE LFN... DOES NOT ACCOUNT FOR MULTI LFN ENTRIES + if (curr_attr == 0x0F) { + char display_name[32] @ $ - 32 [[ + name( + (NAME_1[0] >= 0x20 && NAME_1[0] <= 0x7E ? std::string::to_string(NAME_1[0]) : "") + + (NAME_1[1] >= 0x20 && NAME_1[1] <= 0x7E ? std::string::to_string(NAME_1[1]) : "") + + (NAME_1[2] >= 0x20 && NAME_1[2] <= 0x7E ? std::string::to_string(NAME_1[2]) : "") + + (NAME_1[3] >= 0x20 && NAME_1[3] <= 0x7E ? std::string::to_string(NAME_1[3]) : "") + + (NAME_1[4] >= 0x20 && NAME_1[4] <= 0x7E ? std::string::to_string(NAME_1[4]) : "") + + + (NAME_2[0] >= 0x20 && NAME_2[0] <= 0x7E ? std::string::to_string(NAME_2[0]) : "") + + (NAME_2[1] >= 0x20 && NAME_2[1] <= 0x7E ? std::string::to_string(NAME_2[1]) : "") + + (NAME_2[2] >= 0x20 && NAME_2[2] <= 0x7E ? std::string::to_string(NAME_2[2]) : "") + + (NAME_2[3] >= 0x20 && NAME_2[3] <= 0x7E ? std::string::to_string(NAME_2[3]) : "") + + (NAME_2[4] >= 0x20 && NAME_2[4] <= 0x7E ? std::string::to_string(NAME_2[4]) : "") + + (NAME_2[5] >= 0x20 && NAME_2[5] <= 0x7E ? std::string::to_string(NAME_2[5]) : "") + + + (NAME_3[0] >= 0x20 && NAME_3[0] <= 0x7E ? std::string::to_string(NAME_3[0]) : "") + + (NAME_3[1] >= 0x20 && NAME_3[1] <= 0x7E ? std::string::to_string(NAME_3[1]) : "") + ) + ]]; + } + lfn_count += 1; + + if (SeqByte.status == Entry_Status::DELETED_ENTRY) { + lfn_del_count += 1; + } +}; + +// ------------------------------ +// SHORT FILE NAME PARSER +// ------------------------------ +struct SFN_Entry { + char fileName[8] [[name("SFN"), comment("Short File Name (8dot3)")]]; + char extension[3] [[name("EXT"), comment("File Extension (8dot3)")]]; + Attributes attributes [[name("RASH ATTR"), comment("Read-Only | Archive | System | Hidden | SubDir...")]]; + u8 reserved [[comment("Zeros")]]; + u8 milliseconds [[comment("Add to Times for Refinement")]]; + std::time::DOSTime Created_Time [[format("format_dos_time_field")]]; + std::time::DOSDate Created_Date [[format("format_dos_date_field")]]; + std::time::DOSDate Accessed_Date [[format("format_dos_date_field")]]; + u16 Cluster_Hi [[comment("High Cluster if Needed")]]; + std::time::DOSTime Modified_Time [[format("format_dos_time_field")]]; + std::time::DOSDate Modified_Date [[format("format_dos_date_field")]]; + u16 Cluster_Lo [[comment("Starting Cluster or Combine with Cluster_Hi")]]; + u32 fileSize [[name("FILE_SIZE"), comment("File Size in Bytes")]]; + + u32 first_cluster = (Cluster_Hi << 16) | Cluster_Lo; + + u8 FILE_DATA[fileSize] @ dataRegionStart + (first_cluster -2) * bytesPerCluster [[comment("Pointer to the File Content")]]; + + sfn_count += 1; + + if (fileName[0] == 0xE5) { + sfn_del_count += 1; + } +}; + +// ------------------------------ +// SUBDIRECTORY PARSER | LEVEL 2 +// ------------------------------ +struct SubDirParser { + u8 first = dir_entry_marker($); + u8 attr = dir_entry_attr($); + + u64 next_first_byte = $ + 32; + u8 next_attr = dir_entry_attr(next_first_byte); // current offset plus 12 bytes (offset 0x0B of entry) + + if (first != 0x00 && attr == 0x0F) { + LFN_Entry LFN_ENTRY; + + if (next_first_byte != 0x00 && next_first_byte != 0xE5 && next_attr == 0x0F) { + LFN_Entry next_LFN_ENTRY; + SFN_Entry_Alias SFN_ALIAS; + } + + } else if (first != 0x00 && attr != 0x0F) { + SFN_Entry SFN_ENTRY; + } +}; + +// ------------------------------ +// ROOT DIRECTORY ENTRY PARSER +// ROUGH METHOD OF PARSING SFN/LFN/SFN_ALIAS/SUBDIR TWO LEVELS DEEP +// IF THE PATTERN CRASHES - THIS IS LIKELY WHY +// ------------------------------ +struct RootDirParser { + u64 curr_first_byte = $; + u8 curr_attr = dir_entry_attr(curr_first_byte); // current offset plus 12 bytes (offset 0x0B of entry) + + u64 next_first_byte = $ + 32; + u8 next_attr = dir_entry_attr(next_first_byte); // current offset plus 12 bytes (offset 0x0B of entry) + + bool is_subdir = false; + + if (curr_first_byte != 0x00 && curr_first_byte != 0xE5 && curr_attr == 0x0F) { + LFN_Entry LFN_ENTRY; + + if (next_first_byte != 0x00 && next_first_byte != 0xE5 && next_attr == 0x0F) { + LFN_Entry next_LFN_ENTRY; + SFN_Entry_Alias SFN_ALIAS; + + is_subdir = SFN_ALIAS.attributes.subDirectory; + + if (SFN_ALIAS.attributes.subDirectory && next_first_byte != 0x00 && next_attr != 0xE5) { + is_subdir = SFN_ALIAS.attributes.subDirectory; + + u64 dir_start_addr = dataRegionStart + (SFN_ALIAS.first_cluster - 2) * bytesPerCluster; + SubDirParser SUB_DIR_INDEX[while(std::mem::read_unsigned($, 1) != 0x00)] @ dir_start_addr; + } + } + + if (next_first_byte != 0x00 && next_first_byte != 0xE5 && next_attr != 0x0F) { + SFN_Entry_Alias SFN_ALIAS; + + is_subdir = SFN_ALIAS.attributes.subDirectory; + + if (SFN_ALIAS.attributes.subDirectory && next_first_byte != 0x00 && next_attr != 0xE5) { + is_subdir = SFN_ALIAS.attributes.subDirectory; + + u64 dir_start_addr = dataRegionStart + (SFN_ALIAS.first_cluster - 2) * bytesPerCluster; + SubDirParser SUB_DIR_INDEX[while(std::mem::read_unsigned($, 1) != 0x00)] @ dir_start_addr; + } + + } + + } else if (curr_first_byte != 0x00 && curr_first_byte != 0xE5 && curr_attr != 0x0F) { + SFN_Entry SFN_ENTRY; + is_subdir = SFN_ENTRY.attributes.subDirectory; + + if (SFN_ENTRY.attributes.subDirectory && next_first_byte != 0x00 && next_attr != 0xE5) { + is_subdir = SFN_ENTRY.attributes.subDirectory; + + u64 dir_start_addr = dataRegionStart + (SFN_ENTRY.first_cluster - 2) * bytesPerCluster; + SubDirParser SUB_DIR_INDEX[while(std::mem::read_unsigned($, 1) != 0x00)] @ dir_start_addr; + } + + } else if (curr_first_byte != 0x00 && current_first_byte == 0xE5) { + + if (next_first_byte != 0x00 && next_attr == 0x0F) { + LFN_Entry LFN_ENTRY; + + if (next_first_byte != 0x00 && next_first_byte != 0xE5) { + + if (next_attr != 0x0F) { + SFN_Entry_Alias SFN_ALIAS; + + is_subdir = SFN_ALIAS.attributes.subDirectory; + + if (next_first_byte != 0x00 && next_first_byte != 0xE5 && next_attr != 0x0F) { + SFN_Entry_Alias SFN_ALIAS2; // otherwise switch to SFN + + is_subdir = SFN_ALIAS.attributes.subDirectory; + + if (SFN_ALIAS.attributes.subDirectory && next_first_byte != 0x00 && next_attr != 0xE5) { + is_subdir = SFN_ALIAS.attributes.subDirectory; + + u64 dir_start_addr = dataRegionStart + (SFN_ALIAS.first_cluster - 2) * bytesPerCluster; + SubDirParser SUB_DIR_INDEX[while(std::mem::read_unsigned($, 1) != 0x00)] @ dir_start_addr; + } + } + } + } + } else { + SFN_Entry SFN_ENTRY; + is_subdir = SFN_ENTRY.attributes.subDirectory; + } + } +} [[name(format_element($, start_index, is_subdir)), comment("FILE/DIR [INDX #]")]]; + +// ------------------------------ +// NAME FORMATTER +// ------------------------------ +fn format_element(auto v, u64 offset, bool subdir) { + if (subdir) { + return std::format("SUB_DIR [{:02}]", std::core::array_index() + offset); + } else { + return std::format("FILE [{:02}]", std::core::array_index() + offset); + } +}; + +// ------------------------------ +// ROOT DIRECTORY HEADER PARSER +// ------------------------------ +struct RootDirHeader { + char VolumeName[11] [[comment("User Defined Name of the VOL")]]; + u8 VolumeLabelFlag [[comment("Indicates the Preceding VOL LABEL")]]; + padding[10] [[comment("Zeros")]]; + std::time::DOSTime Created_Time [[format("format_dos_time_field"), comment("Last Write Time - Typically when Created/Formatted, but NOT ALWAYS...(DISK IMAGE/FAT DRIVERS)")]]; + std::time::DOSDate Created_Date [[format("format_dos_date_field"), comment("Last Write Date - Typically when Created/Formatted, but NOT ALWAYS...(DISK IMAGE/FAT DRIVERS)")]]; + padding[6] [[comment("Zeros")]]; +}; + +// ------------------------------ +// VBR SIGNATURE HELPER +// ------------------------------ +enum VBRSignature : u16 { + VBR_SIG = 0xAA55 +}; + +// ------------------------------ +// FILE SYSTEM INFO BLOCK +// ------------------------------ +struct FSInfo { + u32 leadSignature [[comment("RRaA")]]; + padding[480] [[comment("Zeros")]]; + u32 structSignature [[comment("FSINFO Signature")]]; + u32 freeClusterCount [[comment("Approximate Free Cluster Count")]]; + u32 nextFreeCluster [[comment("FAT1: Suggested Starting Point")]]; + padding[14] [[comment("Zeros")]]; + VBRSignature VBR_SIG [[comment("0x55AA")]]; +}; + +// ------------------------------ +// FAT12/16/32 BIOS PARAMETER BLOCK (BPB) +// ------------------------------ +struct BPB_Common { + u8 jmp_boot[3] [[comment("Assembly Instructions to Jump to Boot Code")]]; + char oem_name[8] [[comment("MSDOS/BSD")]]; + u16 bytes_per_sector [[comment("512,1024,2048,4096")]]; + u8 sectors_per_cluster [[comment("Under 32K - Must be a power of 2")]]; + u16 reserved_sectors [[comment("Size of Reserved Area in Sectors")]]; + u8 num_fats [[comment("Typically 2, but can be 1 for Small Volumes")]]; + u16 root_entry_count [[comment("Max Num of Entries -- 0 for FAT32| 512 for FAT16")]]; + u16 total_sectors16 [[comment("if 0, use total_sectors32")]]; + u8 media_type [[comment("0xF8=FIXED DISK | 0xF0=REMOVABLE")]]; + u16 fat_size16 [[comment("Size of each FAT in Sectors for FAT12/16; 0 for FAT32")]]; + u16 sectors_per_track [[comment("Legacy")]]; + u16 num_heads [[comment("Legacy")]]; + u32 hidden_sectors [[comment("Num of Sectors before the Volume")]]; + u32 total_sectors32 [[comment("32bit Value of Total Num of Sectors in Volume")]]; +// ----------------------vvv----- +// FAT32 EXTENDED +// ----------------------vvv----- + u32 FAT_Sector_Count [[comment("Total Sectors per FAT")]]; + u16 ext_flags [[comment("16bit Value: BIT_7 = 1 == 1 FAT USED | Otherwise both FATs USED")]]; + u16 fs_version [[comment("Major and Minor | None")]]; + u32 root_cluster [[comment("Cluster Num of Root Dir")]]; + u16 fs_info_sector [[comment("FS_INFO Location")]]; + u16 backup_boot_sector [[comment("VBR Backup Location")]]; + u8 reserved[12] [[comment("Zeros")]]; + u8 drive_number [[comment("BIOS INT13h Drive Num")]]; + u8 reserved1 [[comment("Zeros")]]; + u8 boot_signature [[comment("Extended Boot Sig = 0x29")]]; + u32 volume_id [[comment("Volume Serial Number - Based on Created Date/Time")]]; + char volume_label[11] [[comment("No Name | User Defined Name | Check Root Dir")]]; + char fs_type[8] [[comment("FAT32 ")]]; + u8 bootstrap[420] [[comment("Until Signature")]]; + VBRSignature VBR_SIG [[comment("0x55AA")]]; +// ----------------------vvv----- +// UPDATE CONSTANTS/GLOBALS +// ----------------------vvv----- + bytesPerCluster = sectors_per_cluster * bytes_per_sector; + rootDirSectors = ((root_entry_count * 32) + (bytes_per_sector - 1)) / bytes_per_sector; + firstDataSector = reserved_sectors + (num_fats * FAT_Sector_Count) + rootDirSectors; + dataRegionStart = firstDataSector * bytes_per_sector; +}; + +// ----------------------------------------------------------------------------- +// FAT32 MAIN RELATED +// ----------------------------------------------------------------------------- +// V V V V V V V V V V +// ----------------------------------------------------------------------------- +// ------------------------------ +// FAT32 VOLUME BOOT RECORD +// ------------------------------ +BPB_Common F32_VBR @ $; +VBR_OFFSET = F32_VBR.hidden_sectors * F32_VBR.bytes_per_sector; + +/// ------------------------------ +// FILE SYSTEM INFO BLOCK +// ------------------------------ +FSInfo FS_INFO @ F32_VBR.fs_info_sector * F32_VBR.bytes_per_sector; +root_dir_start = dataRegionStart + ((F32_VBR.root_cluster - 2) * bytesPerCluster) + 32; + +// ------------------------------ +// FILE ALLOCATION TABLE +// *** HAS GLOBAL AT TOP *** +// ------------------------------ +FAT1_start_offset = F32_VBR.reserved_sectors * F32_VBR.bytes_per_sector; +FAT2_start_offset = FAT1_start_offset + (F32_VBR.FAT_Sector_Count * F32_VBR.bytes_per_sector); +FAT_ClusterHeap_Count = F32_VBR.FAT_Sector_Count * F32_VBR.bytes_per_sector / CLUSTER_SIZE_BYTES; + +FAT_Header FAT1_HEADER @ FAT1_start_offset; +FAT_Entry FAT1[MAX_FAT_CHUNKS] @ FAT1_start_offset + 8; + +FAT_Header FAT2_HEADER @ FAT2_start_offset; +FAT_Entry FAT2[MAX_FAT_CHUNKS] @ FAT2_start_offset + 8; + +// ------------------------------ +// ROOT DIRECTORY HEADER +// ------------------------------ +RootDirHeader ROOT_DIR_HEADER @ dataRegionStart + ((F32_VBR.root_cluster - 2) * bytesPerCluster); + +// ----*-----*------*------*----- +// * * ROOT DIRECTORY PARSER * * +// ----*-----*------*------*----- +RootDirParser ROOT_DIR_ENTRIES[while(std::mem::read_unsigned($, 1) != 0x00)] @ root_dir_start; + +// ------------------------------ +// SFN <-> CLUSTER RELATION OVERLAY +// *** HAS GLOBAL AT TOP *** +// ------------------------------ +INFO_Overlay SFN_CLUSTER_LIST[MAX_SFN_CLUSTER_RELATIONS] @ FAT1_start_offset [[name("SFN <-> CLUSTER (DERIVED)")]]; + +// ------------------------------ +// FAT32 VOLUME REPORT +// *** HAS GLOBAL AT TOP *** +// ------------------------------ +abs_FAT1_start_offset = VBR_OFFSET + (F32_VBR.reserved_sectors * F32_VBR.bytes_per_sector); +abs_FAT2_start_offset = abs_FAT1_start_offset + (F32_VBR.FAT_Sector_Count * F32_VBR.bytes_per_sector); +abs_rootDirStart_offset = VBR_OFFSET + dataRegionStart; + +if (VOLUME_REPORT) { + std::print(" "); + std::print("-----------------------------------------"); + std::print("---------- FAT32 VOLUME_REPORT ----------"); + std::print("-----------------------------------------"); + std::print("VOL_LABEL = {}", F32_VBR.volume_label); + std::print("FILE_SYSTEM = {}", F32_VBR.fs_type); + std::print("SERIAL_NUMBER = 0x{:X}", F32_VBR.volume_id); + + std::print("-----------------------------------------"); + std::print("BYTES/SECTOR = {:02}", F32_VBR.bytes_per_sector); + std::print("SECTORS/CLUSTER = {:02}", F32_VBR.sectors_per_cluster); + std::print("BYTES/CLUSTER = {:02}", bytesPerCluster); + std::print("ROOT_ENTRIES = {:02}", F32_VBR.root_entry_count); + std::print("CLUSTER_COUNT = {:02}", (F32_VBR.total_sectors32 - firstDataSector) / F32_VBR.sectors_per_cluster); + + std::print("-----------------------------------------"); + std::print("VOLUME_SIZE = {:02} SECTORS", F32_VBR.total_sectors32); + std::print("VOLUME_SIZE = {:.4f} GB @ 1000", (F32_VBR.total_sectors32 * F32_VBR.bytes_per_sector) / 1000.0 / 1000.0 / 1000.0); + std::print("VOLUME_SIZE = {:.4f} GiB @ 1024", (F32_VBR.total_sectors32 * F32_VBR.bytes_per_sector) / 1024.0 / 1024.0 / 1024.0); + + std::print("-----------------------------------------"); + std::print("RESERVED_SECTORS = {:02}", F32_VBR.reserved_sectors); + std::print("FAT_COUNT = {:02}", F32_VBR.num_fats); + std::print("FAT_SIZE = {:02} SECTORS", F32_VBR.FAT_Sector_Count); + std::print("FAT1_START_OFF = {} | 0x{:02X}", abs_FAT1_start_offset, abs_FAT1_start_offset); + std::print("FAT2_START_OFF = {} | 0x{:02X}", abs_FAT2_start_offset, abs_FAT2_start_offset); + std::print("ROOT_DIR_CLUSTER = {:02}", F32_VBR.root_cluster); + std::print("ROOT_DIR_OFFSET = {} | 0x{:02X}", abs_rootDirStart_offset, abs_rootDirStart_offset); + + std::print("-----------------------------------------"); + if (sfn_del_count >= 1) { + std::print("SFN_DEL(xE5) = DETECTED"); + } + if (lfn_del_count >= 1) { + std::print("LFN_DEL(xE5) = DETECTED"); + } + std::print("FAT1_EOF_COUNT = {:02}", allocated_file_count / 2); // divided by 2 (FAT1/FAT2) + + std::print("-----------------------------------------"); + std::print("------------------ END ------------------"); + std::print("-----------------------------------------"); + std::print(" "); +} diff --git a/patterns/DFIR/NTFS.hexpat b/patterns/DFIR/NTFS.hexpat new file mode 100644 index 00000000..7afee959 --- /dev/null +++ b/patterns/DFIR/NTFS.hexpat @@ -0,0 +1,1571 @@ +#pragma author MODIFIED BY: Formula Zero One Technologies +#pragma description NT File System (NTFS_v2.0) +#pragma endian little + +// ----------------------------------------------------------------------------- +// CREDIT +// ----------------------------------------------------------------------------- +// OG AUTHOR: Hrant Tadevosyan (Axcient, now ConnectWise) +// OG DESC: ntfs.hexpat_v1.0 +// So much work went into this pattern; NICE JOB!! +// ----------------------------------------------------------------------------- +// NOTES FOR v2.0 +// ----------------------------------------------------------------------------- +// Imported by DISK_PARSER.hexpat +// Added recursive parsing for all $MFT records +// Added D/T conversions +// Changed most ntfschar to char16 to show filenames on hover +// Added comments to DFIR fields of interest +// Changed pattern output naming/structure. Similar to RunTime's DiskExplorer -- FREntry > FRHeader > ATTRName > ATTRHeader > ATTRBody > ATTREnd +// Reconfigured x50 ATTR and ACL processing +// Modified RunList - File Content Pointer +// ----------------------------------------------------------------------------- +// NTFS MANTRAS FROM NW3C... +// ----------------------------------------------------------------------------- +// --- 1 --- EVERYTHING IN NTFS IS A FILE (RECORD) (INCLUDING DIRs) +// --- 2 --- EVERY FILE (& DIR) HAS AN ENTRY IN THE $MFT, INCLUDING THE $MFT ITSELF +// --- 3 --- EVERY FILE (RECORD) IS MADE UP OF ATTRIBUTES +// --------- FILES = x10/x30/x80 (minimum) +// --------- DIRs = x10/x30/x90 (minimum) +// ----------------------------------------------------------------------------- +// ------------------------------------------------------------------------- +// IMPORTS +// ------------------------------------------------------------------------- +// V V V V V V V V V +// ------------------------------------------------------------------------- +import std.core; +import std.array; +import std.ptr; +import std.io; +import std.mem; +import std.string; +import std.time; +import type.magic; +import type.time; +import type.guid; +import type.base; + +// ------------------------------------------------------------------------- +// FWD DECs - GLOBALS +// ------------------------------------------------------------------------- + +// *** ATTENTION *** + +// ---*******---*******----vvvv--- | +const bool VOLUME_REPORT = true; +// ---*******---*******----^^^^--- | + +u64 VBR_OFFSET; +u64 prev_lcn = 0; +u32 MFT_Count; +u32 Unused_Count; +u8 NT_Major; +u8 NT_Minor; +type::Hex mft_num; +type::Hex seq_num; +str Vol_Label; +u8 PRENT_MFT; +u8 PRENT_SEQ; + +// ------------------------------------------------------------------------- +// GENERAL HELPERS RELATED +// ------------------------------------------------------------------------- +// V V V V V V V V V +// ------------------------------------------------------------------------- +using ntfschar = u16; +using leVCN = u64; +using leLCN = u64; +using leLSN = u64; +using leMFT_REF = u64; + +bitfield RUNLIST_HEADER { + unsigned SE_Offset : 4; + unsigned Length : 4; +} [[bitfield_order(std::core::BitfieldOrder::MostToLeastSignificant, 8)]]; + +// ------------------------------------------------------------------------- +// RUNLIST +// ------------------------------------------------------------------------- +struct RUNLIST { + RUNLIST_HEADER RunList_Header + [[comment("Left Nibble = S.E. Bytes | Right Nibble = S.E. Length")]]; + + // Highlight the Stream Extent bytes + char run_extent[RunList_Header.SE_Offset + RunList_Header.Length] @ $ + [[name(std::format("Run_Extent: {} + {} = {} Bytes ", + RunList_Header.SE_Offset, + RunList_Header.Length, + RunList_Header.SE_Offset + RunList_Header.Length)), + comment("Start/Run Extent | Derived from Header")]]; + + // Get number of clusters as a string + str NoC_value = std::string::to_string( + std::mem::read_unsigned($, RunList_Header.Length)); + + // Highlight exactly RunList_Header.Length bytes + char Num_of_Clusters[RunList_Header.Length] + [[name(std::format("Num_of_Clusters = {} ", NoC_value)), + comment("Number of Clusters in a Run")]]; + + // Needed for mft_get_dat_attr_lcn function only + u8 Logical_Cluster_Num[RunList_Header.SE_Offset] [[hidden]]; + + if (RunList_Header.SE_Offset > 0) { + // Read run length (little-endian) + u64 num_clusters = std::mem::read_unsigned( + $ - (RunList_Header.SE_Offset + RunList_Header.Length), + RunList_Header.Length); + + // Read signed LCN delta + s64 lcn_delta = std::mem::read_signed( + $ - RunList_Header.SE_Offset, + RunList_Header.SE_Offset); + + u64 data_size = num_clusters * cluster_size; + u64 content_offset = VBR_OFFSET + (lcn_delta * cluster_size); + + // Show both decimal LCN and absolute byte offset in the label + char Start_LCN[RunList_Header.SE_Offset] @ $ - RunList_Header.SE_Offset + [[name(std::format("Start_LCN {} | Offset 0x{:X} ", + lcn_delta, + content_offset)), + comment("Starting Logical Cluster Number")]]; + + // Pointer to the file content + char MAGIC_BYTES[4] @ content_offset - VBR_OFFSET //u8 as needed... + [[comment("Pointer to Start Cluster"), static, sealed]]; + + // Select all allocated clusters for easy Section View + // May error - array grew past end of data.... + //u8 CLUSTER_CONTENT[data_size] @ content_offset - VBR_OFFSET + // [[comment("Pointer to Cluster Content"), static, sealed]]; + //if (MAGIC_BYTES == 0xFFD8FF) { + // IMPORT AND CALL jpeg.hexpat + //} + } + + // Look ahead at the next byte to detect another run + u8 next_byte @ $ [[hidden]]; + + if (next_byte != 0x00) { + RUNLIST Next_Run; // recursively parse next run + } +} [[inline]]; + +fn calc_lcn_str(u64 start_offset, u32 len) { + if (len == 0) + return "0"; + return std::mem::read_unsigned(start_offset, len); +}; + +// ------------------------------------------------------------------------- +// SIGNATURE HELPER +// ------------------------------------------------------------------------- +enum VBRSignature : u16 { + VBR_SIG = 0xAA55 +}; + +// ------------------------------------------------------------------------- +// WINDOWS FILETIME FUNC +// ------------------------------------------------------------------------- +fn parse_filetime(u64 raw_ft) { + // Convert raw FILETIME to Unix time + u64 unix_time = type::impl::format_filetime_as_unix(raw_ft); + + // Convert Unix time to structured UTC time + std::time::Time ts = std::time::to_utc(unix_time); + + // Format as string + str formatted = std::time::format(ts, "%Y-%m-%d %H:%M:%S"); + + return formatted; +}; + +// ------------------------------------------------------------------------- +// NTFS VOLUME BOOT RECORD RELATED +// ------------------------------------------------------------------------- +// V V V V V V V V V +// ------------------------------------------------------------------------- +struct BIOS_PARAMETER_BLOCK { + u16 bytes_per_sector [[comment("Bytes per Sector")]]; + u8 sectors_per_cluster [[comment("Sectors per Cluster")]]; + u16 reserved_sectors [[comment("Reserved")]];; + u8 fats [[comment("Zeros")]];; + u16 root_entries [[comment("Unused")]]; + u16 sectors [[comment("Unused")]]; + u8 media_descriptor [[comment("Always F8-2003+")]]; + u16 sectors_per_fat [[comment("Legacy")]]; + u16 sectors_per_track [[comment("Legacy")]]; + u16 heads [[comment("Legacy")]]; + u32 hidden_sectors [[comment("Sectors before Volume")]]; + u32 large_sectors [[comment("Legacy")]]; +}; + +// ------------------------------------------------------------------------- +// FILE RECORD ENTRY RELATED +// ------------------------------------------------------------------------- +// V V V V V V V V V +// ------------------------------------------------------------------------- +enum NTFS_RECORD_TYPES : u32 { + magic_FILE = 0x454c4946, + magic_INDX = 0x58444e49, + magic_HOLE = 0x454c4f48, + magic_RSTR = 0x52545352, + magic_RCRD = 0x44524352, + magic_CHKD = 0x444b4843, + magic_BAAD = 0x44414142, + magic_empty = 0xffffffff, +}; + +// For other records, such as Index +struct NTFS_RECORD { + NTFS_RECORD_TYPES magic [[comment("File Record Signature 'FILE'")]];; + u16 usa_ofs [[comment("Offset to Fix Up Array")]]; + u16 usa_count [[comment("# of 2 Byte Entries in FUA")]]; +}; + +enum MFT_RECORD_FLAGS : u16 { + MFT_RECORD_NOT_IN_USE = 0x0000, + MFT_RECORD_IN_USE = 0x0001, + MFT_RECORD_IS_DIRECTORY = 0x0002, + MFT_RECORD_IS_4 = 0x0004, + MFT_RECORD_IS_VIEW_INDEX = 0x0008, + MFT_REC_SPACE_FILLER = 0xffff, +}; + +// ------------------------------------------------------------------------- +// FILE RECORD ATTRIBUTE RELATED +// ------------------------------------------------------------------------- +// V V V V V V V V V +// ------------------------------------------------------------------------- +enum ATTR_TYPES : u32 { + AT_UNUSED = 0x00, + AT_STANDARD_INFORMATION = 0x10, + AT_ATTRIBUTE_LIST = 0x20, + AT_FILE_NAME = 0x30, + AT_OBJECT_ID = 0x40, + AT_SECURITY_DESCRIPTOR = 0x50, + AT_VOLUME_NAME = 0x60, + AT_VOLUME_INFORMATION = 0x70, + AT_DATA = 0x80, + AT_INDEX_ROOT = 0x90, + AT_INDEX_ALLOCATION = 0xa0, + AT_BITMAP = 0xb0, + AT_REPARSE_POINT = 0xc0, + AT_EA_INFORMATION = 0xd0, + AT_EA = 0xe0, + AT_PROPERTY_SET = 0xf0, + AT_LOGGED_UTILITY_STREAM = 0x100, + AT_FIRST_USER_DEFINED_ATTRIBUTE = 0x1000, + AT_END = 0xffffffff, +}; + +enum ATTR_DEF_FLAGS : u32 { + ATTR_DEF_INDEXABLE = 0x02, + ATTR_DEF_MULTIPLE = 0x04, + ATTR_DEF_NOT_ZERO = 0x08, + ATTR_DEF_INDEXED_UNIQUE = 0x10, + ATTR_DEF_NAMED_UNIQUE = 0x20, + ATTR_DEF_RESIDENT = 0x40, + ATTR_DEF_ALWAYS_LOG = 0x80, +}; + +enum COLLATION_RULES : u32 { + COLLATION_BINARY = 0, + COLLATION_FILE_NAME = 1, + COLLATION_UNICODE_STRING = 2, + COLLATION_NTOFS_ULONG = 16, + COLLATION_NTOFS_SID = 17, + COLLATION_NTOFS_SECURITY_HASH = 18, + COLLATION_NTOFS_ULONGS = 19, +}; + +struct ATTR_DEF { + //ntfschar name[0x40]; + char16 name[0x40]; + ATTR_TYPES type; + u32 display_rule; + COLLATION_RULES collation_rule; + ATTR_DEF_FLAGS flags; + u64 min_size; + u64 max_size; +}; + +enum ATTR_FLAGS : u16 { + ATTR_IS_COMPRESSED = 0x0001, + ATTR_COMPRESSION_MASK = 0x00ff, + ATTR_IS_ENCRYPTED = 0x4000, + ATTR_IS_SPARSE = 0x8000, +}; + +enum RESIDENT_ATTR_FLAGS : u8 { + RESIDENT_ATTR_IS_INDEXED = 0x01, + RESIDENT_ATTR_NOT_INDEXED = 0x00, +}; + +enum FILE_ATTR_FLAGS : u32 { + FILE_ATTR_READONLY = 0x00000001, + FILE_ATTR_HIDDEN = 0x00000002, + FILE_ATTR_SYSTEM = 0x00000004, + FILE_ATTR_DIRECTORY = 0x00000010, + FILE_ATTR_ARCHIVE = 0x00000020, + FILE_ATTR_DEVICE = 0x00000040, + FILE_ATTR_NORMAL = 0x00000080, + FILE_ATTR_TEMPORARY = 0x00000100, + FILE_ATTR_SPARSE_FILE = 0x00000200, + FILE_ATTR_REPARSE_POINT = 0x00000400, + FILE_ATTR_COMPRESSED = 0x00000800, + FILE_ATTR_OFFLINE = 0x00001000, + FILE_ATTR_NOT_CONTENT_INDEXED = 0x00002000, + FILE_ATTR_ENCRYPTED = 0x00004000, + FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x00040000, + FILE_ATTR_VALID_FLAGS = 0x00047fb7, + FILE_ATTR_VALID_SET_FLAGS = 0x000031a7, + FILE_ATTR_I30_INDEX_PRESENT = 0x10000000, + FILE_ATTR_VIEW_INDEX_PRESENT = 0x20000000, +}; + +enum FILE_NAME_TYPE_FLAGS : u8 { + FILE_NAME_POSIX = 0x00, + FILE_NAME_WIN32 = 0x01, + FILE_NAME_DOS = 0x02, + FILE_NAME_WIN32_AND_DOS = 0x03, +}; + +// ------------------------------------------------------------------------- +// STANDARD INFO ATTRIBUTE RELATED +// ------------------------------------------------------------------------- +// V V V V V V V V V +// ------------------------------------------------------------------------- +struct STANDARD_INFORMATION_BODY_OLD { + u64 Created_DT_UTC [[format("parse_filetime"), comment("Created DT UTC")]]; + u64 Modified_DT_UTC [[format("parse_filetime"), comment("Modified DT UTC")]]; + u64 MFT_Mod_DT_UTC [[format("parse_filetime"), comment("$MFT Mod DT UTC")]]; + u64 Accessed_DT_UTC [[format("parse_filetime"), comment("Accessed DT UTC")]]; + FILE_ATTR_FLAGS file_attributes [[comment("RASH ATTR")]];; +} [[static]]; + +struct STANDARD_INFORMATION_OLD { + u64 Created_DT_UTC [[format("parse_filetime"), comment("Created DT UTC")]]; + u64 Modified_DT_UTC [[format("parse_filetime"), comment("Modified DT UTC")]]; + u64 MFT_Mod_DT_UTC [[format("parse_filetime"), comment("$MFT Mod DT UTC")]]; + u64 Accessed_DT_UTC [[format("parse_filetime"), comment("Accessed DT UTC")]]; + FILE_ATTR_FLAGS file_attributes [[comment("RASH ATTR")]];; + u8 reserved12[12]; +} [[static]]; + +struct STANDARD_INFORMATION { + u64 Created_DT_UTC [[format("parse_filetime"), comment("Created DT UTC")]]; + u64 Modified_DT_UTC [[format("parse_filetime"), comment("Modified DT UTC")]]; + u64 MFT_Mod_DT_UTC [[format("parse_filetime"), comment("$MFT Mod DT UTC")]]; + u64 Accessed_DT_UTC [[format("parse_filetime"), comment("Accessed DT UTC")]]; + FILE_ATTR_FLAGS file_attributes [[comment("RASH ATTR")]];; + + u32 maximum_versions [[comment("Maximum Versions Allowed: Disabled = 0x00")]];; + u32 version_number [[comment("File Version Number")]];; + u32 class_id [[comment("Class ID")]];; + u32 owner_id [[comment("Owner ID - $Quota")]];; + u32 security_id [[comment("Security Permission Settings")]];; + u64 quota_charged [[comment("Number of Bytes Towards User Quota")]];; + u64 usn [[comment("Update Sequence Number - $USNJournal Index")]];; +} [[static]]; + +struct FILE_NAME_ATTR_PACKED { + u16 packed_ea_size [[comment("Size of xE0 ATTR")]];; + u16 reserved [[comment("Reserved")]];; +} [[static]]; + +union FILE_NAME_ATTR_FORM { + FILE_NAME_ATTR_PACKED packed; // [[inline]]; + u32 reparse_point_tag [[comment("Reparse Point Tag")]];; +} [[static]]; + +struct FILE_NAME_ATTR { + PRENT_MFT = 0x00; + PRENT_SEQ = 0x00; + + u8 MFT_Parent_Dir[6] [[comment("Parent Directory $MFT File Record Number")]]; + u16 Parent_Seq_Num [[comment("Parent Directory $MFT Sequence Number")]]; + u64 Created_DT_UTC [[format("parse_filetime"), comment("Created DT in UTC")]]; + u64 Modified_DT_UTC [[format("parse_filetime"), comment("Modified DT in UTC")]]; + u64 MFT_Mod_DT_UTC [[format("parse_filetime"), comment("MFT Mod DT in UTC")]]; + u64 Accessed_DT_UTC [[format("parse_filetime"), comment("Accessed DT in UTC")]]; + u64 allocated_size [[comment("Size on Disk")]]; + u64 data_size [[comment("Logical File Size")]]; + FILE_ATTR_FLAGS file_attributes [[comment("RASH ATTR")]]; + FILE_NAME_ATTR_FORM form [[inline, comment("Reparse Value")]]; + u8 file_name_length [[comment("File Name Length")]]; + FILE_NAME_TYPE_FLAGS file_name_type [[comment("Namespace Type: POSIX = 0x00 | Win32 = 0x01 | DOS_SFN = 0x02 | Win32&DOS = 0x03")]]; + char16 file_name[file_name_length] [[comment("The Actual File Name")]]; + + PRENT_MFT = MFT_Parent_Dir; + PRENT_SEQ = Parent_Seq_Num; +}; + +// ------------------------------------------------------------------------- +// SECURITY DESC | OBJECT ID RELATED +// ------------------------------------------------------------------------- +// V V V V V V V V V +// ------------------------------------------------------------------------- +struct OBJECT_ID_ATTR_INFO { + type::GUID birth_volume_id; + type::GUID birth_object_id; + type::GUID domain_id; +}; + +union OBJECT_ID_ATTR_FORM { + OBJECT_ID_ATTR_INFO info [[inline]]; + u8 extended_info[48]; +}; + +struct OBJECT_ID_ATTR { + type::GUID object_id; + OBJECT_ID_ATTR_FORM form [[inline]]; +}; + +enum VOLUME_FLAGS : u16 { + VOLUME_IS_DIRTY = 0x0001, + VOLUME_RESIZE_LOG_FILE = 0x0002, + VOLUME_UPGRADE_ON_MOUNT = 0x0004, + VOLUME_MOUNTED_ON_NT4 = 0x0008, + VOLUME_DELETE_USN_UNDERWAY = 0x0010, + VOLUME_REPAIR_OBJECT_ID = 0x0020, + VOLUME_CHKDSK_UNDERWAY = 0x4000, + VOLUME_MODIFIED_BY_CHKDSK = 0x8000, + VOLUME_FLAGS_MASK = 0xc03f, +}; + +struct VOLUME_INFORMATION { + u64 reserved; + u8 major_ver; + u8 minor_ver; + VOLUME_FLAGS flags; + NT_Major = major_ver; + NT_Minor = minor_ver; +} [[static]]; + +enum SECURITY_DESCRIPTOR_CONTROL : u16 { + SE_OWNER_DEFAULTED = 0x0001, + SE_GROUP_DEFAULTED = 0x0002, + SE_DACL_PRESENT = 0x0004, + SE_DACL_DEFAULTED = 0x0008, + SE_SACL_PRESENT = 0x0010, + SE_SACL_DEFAULTED = 0x0020, + SE_DACL_AUTO_INHERIT_REQ = 0x0100, + SE_SACL_AUTO_INHERIT_REQ = 0x0200, + SE_DACL_AUTO_INHERITED = 0x0400, + SE_SACL_AUTO_INHERITED = 0x0800, + SE_DACL_PROTECTED = 0x1000, + SE_SACL_PROTECTED = 0x2000, + SE_RM_CONTROL_VALID = 0x4000, + SE_SELF_RELATIVE = 0x8000, +}; + + +// ------------------------------ +// Enums +// ------------------------------ +enum ACE_TYPES : u8 { + ACCESS_MIN_MS_ACE_TYPE = 0, + ACCESS_ALLOWED_ACE_TYPE = 0, + ACCESS_DENIED_ACE_TYPE = 1, + SYSTEM_AUDIT_ACE_TYPE = 2, + SYSTEM_ALARM_ACE_TYPE = 3, + ACCESS_MAX_MS_V2_ACE_TYPE = 3, + ACCESS_ALLOWED_COMPOUND_ACE_TYPE = 4, + ACCESS_MAX_MS_V3_ACE_TYPE = 4, + ACCESS_MIN_MS_OBJECT_ACE_TYPE = 5, + ACCESS_ALLOWED_OBJECT_ACE_TYPE = 5, + ACCESS_DENIED_OBJECT_ACE_TYPE = 6, + SYSTEM_AUDIT_OBJECT_ACE_TYPE = 7, + SYSTEM_ALARM_OBJECT_ACE_TYPE = 8, + ACCESS_MAX_MS_OBJECT_ACE_TYPE = 8, + ACCESS_MAX_MS_V4_ACE_TYPE = 8, + ACCESS_MAX_MS_ACE_TYPE = 8, + ACCESS_ALLOWED_CALLBACK_ACE_TYPE = 9, + ACCESS_DENIED_CALLBACK_ACE_TYPE = 10, + ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE = 11, + ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE = 12, + SYSTEM_AUDIT_CALLBACK_ACE_TYPE = 13, + SYSTEM_ALARM_CALLBACK_ACE_TYPE = 14, + SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE = 15, + SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE = 16, + SYSTEM_MANDATORY_LABEL_ACE_TYPE = 17, + SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE = 18, + SYSTEM_SCOPED_POLICY_ID_ACE_TYPE = 19, + SYSTEM_PROCESS_TRUST_LABEL_ACE_TYPE = 20, +}; + +enum ACE_FLAGS : u8 { + OBJECT_INHERIT_ACE = 0x01, + CONTAINER_INHERIT_ACE = 0x02, + NO_PROPAGATE_INHERIT_ACE = 0x04, + INHERIT_ONLY_ACE = 0x08, + INHERITED_ACE = 0x10, + VALID_INHERIT_FLAGS = 0x1f, + SUCCESSFUL_ACCESS_ACE_FLAG = 0x40, + FAILED_ACCESS_ACE_FLAG = 0x80, +}; + +enum ACCESS_MASK : u32 { + FILE_READ_DATA = 0x00000001, + FILE_LIST_DIRECTORY = 0x00000001, + FILE_WRITE_DATA = 0x00000002, + FILE_ADD_FILE = 0x00000002, + FILE_APPEND_DATA = 0x00000004, + FILE_ADD_SUBDIRECTORY = 0x00000004, + FILE_READ_EA = 0x00000008, + FILE_WRITE_EA = 0x00000010, + FILE_EXECUTE = 0x00000020, + FILE_TRAVERSE = 0x00000020, + FILE_DELETE_CHILD = 0x00000040, + FILE_READ_ATTRIBUTES = 0x00000080, + FILE_WRITE_ATTRIBUTES = 0x00000100, + DELETE = 0x00010000, + READ_CONTROL = 0x00020000, + WRITE_DAC = 0x00040000, + WRITE_OWNER = 0x00080000, + SYNCHRONIZE = 0x00100000, + STANDARD_RIGHTS_READ = 0x00020000, + STANDARD_RIGHTS_WRITE = 0x00020000, + STANDARD_RIGHTS_EXECUTE = 0x00020000, + STANDARD_RIGHTS_REQUIRED = 0x000f0000, + STANDARD_RIGHTS_ALL = 0x001f0000, + ACCESS_SYSTEM_SECURITY = 0x01000000, + MAXIMUM_ALLOWED = 0x02000000, + GENERIC_ALL = 0x10000000, + GENERIC_EXECUTE = 0x20000000, + GENERIC_WRITE = 0x40000000, + GENERIC_READ = 0x80000000, +}; + +enum OBJECT_ACE_FLAGS : u32 { + ACE_OBJECT_TYPE_PRESENT = 1, + ACE_INHERITED_OBJECT_TYPE_PRESENT = 2, +}; + +// ------------------------------ +// ACE/SID +// ------------------------------ +struct ACE_HEADER { + ACE_TYPES type; + ACE_FLAGS flags; + u16 size; +}; + +struct SID { + u8 revision; + u8 sub_authority_count; + u8 identifier_authority[6]; + u32 sub_authority[sub_authority_count]; +}; + +struct ACCESS_ALLOWED_ACE { + ACE_HEADER header [[inline]]; + ACCESS_MASK mask; + SID sid; +}; + +using ACCESS_DENIED_ACE = ACCESS_ALLOWED_ACE; +using SYSTEM_ALARM_ACE = ACCESS_ALLOWED_ACE; +using SYSTEM_AUDIT_ACE = ACCESS_ALLOWED_ACE; + +struct ACCESS_ALLOWED_OBJECT_ACE { + ACE_HEADER header [[inline]]; + ACCESS_MASK mask; + OBJECT_ACE_FLAGS object_flags; + type::GUID object_type; + type::GUID inherited_object_type; + SID sid; +}; + +using ACCESS_DENIED_OBJECT_ACE = ACCESS_ALLOWED_OBJECT_ACE; +using SYSTEM_AUDIT_OBJECT_ACE = ACCESS_ALLOWED_OBJECT_ACE; +using SYSTEM_ALARM_OBJECT_ACE = ACCESS_ALLOWED_OBJECT_ACE; + +// ------------------------------ +// ACE HELPER +// ------------------------------ +union ACE { + ACE_HEADER hdr [[hidden]]; + + match (hdr.type) { + (ACE_TYPES::ACCESS_ALLOWED_ACE_TYPE): ACCESS_ALLOWED_ACE allowed; + (ACE_TYPES::ACCESS_DENIED_ACE_TYPE): ACCESS_DENIED_ACE denied; + (ACE_TYPES::SYSTEM_AUDIT_ACE_TYPE): SYSTEM_AUDIT_ACE audit; + (ACE_TYPES::SYSTEM_ALARM_ACE_TYPE): SYSTEM_ALARM_ACE alarm; + + (ACE_TYPES::ACCESS_ALLOWED_OBJECT_ACE_TYPE): ACCESS_ALLOWED_OBJECT_ACE obj_allowed; + (ACE_TYPES::ACCESS_DENIED_OBJECT_ACE_TYPE): ACCESS_DENIED_OBJECT_ACE obj_denied; + (ACE_TYPES::SYSTEM_AUDIT_OBJECT_ACE_TYPE): SYSTEM_AUDIT_OBJECT_ACE obj_audit; + (ACE_TYPES::SYSTEM_ALARM_OBJECT_ACE_TYPE): SYSTEM_ALARM_OBJECT_ACE obj_alarm; + } + + // Consume remaining bytes as raw padding + padding[hdr.size - sizeof(ACE_HEADER)]; +}; + +// ------------------------------ +// ACL +// ------------------------------ +struct ACL { + u8 revision; + u8 alignment1; + u16 size; + u16 ace_count; + u16 alignment2; + + ACE aces[ace_count]; +}; + +// ------------------------------ +// SECURITY DESCRIPTOR +// ------------------------------ +#define SECURITY_DESCRIPTOR_RELATIVE_SIZE (20) +struct SECURITY_DESCRIPTOR_RELATIVE { + u32 struct_start = $; + u8 revision; + u8 alignment; + u16 control; // SECURITY_DESCRIPTOR_CONTROL + u32 owner; + u32 group; + u32 sacl; + u32 dacl; + + if (control & SECURITY_DESCRIPTOR_CONTROL::SE_DACL_PRESENT) { + if (dacl != 0) { + // Skip to the relative offset + if ($ - struct_start < dacl) { + padding[dacl - ($ - struct_start)]; + } + ACL dacl_acl; + } + } + if (control & SECURITY_DESCRIPTOR_CONTROL::SE_SACL_PRESENT) { + if (sacl != 0) { + if ($ - struct_start < sacl) { + padding[sacl - ($ - struct_start)]; + } + ACL sacl_acl; + } + } + + if (owner > 0) { + u32 owner_bytes = $ - struct_start; + if (owner > owner_bytes) { + padding[owner - owner_bytes]; + } + SID owner_sid; + } + + if (group > 0) { + u32 group_bytes = $ - struct_start; + if (group > group_bytes) { + padding[group - group_bytes]; + } + SID group_sid; + } +}; + +// ------------------------------------------------------------------------- +// INDEX RELATED +// ------------------------------------------------------------------------- +// V V V V V V V V V +// ------------------------------------------------------------------------- +// ------------------------------ +// INDEX HEADER HELPER +// ------------------------------ +enum INDEX_HEADER_FLAGS : u8 { + SMALL_INDEX = 0, + LARGE_INDEX = 1, + LEAF_NODE = 0, + INDEX_NODE = 1, + NODE_MASK = 1, +}; + +// ------------------------------ +// INDEX HEADER PARSER +// ------------------------------ +struct INDEX_HEADER { + u32 entries_offset; + u32 index_length; + u32 allocated_size; + INDEX_HEADER_FLAGS ih_flags; + u8 reserved[3]; +}; + +// ------------------------------ +// REPARSE INDEX KEY PARSER +// ------------------------------ +struct REPARSE_INDEX_KEY { + u32 reparse_tag; + leMFT_REF file_id; +}; + +// ------------------------------ +// SDS_ENTRY PARSER +// ------------------------------ +struct SDS_ENTRY { + u32 hash; + u32 security_id; + u64 offset; + u32 length; + SECURITY_DESCRIPTOR_RELATIVE sid; +}; + +using SII_INDEX_KEY = u32 ; + +// ------------------------------ +// SDH INDEX KEY PARSER +// ------------------------------ +struct SDH_INDEX_KEY { + u32 hash; + u32 security_id; +}; + +// ------------------------------ +// INDEX ENTRY FLAG HELPER +// ------------------------------ +enum INDEX_ENTRY_FLAGS : u16 { + INDEX_ENTRY_NODE = 1, + INDEX_ENTRY_END = 2, + INDEX_ENTRY_SPACE_FILLER = 0xffff, +}; + +// ------------------------------ +// INDEX HEADER POINTER +// ------------------------------ +struct INDEX_ENTR_HEADER_PTR { + u16 data_offset; + u16 data_length; + u32 reservedV; +}; + +// ------------------------------ +// INDEX HEADER HELPER +// ------------------------------ +union INDEX_ENTR_HEADER_REF { + leMFT_REF indexed_file; + INDEX_ENTR_HEADER_PTR ptr; +}; + +// ------------------------------ +// INDEX HEADER PARSER +// ------------------------------ +struct INDEX_ENTRY_HEADER { + INDEX_ENTR_HEADER_REF file_ref; + u16 length; + u16 key_length; + u16 flags; // INDEX_ENTRY_FLAGS + u16 reserved; +}; + +// ------------------------------ +// INDEX ENTRY FN ATTR +// Duplicated so that parent mft# and parent seq# tracking doesn't break. +// ------------------------------ +struct IDX_FILE_NAME_ATTR{ + u8 MFT_Parent_Dir[6] [[comment("Parent Directory $MFT File Record Number")]]; + u16 Parent_Seq_Num [[comment("Parent Directory $MFT Sequence Number")]]; + u64 Created_DT_UTC [[format("parse_filetime"), comment("Created DT in UTC")]]; + u64 Modified_DT_UTC [[format("parse_filetime"), comment("Modified DT in UTC")]]; + u64 MFT_Mod_DT_UTC [[format("parse_filetime"), comment("MFT Mod DT in UTC")]]; + u64 Accessed_DT_UTC [[format("parse_filetime"), comment("Accessed DT in UTC")]]; + u64 allocated_size [[comment("Size on Disk")]]; + u64 data_size [[comment("Logical File Size")]]; + FILE_ATTR_FLAGS file_attributes [[comment("RASH ATTR")]]; + FILE_NAME_ATTR_FORM form [[inline, comment("Reparse Value")]]; + u8 file_name_length [[comment("File Name Length")]]; + FILE_NAME_TYPE_FLAGS file_name_type [[comment("Namespace Type: POSIX = 0x00 | Win32 = 0x01 | DOS_SFN = 0x02 | Win32&DOS = 0x03")]]; + char16 file_name[file_name_length] [[comment("The Actual File Name")]]; +}; + +// ------------------------------ +// INDEX ENTRY FN PARSER +// ------------------------------ +struct INDEX_ENTRY_FILE_NAME { + INDEX_ENTRY_HEADER header; + + if (!(header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_END)) { + // Workaround -- Using new structure for IDX FNs -- Allows for setting parent info on non-IDX items + IDX_FILE_NAME_ATTR key; + } + + if (header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_NODE) { + leVCN vcn; + } + std::mem::AlignTo<8>; +}; + +// ------------------------------ +// INDEX ENTRY SII PARSER +// ------------------------------ +struct INDEX_ENTRY_SII { + INDEX_ENTRY_HEADER header; + + if (!(header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_END)) { + SII_INDEX_KEY key; + } + + if (header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_NODE) { + leVCN vcn; + } + + std::mem::AlignTo<8>; +}; + +// ------------------------------ +// INDEX ENTRY SDH PARSER +// ------------------------------ +struct INDEX_ENTRY_SDH { + INDEX_ENTRY_HEADER header; + + if (!(header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_END)) { + SDH_INDEX_KEY key; + } + + if (header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_NODE) { + leVCN vcn; + } + std::mem::AlignTo<8>; +}; + +// ------------------------------ +// INDEX ENTRY OBJECT ID PARSER +// ------------------------------ +struct INDEX_ENTRY_OBJ_ID { + INDEX_ENTRY_HEADER header; + + if (!(header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_END)) { + type::GUID key; + } + + if (header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_NODE) { + leVCN vcn; + } + std::mem::AlignTo<8>; +}; + +// ------------------------------ +// INDEX ENTRY REPARSE POINT PARSER +// ------------------------------ +struct INDEX_ENTRY_REPARSE { + INDEX_ENTRY_HEADER header; + + if (!(header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_END)) { + REPARSE_INDEX_KEY key; + } + + if (header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_NODE) { + leVCN vcn; + } + std::mem::AlignTo<8>; +}; + +// ------------------------------ +// INDEX ENTRY SID PARSER +// ------------------------------ +struct INDEX_ENTRY_SID { + INDEX_ENTRY_HEADER header; + + if (!(header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_END)) { + SID key; + } + + if (header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_NODE) { + leVCN vcn; + } + std::mem::AlignTo<8>; +}; + +// ------------------------------ +// INDEX ENTRY OWNER ID PARSER +// ------------------------------ +struct INDEX_ENTRY_OWNER_ID { + INDEX_ENTRY_HEADER header; + + if (!(header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_END)) { + u64 key; + } + + if (header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_NODE) { + leVCN vcn; + } + std::mem::AlignTo<8>; +}; + +// ------------------------------ +// INDEX FLAGS FUNC +// ------------------------------ +fn index_get_flags(u64 offset) { + INDEX_ENTRY_HEADER index_entry_header @ offset; + return index_entry_header.flags; +}; + +// ------------------------------ +// INDEX BLOCK PARSER +// ------------------------------ +struct INDEX_BLOCK { + NTFS_RECORD header [[inline]]; + leLSN lsn; + leVCN index_block_vcn; + + u32 index_head = $; + INDEX_HEADER index; + + if (index.entries_offset > sizeof (index)) { + padding[index.entries_offset - sizeof (index)]; + } + + INDEX_ENTRY_FILE_NAME ents[while(!(index_get_flags($) & INDEX_ENTRY_FLAGS::INDEX_ENTRY_END))]; + INDEX_ENTRY_FILE_NAME ent_end; + + u32 index_used = $ - index_head; + if (index.index_length > index_used) { + padding[index.index_length - index_used]; + } +}; + +// ------------------------------ +// INDEX ROOT PARSER +// ------------------------------ +struct INDEX_ROOT { + ATTR_TYPES type; + COLLATION_RULES collation_rule; + u32 index_block_size; + s8 clusters_per_index_block; + u8 reserved[3] [[static, sealed]]; + + u32 index_head = $; + INDEX_HEADER index; + + if (index.entries_offset > sizeof (index)) { + padding[index.entries_offset - sizeof (index)]; + } + + match (type) { + (ATTR_TYPES::AT_FILE_NAME): { + INDEX_ENTRY_FILE_NAME ents[while(!(index_get_flags($) & INDEX_ENTRY_FLAGS::INDEX_ENTRY_END))]; + INDEX_ENTRY_FILE_NAME ent_end; + } + } + + u32 index_used = $ - index_head; + if (index.index_length > index_used) { + padding[index.index_length - index_used]; + } +}; + +// ------------------------------------------------------------------------- +// RESTART AREA RELATED +// ------------------------------------------------------------------------- +// V V V V V V V V V +// ------------------------------------------------------------------------- +// ------------------------------ +// RESTART AREA HEADER PARSER +// ------------------------------ +struct RESTART_PAGE_HEADER { + NTFS_RECORD_TYPES magic; + u16 usa_ofs; + u16 usa_count; + leLSN chkdsk_lsn; + u32 system_page_size; + u32 log_page_size; + u16 restart_area_offset; + u16 minor_ver; + u16 major_ver; + u16 usn; +}; + +// ------------------------------ +// RESTART AREA FLAG PARSER +// ------------------------------ +enum RESTART_AREA_FLAGS : u16 { + RESTART_VOLUME_IS_CLEAN = 0x0002, + RESTART_SPACE_FILLER = 0xffff, +}; + +// ------------------------------ +// RESTART AREA PARSER +// ------------------------------ +struct RESTART_AREA { + leLSN current_lsn; + u16 log_clients; + u16 client_free_list; + u16 client_in_use_list; + RESTART_AREA_FLAGS flags; + u32 seq_number_bits; + u16 restart_area_length; + u16 client_array_offset; + u64 file_size; + u32 last_lsn_data_length; + u16 log_record_header_length; + u16 log_page_data_offset; + u32 restart_log_open_count; + u32 reserved; +}; + +// ------------------------------------------------------------------------- +// LOG CLIENT RELATED +// ------------------------------------------------------------------------- +// V V V V V V V V V +// ------------------------------------------------------------------------- +// ------------------------------ +// LOG CLIENT PARSER +// ------------------------------ +struct LOG_CLIENT_RECORD { + leLSN oldest_lsn; + leLSN client_restart_lsn; + u16 prev_client; + u16 next_client; + u16 seq_number; + u8 reserved[6]; + u32 client_name_length; + //ntfschar client_name[64]; + char16 client_name[64]; +}; + +// ------------------------------------------------------------------------- +// ATTRIBUTE RELATED +// ------------------------------------------------------------------------- +// V V V V V V V V V +// ------------------------------------------------------------------------- +// ------------------------------ +// xD0 ATTRIBUTE PARSER +// ------------------------------ +struct EA_INFO_ENTRY { + u16 EA_packed_size [[comment("Size of the Packed Extended Attributes")]]; + u16 EA_Count [[comment("Number of Extended Attributes which have NEED_EA set")]]; + u32 EA_unpacked_size [[comment("Size of the Unpacked Extended Attributes")]]; +}; + +// ------------------------------ +// xE0 ATTRIBUTE PARSER +// ------------------------------ +struct EA_ENTRY { + u32 entry_size [[comment("EA Size: Offset to Next Extended Attribute")]]; + u8 flags [[comment("Flags: 0x80 = Needs EA")]]; + u8 name_length [[comment("EA Name Size")]]; + u16 value_length [[comment("EA Value Size")]]; + char name[name_length] [[comment("EA Name: LXUID/LXGID/LXMOD/ETC")]]; + padding[2]; + char value[value_length] [[comment("EA Value: Related to HPFS+ Compatibility")]]; +}; + +// ------------------------------ +// ATTRIBUTE DEFS +// ------------------------------ +#define ATTR_RECORD_HEADER_SIZE (16) +#define ATTR_RECORD_RESIDENT_SIZE (16 + 8) +#define ATTR_RECORD_NONRESIDENT_SIZE (16 + 48) +#define ATTR_RECORD_NONRESIDENT_CMPR_SIZE (16 + 48 + 8) + +// ------------------------------ +// ATTRIBUTE HEADER +// ------------------------------ +struct ATTRHeader { + ATTR_TYPES type [[comment("Attribute Identifier")]]; + u32 length [[comment("Size of this ATTR")]]; + u8 non_resident [[comment("0 = Resident | 1 = Non-Resident")]]; + u8 name_length [[comment("Size of the ATTR Stream Name, if any")]]; + u16 name_offset [[comment("Offset to the ATTR Stream Name")]]; + u16 flags [[comment("0x0001 = Compressed | 0x4000 = Encrypted | 0x8000 = Sparse")]]; // ATTR_FLAGS + u16 instance [[comment("Sequential Order: 0x0000 = 1st")]]; + + u32 name_offset_delta = 0; + + if (!non_resident) { + u32 value_length [[comment("Size of Resident Data")]]; + u16 value_offset [[comment("Offset to Resident Data")]]; + RESIDENT_ATTR_FLAGS resident_flags [[comment("Indexed Flag: 0x01 = Indexed | 0x00 = Not Indexed")]]; + s8 reservedR [[comment("Reserved")]]; + + //if (name_offset > ATTR_RECORD_RESIDENT_SIZE) { + // name_offset_delta = name_offset - ATTR_RECORD_RESIDENT_SIZE; + // padding[name_offset_delta]; + //} + + } +}; + +// ------------------------------ +// ATTRIBUTE BODY NON_RESIDENT +// ------------------------------ +struct ATTRBody { + leVCN lowest_vcn [[comment("Start Virtual Cluster Number")]]; + leVCN highest_vcn [[comment("End Virtual Cluster Number")]]; + u16 mapping_pairs_offset [[comment("Offset to RunList")]]; + u8 compression_unit [[comment("Compression Unit Size")]]; + u8 reserved1[5] [[comment("Reserved")]]; + u64 allocated_size [[comment("Size on Disk")]]; + u64 data_size [[comment("Logical File Size")]]; + u64 initialized_size [[comment("Initialized Size: Typical for Downloads")]]; +}; + +// ------------------------------ +// ATTRIBUTE RECORD BODY RELATED +// ------------------------------ +struct ATTR_RECORD { + ATTRHeader ATTR_HEADER [[name("Header")]]; + + if (ATTR_HEADER.non_resident) { + ATTRBody x80_xB0_Body [[name(attr_type_name(u32(ATTR_HEADER.type)) + "_Body")]]; + + if (ATTR_HEADER.flags & ATTR_FLAGS::ATTR_IS_COMPRESSED) { + u64 compressed_size [[comment("Compressed Size")]]; + + if (ATTR_HEADER.name_offset > ATTR_RECORD_NONRESIDENT_CMPR_SIZE) { + name_offset_delta = ATTR_HEADER.name_offset - ATTR_RECORD_NONRESIDENT_CMPR_SIZE; + padding[name_offset_delta]; + } + } else { + if (ATTR_HEADER.name_offset > ATTR_RECORD_NONRESIDENT_SIZE) { + name_offset_delta = ATTR_HEADER.name_offset - ATTR_RECORD_NONRESIDENT_SIZE; + padding[name_offset_delta]; + } + } + } + + u32 name_length_bytes = ATTR_HEADER.name_length * sizeof (ntfschar); + + if (ATTR_HEADER.name_length > 0) { + char16 name[ATTR_HEADER.name_length]; + } + + if (ATTR_HEADER.non_resident) { + if (ATTR_HEADER.flags & ATTR_FLAGS::ATTR_IS_COMPRESSED) { + if (x80_xB0_Body.mapping_pairs_offset > (ATTR_RECORD_NONRESIDENT_CMPR_SIZE + ATTR_HEADER.name_offset_delta + name_length_bytes)) { + padding[x80_xB0_Body.mapping_pairs_offset - (ATTR_RECORD_NONRESIDENT_CMPR_SIZE + name_length_bytes)]; + } + } else { + if (x80_xB0_Body.mapping_pairs_offset > (ATTR_RECORD_NONRESIDENT_SIZE + ATTR_HEADER.name_offset_delta + name_length_bytes)) { + padding[x80_xB0_Body.mapping_pairs_offset - (ATTR_RECORD_NONRESIDENT_SIZE + name_length_bytes)]; + } + } + + RUNLIST x80_xB0_RUNLIST[] [[name(attr_type_name(u32(ATTR_HEADER.type)) + "_RUNLIST"), comment("$DATA or $BITMAP Run")]]; + } else { + + u32 value_offset_delta = 0; + if (ATTR_HEADER.value_offset > (ATTR_RECORD_RESIDENT_SIZE + ATTR_HEADER.name_offset_delta + name_length_bytes)) { + value_offset_delta = ATTR_HEADER.value_offset - (ATTR_RECORD_RESIDENT_SIZE + ATTR_HEADER.name_offset_delta + name_length_bytes); + padding[value_offset_delta]; + } + + match (ATTR_HEADER.type) { + (ATTR_TYPES::AT_UNUSED): { + u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + ATTR_HEADER.name_offset_delta + name_length_bytes + value_offset_delta); + if (ATTR_HEADER.length > attr_sum) { + padding[ATTR_HEADER.length - attr_sum]; + } + } + (ATTR_TYPES::AT_STANDARD_INFORMATION): { + if (ATTR_HEADER.value_length > sizeof(STANDARD_INFORMATION_OLD)) { + STANDARD_INFORMATION x10_Body [[name(attr_type_name(u32(ATTR_HEADER.type)) + "_Body")]]; + + } else { + STANDARD_INFORMATION_OLD x10_Body [[name(attr_type_name(u32(ATTR_HEADER.type)) + "_Body")]]; + } + + u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + ATTR_HEADER.name_offset_delta + name_length_bytes + value_offset_delta + sizeof (x10_Body)); + if (ATTR_HEADER.length > attr_sum) { + padding[ATTR_HEADER.length - attr_sum]; + } + } + (ATTR_TYPES::AT_ATTRIBUTE_LIST): { + u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + ATTR_HEADER.name_offset_delta + name_length_bytes + value_offset_delta); + if (ATTR_HEADER.length > attr_sum) { + padding[ATTR_HEADER.length - attr_sum]; + } + } + (ATTR_TYPES::AT_FILE_NAME): FILE_NAME_ATTR x30_Body [[name(attr_type_name(u32(ATTR_HEADER.type)) + "_Body")]]; + (ATTR_TYPES::AT_OBJECT_ID): { + u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + ATTR_HEADER.name_offset_delta + name_length_bytes + value_offset_delta); + if (ATTR_HEADER.length > attr_sum) { + padding[ATTR_HEADER.length - attr_sum]; + } + } + (ATTR_TYPES::AT_SECURITY_DESCRIPTOR): { + SECURITY_DESCRIPTOR_RELATIVE x50_Body [[name(attr_type_name(u32(ATTR_HEADER.type)) + "_Body")]]; + u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + ATTR_HEADER.name_offset_delta + name_length_bytes + value_offset_delta + sizeof (x50_Body)); + if (ATTR_HEADER.length > attr_sum) { + padding[ATTR_HEADER.length - attr_sum]; + } + } + //(ATTR_TYPES::AT_VOLUME_NAME): char16 vol_name[ATTR_HEADER.value_length / sizeof (ntfschar)] [[name(attr_type_name(u32(ATTR_HEADER.type)) + "_Body"), comment("User Defined Volume Name")]]; + (ATTR_TYPES::AT_VOLUME_NAME): { char16 vol_name[ATTR_HEADER.value_length / sizeof (ntfschar)] [[name(attr_type_name(u32(ATTR_HEADER.type)) + "_Body"), comment("User Defined Volume Name")]]; + Vol_Label = str(vol_name); + } + + (ATTR_TYPES::AT_VOLUME_INFORMATION): VOLUME_INFORMATION vol_info; + (ATTR_TYPES::AT_DATA): { + char x80_xB0_Stream[ATTR_HEADER.value_length] [[name(attr_type_name(u32(ATTR_HEADER.type)) + "_Body")]]; + u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + ATTR_HEADER.name_offset_delta + name_length_bytes + value_offset_delta + sizeof (x80_xB0_Stream)); + if (ATTR_HEADER.length > attr_sum) { + padding[ATTR_HEADER.length - attr_sum]; + } + } + (ATTR_TYPES::AT_INDEX_ROOT): { + INDEX_ROOT x90_Body [[name(attr_type_name(u32(ATTR_HEADER.type)) + "_Body")]]; + } + (ATTR_TYPES::AT_INDEX_ALLOCATION): { + u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + ATTR_HEADER.name_offset_delta + name_length_bytes + value_offset_delta); + if (ATTR_HEADER.length > attr_sum) { + padding[ATTR_HEADER.length - attr_sum]; + } + } + (ATTR_TYPES::AT_BITMAP): u8 buffer[ATTR_HEADER.value_length] [[name(attr_type_name(u32(ATTR_HEADER.type)) + "_Body")]]; + (ATTR_TYPES::AT_REPARSE_POINT): { + u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + ATTR_HEADER.name_offset_delta + name_length_bytes + value_offset_delta); + if (ATTR_HEADER.length > attr_sum) { + padding[ATTR_HEADER.length - attr_sum]; + } + } + (ATTR_TYPES::AT_EA_INFORMATION): { + u64 attr_start = $ - (ATTR_HEADER.non_resident ? ATTR_RECORD_NONRESIDENT_SIZE : ATTR_RECORD_RESIDENT_SIZE); + EA_INFO_ENTRY xD0_Body[while($ - (attr_start + ATTR_HEADER.value_offset) < ATTR_HEADER.value_length)] [[name(attr_type_name(u32(ATTR_HEADER.type)) + "_Body")]]; + } + (ATTR_TYPES::AT_EA): { + u64 attr_start = $ - (ATTR_HEADER.non_resident ? ATTR_RECORD_NONRESIDENT_SIZE : ATTR_RECORD_RESIDENT_SIZE); + EA_ENTRY xE0_Body[while($ - (attr_start + ATTR_HEADER.value_offset) < ATTR_HEADER.value_length)] [[name(attr_type_name(u32(ATTR_HEADER.type)) + "_Body")]]; + } + (ATTR_TYPES::AT_PROPERTY_SET): { + u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + ATTR_HEADER.name_offset_delta + name_length_bytes + value_offset_delta); + if (ATTR_HEADER.length > attr_sum) { + padding[ATTR_HEADER.length - attr_sum]; + } + } + (ATTR_TYPES::AT_LOGGED_UTILITY_STREAM): { + u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + ATTR_HEADER.name_offset_delta + name_length_bytes + value_offset_delta); + if (ATTR_HEADER.length > attr_sum) { + padding[ATTR_HEADER.length - attr_sum]; + } + } + (ATTR_TYPES::AT_FIRST_USER_DEFINED_ATTRIBUTE): { + u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + ATTR_HEADER.name_offset_delta + name_length_bytes + value_offset_delta); + if (ATTR_HEADER.length > attr_sum) { + padding[ATTR_HEADER.length - attr_sum]; + } + } + (ATTR_TYPES::AT_END): { + //length is no longer valid + } + } + } + std::mem::AlignTo<8>; +}; + +// ------------------------------ +// ATTRIBUTE TYPE FUNC +// ------------------------------ +fn attr_get_type(u64 offset) { + ATTR_RECORD ATTR @ offset; + return ATTR.ATTR_HEADER.type; +}; + +// ------------------------------------------------------------------------- +// FILE RECORD ENTRY RELATED +// ------------------------------------------------------------------------- +// V V V V V V V V V +// ------------------------------------------------------------------------- + +// ------------------------------ +// PRIMARY FILE RECORD HEADER PARSER +// ------------------------------ +struct NTFS_RECORD_HDR { + NTFS_RECORD_TYPES magic [[comment("File Record Signature 'FILE'")]];; + u16 usa_ofs [[comment("Offset to Fix Up Array")]]; + u16 usa_count [[comment("# of 2 Byte Entries in FUA")]]; + leLSN lsn [[comment("$LogFile Sequence #")]]; + u16 sequence_number [[comment("Count of FR Entry Usage: 1=Once/Not Del")]]; + u16 link_count [[comment("(HARDLINK) Count of File Name ATTR")]]; + u16 attr_offset [[comment("Offset to 1st ATTR")]]; + MFT_RECORD_FLAGS flags [[comment("Allocation Status Flags: 0x00=Del File||0x01=Alloc.File||0x02=Del DIR||0x03=Alloc.DIR")]];; + u32 bytes_in_use [[comment("Logical Size of FR Entry")]]; + u32 bytes_allocated [[comment("Phys (Alloc.) Size of FR Entry")]]; + leMFT_REF base_mft_record [[comment("Ref to additional FR Entry, if any")]]; + u16 next_attr_instance [[comment("Count of ATTRs")]]; + u16 FU_Array [[comment("Fix Up Array and ATTR-NT3.0")]]; // 2 bytes NTFS 3.0 (LEGACY) + + if (FU_Array <= 0) { // FU_Array not in use -- NTFS 3.1+ + u32 mft_record_number [[comment("MFT File Record Num")]]; // 4 bytes + mft_num = mft_record_number; + seq_num = sequence_number; + + if (usa_count > 0) { + u16 update_sequence[usa_count] [[comment("Fix Up Array and ATTR-NT3.1+")]]; + } + } else { + padding[4]; + mft_num += 0x01; + } +}; + +// ------------------------------ +// QUICK ATTRIBUTE CHECK +// ------------------------------ +fn attr_type_name(u32 type) { + if (type == ATTR_TYPES::AT_STANDARD_INFORMATION) return "STANDARD_INFORMATION"; + if (type == ATTR_TYPES::AT_ATTRIBUTE_LIST) return "ATTRIBUTE_LIST"; + if (type == ATTR_TYPES::AT_FILE_NAME) return "FILE_NAME"; + if (type == ATTR_TYPES::AT_OBJECT_ID) return "OBJECT_ID"; + if (type == ATTR_TYPES::AT_SECURITY_DESCRIPTOR) return "SECURITY_DESCRIPTOR"; + if (type == ATTR_TYPES::AT_VOLUME_NAME) return "VOLUME_NAME"; + if (type == ATTR_TYPES::AT_VOLUME_INFORMATION) return "VOLUME_INFORMATION"; + if (type == ATTR_TYPES::AT_DATA) return "DATA"; + if (type == ATTR_TYPES::AT_INDEX_ROOT) return "INDEX_ROOT"; + if (type == ATTR_TYPES::AT_INDEX_ALLOCATION) return "INDEX_ALLOCATION"; + if (type == ATTR_TYPES::AT_BITMAP) return "BITMAP"; + if (type == ATTR_TYPES::AT_REPARSE_POINT) return "REPARSE_POINT"; + if (type == ATTR_TYPES::AT_EA_INFORMATION) return "EA_INFORMATION"; + if (type == ATTR_TYPES::AT_EA) return "EA"; + if (type == ATTR_TYPES::AT_PROPERTY_SET) return "PROPERTY_SET"; + if (type == ATTR_TYPES::AT_LOGGED_UTILITY_STREAM) return "LOGGED_UTILITY_STREAM"; + return "UNKNOWN"; +}; + +fn peek_attr_type(u64 offset) { + return std::mem::read_unsigned(offset, 4); // first 4 bytes of ATTRHeader +}; + +// ------------------------------ +// ATTRIBUTE WRAPPER +// ------------------------------ +struct ATTR_WRAPPER { + ATTR_RECORD ATTR [[name(attr_type_name(peek_attr_type($)))]]; +} [[inline, name(attr_type_name(peek_attr_type($)))]]; + +// ------------------------------ +// PATTERN NAMING FUNC +// ------------------------------ +fn get_file_name(auto ATTRS) { + str fname = ""; + + for (u32 i = 0, i < std::core::member_count(ATTRS), i = i + 1) { + + if (ATTRS[i].ATTR.ATTR_HEADER.type == ATTR_TYPES::AT_FILE_NAME) { + u8 len = ATTRS[i].ATTR.x30_Body.file_name_length; + + for (u32 j = 0, j < len, j = j + 1) { + char16 cu = ATTRS[i].ATTR.x30_Body.file_name[j]; + u8 c = u8(cu); // Cast to low byte + fname = std::format("{}{}", fname, char(c)); + } + break; + } + } + return fname; +}; + +// ------------------------------ +// PRIMARY FILE RECORD PARSER +// ------------------------------ +u64 start_index = 12; // First 12 are hard-coded - We're parsing the rest of the MFT Entries + +struct MFT_RECORD { + u64 record_start_offset = $; + NTFS_RECORD_HDR FR_HEADER; + + if (FR_HEADER.flags == 0x00 || FR_HEADER.flags == 0x02) { + Unused_Count += 1; + } + if (FR_HEADER.flags == 0x01 || FR_HEADER.flags == 0x03) { + MFT_Count += 1; + } + + std::mem::AlignTo<8>; + ATTR_WRAPPER ATTRS[while(attr_get_type($) != ATTR_TYPES::AT_END)] [[inline]]; + str filename = get_file_name(ATTRS); + + if (filename == "") { + PRENT_MFT = 0x00; + PRENT_SEQ = 0x00; + filename = "FILE_REC"; + } + + ATTR_RECORD ATTR_END [[static, name("ATTR_END")]] ; + std::mem::AlignTo; + +} [[name(format_element(filename, $, start_index)), + comment("DEC:[MFT#][SEQ#] | HEX:[MFT#][SEQ#] | PARENT_HEX:[MFT#][SEQ#]")]]; + +fn format_element(auto filename, auto v, u64 offset) { + return std::format("{} [{:02}][{:02}] | [x{:02X}][x{:02X}] | PRENT:[x{:02X}][x{:02X}]", filename, mft_num, seq_num, mft_num, seq_num, PRENT_MFT, PRENT_SEQ); +}; + + +// ------------------------------------------------------------------------- +// VOLUME BOOT RECORD RELATED +// ------------------------------------------------------------------------- +// V V V V V V V V V +// ------------------------------------------------------------------------- +// ------------------------------ +// NTFS VBR +// ------------------------------ +struct NTFS_BOOT_SECTOR { + u8 jmp_boot[3] [[comment("Jump Instructions")]]; + char oem_id[8] [[comment("FS IDENTIFIER")]]; + BIOS_PARAMETER_BLOCK bpb; + u8 physical_drive [[comment("Legacy")]]; + u8 current_head [[comment("Legacy")]]; + u8 extended_boot_signature [[comment("Legacy")]]; + u8 reserved1 [[comment("Legacy")]]; + u64 total_sectors [[comment("Volume Size in Sectors")]]; + u64 mft_lcn [[comment("MFT Start Cluster")]]; + u64 mftmirr_lcn [[comment("MFTMirror Start Cluster")]]; + u8 clusters_per_mft_record [[comment("Size of MFT/Mirror in Clusters")]]; + u8 reserved2[3] [[comment("Unused")]]; + u8 clusters_per_index_buffer [[comment("Size of Index Buffer")]]; + u8 reserved3[3] [[comment("Unused")]]; + u64 volume_serial [[comment("Volume Serial #")]]; + u32 checksum [[comment("VBR Sector CRC32")]]; + u8 bootstrap[426] [[comment("Boot-strapping Instructions")]]; + VBRSignature VBR_SIG [[comment("End of VBR - 0x55AA")]]; +}; + +// ------------------------------ +// $DATA FUNC HELPER FOR INDEXES +// ------------------------------ +fn mft_get_dat_attr_lcn(MFT_RECORD record, ATTR_TYPES type = ATTR_TYPES::AT_DATA) { + u64 lcn = 0; + + for (u64 i = 0, i < record.FR_HEADER.next_attr_instance, i += 1) { + if (record.ATTRS[i].ATTR.ATTR_HEADER.type == type) { + for (s64 j = record.ATTRS[i].ATTR.x80_xB0_RUNLIST[0].RunList_Header.SE_Offset - 1, j >= 0, j -= 1) { + lcn |= record.ATTRS[i].ATTR.x80_xB0_RUNLIST[0].Logical_Cluster_Num[j] << (8 * j); + } + break; + } + } + return lcn; +}; + +// ------------------------------------------------------------------------- +// MAIN ENTRY RELATED +// ------------------------------------------------------------------------- +// V V V V V V V V V +// ------------------------------------------------------------------------- + +// ============= VBR ============================================================================================= +NTFS_BOOT_SECTOR nbs @ $ [[name("NTFS_VBR")]]; +u32 cluster_size = nbs.bpb.bytes_per_sector * nbs.bpb.sectors_per_cluster; +u32 mft_rec_size = 1 << -nbs.clusters_per_mft_record; + +VBR_OFFSET = $ + (nbs.bpb.hidden_sectors - 1) * nbs.bpb.bytes_per_sector; + +// ============= $MFT ============================================================================================ +MFT_RECORD mft @ nbs.mft_lcn * cluster_size + 0 * mft_rec_size + [[name(std::format("$MFT [{:02}][{:02}] | [x{:02X}][x{:02X}] | PRENT:[x{:02X}][x{:02X}]", + mft_num, seq_num, mft_num, seq_num, PRENT_MFT, PRENT_SEQ))]]; + +MFT_RECORD mftmirr @ nbs.mftmirr_lcn * cluster_size + [[name(std::format("$MFT COPY (DERIVED FROM $MFTMirr)"))]]; + +// ============= $MFTMirr ======================================================================================== +MFT_RECORD mft_mirr @ nbs.mft_lcn * cluster_size + 1 * mft_rec_size + [[name(std::format("$MFT Mirror [{:02}][{:02}] | [x{:02X}][x{:02X}] | PRENT:[x{:02X}][x{:02X}]", + mft_num, seq_num, mft_num, seq_num, PRENT_MFT, PRENT_SEQ))]]; + +// ============= $LogFile ======================================================================================== +MFT_RECORD mft_log @ nbs.mft_lcn * cluster_size + 2 * mft_rec_size + [[name(std::format("$LogFile [{:02}][{:02}] | [x{:02X}][x{:02X}] | PRENT:[x{:02X}][x{:02X}]", + mft_num, seq_num, mft_num, seq_num, PRENT_MFT, PRENT_SEQ))]]; + +// ============= $Volume ========================================================================================= +MFT_RECORD mft_vol @ nbs.mft_lcn * cluster_size + 3 * mft_rec_size + [[name(std::format("$Volume [{:02}][{:02}] | [x{:02X}][x{:02X}] | PRENT:[x{:02X}][x{:02X}]", + mft_num, seq_num, mft_num, seq_num, PRENT_MFT, PRENT_SEQ))]]; + +// ============= $AttrDef ======================================================================================== +MFT_RECORD mft_adef @ nbs.mft_lcn * cluster_size + 4 * mft_rec_size + [[name(std::format("$AttrDef [{:02}][{:02}] | [x{:02X}][x{:02X}] | PRENT:[x{:02X}][x{:02X}]", + mft_num, seq_num, mft_num, seq_num, PRENT_MFT, PRENT_SEQ))]]; + +ATTR_DEF adef_dat_buf[16] @ mft_get_dat_attr_lcn(mft_adef) * cluster_size + [[name("$AttrDef ARRAY (DERIVED)")]]; + +// ============= $I30 (Root) ===================================================================================== +MFT_RECORD mft_root @ nbs.mft_lcn * cluster_size + 5 * mft_rec_size + [[name(std::format("$I30 (Root) [{:02}][{:02}] | [x{:02X}][x{:02X}] | PRENT:[x{:02X}][x{:02X}]", + mft_num, seq_num, mft_num, seq_num, PRENT_MFT, PRENT_SEQ))]]; +INDEX_BLOCK root_index_block @ mft_get_dat_attr_lcn(mft_root, ATTR_TYPES::AT_INDEX_ALLOCATION) * cluster_size + [[name(std::format("$I30 (Root) INDEX_BLOCK (DERIVED)"))]]; + +// ============= $Bitmap ========================================================================================= +MFT_RECORD mft_bm @ nbs.mft_lcn * cluster_size + 6 * mft_rec_size + [[name(std::format("$Bitmap [{:02}][{:02}] | [x{:02X}][x{:02X}] | PRENT:[x{:02X}][x{:02X}]", + mft_num, seq_num, mft_num, seq_num, PRENT_MFT, PRENT_SEQ))]]; + +// ============= $Boot =========================================================================================== +MFT_RECORD mft_boot @ nbs.mft_lcn * cluster_size + 7 * mft_rec_size + [[name(std::format("$Boot [{:02}][{:02}] | [x{:02X}][x{:02X}] | PRENT:[x{:02X}][x{:02X}]", + mft_num, seq_num, mft_num, seq_num, PRENT_MFT, PRENT_SEQ))]]; + +// ============= $BadClus ======================================================================================== +MFT_RECORD mft_badclus @ nbs.mft_lcn * cluster_size + 8 * mft_rec_size + [[name(std::format("$BadClus [{:02}][{:02}] | [x{:02X}][x{:02X}] | PRENT:[x{:02X}][x{:02X}]", + mft_num, seq_num, mft_num, seq_num, PRENT_MFT, PRENT_SEQ))]]; + +// ============= $Secure ($Quota for NTFS 1.2) =================================================================== +MFT_RECORD mft_sec @ nbs.mft_lcn * cluster_size + 9 * mft_rec_size + [[name(std::format("$Secure [{:02}][{:02}] | [x{:02X}][x{:02X}] | PRENT:[x{:02X}][x{:02X}]", + mft_num, seq_num, mft_num, seq_num, PRENT_MFT, PRENT_SEQ))]]; + +// ============= $UpCase ========================================================================================= +MFT_RECORD mft_uc @ nbs.mft_lcn * cluster_size + 10 * mft_rec_size + [[name(std::format("$UpCase [{:02}][{:02}] | [x{:02X}][x{:02X}] | PRENT:[x{:02X}][x{:02X}]", + mft_num, seq_num, mft_num, seq_num, PRENT_MFT, PRENT_SEQ))]]; +u8 uc_tbl[1] @ mft_get_dat_attr_lcn(mft_uc) * cluster_size [[name("$UpCase TABLE (DERIVED)")]]; + +// ============= $Extend ========================================================================================= +MFT_RECORD mft_ext @ nbs.mft_lcn * cluster_size + 11 * mft_rec_size + [[name(std::format("$Extend [{:02}][{:02}] | [x{:02X}][x{:02X}] | PRENT:[x{:02X}][x{:02X}]", + mft_num, seq_num, mft_num, seq_num, PRENT_MFT, PRENT_SEQ))]]; + +//============== RESERVED / UNUSED================================================================================ +// RESERVED/UNUSED FILE RECORD ENTRIES 12-23 | 0x0C-0x17 (13th-24th) + +// ============= NEXT RECORDS ==================================================================================== +// ---- FUNC TO CHECK FILE RECORD MAGIC +fn fr_magic(u64 abs_off) { + NTFS_RECORD_TYPES magic @ abs_off; + return magic; +}; + +// ---- CALL THE NEXT RECORDS ARRAY +MFT_RECORD NEXT_RECORDS[while(fr_magic($) == NTFS_RECORD_TYPES::magic_FILE)] @ (nbs.mft_lcn * cluster_size + 12 * mft_rec_size) [[inline, static]]; + +// ============= REPORT ========================================================================================== +// ------------------------------ +// NTFS VOLUME REPORT +// *** HAS GLOBAL AT TOP *** +// ------------------------------ +u64 mft_offset = VBR_OFFSET + nbs.mft_lcn * cluster_size + 0 * mft_rec_size; +u64 mft_mrr_offset = VBR_OFFSET + nbs.mftmirr_lcn * cluster_size; +u64 rootDir_offset = VBR_OFFSET + mft_get_dat_attr_lcn(mft_root, ATTR_TYPES::AT_INDEX_ALLOCATION) * cluster_size; + +if (VOLUME_REPORT) { + std::print("-----------------------------------------"); + std::print("------------- VOLUME_REPORT -------------"); + std::print("-----------------------------------------"); + std::print("VOL_LABEL = {}", Vol_Label); + std::print("NTFS_VERS = {}.{}", NT_Major, NT_Minor); + std::print("SECTOR_SIZE = {:02} BYTES", nbs.bpb.bytes_per_sector); + std::print("CLUSTER_SIZE = {:02} BYTES", cluster_size); + std::print("VOLUME_SIZE = {:02} SECTORS", nbs.total_sectors); + std::print("VOLUME_SIZE = {:.4f} GB @ 1000", (nbs.total_sectors * nbs.bpb.bytes_per_sector) / 1000.0 / 1000.0 / 1000.0); + std::print("VOLUME_SIZE = {:.4f} GiB @ 1024", (nbs.total_sectors * nbs.bpb.bytes_per_sector) / 1024.0 / 1024.0 / 1024.0); + + std::print("-----------------------------------------"); + std::print("MFT_OFFSET = {} | 0x{:02X}", mft_offset, mft_offset); + std::print("MFT_RECORDS = ~{:02} IN USE", MFT_Count); + std::print("MFT_RECORDS = ~{:02} NOT IN USE", Unused_Count); + + std::print("-----------------------------------------"); + std::print("MFT_COPY_OFFSET = {} | 0x{:02X}", mft_mrr_offset, mft_mrr_offset); + std::print("ROOT_DIR_OFFSET = {} | 0x{:02X}", rootDir_offset, rootDir_offset); + std::print("-----------------------------------------"); + std::print("------------------ END ------------------"); + std::print("-----------------------------------------"); + std::print(" "); +} \ No newline at end of file diff --git a/patterns/DFIR/exFAT.hexpat b/patterns/DFIR/exFAT.hexpat new file mode 100644 index 00000000..3f8975a1 --- /dev/null +++ b/patterns/DFIR/exFAT.hexpat @@ -0,0 +1,616 @@ +#pragma author Formula Zero One Technologies +#pragma description exFAT Filesystem (exFAT_v2.0) +#pragma MIME application/x-ima +#pragma endian little + +// ----------------------------------------------------------------------------- +// CREDIT +// ----------------------------------------------------------------------------- +// Based on /fs/exfat.hexpat by WerWolv +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// NOTES +// ----------------------------------------------------------------------------- +// Imported by DISK_PARSER.hexpat +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// TODO +// ----------------------------------------------------------------------------- +// Recursive parsing of Root Directory / SubDirs +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// IMPORTS +// ----------------------------------------------------------------------------- +import std.core; +import std.io; +import std.time; +import std.mem; +import type.guid; +import type.magic; +import type.base; + +// ------------------------------ +// FORWARD DECS/GLOBALS +// ------------------------------ + +// *** ATTENTION *** +// SET MAXIMUM NUMBER OF 4 BYTE CHUNKS TO PARSE FROM FAT1 +// SET MAXIMUM NUMBER OF DIRECTORY ENTRIES TO PARSE FROM ROOT DIRECTORY +// DEFAULTS ARE 4096 | 2500 +// Choose a value greater than 1 and less than 65536 OR increase the Array size limit with "#define... " + +// -------**************---vvvv--- | +const u64 MAX_FAT_CHUNKS = 4096; +// -------**************---^^^^--- | + +// -------**************---vvvv--- | +const u64 MAX_DIR_ENTRIES = 2500; +// -------**************---^^^^--- | + +// *** ATTENTION *** +// ---*******---*******----vvvv--- | +const bool VOLUME_REPORT = true; +// ---*******---*******----^^^^--- | + +u64 allocated_file_count; +u64 rdc; + +// -------------------------- +// exFAT DIRECTORY ENTRY HELPER +// -------------------------- +enum EntryType : u8 { + UNUSED_ENTRY = 0x00, + ACTIVE_VOLUME_GUID_ENTRY = 0xA0, + INACTIVE_VOLUME_GUID_ENTRY = 0x20, + ACTIVE_TEXFAT_ENTRY = 0xA1, + INACTIVE_TEXFAT_ENTRY = 0x21, + ACTIVE_ACCESS_CONTROL_ENTRY = 0xA2, + INACTIVE_ACCESS_CONTROL_ENTRY = 0x22, + ACTIVE_VOLUME_LABEL_ENTRY = 0x83, + INACTIVE_VOLUME_LABEL_ENTRY = 0x03, + ACTIVE_ALLOCATION_BITMAP_ENTRY = 0x81, + INACTIVE_ALLOCATION_BITMAP_ENTRY = 0x01, + ACTIVE_UPCASE_TABLE_ENTRY = 0x82, + INACTIVE_UPCASE_TABLE_ENTRY = 0x02, + ACTIVE_FILE_INFO_ENTRY = 0x85, + INACTIVE_FILE_INFO_ENTRY = 0x05, + ACTIVE_STREAM_ENTRY = 0xC0, + INACTIVE_STREAM_ENTRY = 0x40, + ACTIVE_FILENAME_ENTRY = 0xC1, + INACTIVE_FILENAME_ENTRY = 0x41, +}; + +// ------------------------------ +// DATES AND TIMES FUNC +// ------------------------------ +fn format_dos_time_field(std::time::DOSTime t) { + return std::time::format_dos_time(t, "{:02}:{:02}:{:02}"); +}; + +fn format_dos_date_field(std::time::DOSDate d) { + return std::time::format_dos_date(d, "{1:02}-{0:02}-{2:04}"); +}; + +// ------------------------------ +// BITFIELD HELPERS +// ------------------------------ +bitfield Entry_Flags { + unsigned TypeCode : 5; + unsigned Importance : 1; + unsigned Category : 1; + unsigned InUse : 1; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 8)]]; + +bitfield Bitmap_Flags { + unsigned Bitmap_1 : 1; + unsigned Bitmap_2 : 1; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 8)]]; + +bitfield General_Primary_Flags { + unsigned Allocation_Possible : 1; + unsigned No_FAT_Chain : 1; + unsigned Reserved : 6; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 8)]]; + +bitfield General_Secondary_Flags { + unsigned Allocation_Possible : 1; + unsigned No_FAT_Chain : 1; + unsigned Reserved : 6; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 8)]]; + +bitfield File_Attr_Flags { + unsigned Read_Only : 1; + unsigned Hidden : 1; + unsigned System : 1; + unsigned Directory : 1; + unsigned Archive : 1; + Reserved : 11; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 16)]]; + +// -------------------------- +// exFAT DIRECTORY ENTRY STRUCTURES +// -------------------------- +// xA0 / x20 = Volume GUID Entry +struct VolumeGUID_Entry { + Entry_Flags EntryFlags [[comment("ENTRY TYPE IDENTIFIER")]]; + u8 SecondaryCount[3] [[comment("COUNT OF SUBSEQUENT ENTRIES")]]; + type::Hex SetChecksum [[comment("16bit CHECKSUM")]]; + General_Primary_Flags PrimaryFlags; + type::GUID GUID; + u8 Reserved_1[9]; +}; + +// xA1 / x21 = TexFAT / Padding Entry +struct TexFATPadding_Entry { + Entry_Flags Flags [[comment("ENTRY TYPE IDENTIFIER")]]; + u8 Reserved_1[31]; +}; + +// xA2 / x22 = Access Control Entry +struct AccessControl_Entry { + Entry_Flags Flags [[comment("ENTRY TYPE IDENTIFIER")]]; + u8 Reserved[31]; +}; + +// x83 / x03 = Volume Label Entry +struct VolumeLabel_Entry { + Entry_Flags Flags [[comment("ENTRY TYPE IDENTIFIER")]]; + u8 LabelLength [[comment("NUMBER OF UTF-16 CHARACTERS")]]; + char16 Label[LabelLength] [[comment("VOLUME LABEL: UTF-16")]]; + u8 Reserved[32-2-(LabelLength * 2)]; +}; + +// x81 / x01 = Allocation Bitmap Entry +struct AllocationBitmap_Entry { + Entry_Flags Flags [[comment("ENTRY TYPE IDENTIFIER")]]; + Bitmap_Flags BitmapFlags; + u8 Reserved_1[18]; + u32 FirstCluster [[comment("FIRST LOGICAL CLUSTER NUMBER")]]; + u64 DataLength [[comment("DATA SIZE")]]; + u8 FILE_DATA[DataLength] @ temp_root_location + (FirstCluster - exFAT_VBR.root_dir_cluster) * bytesPerCluster [[comment("POINTER TO THE CLUSTER CONTENT")]]; +}; + +// x82 / x02 = UpCase Table Entry +struct UpCaseTable_Entry { + Entry_Flags Flags [[comment("ENTRY TYPE IDENTIFIER")]]; + u8 Reserved_1[3]; + type::Hex TableChecksum [[comment("16bit CHECKSUM")]]; + u8 Reserved_2[12]; + u32 FirstCluster [[comment("FIRST LOGICAL CLUSTER NUMBER")]]; + u64 DataLength [[comment("DATA SIZE")]]; + u8 FILE_DATA[DataLength] @ temp_root_location + (FirstCluster - exFAT_VBR.root_dir_cluster) * bytesPerCluster [[comment("POINTER TO THE CLUSTER CONTENT")]]; +}; + +// x85 / x05 = File Info Entry +struct FileInfo_Entry { + Entry_Flags EntryFlags [[comment("ENTRY TYPE IDENTIFIER")]]; + u8 SecondaryCount [[comment("COUNT OF SUBSEQUENT ENTRIES")]]; + type::Hex SetChecksum [[comment("16bit CHECKSUM")]]; + File_Attr_Flags AttrFlags [[comment("FILE ATTRS: RASH")]]; + u16 Reserved_1; + std::time::DOSTime Created_Time [[format("format_dos_time_field")]]; + std::time::DOSDate Created_Date [[format("format_dos_date_field")]]; + std::time::DOSTime Accessed_Time [[format("format_dos_time_field")]]; + std::time::DOSDate Accessed_Date [[format("format_dos_date_field")]]; + std::time::DOSTime Modified_Time [[format("format_dos_time_field")]]; + std::time::DOSDate Modified_Date [[format("format_dos_date_field")]]; + u8 Created_10ms_Increments [[comment("Add to Times for Refinement")]]; + u8 Modified_10ms_Increments [[comment("Add to Times for Refinement")]]; + s8 Created_UTC_Diff [[comment("Add to Times for Refinement")]]; + s8 Modified_UTC_Diff [[comment("Add to Times for Refinement")]]; + s8 Accessed_UTC_Diff [[comment("Add to Times for Refinement")]]; + u8 Reserved[7]; +}; + +// xC1 / x41 = File Name Entry +struct FileName_Entry { + Entry_Flags EntryFlags [[comment("ENTRY TYPE IDENTIFIER")]]; + General_Secondary_Flags SecondaryFlags [[comment("COUNT OF SUBSEQUENT ENTRIES")]]; + char16 FileName[15] [[comment("FILE NAME: UTF-16")]]; +}; + +// xC0 / x40 = Stream Extension Entry +struct Stream_Entry { + Entry_Flags EntryFlags [[comment("ENTRY TYPE IDENTIFIER")]];; + General_Secondary_Flags SecondaryFlags [[comment("COUNT OF SUBSEQUENT ENTRIES")]]; + u8 Reserved_1; + u8 NameLength [[comment("STREAM LENGTH")]]; + type::Hex NameHash [[comment("16bit QUICK HASH: USED FOR FILE SEARCHING")]]; + u16 Reserved_2; + u64 InitSize [[comment("INITIALIZED SIZE")]]; + u32 Reserved_3; + u32 FirstCluster [[comment("FIRST LOGICAL CLUSTER NUMBER")]]; + u64 ActualSize [[comment("PHYSICAL DATA SIZE")]];; + u8 FILE_DATA[InitSize] @ temp_root_location + (FirstCluster - exFAT_VBR.root_dir_cluster) * bytesPerCluster [[comment("POINTER TO THE CLUSTER CONTENT")]]; +}; + +// -------------------------- +// exFAT ROOT DIRECTORY +// -------------------------- +struct RootDir { + EntryType Type; + padding[31]; + + match (Type) { + (EntryType::UNUSED_ENTRY): { + continue; + } + (EntryType::ACTIVE_VOLUME_GUID_ENTRY | EntryType::INACTIVE_VOLUME_GUID_ENTRY):{ + VolumeGUID_Entry VOLUME_GUID_ENTRY @ $ - 32; + } + (EntryType::ACTIVE_TEXFAT_ENTRY | EntryType::INACTIVE_TEXFAT_ENTRY):{ + TexFATPadding_Entry TEXFAT_PADDING_ENTRY @ $ - 32; + } + (EntryType::ACTIVE_ACCESS_CONTROL_ENTRY | EntryType::INACTIVE_ACCESS_CONTROL_ENTRY):{ + AccessControl_Entry ACCESS_CONTROL_ENTRY @ $ - 32; + } + (EntryType::ACTIVE_VOLUME_LABEL_ENTRY | EntryType::INACTIVE_VOLUME_LABEL_ENTRY):{ + VolumeLabel_Entry VOLUME_LABEL_ENTRY @ $ - 32; + } + (EntryType::ACTIVE_ALLOCATION_BITMAP_ENTRY | EntryType::INACTIVE_ALLOCATION_BITMAP_ENTRY):{ + AllocationBitmap_Entry ALLOCATION_BITMAP_ENTRY @ $ - 32; + u64 bitmap_cluster = ALLOCATION_BITMAP_ENTRY.FirstCluster; + char dolla_BITMAP_label[4] @ FAT1_start_offset + (bitmap_cluster * 4) [[name(std::format("$Bitmap"))]]; + } + (EntryType::ACTIVE_UPCASE_TABLE_ENTRY | EntryType::INACTIVE_UPCASE_TABLE_ENTRY):{ + UpCaseTable_Entry UPCASE_TABLE_ENTRY @ $ - 32; + u64 upcase_cluster = UPCASE_TABLE_ENTRY.FirstCluster; + char dolla_UPCASE_label[4] @ FAT1_start_offset + (upcase_cluster * 4) [[name(std::format("$UpCase"))]]; + } + (EntryType::ACTIVE_FILE_INFO_ENTRY | EntryType::INACTIVE_FILE_INFO_ENTRY):{ + FileInfo_Entry FILE_INFO_ENTRY @ $ - 32; + } + (EntryType::ACTIVE_STREAM_ENTRY | EntryType::INACTIVE_STREAM_ENTRY):{ + Stream_Entry STREAM_EXT_ENTRY @ $ - 32; + } + (EntryType::ACTIVE_FILENAME_ENTRY | EntryType::INACTIVE_FILENAME_ENTRY):{ + FileName_Entry FILE_NAME_ENTRY @ $ - 32; + } + } +} [[name(format_entry_name(std::mem::read_unsigned($-32, 1), std::core::array_index()))]]; + +fn format_entry_name(auto entry_type, u64 idx) { + return std::format("{}[{}]", type_name(entry_type), idx); +}; + +// ------------------------------ +// TYPE RE-NAMER +// ------------------------------ +fn type_name(u32 type) { + if (type == EntryType::UNUSED_ENTRY) return "UNUSED_ENTRY"; + if (type == EntryType::ACTIVE_VOLUME_GUID_ENTRY) return "ACTIVE_VOLUME_GUID_ENTRY"; + if (type == EntryType::INACTIVE_VOLUME_GUID_ENTRY) return "INACTIVE_VOLUME_GUID_ENTRY"; + if (type == EntryType::ACTIVE_TEXFAT_ENTRY) return "ACTIVE_TEXFAT_ENTRY"; + if (type == EntryType::INACTIVE_TEXFAT_ENTRY) return "INACTIVE_TEXFAT_ENTRY"; + if (type == EntryType::ACTIVE_ACCESS_CONTROL_ENTRY) return "ACTIVE_ACCESS_CONTROL_ENTRY"; + if (type == EntryType::INACTIVE_ACCESS_CONTROL_ENTRY) return "INACTIVE_ACCESS_CONTROL_ENTRY"; + if (type == EntryType::ACTIVE_VOLUME_LABEL_ENTRY) return "ACTIVE_VOLUME_LABEL_ENTRY"; + if (type == EntryType::INACTIVE_VOLUME_LABEL_ENTRY) return "INACTIVE_VOLUME_LABEL_ENTRY"; + if (type == EntryType::ACTIVE_ALLOCATION_BITMAP_ENTRY) return "ACTIVE_ALLOCATION_BITMAP_ENTRY"; + if (type == EntryType::INACTIVE_ALLOCATION_BITMAP_ENTRY) return "INACTIVE_ALLOCATION_BITMAP_ENTRY"; + if (type == EntryType::ACTIVE_UPCASE_TABLE_ENTRY) return "ACTIVE_UPCASE_TABLE_ENTRY"; + if (type == EntryType::INACTIVE_UPCASE_TABLE_ENTRY) return "INACTIVE_UPCASE_TABLE_ENTRY"; + if (type == EntryType::ACTIVE_FILE_INFO_ENTRY) return "ACTIVE_FILE_INFO_ENTRY"; + if (type == EntryType::INACTIVE_FILE_INFO_ENTRY) return "INACTIVE_FILE_INFO_ENTRY"; + if (type == EntryType::ACTIVE_STREAM_ENTRY) return "ACTIVE_STREAM_ENTRY"; + if (type == EntryType::INACTIVE_STREAM_ENTRY) return "INACTIVE_STREAM_ENTRY"; + if (type == EntryType::ACTIVE_FILENAME_ENTRY) return "ACTIVE_FILENAME_ENTRY"; + if (type == EntryType::INACTIVE_FILENAME_ENTRY) return "INACTIVE_FILENAME_ENTRY"; + return "UNKNOWN"; +}; + +// ----------------------------------------------------------------------------- +// exFAT FILE ALLOCATION TABLE (FAT1) PARSER +// ----------------------------------------------------------------------------- + +const u32 CLUSTER_SIZE_BYTES = 4; // Each FAT32 entry = 4 bytes +const u32 FAT_EOF = 0x0FFFFFFF; // End-of-file marker +const u32 FAT_BAD = 0x0FFFFFF7; // Bad cluster marker +const u32 FIRST_ALLOC_CLUSTER = 2; // First usable cluster after reserved + +enum FAT_Flags : u32 { + UNALLOCATED = 0x00000000, + END_OF_FILE = 0xFFFFFFFF, // L.END + BAD_CLUSTER = 0xFFFFFFF7, // L.END +}; + +union FAT_Union { + u32 DECIMAL [[hidden]]; + FAT_Flags FAT_FLAG; +}; + +// ------------------------------ +// Helper function for pointer label +// ------------------------------ +fn cluster_label(u32 val) { + if (val == FAT_Flags::UNALLOCATED) + return "UNALLOCATED"; + if (val == FAT_Flags::BAD_CLUSTER) + return "BAD"; + if (val >= 0x0FFFFFF8) + return "EOF"; + return std::format("{}", val); +}; + +// ------------------------------ +// FAT1 HEAPS/CHAINS +// ------------------------------ +struct FAT_Entry { + FAT_Union FAT [[inline]]; + + u32 cluster_num = (FIRST_ALLOC_CLUSTER) + (std::core::array_index()); + + u32 next_cluster = FAT.DECIMAL & 0x0FFFFFFF; + + char hover_label[4] @ $ - 4 [[ + name(std::format( + "Cluster: {} → {}", + cluster_num, + cluster_label(next_cluster) + )) + ]]; + + bool is_eof = next_cluster >= 0x0FFFFFF8; + bool is_bad = next_cluster == FAT_BAD; + bool is_free = next_cluster == 0; + + if (is_eof) { + allocated_file_count += 1; + } +} [[name(format_fat_entry(FAT.DECIMAL, std::core::array_index(), FIRST_ALLOC_CLUSTER))]]; + +// ------------------------------ +// FAT FORMATTER FUNC +// ------------------------------ +fn format_fat_entry(u32 raw_value, u32 cluster_index, u32 first_alloc_cluster) { + u32 next_cluster = raw_value & 0x0FFFFFFF; + + str next_label; + + if (next_cluster == 0) + next_label = "UNALLOCATED"; + + else if (next_cluster == FAT_BAD) + next_label = "BAD"; + + else if (next_cluster == 0x0FFFFFFF) + next_label = "EOF"; + + else + next_label = std::format("{}", next_cluster); + + u32 logical_cluster = first_alloc_cluster + cluster_index; + + if (next_label == "UNALLOCATED" || next_label == "BAD" || next_label == "EOF") + return std::format("Cluster {}: {}", logical_cluster, next_label); + else + return std::format("Cluster {} → {}", logical_cluster, next_label); +}; + +// ------------------------------ +// MEDIA DESCRIPTOR HELPER +// ------------------------------ +enum Media_Descriptor : u8 { + SINGLE_SIDE_FLOPPY = 0xF0, + DOUBLE_SIDE_FLOPPY = 0xF9, + HARD_DISK_DRIVE = 0xF8, +}; + +// ------------------------------ +// FAT HEADER PARSER +// ------------------------------ +struct FAT_Header { + Media_Descriptor mediaDescriptor [[comment("0xF8=FIXED DISK | 0xF0=REMOVABLE")]];; + u8 exFAT_FAT_HEADER[7] [[comment("8 BYTES TOTAL: 4 BYTES REPRESENT PSUEDO CLUSTER 0 (SYSTEM) | 4 BYTES REPRESENT PSUEDO CLUSTER 1 (SYSTEM)(EOF)")]]; + + //Bitmap and UpCase overlays handled in RootDir parser + + char root_dir_label[4] @ $ + ((rdc - 2) * 4) [[ + name(std::format( + "ROOT_DIRECTORY" + )) + ]]; +}; + +// ------------------------------ +//SIGNATURE HELPER +// ------------------------------ +enum VBRSignature : u16 { + VBR_SIG = 0xAA55 +}; + +// ------------------------------ +// EXTENDED BOOT REGION +// ------------------------------ +struct ExtendedBoot { + u8 Extended_Boot_Sector[1 * bytesPerSector]; + VBRSignature VBR_SIG @ $ - 2; +}; + +// ------------------------------ +// BOOT SECTOR BITFIELD FLAGS +// ------------------------------ +bitfield VolumeFlags { + unsigned Active : 1; + unsigned VolumeDirty : 1; + unsigned Media_Failure : 1; + unsigned Clear_to_Zero : 1; + Rserved : 12; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 16)]]; + +// ------------------------------ +// EXFAT VOLUME BOOT RECORD +// ------------------------------ +struct exFAT_BootSector { + u8 jmp_boot[3]; + char fs_name[8]; // "EXFAT " + u8 must_be_zero[53]; + u64 partition_offset; // in sectors + u64 volume_length; // in sectors + u32 fat_offset; // in sectors + u32 fat_length; // in sectors + u32 cluster_heap_offset; // in sectors + u32 cluster_count; + u32 root_dir_cluster; + u32 volume_serial; + u16 fs_revision; + VolumeFlags volume_flags; + u8 bytes_per_sector_shift; // 2^n + u8 sectors_per_cluster_shift; // 2^n + u8 number_of_fats; + u8 drive_select; + u8 percent_in_use; + u8 reserved[7]; + u8 bootstrap[390]; + VBRSignature VBR_SIG; // 0x55AA + + rdc = root_dir_cluster; +}; + +// ------------------------------------------------------------------------- +// MAIN +// ------------------------------------------------------------------------- +// V V V V V V V V V +// ------------------------------------------------------------------------- + +exFAT_BootSector exFAT_VBR @ 0x0; + +// ------------------------------------------------------------------------- +// DERIVED CONSTANTS +// ------------------------------------------------------------------------- + +// ============= SIZES =================================================================== +u32 bytesPerSector = 1 << exFAT_VBR.bytes_per_sector_shift; +u32 bytesPerCluster = bytesPerSector << exFAT_VBR.sectors_per_cluster_shift; + +// ============= OFFSETS ================================================================= +u64 volumeStartSector = exFAT_VBR.partition_offset; +u64 volumeStartOffset = volumeStartSector * bytesPerSector; +u64 volumeSize = exFAT_VBR.volume_length * bytesPerSector; + +u64 FAT1_start_offset = exFAT_VBR.fat_offset * bytesPerSector; + +//For printing absolute offset +u64 RootDir_Offset = (exFAT_VBR.cluster_heap_offset + + ((exFAT_VBR.root_dir_cluster - 2) << exFAT_VBR.sectors_per_cluster_shift)) + * bytesPerSector + volumeStartOffset; + +// ============= CLUSTERS ================================================================ +u32 clusterSize = bytesPerCluster; +u32 clusterCount = exFAT_VBR.cluster_count; +u64 clusterHeapOffset = exFAT_VBR.cluster_heap_offset * bytesPerSector; + +// ------------------------------------------------------------------------- +// SECONDARY +// ------------------------------------------------------------------------- +// V V V V V V V V V +// ------------------------------------------------------------------------- +// ============= USAGE =================================================================== +u8 percentInUse = exFAT_VBR.percent_in_use; + +// ============= EBS ===================================================================== +ExtendedBoot Extended_Boot_Sectors[8] @ $; + +// ============= OEM ===================================================================== +u8 OEM_Parameters[1 * bytesPerSector] @ $; + +// ============= ER ====================================================================== +u8 Extended_Reserved[1 * bytesPerSector] @ $; + +// ============= BCS ===================================================================== +u8 Boot_Checksum[1 * bytesPerSector] @ $; + +// ============= BBS ===================================================================== +exFAT_BootSector Backup_Boot_Sector @ $; + +// ============= BEBS ==================================================================== +ExtendedBoot Backup_Extended_Boot_Sectors[8] @ $; + +// ============= BOEM ==================================================================== +u8 Backup_OEM_Parameters[1 * bytesPerSector] @ $; + +// ============= BER ===================================================================== +u8 Backup_Extended_Reserved[1 * bytesPerSector] @ $; + +// ============= BBCS ==================================================================== +u8 Backup_Boot_Checksum[1 * bytesPerSector] @ $; + +// ============= FAT ===================================================================== +// *** HAS GLOBAL AT TOP *** + +FAT_Header FAT1_HEADER @ FAT1_start_offset; +FAT_Entry FAT1[MAX_FAT_CHUNKS] @ FAT1_start_offset + 8; + +// ============= ROOT ==================================================================== +// ROOT DIRECTORY +// *** HAS GLOBAL AT TOP *** + +// for locating root directory within memory +u64 temp_root_location = (exFAT_VBR.root_dir_cluster - 2) * clusterSize + clusterHeapOffset; +RootDir ROOT_DIRECTORY[MAX_DIR_ENTRIES] @ temp_root_location; + + +// ============= REPORT ================================================================== +// VOLUME REPORT +// *** HAS GLOBAL AT TOP *** + +if (VOLUME_REPORT) { + std::print(" "); + std::print("-----------------------------------------"); + std::print("---------- EXFAT VOLUME_REPORT ----------"); + std::print("-----------------------------------------"); + std::print("FILE_SYSTEM = {}", exFAT_VBR.fs_name); + std::print("SERIAL_NUMBER = 0x{:X}", exFAT_VBR.volume_serial); + std::print("FS_REVISION = {}.{}", (exFAT_VBR.fs_revision >> 8) & 0xFF, exFAT_VBR.fs_revision & 0xFF); + + bool _any = false; + if(exFAT_VBR.volume_flags.Active) { + std::print("FAT_FLAG = 0b{:X}", exFAT_VBR.volume_flags.Active); + _any = true; + } + if(exFAT_VBR.volume_flags.VolumeDirty) { + std::print("DIRTY_FLAG = 0b{:X}", exFAT_VBR.volume_flags.Volume_Dirty); + _any = true; + } + if(exFAT_VBR.volume_flags.Media_Failure) { + std::print("FAILURE_FLAG = 0b{:X}", exFAT_VBR.volume_flags.Media_Failure); + _any = true; + } + if(exFAT_VBR.volume_flags.Clear_to_Zero) { + std::print("CLEAR_TO_ZERO_FLAG = 0b{:X}", exFAT_VBR.volume_flags.Clear_to_Zero); + _any = true; + } + if (!_any){ + std::print("VOL_FLAGS = NONE"); + } + + std::print("-----------------------------------------"); + std::print("BYTES/SECTOR = {}", bytesPerSector); + std::print("SECTORS/CLUSTER = {}", 1 << exFAT_VBR.sectors_per_cluster_shift); + std::print("BYTES/CLUSTER = {}", bytesPerCluster); + std::print("CLUSTER_COUNT = {}", clusterCount); + + std::print("-----------------------------------------"); + std::print("VOLUME_SIZE = {} SECTORS", exFAT_VBR.volume_length); + std::print("VOLUME_SIZE = {:.4f} GB @ 1000", volumeSize / 1000.0 / 1000.0 / 1000.0); + std::print("VOLUME_SIZE = {:.4f} GiB @ 1024", volumeSize / 1024.0 / 1024.0 / 1024.0); + + std::print("-----------------------------------------"); + std::print("VOLUME_START_SEC = {}", volumeStartSector); + std::print("VOLUME_START_OFF = 0x{:X}", volumeStartOffset); + + std::print("FAT1_START_OFF = 0x{:02X}", FAT1_start_offset); + std::print("CLUSTER_HEAP_OFF = 0x{:02X}", clusterHeapOffset); + std::print("ROOT_DIR_CLUSTER = {:02}", exFAT_VBR.root_dir_cluster); + std::print("ROOT_DIR_OFFSET = 0x{:02X}", RootDir_Offset); + + std::print("-----------------------------------------"); + std::print("PERCENT_IN_USE = {:02} %", percentInUse); + std::print("NUMBER_OF_FATS = {:02}", exFAT_VBR.number_of_fats); + std::print("DRIVE_SELECT = 0x{:02X}", exFAT_VBR.drive_select); + + std::print("-----------------------------------------"); + std::print("------------------ END ------------------"); + std::print("-----------------------------------------"); + std::print(" "); +} diff --git a/patterns/Devil May Cry HD Collection/dmc3_hd_mod.hexpat b/patterns/Devil May Cry HD Collection/dmc3_hd_mod.hexpat new file mode 100644 index 00000000..67a4ebac --- /dev/null +++ b/patterns/Devil May Cry HD Collection/dmc3_hd_mod.hexpat @@ -0,0 +1,165 @@ +#pragma description Devil May Cry 3 HD .mod 3D model file +#pragma author haru233 + +// many thanks to AxCut +// ImHex Hex Pattern File for Capcom's Devil May Cry 3 HD .mod files + + +import std.core; + + +struct ModelHeader { + char ID[4]; + float Version; + padding[8]; + u8 objectCount; + u8 boneCount; + u8 numberTextures; + u8; + u32; + u64; + u64 skeletonOffset; + padding[24]; +}; + +struct ObjectInfo { + u8 meshCount; + u8; + u16 numberVertices; + padding[4]; + u64 meshOffset; + u32 flags; + padding[28]; + float X, Y, Z; + float radius; +}; + +struct Positions { + float positions[3]; +}; + + +struct Normals { + float normal[3]; +}; + + +struct UVs { + s16 uv[2]; +}; + +struct BoneIndices { + u8 boneindex[4]; +}; + +struct Weights { + u16 weight[1]; +}; + +struct MeshSCM { + u16 numberVertices; + u16 textureIndex; + padding[12]; + u64 VerticesPositionsOffset; + u64 NormalsPositionsOffset; + u64 UVsPositionsOffset; + + padding[16]; + u64 unknownOffset; + + u64; + padding[8]; + + Positions positions[numberVertices] @VerticesPositionsOffset; + Normals normals[numberVertices] @NormalsPositionsOffset; + UVs uvs[numberVertices] @UVsPositionsOffset; + + +}; + +struct Mesh { + u16 numberVertices; + u16 textureIndex; + padding[12]; + u64 VerticesPositionsOffset; + u64 NormalsPositionsOffset; + u64 UVsPositionsOffset; + + u64 BoneIndicesOffset; + u64 WeightsOffset; + padding[8]; + + u64; + padding[8]; + + Positions positions[numberVertices] @VerticesPositionsOffset; + Normals normals[numberVertices] @NormalsPositionsOffset; + UVs uvs[numberVertices] @UVsPositionsOffset; + + BoneIndices b_index[numberVertices] @BoneIndicesOffset; + Weights weights[numberVertices] @WeightsOffset; + + +}; + + +struct Hierarchy { + u8 hierarchy; +}; + +struct HierarchyOrder { + u8 hierarchyorder; +}; + +struct Unknown { + u8; +}; + +struct Transform { + float x; + float y; + float z; + float length; // sqrt(x*x + y*y + z*z) + padding[16]; +}; + +struct Skeleton{ + u32 hierarchyOffset; + u32 hierarchyOrderOffset; + u32 unknownOffset; + u32 transformsOffset; +}; + + + + + +ModelHeader modelheader @ 0x00; +ObjectInfo objects_info[modelheader.objectCount] @ 0x40; + +u32 objectOffset; + +struct Object { + u64 i = std::core::array_index(); + if (modelheader.ID == "SCM ") { + objectOffset = objects_info[0].meshOffset; + MeshSCM meshscm[objects_info[i].meshCount] @ objects_info[i].meshOffset; + + + } else { + objectOffset = objects_info[0].meshOffset; + Mesh mesh[objects_info[i].meshCount] @ objects_info[i].meshOffset; + } +}; + +Object objects[modelheader.objectCount] @objectOffset; + +Skeleton skeleton @modelheader.skeletonOffset; + +Hierarchy hierarchy[modelheader.boneCount] @(modelheader.skeletonOffset + skeleton.hierarchyOffset); + +HierarchyOrder hierarchyorder[modelheader.boneCount] @(modelheader.skeletonOffset + skeleton.hierarchyOrderOffset); + +Unknown unknown[modelheader.boneCount] @(modelheader.skeletonOffset + skeleton.unknownOffset); + +Transform transform[modelheader.boneCount] @(modelheader.skeletonOffset + skeleton.transformsOffset); diff --git a/patterns/SHR.hexpat b/patterns/SHR.hexpat new file mode 100644 index 00000000..0c4386f8 --- /dev/null +++ b/patterns/SHR.hexpat @@ -0,0 +1,189 @@ +/*! + Apple IIgs Super Hi-Res (SHR) + PaintWorks Animation (ANI) — ImHex pattern + + Supports: + • PIC $C1/$0000 — 32 KB uncompressed SHR screen image + • PIC $C1/$0002 — 3200-color (“Brooks”) per-scanline palettes + • ANI $C2/$0000 — PaintWorks animation: + 0x0000..0x7FFF : base SHR $C1/$0000 image + 0x8000.. : animation header + chunks + + PaintWorks animation structure (per reversed docs): + - Base image: uncompressed SHR ($C1/$0000) + - +0x8000 u32: total animation data length after header (file_len - 0x8008) + - +0x8004 u16: global frame delay in VBLs + - +0x8006 u16: flag/? (commonly 0x00C0 or 0x00C1) + - +0x8008 ...: one or more animation chunks: + chunk: + u32 chunk_len (includes this length field) + repeated { u16 offset; u16 value; } pairs + offset==0x0000 marks End-of-Frame (value is ignored) + - Offsets may target any byte in the 32 KB SHR space (pixels, SCBs, palettes). + This enables palette-cycling and SCB effects. + + References: + - CiderPress2 PaintWorks Animation notes (file structure, fields, semantics). +*/ + +import std.core; +import std.sys; +import std.math; + +#pragma endian little +#include + +#pragma description Apple IIgs Super Hi-Res (SHR) + PaintWorks Animation (ANI) +#pragma author hasseily + + +// ------------------------------ Constants ------------------------------ + +const u32 SHR_ROWS = 200; +const u32 SHR_BYTES_PER_ROW = 160; +const u32 SHR_PIXEL_DATA_SIZE = SHR_ROWS * SHR_BYTES_PER_ROW; // 32000 (0x7D00) + +const u32 PIC0000_FILE_SIZE = 0x8000; // 32768 +const u32 PIC0002_FILE_SIZE = 0x9600; // 38400 (32000 + 200*32) + +const u32 PIC0000_OFF_PIXELS = 0x0000; +const u32 PIC0000_OFF_SCB = 0x7D00; +const u32 PIC0000_OFF_RESERVED = 0x7DC8; +const u32 PIC0000_OFF_PALETTES = 0x7E00; + +const u32 PALETTE_COUNT = 16; +const u32 PALETTE_COLORS = 16; + +const u32 ANI_BASE_SHR_SIZE = 0x8000; // First 32 KB is a $C1/$0000 image +const u32 ANI_HDR_OFF = 0x8000; // Animation header starts here +const u32 ANI_MIN_TOTAL_SIZE = 0x8008; // base + header (no chunks) + +// ------------------------------ Types: SHR core ------------------------------ + +struct Row160 { u8 data[SHR_BYTES_PER_ROW]; }; + +// Scanline Control Byte +bitfield ShrSCB { + palette : 4; // 0..15 + reserved : 1; + color_fill : 1; + interrupt : 1; + mode_640 : 1; // 0=320, 1=640 +}; + +// helper: expand a 4-bit channel to 8-bit (0x0..0xF -> 0x00..0xFF) +fn expand4(u8 v) { return (v << 4) | v; }; + +bitfield Colors_gb { + blue : 4; // Blue (B3..B0) + green : 4; // Green (G3..G0) +}; + +bitfield Colors_r0 { + red : 4; // Red (R3..R0) + unused : 4; // Unused / reserved +}; + +// RGB444 stored as 0RGB +struct Rgb444_0RGB { + Colors_gb gb; + Colors_r0 r0; +} [[color(std::format("{:02X}{:02X}{:02X}", expand4(r0.red), expand4(gb.green), expand4(gb.blue)))]]; + +struct Palette16 { Rgb444_0RGB color[PALETTE_COLORS]; }; + +// $C1/$0000 raw 32 KB screen dump +struct SHR_PIC0000 { + Row160 pixels[SHR_ROWS] @ PIC0000_OFF_PIXELS; + ShrSCB scb[SHR_ROWS] @ PIC0000_OFF_SCB; + u8 reserved[56] @ PIC0000_OFF_RESERVED; + Palette16 palettes[PALETTE_COUNT] @ PIC0000_OFF_PALETTES; +}; + +// “Brooks” 3200-color: pixels + 200 per-line palettes (no SCBs) +struct BrooksLinePalette { Rgb444_0RGB color[PALETTE_COLORS]; }; + +struct SHR_PIC0002 { + Row160 pixels[SHR_ROWS] @ 0x0000; + BrooksLinePalette line_palettes[SHR_ROWS] @ SHR_PIXEL_DATA_SIZE; // 0x7D00 +}; + +// ------------------------------ Types: PaintWorks ANI ($C2/$0000) ------------------------------ + +/* Each operation modifies 1 word at an absolute offset in the 32 KB SHR area. + End-of-frame marker: offset == 0x0000 (value is ignored). */ +struct AniOp { + u16 offset [[color("0000AA")]]; // 0x0000..0x7FFE valid; 0x0000 = End-of-Frame + u16 value [[color("00AAAA")]]; // word to store at [offset] + // For convenience in the sidebar: + bool is_eof = (offset == 0x0000); +}; + +// A contiguous animation chunk: length + packed AniOp pairs. +// Most files have exactly one chunk that spans all frames. +struct AniChunk { + u32 chunk_len; // includes this field + + // ops_count = (chunk_len - 4)/4, unless chunk_len == 4 + // in which case: (__file_size - 0x8000 - 12)/4 + u64 ops_count64 = + (chunk_len == 4) + ? ( (__file_size > (0x8000 + 12)) ? ((__file_size - 0x8000 - 12) / 4) : 0 ) + : ( (chunk_len >= 4) ? (u64(chunk_len - 4) / 4) : 0 ); + + u32 ops_count = u32(ops_count64); + + // ops start immediately after chunk_len (offset +4) + AniOp ops[ops_count]; +}; + +// Header located at 0x8000 after the base 32 KB image +struct AniHeader { + u32 anim_data_len [[color("660000")]]; // total bytes of animation data after header + u16 frame_delay_vbl [[color("CC0000")]]; // global per-frame delay in VBLs (NTSC/PAL differ) + u16 flag_unknown; // usually 0x00C0 or 0x00C1 +}; + +// Full PaintWorks animation container +struct ANI_PaintWorks { + // Base frame: a normal uncompressed SHR image + SHR_PIC0000 base @ 0x0000; + + // Global animation header + AniHeader hdr @ ANI_HDR_OFF; + + // One or more chunks, typically exactly one: + AniChunk chunks[ std::math::min( + u32(16), // cap to keep ImHex happy in pathological cases + u32((__file_size - (ANI_HDR_OFF + sizeof(AniHeader))) > 3 ? + 1 + u32((__file_size - (ANI_HDR_OFF + sizeof(AniHeader))) / 0x10000000) : + 1) + ) ] @ (ANI_HDR_OFF + sizeof(AniHeader)); + + // Helpful computed values for inspection: + u64 file_len = __file_size; + u64 expected_anim_end = ANI_HDR_OFF + sizeof(AniHeader) + u64(hdr.anim_data_len); +}; + +// ------------------------------ Dispatcher ------------------------------ + +u64 __file_size = std::mem::size(); + +if (__file_size == PIC0000_FILE_SIZE) { + // Plain SHR dump + SHR_PIC0000 pic0000 @ 0x0000; + +} else if (__file_size == PIC0002_FILE_SIZE) { + // Brooks 3200-color + SHR_PIC0002 pic0002 @ 0x0000; + +} else if (__file_size >= ANI_MIN_TOTAL_SIZE) { + // Heuristic: treat as PaintWorks ANI if there’s room for base+header. + // (Many PW ANI files use ProDOS type $C2/$0000.) + ANI_PaintWorks ani @ 0x0000; + +} else if (__file_size >= SHR_PIXEL_DATA_SIZE) { + // Fallback: show pixels only for odd dumps + Row160 pixels_only[SHR_ROWS] @ 0x0000; +} + +ANI_PaintWorks ani_paintworks_at_0 @ 0; \ No newline at end of file diff --git a/patterns/apple_single_double.hexpat b/patterns/apple_single_double.hexpat new file mode 100644 index 00000000..e1b38b47 --- /dev/null +++ b/patterns/apple_single_double.hexpat @@ -0,0 +1,50 @@ +#pragma author Lexi Mayfield +#pragma description AppleSingle/AppleDouble file format +#pragma endian big +#pragma magic [00 05 16 0?] @ 0x00 + +import type.magic; + +enum EntryID : u32 { + DataFork = 1, + ResourceFork = 2, + RealName = 3, + Comment = 4, + IconBW = 5, + IconColor = 6, + FileDatesInfo = 8, + FinderInfo = 9, + MacFileInfo = 10, + ProDOSFileInfo = 11, + MSDOSFileInfo = 12, + ShortName = 13, + AFPFileInfo = 14, + DirectoryID = 15, +}; + +struct Entry { + EntryID entryID; + u32 offset; + u32 length; + u8 data[length] @ offset [[sealed]]; +}; + +enum FileType : u32 { + AppleSingle = 0x00051600, + AppleDouble = 0x00051607, +}; + +enum Version : u32 { + V1 = 0x00010000, + V2 = 0x00020000, +}; + +struct AppleSingleDouble { + FileType fileType; + Version version; + char homeFileSystem[16]; + u16 numEntries; + Entry entryDescs[numEntries]; +}; + +AppleSingleDouble appleSingleDouble @ 0x00; \ No newline at end of file diff --git a/patterns/chd.hexpat b/patterns/chd.hexpat new file mode 100644 index 00000000..d10ced8f --- /dev/null +++ b/patterns/chd.hexpat @@ -0,0 +1,105 @@ +#pragma author Lexi Mayfield +#pragma description MAME Compressed Hunks of Data +#pragma endian big + +fn CHD_MAKE_TAG(char a, char b, char c, char d) { + return (u32(u8(a)) << 24) | + u32(u8((b)) << 16) | + u32(u8((c)) << 8) | + u32(u8(d)); +}; + +enum CHDv5_CODEC : u32 { + NONE = 0, + ZLIB = CHD_MAKE_TAG('z','l','i','b'), + ZSTD = CHD_MAKE_TAG('z','s','t','d'), + LZMA = CHD_MAKE_TAG('l','z','m','a'), + HUFFMAN = CHD_MAKE_TAG('h','u','f','f'), + FLAC = CHD_MAKE_TAG('f','l','a','c'), + CD_ZLIB = CHD_MAKE_TAG('c','d','z','l'), + CD_ZSTD = CHD_MAKE_TAG('c','d','z','s'), + CD_LZMA = CHD_MAKE_TAG('c','d','l','z'), + CD_FLAC = CHD_MAKE_TAG('c','d','f','l'), + AVHUFF = CHD_MAKE_TAG('a','v','h','u'), +}; + +enum CHDv5_METADATA_TAG : u32 { + CHDMETATAG_WILDCARD = 0, + HARD_DISK_METADATA_TAG = CHD_MAKE_TAG('G','D','D','D'), + HARD_DISK_IDENT_METADATA_TAG = CHD_MAKE_TAG('I','D','N','T'), + HARD_DISK_KEY_METADATA_TAG = CHD_MAKE_TAG('K','E','Y',' '), + PCMCIA_CIS_METADATA_TAG = CHD_MAKE_TAG('C','I','S',' '), + CDROM_OLD_METADATA_TAG = CHD_MAKE_TAG('C','H','C','D'), + CDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C','H','T','R'), + CDROM_TRACK_METADATA2_TAG = CHD_MAKE_TAG('C','H','T','2'), + GDROM_OLD_METADATA_TAG = CHD_MAKE_TAG('C','H','G','T'), + GDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C','H','G','D'), + DVD_METADATA_TAG = CHD_MAKE_TAG('D','V','D',' '), + AV_METADATA_TAG = CHD_MAKE_TAG('A','V','A','V'), + AV_LD_METADATA_TAG = CHD_MAKE_TAG('A','V','L','D'), +}; + +struct CHDv5UncompressedMap { + u32 offset; +}; + +struct CHDv5CompressedMapEntry { + u8 compression; + u24 complength; + u48 offset; + u16 crc; +}; + +struct CHDv5CompressedMap { + u32 length; + u48 datastart; + u16 crc; + u8 lengthbits; + u8 hunkbits; + u8 parentunitbits; + u8 reserved; +}; + +struct CHDv5MetadataEntry { + CHDv5_METADATA_TAG metatag; + u8 flags; + u24 length; + u64 next; + char entry[length]; + + if (next != 0) { + CHDv5MetadataEntry nextMeta @ next; + } +}; + +struct CHDv5 { + CHDv5_CODEC compressors[4]; + u64 logicalbytes; + u64 mapoffset; + u64 metaoffset; + u32 hunkbytes; + u32 unitbytes; + u8 rawsha1[20]; + u8 sha1[20]; + u8 parentsha1[20]; + + if (compressors[0] == CHDv5_CODEC::NONE) { + CHDv5UncompressedMap map @ mapoffset; + } else { + CHDv5CompressedMap map @ mapoffset; + } + + CHDv5MetadataEntry meta @ metaoffset; +}; + +struct CHD { + char tag[8]; + u32 length; + u32 version; + + if (version == 5) { + CHDv5 chd; + } +}; + +CHD chd @ 0x00; \ No newline at end of file diff --git a/patterns/commodore_basic.hexpat b/patterns/commodore_basic.hexpat new file mode 100644 index 00000000..5f4cf029 --- /dev/null +++ b/patterns/commodore_basic.hexpat @@ -0,0 +1,158 @@ +#pragma description Commodore BASIC +#pragma author Stephen Hewitt + +import std.io; +import std.mem; + +bool in_quotes = false; + +le u16 LoadAddress @0 [[color("ff0000")]]; + +fn formatll(u16 offset) { + if (offset&0xff00 == 0) + return "No next line"; + u16 fo = offset-LoadAddress+2; + return std::format("Next line: ${:04X} (offset ${:04X})", offset, fo); +}; + +enum Token : u8 { + END = 0x80, + FOR = 0x81, + NEXT = 0x82, + DATA = 0x83, + INPUT_ = 0x84, + INPUT = 0x85, + DIM = 0x86, + READ = 0x87, + LET = 0x88, + GOTO = 0x89, + RUN = 0x8a, + IF = 0x8b, + RESTORE = 0x8c, + GOSUB = 0x8d, + RETURN = 0x8e, + REM = 0x8f, + STOP = 0x90, + ON = 0x91, + WAIT = 0x92, + LOAD = 0x93, + SAVE = 0x94, + VERIFY = 0x95, + DEF = 0x96, + POKE = 0x97, + PRINT_ = 0x98, + PRINT = 0x99, + CONT = 0x9a, + LIST = 0x9b, + CLR = 0x9c, + CMD = 0x9d, + SYS = 0x9e, + OPEN = 0x9f, + CLOSE = 0xa0, + GET = 0xa1, + NEW = 0xa2, + TAB_ = 0xa3, + TO = 0xa4, + FN = 0xa5, + SPC_ = 0xa6, + THEN = 0xa7, + NOT = 0xa8, + STEP = 0xa9, + PLUS_ = 0xaa, + MINUS_ = 0xab, + TIMES_ = 0xac, + DIVIDE_ = 0xad, + POW_ = 0xae, + AND = 0xaf, + OR = 0xb0, + GT_ = 0xb1, + EQ_ = 0xb2, + LT_ = 0xb3, + SGN = 0xb4, + INT = 0xb5, + ABS = 0xb6, + USR = 0xb7, + FRE = 0xb7, + POS = 0xb9, + SQR = 0xba, + RND = 0xbb, + LOG = 0xbc, + EXP = 0xbd, + COS = 0xbe, + SIN = 0xbf, + TAN = 0xc0, + ATN = 0xc1, + PEEK = 0xc2, + LEN = 0xc3, + STR_ = 0xc4, + VAL = 0xc5, + ASC = 0xc6, + CHR_ = 0xc7, + LEFT_ = 0xc8, + RIGHT_ = 0xc9, + MID_ = 0xca, + PI_ = 0xff +} [[format("formate")]]; + +// Can't seem to put attributes on enum members. Hack around it. +fn formate(Token t) { + match (t) { + (Token::INPUT_): return "INPUT#"; // $84 + (Token::PRINT_): return "PRINT#"; // $98 + (Token::TAB_): return "TAB("; // $a3 + (Token::SPC_): return "SPC("; // $a6 + (Token::PLUS_): return "+"; // $aa + (Token::MINUS_): return "-"; // $ab + (Token::TIMES_): return "*"; // $ac + (Token::DIVIDE_): return "/"; // $ad + //(Token::POW_): return "↑"; // $ae + (Token::GT_): return ">"; // $b1 + (Token::EQ_): return "="; // $b2 + (Token::LT_): return "<"; // $b3 + (Token::STR_): return "STR$"; // $c4 + (Token::CHR_): return "CHR$"; // $c7 + (Token::LEFT_): return "LEFT$"; // $c8 + (Token::RIGHT_): return "RIGHT$"; // $c9 + (Token::MID_): return "MID$"; // $ca + //(Token::PI_): return "π"; // $ff + } + + return t; +}; + +fn NotZero() { + u8 b = std::mem::read_unsigned($, 1); + return b!=0; +}; + +fn IsToken() { + u8 b = std::mem::read_unsigned($, 1); + return b&0x80!=0; +}; + +fn IsPETSCII() { + u8 b = std::mem::read_unsigned($, 1); + if (b == '"') + in_quotes = !in_quotes; + return (b!=0) && (in_quotes || (b&0x80)==0); +}; + +struct LineSegment +{ + Token tokens[while(IsToken())] [[color("a040a0")]]; + char petscii[while(IsPETSCII())] [[color("a0a0a0")]]; +}; + +struct Line +{ + in_quotes = false; + + u16 next [[color("8080ff"), format("formatll")]]; + if (next&0xff00 == 0) + break; + le u16 line_number [[color("00FF00")]]; + LineSegment contents[while(NotZero())]; + u8 eol [[color("00ffff")]]; +}; + +Line Lines[while(!std::mem::eof())] @ 2; \ No newline at end of file diff --git a/patterns/dmc3_hd_mod.hexpat b/patterns/dmc3_hd_mod.hexpat new file mode 100644 index 00000000..9872b781 --- /dev/null +++ b/patterns/dmc3_hd_mod.hexpat @@ -0,0 +1,165 @@ +#pragma description Devil May Cry 3 HD .mod 3D model file +#pragma MIME 3d-model/capcom.dmc3-hd-mod + +// author = haru233, many thanks to AxCut +// ImHex Hex Pattern File for Capcom's Devil May Cry 3 HD .mod files + + +import std.core; + + +struct ModelHeader { + char ID[4]; + float Version; + padding[8]; + u8 objectCount; + u8 boneCount; + u8 numberTextures; + u8; + u32; + u64; + u64 skeletonOffset; + padding[24]; +}; + +struct ObjectInfo { + u8 meshCount; + u8; + u16 numberVertices; + padding[4]; + u64 meshOffset; + u32 flags; + padding[28]; + float X, Y, Z; + float radius; +}; + +struct Positions { + float positions[3]; +}; + + +struct Normals { + float normal[3]; +}; + + +struct UVs { + s16 uv[2]; +}; + +struct BoneIndices { + u8 boneindex[4]; +}; + +struct Weights { + u16 weight[1]; +}; + +struct MeshSCM { + u16 numberVertices; + u16 textureIndex; + padding[12]; + u64 VerticesPositionsOffset; + u64 NormalsPositionsOffset; + u64 UVsPositionsOffset; + + padding[16]; + u64 unknownOffset; + + u64; + padding[8]; + + Positions positions[numberVertices] @VerticesPositionsOffset; + Normals normals[numberVertices] @NormalsPositionsOffset; + UVs uvs[numberVertices] @UVsPositionsOffset; + + +}; + +struct Mesh { + u16 numberVertices; + u16 textureIndex; + padding[12]; + u64 VerticesPositionsOffset; + u64 NormalsPositionsOffset; + u64 UVsPositionsOffset; + + u64 BoneIndicesOffset; + u64 WeightsOffset; + padding[8]; + + u64; + padding[8]; + + Positions positions[numberVertices] @VerticesPositionsOffset; + Normals normals[numberVertices] @NormalsPositionsOffset; + UVs uvs[numberVertices] @UVsPositionsOffset; + + BoneIndices b_index[numberVertices] @BoneIndicesOffset; + Weights weights[numberVertices] @WeightsOffset; + + +}; + + +struct Hierarchy { + u8 hierarchy; +}; + +struct HierarchyOrder { + u8 hierarchyorder; +}; + +struct Unknown { + u8; +}; + +struct Transform { + float x; + float y; + float z; + float length; // sqrt(x*x + y*y + z*z) + padding[16]; +}; + +struct Skeleton{ + u32 hierarchyOffset; + u32 hierarchyOrderOffset; + u32 unknownOffset; + u32 transformsOffset; +}; + + + + + +ModelHeader modelheader @ 0x00; +ObjectInfo objects_info[modelheader.objectCount] @ 0x40; + +u32 objectOffset; + +struct Object { + u64 i = std::core::array_index(); + if (modelheader.ID == "SCM ") { + objectOffset = objects_info[0].meshOffset; + MeshSCM meshscm[objects_info[i].meshCount] @ objects_info[i].meshOffset; + + + } else { + objectOffset = objects_info[0].meshOffset; + Mesh mesh[objects_info[i].meshCount] @ objects_info[i].meshOffset; + } +}; + +Object objects[modelheader.objectCount] @objectOffset; + +Skeleton skeleton @modelheader.skeletonOffset; + +Hierarchy hierarchy[modelheader.boneCount] @(modelheader.skeletonOffset + skeleton.hierarchyOffset); + +HierarchyOrder hierarchyorder[modelheader.boneCount] @(modelheader.skeletonOffset + skeleton.hierarchyOrderOffset); + +Unknown unknown[modelheader.boneCount] @(modelheader.skeletonOffset + skeleton.unknownOffset); + +Transform transform[modelheader.boneCount] @(modelheader.skeletonOffset + skeleton.transformsOffset); diff --git a/patterns/dos.hexpat b/patterns/dos.hexpat new file mode 100644 index 00000000..73cb5d51 --- /dev/null +++ b/patterns/dos.hexpat @@ -0,0 +1,242 @@ +#pragma author Stephen Hewitt +#pragma description MSDOS executable file + +#pragma MIME application/x-dosexec +#pragma MIME application/x-msdownload +#pragma MIME application/x-dosexecapplication/zip +#pragma MIME application/vnd.microsoft.portable-executable + +import type.magic; +import std.io; +import std.mem; +import std.math; +import std.string; + +/* + * A DOS EXE file, at a high level, consists of three regions: + * + * Header + * As it's name suggests. Contains info the loader uses. + * + * Load module + * Contains the program data that is loaded into memory. + * + * Extra data + * Data appended to the file that isn't loaded into memory. + * + * We'll call the combined header and load module the + * "program image". It's what the DOS loader cares about. + */ + + /* + * Wikipedia: The New Executable (NE or NewEXE) is a 16-bit executable + * file format, a successor to the DOS MZ executable format. It was used + * in Windows 1.0–3.x, Windows 9x, multitasking MS-DOS 4.0,[1] OS/2 1.x, + * and the OS/2 subset of Windows NT up to version 5.0 (Windows 2000). + * + * Since it was used in DOS we'll support it. + * + * We'll make it optional since some programs increased + * 'headerSizeInParagraphs' and stashed all kind of stuff there. + */ +bool EnableNEHeaderExt in; + +/* + * DOS file offsets/sizes. DOS uses INT 21h for file I/O. File positions and + * lengths are tracked using 32-bit signed integers. DOS INT 21h functions + * treat the offset as signed, so the highest positive offset is 0x7FFFFFFF. + * Attempting to seek beyond that or read/write beyond that will fail. + * We'll use a u32. + */ +u32 g_loadModule; +u32 g_loadModuleSize; +u32 g_programImageSize; + +fn formatNumber(u32 num, str msg="") { + if (std::string::length(msg)==0) + return std::format("0x{:x} ({})", num, num); + else + return std::format("{} 0x{:x} ({})", msg, num, num); +}; + +fn inLoadModule(u32 off, u32 sz) { + return off>=g_loadModule && off+sz<=g_loadModule+g_loadModuleSize; +}; + +struct Relocation { + u16 offset [[color("9AE630")]]; + u16 segment [[color("FE9A37")]]; +}; + +struct RelocationAnnotated : Relocation { + u32 fileOffset = g_loadModule+offset+segment*16; + if (inLoadModule(fileOffset, 2)) { + u16 __goto__target @ fileOffset [[highlight_hidden]]; + } + else { + str __goto__target = formatNumber(fileOffset, "Not in load module") [[export, highlight_hidden]]; + } +}; + +struct Relocations { + if (parent.dosHeader.relocations>0) { + Relocation __goto__firstReloc @ $ [[highlight_hidden]]; + Relocation __goto__lastReloc @ $+(parent.dosHeader.relocations-1)*sizeof(Relocation) [[highlight_hidden]]; + } + RelocationAnnotated data[parent.dosHeader.relocations] [[inline]]; +}; + +struct DOSHeader { + type::Magic<"MZ"> signature [[hex::spec_name("e_magic")]]; + u16 extraPageSize [[hex::spec_name("e_cblp")]]; + u16 numberOfPages [[hex::spec_name("e_cp")]]; + g_programImageSize = (extraPageSize==0) ? + (numberOfPages*512) : + (numberOfPages-1)*512 + extraPageSize; + str __programImageSize = formatNumber(g_programImageSize) [[export, highlight_hidden]]; + u8 __goto__lastByteInProgramImage @ g_programImageSize-1 [[highlight_hidden]]; + u16 relocations [[name("stubRelocations"), hex::spec_name("e_crlc")]]; + u16 headerSizeInParagraphs [[hex::spec_name("e_cparhdr")]]; + u32 headerSize = headerSizeInParagraphs*16; + g_loadModule = headerSizeInParagraphs*16; + g_loadModuleSize = g_programImageSize - headerSize; + str __headerSize = formatNumber(headerSize) [[export, highlight_hidden]]; + u8 __goto__lastByteInHeader @ headerSize-1 [[highlight_hidden]]; + u16 minimumAllocatedParagraphs [[hex::spec_name("e_minalloc")]]; + u16 maximumAllocatedParagraphs [[hex::spec_name("e_maxalloc")]]; + u16 initialSSValue [[hex::spec_name("e_ss")]]; + u16 initialRelativeSPValue [[hex::spec_name("e_sp")]]; + u16 checksum [[name("stubChecksum"), hex::spec_name("e_csum")]]; + u16 initialRelativeIPValue [[hex::spec_name("e_ip")]]; + u16 initialCSValue [[hex::spec_name("e_cs")]]; + + u32 csAddrFirst = initialCSValue<<4; + u32 csAddrLast = (csAddrFirst+0xffff) & ((1<<20)-1); + + u32 csEndGap = 0; + if (csAddrFirst <= csAddrLast) { + u32 csOffsetFirst = headerSize+csAddrFirst; + u32 csOffsetLast = csOffsetFirst+std::math::min(0x10000, g_loadModuleSize)-1; + } + else { + u32 csOffsetFirst = headerSize; + csEndGap = (1<<20)-csAddrFirst; + u32 csOffsetLast = headerSize+(0x10000-csEndGap-1); + + std::warning("EXE has 'initialCSValue' set such that 20-bit address wraps."); + std::warning(" My guess would be to get the PSP into the CS."); + } + + + /* + * Adding `csEndGap` to the `initialIP` calculation below is required because the + * program is started by transferring execution to CS:IP. If `csEndGap` is non-zero + * CS and the start of the load-module value do not align; there’s some extra data + * the CPU can see before the data in the EXE. What confused me for a bit was why + * it’s not required in the relocation target locations I make. The reason, I think, + * is that when the loader loads the load-module into memory and then proceeds to + * apply the relocations, the offsets are relative to the segment the code is loaded + * in and not the execution environment (the CS register from `initialCSValue`). + */ + u32 initialIP = csOffsetFirst+initialRelativeIPValue-csEndGap; + + if (inLoadModule(initialIP, 1)) + u8 __goto__initiaIP @ initialIP [[highlight_hidden]]; + else + str __goto__initiaIP = formatNumber(initialIP, "Not in load module!") [[export, highlight_hidden]]; + + u32 csSize = csOffsetLast-csOffsetFirst+1; + if (inLoadModule(csOffsetFirst, csSize)) { + std::mem::Bytes __select__InitialCS @ csOffsetFirst [[highlight_hidden]]; + u8 __goto__InitialCS_first @ csOffsetFirst [[highlight_hidden]]; + u8 __goto__InitialCS_last @ csOffsetFirst+csSize-1 [[highlight_hidden]]; + } + else { + str __select__CS = formatNumber(csOffsetFirst, "Not in image!") [[export, highlight_hidden]]; + } + + u16 relocationsTablePointer [[hex::spec_name("e_lfarlc")]]; + u32 sizeofRelocations = relocations*sizeof(Relocation); + if (relocations>0 && relocationsTablePointer+sizeofRelocations __select__relocationsTable + @ relocationsTablePointer [[highlight_hidden]]; + } + else { + str __select__relocationsTable = + "Not in image or zero length" [[export, highlight_hidden]]; + } + u16 overlayNumber [[hex::spec_name("e_ovno")]]; +}; + +struct NEDOSHeaderExt { + u16 reservedWords[4] [[hex::spec_name("e_res")]]; + u16 oemIdentifier [[hex::spec_name("e_oemid")]]; + u16 oemInformation [[hex::spec_name("e_oeminfo")]]; + u16 otherReservedWords[10] [[hex::spec_name("e_res2")]]; + u32 newHeaderPointer [[hex::spec_name("e_lfanew")]]; +}; + +struct NEDOSHeaderExtAnnotated : NEDOSHeaderExt { + if (newHeaderPointer < std::mem::size()) + u8 __goto__newHeader @ newHeaderPointer [[highlight_hidden]]; + else + str __goto__newHeader + = formatNumber(newHeaderPointer, "Not in image!") [[export, highlight_hidden]]; +}; + +/* + * The header of a DOS EXE file consists of three regions. + * + * DOSHeader + * Present in all DOS EXEs. Used by the loader. + * + * NEDOSHeaderExt + * An extension to the header. Optional. + * + * Relocations + * An array of segment relocations to the apply to the load module. Optional. + * + * The header is followed by the load module. There can be gaps between + * DOSHeader (or NEDOSHeaderExt if present) and Relocations, and between the + * Relocations and the load module. It is not uncommon for EXEs to stash candy + * in these gaps. + */ + +struct Header { + DOSHeader dosHeader; + + if (EnableNEHeaderExt) { + if (dosHeader.relocationsTablePointer < $+sizeof(NEDOSHeaderExt)) { + std::warning("NEHeaderExt and Relocations overlap. Disabling NEHeaderExt."); + } + else { + NEDOSHeaderExtAnnotated extHeader; + } + } + + if (dosHeader.relocations > 0) { + if (dosHeader.relocationsTablePointer < $) { + std::warning("Relocation table overlaps previous header members"); + } + if (dosHeader.relocationsTablePointer+dosHeader.relocations*sizeof(Relocation) > g_loadModule) { + std::warning("Relocation table ends past header."); + } + } + + if (dosHeader.relocationsTablePointer > $) { + u8 header_reloc_gap[dosHeader.relocationsTablePointer-$] [[highlight_hidden]]; + } + Relocations relocations; + if (g_loadModule > $) { + u8 reloc_loadModule_gap[g_loadModule-$] [[highlight_hidden]]; + } +}; + +struct LoadModule { + u8 __goto__first @ $ [[highlight_hidden]]; + u8 __goto__last @ $+g_loadModuleSize-1 [[highlight_hidden]]; + u8 data[g_loadModuleSize]; +} [[color("7393B3")]]; + +Header header @0; +LoadModule loadModule @g_loadModule;; \ No newline at end of file diff --git a/patterns/dotnet_binaryformatter.hexpat b/patterns/dotnet_binaryformatter.hexpat new file mode 100644 index 00000000..ea17ecd8 --- /dev/null +++ b/patterns/dotnet_binaryformatter.hexpat @@ -0,0 +1,921 @@ + +/* + References: + .NET BinaryFormatter Specification "MS-NRBF": + https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-nrbf/75b9fe09-be15-475f-85b8-ae7b7558cfe5 + .NET runtime: + https://github.com/dotnet/runtime/blob/v8.0.17/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryParser.cs + .NET Library for Parsing MS-NRBF streams: + https://github.com/bbowyersmyth/BinaryFormatDataStructure +*/ + +#pragma author ODeux +#pragma description .NET BinaryFormatter (System.Runtime.Serialization.Formatters.Binary, obsolete) + +#pragma endian little + +import std.core; +import std.sys; +import std.mem; +import std.ptr; + +fn offsetOf(ref auto value, ref auto value_field){ + return addressof(value_field) - addressof(value); +}; + +struct NullableArrayPtr{ + pointerSize pointerValue [[no_unique_address, hidden]]; + if(pointerValue != 0) + T *data[size]: pointerSize; + else + padding[sizeof(pointerSize)]; +}; + +using _Ptr = std::ptr::NullablePtr; +using _ArrayPtr = NullableArrayPtr; + +fn append_value_to_section(ref auto value, std::mem::Section section){ + u128 old_section_size = std::mem::get_section_size(section); + std::mem::copy_value_to_section(value, section, old_section_size); + return old_section_size; +}; + +fn todo(auto message){ + std::error(std::format("@0x{:08X} TODO: " + message, $)); +}; + +using _Trackers; +std::mem::Section _TrackersSection = std::mem::create_section("_TrackersSection"); +_Trackers _trackers @ 0x0 in _TrackersSection; +bool NeedUpdateTrackers = false; +bool IsUpdatingTrackers = false; + +enum _ObjEnum: u64{ + Empty = 0, + SerializedStreamHeader = 1, + ClassWithId = 2, Object = _ObjEnum::ClassWithId, + SystemClassWithMembers = 3, ObjectWithMap = _ObjEnum::SystemClassWithMembers, + ClassWithMembers = 4, ObjectWithMapAssemId = _ObjEnum::ClassWithMembers, + SystemClassWithMembersAndTypes = 5, ObjectWithMapTyped = _ObjEnum::SystemClassWithMembersAndTypes, + ClassWithMembersAndTypes = 6, ObjectWithMapTypedAssemId = _ObjEnum::ClassWithMembersAndTypes, + BinaryObjectString = 7, ObjectString = _ObjEnum::BinaryObjectString, + BinaryArray = 8, Array = _ObjEnum::BinaryArray, + MemberPrimitiveTyped = 9, + MemberReference = 10, + BinaryLibrary = 11, Assembly = _ObjEnum::BinaryLibrary, + ObjectNullMultiple256 = 12, + ObjectNullMultiple = 13, + ArraySinglePrimitive = 14, + ArraySingleObject = 15, + ArraySingleString = 16, + CrossAppDomainMap = 17, + CrossAppDomainString = 18, + CrossAppDomainAssembly = 19, + MethodCall = 20, + MethodReturn = 21 +}; + +fn zeroedCurrObjTrackers(){ + _trackers.currentObj.TypeName.pointerValue = 0; + _trackers.currentObj.AssemblyName.pointerValue = 0; + _trackers.currentObj.objEnum = _ObjEnum::Empty; + _trackers.currentObj.RawPtr = 0; +}; + +fn copyCurrObjAtIdTrackers(auto id){ + NeedUpdateTrackers = true; + _trackers.objs[id].TypeName.pointerValue = _trackers.currentObj.TypeName.pointerValue; + _trackers.objs[id].AssemblyName.pointerValue = _trackers.currentObj.AssemblyName.pointerValue; + /* ! Enum does not get copied if we don't use a cast here for some reason ! */ + _trackers.objs[id].objEnum = u64(_trackers.currentObj.objEnum); + _trackers.objs[id].RawPtr = _trackers.currentObj.RawPtr; +}; + +using BitfieldOrder = std::core::BitfieldOrder; + +using TimeSpan = s64; + +enum PrimitiveTypeEnum: u8{ + Invalid = 0, + Boolean = 1, + Byte = 2, + Char = 3, + Currency = 4, /* Not Used in this protocol */ + Decimal = 5, + Double = 6, + Int16 = 7, + Int32 = 8, + Int64 = 9, + SByte = 10, + Single = 11, + TimeSpan = 12, + DateTime = 13, + UInt16 = 14, + UInt32 = 15, + UInt64 = 16, + Null = 17, + String = 18 +}; + +struct PrimitiveTypeEnumT{ + PrimitiveTypeEnum primitiveTypeEnumT = _primitiveTypeEnumT; + PrimitiveTypeEnum primitiveTypeEnum; + if(_primitiveTypeEnumT > 0) + std::assert(primitiveTypeEnum == primitiveTypeEnumT, std::format("Expected {} but got {}", primitiveTypeEnumT, primitiveTypeEnum)); +}; + +enum BinaryTypeEnum: u8{ + Primitive = 0, + String = 1, + Object = 2, + SystemClass = 3, ObjectUrt = BinaryTypeEnum::SystemClass, + Class = 4, ObjectUser = BinaryTypeEnum::Class, + ObjectArray = 5, + StringArray = 6, + PrimitiveArray = 7 +}; + +enum BinaryArrayTypeEnum: u8{ + Single = 0, + Jagged = 1, + Rectangular = 2, + SingleOffset = 3, + JaggedOffset = 4, + RectangularOffset = 5 +}; + +enum RecordTypeEnum: u8{ + SerializedStreamHeader = 0, + ClassWithId = 1, Object = RecordTypeEnum::ClassWithId, + SystemClassWithMembers = 2, ObjectWithMap = RecordTypeEnum::SystemClassWithMembers, + ClassWithMembers = 3, ObjectWithMapAssemId = RecordTypeEnum::ClassWithMembers, + SystemClassWithMembersAndTypes = 4, ObjectWithMapTyped = RecordTypeEnum::SystemClassWithMembersAndTypes, + ClassWithMembersAndTypes = 5, ObjectWithMapTypedAssemId = RecordTypeEnum::ClassWithMembersAndTypes, + BinaryObjectString = 6, ObjectString = RecordTypeEnum::BinaryObjectString, + BinaryArray = 7, Array = RecordTypeEnum::BinaryArray, + MemberPrimitiveTyped = 8, + MemberReference = 9, + ObjectNull = 10, + MessageEnd = 11, + BinaryLibrary = 12, Assembly = RecordTypeEnum::BinaryLibrary, + ObjectNullMultiple256 = 13, + ObjectNullMultiple = 14, + ArraySinglePrimitive = 15, + ArraySingleObject = 16, + ArraySingleString = 17, + CrossAppDomainMap = 18, + CrossAppDomainString = 19, + CrossAppDomainAssembly = 20, + MethodCall = 21, + MethodReturn = 22 +}; +using BinaryHeaderEnum = RecordTypeEnum; + +struct RecordTypeEnumT{ + RecordTypeEnum recordTypeEnumT = _recordTypeEnumT; + RecordTypeEnum recordTypeEnum; + if(_recordTypeEnumT > 0) + std::assert(recordTypeEnum == recordTypeEnumT, std::format("Expected {} but got {}", recordTypeEnumT, recordTypeEnum)); +}; + +bitfield MessageFlags{ + bool NoArgs: 1; /* Arg Category */ + bool ArgsInline: 1; /* Arg Category */ + bool ArgsIsArray: 1; /* Arg Category */ + bool ArgsInArray: 1; /* Arg Category */ + bool NoContext: 1; /* Context Category */ + bool ContextInline: 1; /* Context Category */ + bool ContextInArray: 1; /* Context Category */ + bool MethodSignatureInArray: 1; /* Signature Category */ + bool PropertiesInArray: 1; /* Property Category */ + bool NoReturnValue: 1; /* Return Category */ + bool ReturnValueVoid: 1; /* Return Category */ + bool ReturnValueInline: 1; /* Return Category */ + bool ReturnValueInArray: 1; /* Return Category */ + bool ExceptionInArray: 1; /* Exception Category */ + bool GenericMethod: 1; /* Generic Category */ + unsigned unused: 17; +} [[bitfield_order(BitfieldOrder::LeastToMostSignificant, 32)]]; + +fn validate_MessageFlags(MessageFlags flags){ + u8 arg_cnt = flags.NoArgs + flags.ArgsInline + flags.ArgsIsArray + flags.ArgsInArray; + u8 ctx_cnt = flags.NoContext + flags.ContextInline + flags.ContextInArray; + u8 sig_cnt = flags.MethodSignatureInArray; + u8 ret_cnt = flags.NoReturnValue + flags.ReturnValueVoid + flags.ReturnValueInline + flags.ReturnValueInArray; + u8 excep_cnt = flags.ExceptionInArray; + u8 prop_cnt = flags.PropertiesInArray; + u8 gen_cnt = flags.GenericMethod; + if(arg_cnt > 1 || ctx_cnt > 1 || sig_cnt > 1 || ret_cnt > 1 || excep_cnt > 1 || prop_cnt > 1 || gen_cnt > 1) + return -1; + if(arg_cnt != 0 && excep_cnt != 0) return -1; + if(ret_cnt != 0 && excep_cnt != 0) return -1; + if(ret_cnt != 0 && sig_cnt != 0) return -1; + if(excep_cnt != 0 && sig_cnt != 0) return -1; + return 1; +}; + +enum DateTimeKind: u8{ + NOT_SPECIFIED = 0, + UTC = 1, + Local = 2 +}; + +bitfield DateTime{ + s64 Ticks: 62; + DateTimeKind kind: 2; +} [[bitfield_order(BitfieldOrder::LeastToMostSignificant, 64)]]; + +struct vLength{ + /* + Can't use that, it breaks when struct get re-parsed in _TrackersSection + u8 data[while(std::mem::read_unsigned($, 1) & 0x80)]; + u8 last; + */ + u64 bytes [[no_unique_address, hidden]]; + u8 cnt = 0; + if(bytes & 0x80){ + if(bytes & 0x8000){ + if(bytes & 0x800000){ + if(bytes & 0x80000000){ + if(bytes & 0x8000000000){ + /* exceeding vLength 5 bytes, caller should crash */ + cnt = 5; + }else cnt = 4; + }else cnt = 3; + }else cnt = 2; + }else cnt = 1; + }else cnt = 0; + u8 data[cnt]; + u8 last; +} [[sealed, transform("LPS_Length_decode"), format("LPS_Length_decode")]]; + +fn LPS_Length_decode(auto Length){ + u64 length = 0; + u8 i = 0; + for(i = 0, i < sizeof(Length.data), i += 1) + length |= u64(Length.data[i] & 0x7F) << i * 7; + length |= u64(Length.last) << i * 7; + return length; +}; + +struct LengthPrefixedString{ + vLength Length; + std::assert(sizeof(Length) <= 5, "LengthPrefixedString.Length must be at most 5 bytes long"); + char String[Length]; +}; + +using Decimal = LengthPrefixedString; + +struct ClassTypeInfo{ + LengthPrefixedString TypeName; + s32 LibraryId; +}; + +struct ValueWithCode{ + PrimitiveTypeEnum PrimitiveType; + match(PrimitiveType){ + (PrimitiveTypeEnum::Boolean): bool Value; + (PrimitiveTypeEnum::Byte): u8 Value; + (PrimitiveTypeEnum::Char): char Value; + (PrimitiveTypeEnum::Currency): std::error("Primitive currency not used in this protocol"); + (PrimitiveTypeEnum::Decimal): Decimal Value; + (PrimitiveTypeEnum::Double): double Value; + (PrimitiveTypeEnum::Int16): s16 Value; + (PrimitiveTypeEnum::Int32): s32 Value; + (PrimitiveTypeEnum::Int64): s64 Value; + (PrimitiveTypeEnum::SByte): s8 Value; + (PrimitiveTypeEnum::Single): float Value; + (PrimitiveTypeEnum::TimeSpan): TimeSpan Value; + (PrimitiveTypeEnum::DateTime): DateTime Value; + (PrimitiveTypeEnum::UInt16): u16 Value; + (PrimitiveTypeEnum::UInt32): u32 Value; + (PrimitiveTypeEnum::UInt64): u64 Value; + (PrimitiveTypeEnum::Null): {} + (PrimitiveTypeEnum::String): LengthPrefixedString Value; + (_): std::error(std::format("Unexpected {}", PrimitiveType)); + } +}; + +struct StringValueWithCode: PrimitiveTypeEnumT{ + LengthPrefixedString StringValue; +}; + +struct ArrayOfValueWithCode{ + s32 Length; + ValueWithCode ListOfValueWithCode[Length]; +}; + +struct ArrayInfo{ + s32 ObjectId; + s32 Length; +}; + +struct ClassInfo{ + s32 ObjectId; + LengthPrefixedString Name; + s32 MemberCount; + LengthPrefixedString MemberNames[MemberCount]; +}; + +struct AdditionalInfo{ + BinaryTypeEnum binaryTypeEnum = _binaryTypeEnum; + match(binaryTypeEnum){ + (BinaryTypeEnum::SystemClass): /* ObjectUrt */ + LengthPrefixedString String; + (BinaryTypeEnum::Class): /* ObjectUser */ + ClassTypeInfo classTypeInfo; + (BinaryTypeEnum::Primitive | BinaryTypeEnum::PrimitiveArray): { + PrimitiveTypeEnum primitiveType; + std::assert(primitiveType != PrimitiveTypeEnum::Null && + primitiveType != PrimitiveTypeEnum::String, "Must not be Null or String"); + } + (BinaryTypeEnum::String | BinaryTypeEnum::Object | + BinaryTypeEnum::ObjectArray | BinaryTypeEnum::StringArray): + {/* not using continue here, need to keep array index matching */} + (_): std::error(std::format("Unrecognized {}", binaryTypeEnum)); + } +}; + +struct MemberTypeInfo{ + BinaryTypeEnum binaryTypeEnums[MemberCount]; + AdditionalInfo additionalInfo[MemberCount]; +}; + +struct UntypedMember{ + str className = _className; + u64 MemberCount = classInfo.MemberCount; + if(className == "System.Guid" && MemberCount == 11){ + match(std::core::array_index()){ + (0): s32 _a; + (1): s16 _b; + (2): s16 _c; + (3): u8 _d; + (4): u8 _e; + (5): u8 _f; + (6): u8 _g; + (7): u8 _h; + (8): u8 _i; + (9): u8 _j; + (10): u8 _k; + (_): std::error("unreachable"); + } + }else if(MemberCount == 1 && classInfo.MemberNames[0].String == "value__"){ + str Name = classInfo.MemberNames[0].String; + s32 member [name(Name)]; + }else + std::error(std::format("Unsupported untyped member: {}", className)); +}; + +using Record; + +struct Members{ + u64 i = std::core::array_index(); + str Name = _Name; + BinaryTypeEnum binaryTypeEnum = memberTypeInfo.binaryTypeEnums[i]; + if(binaryTypeEnum == BinaryTypeEnum::Primitive){ + PrimitiveTypeEnum primitiveTypeEnum = memberTypeInfo.additionalInfo[i].primitiveType; + match(primitiveTypeEnum){ + (PrimitiveTypeEnum::Boolean): bool Value [[name(Name)]]; + (PrimitiveTypeEnum::Byte): u8 Value [[name(Name)]]; + (PrimitiveTypeEnum::Char): char Value [[name(Name)]]; + (PrimitiveTypeEnum::Currency): std::error("Primitive currency not used in this protocol"); + (PrimitiveTypeEnum::Decimal): Decimal Value [[name(Name)]]; + (PrimitiveTypeEnum::Double): double Value [[name(Name)]]; + (PrimitiveTypeEnum::Int16): s16 Value [[name(Name)]]; + (PrimitiveTypeEnum::Int32): s32 Value [[name(Name)]]; + (PrimitiveTypeEnum::Int64): s64 Value [[name(Name)]]; + (PrimitiveTypeEnum::SByte): s8 Value [[name(Name)]]; + (PrimitiveTypeEnum::Single): float Value [[name(Name)]]; + (PrimitiveTypeEnum::TimeSpan): TimeSpan Value [[name(Name)]]; + (PrimitiveTypeEnum::DateTime): DateTime Value [[name(Name)]]; + (PrimitiveTypeEnum::UInt16): u16 Value [[name(Name)]]; + (PrimitiveTypeEnum::UInt32): u32 Value [[name(Name)]]; + (PrimitiveTypeEnum::UInt64): u64 Value [[name(Name)]]; + (PrimitiveTypeEnum::Null): {} + (PrimitiveTypeEnum::String): LengthPrefixedString Value [[name(Name)]]; + (_): std::error(std::format("Unexpected {}", primitiveTypeEnum)); + } + }else{ + Record record [[name(Name)]]; + } +}; + +struct ClassWithMembersAndTypes: RecordTypeEnumT{ + ClassInfo classInfo; + MemberTypeInfo memberTypeInfo; + s32 LibraryId; + Members members[classInfo.MemberCount]; +}; + +struct ClassWithMembers: RecordTypeEnumT{ + ClassInfo classInfo; + s32 LibraryId; + UntypedMember members[classInfo.MemberCount]; +}; + +struct SystemClassWithMembersAndTypes: RecordTypeEnumT{ + ClassInfo classInfo; + MemberTypeInfo memberTypeInfo; + Members members[classInfo.MemberCount]; +}; + +struct SystemClassWithMembers: RecordTypeEnumT{ + ClassInfo classInfo; + UntypedMember members[classInfo.MemberCount]; +}; + +struct ClassWithId: RecordTypeEnumT{ + s32 ObjectId; + s32 MetadataId; + if(!IsUpdatingTrackers && NeedUpdateTrackers){ + IsUpdatingTrackers = true; + _Trackers _trackers @ 0x0 in _TrackersSection; + IsUpdatingTrackers = false; + NeedUpdateTrackers = false; + } + match(_trackers.objs[MetadataId].objEnum){ + (_ObjEnum::ClassWithMembersAndTypes): { + u32 MemberCount = _trackers.objs[MetadataId].classWithMembersAndTypes.data.classInfo.MemberCount; + Members<_trackers.objs[MetadataId].classWithMembersAndTypes.data.classInfo.MemberNames[std::core::array_index()].String, _trackers.objs[MetadataId].classWithMembersAndTypes.data.memberTypeInfo> members[MemberCount]; + } + (_ObjEnum::SystemClassWithMembersAndTypes): { + u32 MemberCount = _trackers.objs[MetadataId].systemClassWithMemberAndTypes.data.classInfo.MemberCount; + Members<_trackers.objs[MetadataId].systemClassWithMemberAndTypes.data.classInfo.MemberNames[std::core::array_index()].String, _trackers.objs[MetadataId].systemClassWithMemberAndTypes.data.memberTypeInfo> members[MemberCount]; + } + (_ObjEnum::SystemClassWithMembers): { + str className = _trackers.objs[MetadataId].TypeName.data.String; + u32 MemberCount = _trackers.objs[MetadataId].systemClassWithMembers.data.classInfo.MemberCount; + UntypedMember members[MemberCount]; + } + (_ObjEnum::ClassWithMembers): { + str className = _trackers.objs[MetadataId].TypeName.data.String; + u32 MemberCount = _trackers.objs[MetadataId].classWithMembers.data.classInfo.MemberCount; + UntypedMember members[MemberCount]; + } + (_): std::error(std::format("Unexpected {}", _trackers.objs[MetadataId].objEnum)); + } +}; + +struct BinaryArray: RecordTypeEnumT{ + s32 ObjectId; + BinaryArrayTypeEnum binaryArrayTypeEnum; + s32 Rank; + s32 Length[Rank]; + if(binaryArrayTypeEnum == BinaryArrayTypeEnum::SingleOffset || + binaryArrayTypeEnum == BinaryArrayTypeEnum::JaggedOffset || + binaryArrayTypeEnum == BinaryArrayTypeEnum::RectangularOffset) + s32 LowerBounds[Rank]; + BinaryTypeEnum TypeEnum; + AdditionalInfo additionalInfo; +}; + +struct ArraySinglePrimitive: RecordTypeEnumT{ + ArrayInfo arrayInfo; + PrimitiveTypeEnum primitiveTypeEnum; + std::assert(primitiveTypeEnum != PrimitiveTypeEnum::Null && + primitiveTypeEnum != PrimitiveTypeEnum::String, "Can't be one of those"); + match(primitiveTypeEnum){ + (PrimitiveTypeEnum::Boolean): bool Values[arrayInfo.Length]; + (PrimitiveTypeEnum::Byte): u8 Value[arrayInfo.Length]; + (PrimitiveTypeEnum::Char): char Value[arrayInfo.Length]; + (PrimitiveTypeEnum::Currency): std::error("Primitive currency not used in this protocol"); + (PrimitiveTypeEnum::Decimal): Decimal Value[arrayInfo.Length]; + (PrimitiveTypeEnum::Double): double Value[arrayInfo.Length]; + (PrimitiveTypeEnum::Int16): s16 Value[arrayInfo.Length]; + (PrimitiveTypeEnum::Int32): s32 Value[arrayInfo.Length]; + (PrimitiveTypeEnum::Int64): s64 Value[arrayInfo.Length]; + (PrimitiveTypeEnum::SByte): s8 Value[arrayInfo.Length]; + (PrimitiveTypeEnum::Single): float Value[arrayInfo.Length]; + (PrimitiveTypeEnum::TimeSpan): TimeSpan Value[arrayInfo.Length]; + (PrimitiveTypeEnum::DateTime): DateTime Value[arrayInfo.Length]; + (PrimitiveTypeEnum::UInt16): u16 Value[arrayInfo.Length]; + (PrimitiveTypeEnum::UInt32): u32 Value[arrayInfo.Length]; + (PrimitiveTypeEnum::UInt64): u64 Value[arrayInfo.Length]; + (PrimitiveTypeEnum::Null): {} + (PrimitiveTypeEnum::String): LengthPrefixedString Value[arrayInfo.Length]; + (_): std::error(std::format("Unexpected {}", primitiveTypeEnum)); + } +}; + +u64 ArraySinglElementSkipCount = 0; +struct ArraySingleElement{ + RecordTypeEnum recordTypeEnum = _recordTypeEnum; + if(ArraySinglElementSkipCount > 0) + ArraySinglElementSkipCount -= 1; + else{ + if(recordTypeEnum == RecordTypeEnum::ArraySingleString){ + Record record [[inline]]; + match(record.recordTypeEnum){ + (RecordTypeEnum::ObjectNullMultiple): + ArraySinglElementSkipCount = record.objectNullMultiple.NullCount; + (RecordTypeEnum::ObjectNullMultiple256): + ArraySinglElementSkipCount = record.objectNullMultiple256.NullCount; + (_): {} + } + }else{ + Record record [[inline]]; + if(record.recordTypeEnum == RecordTypeEnum::BinaryLibrary){ + Record record2 [[inline]]; + match(record2.recordTypeEnum){ + (RecordTypeEnum::ObjectNullMultiple): + ArraySinglElementSkipCount = record2.objectNullMultiple.NullCount; + (RecordTypeEnum::ObjectNullMultiple256): + ArraySinglElementSkipCount = record2.objectNullMultiple256.NullCount; + (_): {} + } + }else{ + match(record.recordTypeEnum){ + (RecordTypeEnum::ObjectNullMultiple): + ArraySinglElementSkipCount = record.objectNullMultiple.NullCount; + (RecordTypeEnum::ObjectNullMultiple256): + ArraySinglElementSkipCount = record.objectNullMultiple256.NullCount; + (_): {} + } + } + } + } +}; + +struct ArraySingleObject: RecordTypeEnumT{ + ArrayInfo arrayInfo; + ArraySinglElementSkipCount = 0; + ArraySingleElement records[arrayInfo.Length]; +}; + +struct ArraySingleString: RecordTypeEnumT{ + ArrayInfo arrayInfo; + ArraySinglElementSkipCount = 0; + ArraySingleElement records[arrayInfo.Length]; +}; + +struct MethodReturnCallArray{ + if(parent.MessageEnum.ReturnValueInArray) + ArraySingleObject ReturnValue; + if(parent.MessageEnum.ArgsInArray) + ArraySingleObject OutputArguments; + if(parent.MessageEnum.ExceptionInArray) + ArraySingleObject Exception; + if(parent.MessageEnum.ContextInArray) + ArraySingleObject CallContext; + if(parent.MessageEnum.PropertiesInArray) + ArraySingleObject MessageProperties; +}; + +struct MethodCallArray{ + if(parent.MessageEnum.ArgsInArray) + ArraySingleObject InputArguments; + if(parent.MessageEnum.GenericMethod) + ArraySingleObject GenericTypeArguments; + if(parent.MessageEnum.MethodSignatureInArray) + ArraySingleObject MethodSignature; + if(parent.MessageEnum.ContextInArray) + ArraySingleObject CallContext; + if(parent.MessageEnum.PropertiesInArray) + ArraySingleObject MessageProperties; +}; + +struct BinaryMethodReturn: RecordTypeEnumT{ + MessageFlags MessageEnum; + std::assert(validate_MessageFlags(MessageEnum) >= 0, "Validation Failed"); + std::assert(!MessageEnum.MethodSignatureInArray && !MessageEnum.GenericMethod, "Can't be one of those"); + if(MessageEnum.ReturnValueInline) + ValueWithCode ReturnValue; + if(MessageEnum.ContextInline) + StringValueWithCode CallContext; + if(MessageEnum.ArgsInline) + ArrayOfValueWithCode Args; + MethodReturnCallArray ReturnCallArray; +}; + +struct BinaryMethodCall: RecordTypeEnumT{ + MessageFlags MessageEnum; + std::assert(validate_MessageFlags(MessageEnum) >= 0, "Validation Failed"); + std::assert(!MessageEnum.NoReturnValue && !MessageEnum.ReturnValueVoid && + !MessageEnum.ReturnValueInline && !MessageEnum.ReturnValueInArray && + !MessageEnum.ExceptionInArray, "Can't be one of those"); + StringValueWithCode MethodName; + StringValueWithCode TypeName; + if(MessageEnum.ContextInline) + StringValueWithCode CallContext; + if(MessageEnum.ArgsInline) + ArrayOfValueWithCode Args; + MethodCallArray CallArray; +}; + +struct MemberPrimitiveTyped: RecordTypeEnumT{ + PrimitiveTypeEnum primitiveTypeEnum; + std::assert(primitiveTypeEnum != PrimitiveTypeEnum::Null && + primitiveTypeEnum != PrimitiveTypeEnum::String, "Can't be one of those"); + match(primitiveTypeEnum){ + (PrimitiveTypeEnum::Boolean): bool Value; + (PrimitiveTypeEnum::Byte): u8 Value; + (PrimitiveTypeEnum::Char): char Value; + (PrimitiveTypeEnum::Currency): std::error("Primitive currency not used in this protocol"); + (PrimitiveTypeEnum::Decimal): Decimal Value; + (PrimitiveTypeEnum::Double): double Value; + (PrimitiveTypeEnum::Int16): s16 Value; + (PrimitiveTypeEnum::Int32): s32 Value; + (PrimitiveTypeEnum::Int64): s64 Value; + (PrimitiveTypeEnum::SByte): s8 Value; + (PrimitiveTypeEnum::Single): float Value; + (PrimitiveTypeEnum::TimeSpan): TimeSpan Value; + (PrimitiveTypeEnum::DateTime): DateTime Value; + (PrimitiveTypeEnum::UInt16): u16 Value; + (PrimitiveTypeEnum::UInt32): u32 Value; + (PrimitiveTypeEnum::UInt64): u64 Value; + (_): std::error(std::format("Unexpected {}", primitiveTypeEnum)); + } +}; + +struct MemberPrimitiveUnTyped{ + match(PrimitiveType){ + (PrimitiveTypeEnum::Boolean): bool Value; + (PrimitiveTypeEnum::Byte): u8 Value; + (PrimitiveTypeEnum::Char): char Value; + (PrimitiveTypeEnum::Currency): std::error("Primitive currency not used in this protocol"); + (PrimitiveTypeEnum::Decimal): Decimal Value; + (PrimitiveTypeEnum::Double): double Value; + (PrimitiveTypeEnum::Int16): s16 Value; + (PrimitiveTypeEnum::Int32): s32 Value; + (PrimitiveTypeEnum::Int64): s64 Value; + (PrimitiveTypeEnum::SByte): s8 Value; + (PrimitiveTypeEnum::Single): float Value; + (PrimitiveTypeEnum::TimeSpan): TimeSpan Value; + (PrimitiveTypeEnum::DateTime): DateTime Value; + (PrimitiveTypeEnum::UInt16): u16 Value; + (PrimitiveTypeEnum::UInt32): u32 Value; + (PrimitiveTypeEnum::UInt64): u64 Value; + (PrimitiveTypeEnum::Null): {} + (PrimitiveTypeEnum::String):std::error("Can't be String"); + (_): std::error(std::format("Unexpected {}", PrimitiveType)); + } +}; + +struct MemberReference: RecordTypeEnumT{ + s32 IdRef; +}; + +struct ObjectNull: RecordTypeEnumT{ +}; + +struct ObjectNullMultiple: RecordTypeEnumT{ + s32 NullCount; +}; + +struct ObjectNullMultiple256: RecordTypeEnumT{ + u8 NullCount; +}; + +struct BinaryObjectString: RecordTypeEnumT{ + s32 ObjectId; + LengthPrefixedString Value; +}; + +struct SerializationHeaderRecord: RecordTypeEnumT{ + s32 RootId; + s32 HeaderId; + s32 MajorVersion; + std::assert_warn(MajorVersion == 1, "MajorVersion should be 1"); + s32 MinorVersion; + std::assert_warn(MinorVersion == 0, "MinorVersion should be 0"); +}; + +struct BinaryLibrary: RecordTypeEnumT{ + s32 LibraryId; + LengthPrefixedString LibraryName; +}; + +struct MessageEnd: RecordTypeEnumT{ +}; + +struct CrossAppDomainMap: RecordTypeEnumT{ + s32 crossAppDomainArrayIndex; +}; + +struct CrossAppDomainString: RecordTypeEnumT{ + s32 ObjectId; + s32 Value; +}; + +struct CrossAppDomainAssembly: RecordTypeEnumT{ + s32 LibraryId; + s32 LibraryIndex; +}; + +struct Record{ + RecordTypeEnum recordTypeEnum [[no_unique_address, hidden]]; + match(recordTypeEnum){ + (RecordTypeEnum::SerializedStreamHeader): + std::error("SerializationStreamHeader can only appear at the top of the document"); + (RecordTypeEnum::ClassWithId): { /* RecordTypeEnum::Object */ + ClassWithId classWithId; + if(!IsUpdatingTrackers){ + zeroedCurrObjTrackers(); + _trackers.currentObj.objEnum = _ObjEnum::ClassWithId; + _trackers.currentObj.RawPtr = append_value_to_section(classWithId, _TrackersSection); + _trackers.currentObj.TypeName.pointerValue = _trackers.objs[classWithId.MetadataId].TypeName.pointerValue; + _trackers.currentObj.AssemblyName.pointerValue =_trackers.objs[classWithId.MetadataId].AssemblyName.pointerValue ; + if(classWithId.ObjectId != 0) + copyCurrObjAtIdTrackers(classWithId.ObjectId); + } + } + (RecordTypeEnum::SystemClassWithMembers): { /* RecordTypeEnum::ObjectWithMap */ + SystemClassWithMembers systemClassWithMembers; + if(!IsUpdatingTrackers){ + zeroedCurrObjTrackers(); + _trackers.currentObj.objEnum = _ObjEnum::SystemClassWithMembers; + _trackers.currentObj.RawPtr = append_value_to_section(systemClassWithMembers, _TrackersSection); + _trackers.currentObj.TypeName.pointerValue = _trackers.currentObj.RawPtr + offsetOf(systemClassWithMembers, systemClassWithMembers.classInfo.Name); + if(systemClassWithMembers.classInfo.ObjectId != 0) + copyCurrObjAtIdTrackers(systemClassWithMembers.classInfo.ObjectId); + } + } + (RecordTypeEnum::ClassWithMembers): { /* RecordTypeEnum::ObjectWithMapAssemId */ + ClassWithMembers classWithMembers; + if(!IsUpdatingTrackers){ + zeroedCurrObjTrackers(); + _trackers.currentObj.objEnum = _ObjEnum::ClassWithMembers; + _trackers.currentObj.RawPtr = append_value_to_section(classWithMembers, _TrackersSection); + _trackers.currentObj.TypeName.pointerValue = _trackers.currentObj.RawPtr + offsetOf(classWithMembers, classWithMembers.classInfo.Name); + _trackers.currentObj.AssemblyName.pointerValue = _trackers.libs[classWithMembers.LibraryId].pointerValue; + if(classWithMembers.classInfo.ObjectId != 0) + copyCurrObjAtIdTrackers(classWithMembers.classInfo.ObjectId); + } + } + (RecordTypeEnum::SystemClassWithMembersAndTypes): { /* RecordTypeEnum::ObjectWithMapTyped */ + SystemClassWithMembersAndTypes systemClassWithMembersAndTypes; + if(!IsUpdatingTrackers){ + zeroedCurrObjTrackers(); + _trackers.currentObj.objEnum = _ObjEnum::SystemClassWithMembersAndTypes; + _trackers.currentObj.RawPtr = append_value_to_section(systemClassWithMembersAndTypes, _TrackersSection); + _trackers.currentObj.TypeName.pointerValue = _trackers.currentObj.RawPtr + offsetOf(systemClassWithMembersAndTypes, systemClassWithMembersAndTypes.classInfo.Name); + if(systemClassWithMembersAndTypes.classInfo.ObjectId != 0) + copyCurrObjAtIdTrackers(systemClassWithMembersAndTypes.classInfo.ObjectId); + } + } + (RecordTypeEnum::ClassWithMembersAndTypes): { /* RecordTypeEnum::ObjectWithMapTypedAssemId */ + ClassWithMembersAndTypes classWithMembersAndTypes; + if(!IsUpdatingTrackers){ + zeroedCurrObjTrackers(); + _trackers.currentObj.objEnum = _ObjEnum::ClassWithMembersAndTypes; + _trackers.currentObj.RawPtr = append_value_to_section(classWithMembersAndTypes, _TrackersSection); + _trackers.currentObj.TypeName.pointerValue = _trackers.currentObj.RawPtr + offsetOf(classWithMembersAndTypes, classWithMembersAndTypes.classInfo.Name); + _trackers.currentObj.AssemblyName.pointerValue = _trackers.libs[classWithMembersAndTypes.LibraryId].pointerValue; + if(classWithMembersAndTypes.classInfo.ObjectId != 0) + copyCurrObjAtIdTrackers(classWithMembersAndTypes.classInfo.ObjectId); + } + } + (RecordTypeEnum::BinaryObjectString): { /* RecordTypeEnum::ObjectString */ + BinaryObjectString binaryObjectString; + if(!IsUpdatingTrackers){ + zeroedCurrObjTrackers(); + _trackers.currentObj.objEnum = _ObjEnum::BinaryObjectString; + _trackers.currentObj.RawPtr = append_value_to_section(binaryObjectString, _TrackersSection); + if(binaryObjectString.ObjectId != 0) + copyCurrObjAtIdTrackers(binaryObjectString.ObjectId); + } + } + (RecordTypeEnum::BinaryArray): { /* RecordTypeEnum::Array */ + BinaryArray binaryArray; + if(!IsUpdatingTrackers){ + zeroedCurrObjTrackers(); + _trackers.currentObj.objEnum = _ObjEnum::BinaryArray; + _trackers.currentObj.RawPtr = append_value_to_section(binaryArray, _TrackersSection); + if(binaryArray.ObjectId != 0) + copyCurrObjAtIdTrackers(binaryArray.ObjectId); + } + } + (RecordTypeEnum::MemberPrimitiveTyped): { + MemberPrimitiveTyped memberPrimitiveTyped; + } + (RecordTypeEnum::MemberReference): { + MemberReference memberReference; + } + (RecordTypeEnum::ObjectNull): {} + (RecordTypeEnum::MessageEnd): break; + (RecordTypeEnum::BinaryLibrary): { /* RecordTypeEnum::Assembly */ + BinaryLibrary library; + if(!IsUpdatingTrackers) + _trackers.libs[library.LibraryId].pointerValue = append_value_to_section(library.LibraryName, _TrackersSection); + } + (RecordTypeEnum::ObjectNullMultiple256): { + ObjectNullMultiple256 objectNullMultiple256; + } + (RecordTypeEnum::ObjectNullMultiple): { + ObjectNullMultiple objectNullMultiple; + } + (RecordTypeEnum::ArraySinglePrimitive): { + ArraySinglePrimitive arraySinglePrimitive; + if(!IsUpdatingTrackers){ + zeroedCurrObjTrackers(); + _trackers.currentObj.objEnum = _ObjEnum::ArraySinglePrimitive; + _trackers.currentObj.RawPtr = append_value_to_section(arraySinglePrimitive, _TrackersSection); + if(arraySinglePrimitive.arrayInfo.ObjectId != 0) + copyCurrObjAtIdTrackers(arraySinglePrimitive.arrayInfo.ObjectId); + } + } + (RecordTypeEnum::ArraySingleObject): { + ArraySingleObject arraySingleObject; + } + (RecordTypeEnum::ArraySingleString): { + ArraySingleString arraySingleString; + } + (RecordTypeEnum::CrossAppDomainMap): + CrossAppDomainMap crossAppDomainMap; + (RecordTypeEnum::CrossAppDomainString): + CrossAppDomainString crossAppDomainString; + (RecordTypeEnum::CrossAppDomainAssembly): + CrossAppDomainAssembly crossAppDomainAssembly; + (RecordTypeEnum::MethodCall): { + BinaryMethodCall binaryMethodCall; + } + (RecordTypeEnum::MethodReturn): { + BinaryMethodReturn binaryMethodReturn; + } + (_): std::error(std::format("Unrecognized {}", recordTypeEnum)); + } +}; + +struct DotNetBinnaryFormatter{ + SerializationHeaderRecord Header; + Record records[while(std::mem::read_unsigned($, 1) != RecordTypeEnum::MessageEnd)]; + MessageEnd End; + if(!IsUpdatingTrackers && NeedUpdateTrackers){ + IsUpdatingTrackers = true; + _Trackers _trackers @ 0x0 in _TrackersSection; + IsUpdatingTrackers = false; + NeedUpdateTrackers = false; + } +}; + +struct _ObjTracker{ + _Ptr TypeName; + _Ptr AssemblyName; + _ObjEnum objEnum; + u64 RawPtr [[no_unique_address]]; + /* Only enable the one we actually use to avoid significant slow down */ + match(objEnum){ + (_ObjEnum::Empty): + u64 ptr; + /* + (_ObjEnum::ClassWithId): // _ObjEnum::Object + _Ptr classWithId; + */ + (_ObjEnum::SystemClassWithMembers): // _ObjEnum::ObjectWithMap + _Ptr systemClassWithMembers; + (_ObjEnum::ClassWithMembers): // _ObjEnum::ObjectWithMapAssemId + _Ptr classWithMembers; + (_ObjEnum::SystemClassWithMembersAndTypes): // _ObjEnum::ObjectWithMapTyped + _Ptr systemClassWithMemberAndTypes; + (_ObjEnum::ClassWithMembersAndTypes): // _ObjEnum::ObjectWithMapTypedAssemId + _Ptr classWithMembersAndTypes; + /* + (_ObjEnum::BinaryObjectString): // _ObjEnum::ObjectString + _Ptr binaryObjectString; + (_ObjEnum::BinaryArray): // _ObjEnum::Array + _Ptr binaryArray; + (_ObjEnum::MemberPrimitiveTyped): + _Ptr memberPrimitiveTyped; + (_ObjEnum::MemberReference): + _Ptr memberReference; + (_ObjEnum::BinaryLibrary): // _ObjEnum::Assembly + _Ptr library; + (_ObjEnum::ObjectNullMultiple256): + _Ptr objectNullMultiple256; + (_ObjEnum::ObjectNullMultiple): + _Ptr objectNullMultiple; + (_ObjEnum::ArraySinglePrimitive): + _Ptr arraySinglePrimitive; + (_ObjEnum::ArraySingleObject): + _Ptr arraySingleObject; + (_ObjEnum::ArraySingleString): + _Ptr arraySingleString; + (_ObjEnum::CrossAppDomainMap): + _Ptr crossAppDomainMap; + (_ObjEnum::CrossAppDomainString): + _Ptr crossAppDomainString; + (_ObjEnum::CrossAppDomainAssembly): + _Ptr crossAppDomainAssembly; + (_ObjEnum::MethodCall): + _Ptr binaryMethodCall; + (_ObjEnum::MethodReturn): + _Ptr binaryMethodReturn; + */ + (_): u64 ptr; //std::error(std::format("Unexpected {}", objEnum)); + } +}; + +/* Should be an In variable with default non zero value in the future until we use a hash map */ +#define _OBJECTS_TRACKER_CNT 232 +#define _LIBRARIES_CNT 8 + +struct _Trackers{ + _ObjTracker currentObj; + /* TODO: this should really be an hash map, the algorithm that generated is unspecified */ + _ObjTracker objs[_OBJECTS_TRACKER_CNT]; + _Ptr libs[_LIBRARIES_CNT]; +}; + +std::mem::set_section_size(_TrackersSection, sizeof(_trackers)); + +DotNetBinnaryFormatter dotNetBinnaryFormatter @ 0x0; diff --git a/patterns/esp32_image.hexpat b/patterns/esp32_image.hexpat new file mode 100644 index 00000000..f35daf9c --- /dev/null +++ b/patterns/esp32_image.hexpat @@ -0,0 +1,119 @@ +#pragma author timschneeb +#pragma description ESP32 Firmware Image Format + +#pragma endian little + +// Reference: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html + +import std.mem; + +enum esp_chip_id_t : u16 { + ESP_CHIP_ID_ESP32 = 0x0000, + ESP_CHIP_ID_ESP32S2 = 0x0002, + ESP_CHIP_ID_ESP32C3 = 0x0005, + ESP_CHIP_ID_ESP32S3 = 0x0009, + ESP_CHIP_ID_ESP32C2 = 0x000C, + ESP_CHIP_ID_ESP32C6 = 0x000D, + ESP_CHIP_ID_ESP32H2 = 0x0010, + ESP_CHIP_ID_ESP32P4 = 0x0012, + ESP_CHIP_ID_ESP32C5 = 0x0017, + ESP_CHIP_ID_ESP32C61 = 0x0014, + ESP_CHIP_ID_ESP32H21 = 0x0019, + ESP_CHIP_ID_ESP32H4 = 0x001C, + ESP_CHIP_ID_INVALID = 0xFFFF +}; + +enum esp_image_spi_mode_t : u8 { + QIO, + QOUT, + DIO, + DOUT, + FAST_READ, + SLOW_READ +}; + +enum esp_image_spi_freq_t : u8 { + DIV_2, + DIV_3, + DIV_4, + DIV_1 = 0xF +}; + +enum esp_image_flash_size_t : u8 { + FLASH_1MB, + FLASH_2MB, + FLASH_4MB, + FLASH_8MB, + FLASH_16MB, + FLASH_32MB, + FLASH_64MB, + FLASH_128MB, + FLASH_MAX +}; + +bitfield spi_config_t { + esp_image_spi_freq_t spi_speed : 4; + esp_image_flash_size_t spi_size : 4; +}; + +const u32 ESP_APP_DESC_MAGIC_WORD = 0xABCD5432; + +struct esp_app_desc_t { + u32 magic_word; // ESP_APP_DESC_MAGIC_WORD + u32 secure_version; + u32 reserv1[2]; + char version[32]; + char project_name[32]; + char compile_time[16]; + char compile_date[16]; + char idf_ver[32]; + u8 app_elf_sha256[32]; + u16 min_efuse_blk_rev_full; + u16 max_efuse_blk_rev_full; + u8 mmu_page_size; // in log2 format + u8 reserv3[3]; + u32 reserv2[18]; +}; + +struct esp_image_header_t { + u8 magic; // 0xE9 + u8 segment_count; + esp_image_spi_mode_t spi_mode; + spi_config_t spi_cfg; + u32 entry_addr; + u8 wp_pin [[comment("Write protect pin")]]; + u8 spi_pin_drv[3] [[comment("Drive settings for the SPI flash pins")]]; + esp_chip_id_t chip_id; + u8 min_chip_rev [[comment("Deprecated, replaced by min_chip_rev_full")]]; + u16 min_chip_rev_full [[comment("Minimal revision (major*100+minor)")]]; + u16 max_chip_rev_full [[comment("Maximal revision (major*100+minor)")]]; + u8 reserved[4]; + + u8 hash_appended [[comment("If 1, a SHA256 digest 'simple hash' (of the entire image) is appended after the checksum")]]; +}; + +struct esp_image_segment_header_t { + u32 load_addr; + u32 data_len; +}; + +union esp_image_segment_data_t { + u8 data[parent.header.data_len] [[hidden]]; + + // Application segment + if (std::mem::read_unsigned(addressof(data), 4) == ESP_APP_DESC_MAGIC_WORD) { + esp_app_desc_t app_descriptor; + } +}; + +struct esp_image_segment_t { + esp_image_segment_header_t header; + esp_image_segment_data_t data; +}; + +struct esp_image_t { + esp_image_header_t header; + esp_image_segment_t segments[header.segment_count]; +}; + +esp_image_t image @ 0x0; diff --git a/patterns/fas_oskasoftware.hexpat b/patterns/fas_oskasoftware.hexpat index 617ba4bc..f72b8559 100644 --- a/patterns/fas_oskasoftware.hexpat +++ b/patterns/fas_oskasoftware.hexpat @@ -1,25 +1,25 @@ #pragma author DmitriLeon2000 -#pragma description Oska Software DeskMates FAS (Frames and Sequences) file +#pragma description Oska Software DeskMates FAS (Frames and Sequences) #pragma endian little enum Compression : u32 { - BI_RGB, - BI_RLE8, - BI_RLE4, - BI_BITFIELDS, - BI_JPEG, - BI_PNG, - BI_ALPHABITFIELDS, - BI_CMYK, - BI_CMYKRLE8, - BI_CMYKRLE4, + BI_RGB, + BI_RLE8, + BI_RLE4, + BI_BITFIELDS, + BI_JPEG, + BI_PNG, + BI_ALPHABITFIELDS, + BI_CMYK, + BI_CMYKRLE8, + BI_CMYKRLE4, }; struct Colors { - u8 blue; - u8 green; - u8 red; - u8 reserved; + u8 blue; + u8 green; + u8 red; + u8 reserved; }; struct FASHeader { @@ -69,7 +69,7 @@ struct SeqHeader { }; struct Bitmap { - u8 byte[fas.fasHeader.version >= 3 ? fas.fasHeader.frameSize + (fas.frameSizeHigh << 16) : fas.fasHeader.frameSize]; + u8 byte[parent.fasHeader.version >= 3 ? parent.fasHeader.frameSize + (parent.frameSizeHigh << 16) : parent.fasHeader.frameSize] [[sealed]]; }; struct FramesHeader { @@ -105,7 +105,7 @@ struct FAS { u8 filenameChecksum; // a checksum for a filename in ASCII AnimSequence sequences[seqHeader.count]; FramesHeader framesHeader; + Bitmap framesBitmap[framesHeader.count]; }; FAS fas @ 0x00; -Bitmap framesBitmap[fas.framesHeader.count] @ $; \ No newline at end of file diff --git a/patterns/fas_oskasoftware_old.hexpat b/patterns/fas_oskasoftware_old.hexpat index 9c7e7902..fc04ec43 100644 --- a/patterns/fas_oskasoftware_old.hexpat +++ b/patterns/fas_oskasoftware_old.hexpat @@ -1,25 +1,25 @@ #pragma author DmitriLeon2000 -#pragma description Oska Software DeskMates FAS (Frames and Sequences) file (Oska DeskMate versions 1.3 and 2.06) +#pragma description Oska Software DeskMates FAS (Frames and Sequences) (Oska DeskMate 1.03 and 2.06) #pragma endian little enum Compression : u32 { - BI_RGB, - BI_RLE8, - BI_RLE4, - BI_BITFIELDS, - BI_JPEG, - BI_PNG, - BI_ALPHABITFIELDS, - BI_CMYK, - BI_CMYKRLE8, - BI_CMYKRLE4, + BI_RGB, + BI_RLE8, + BI_RLE4, + BI_BITFIELDS, + BI_JPEG, + BI_PNG, + BI_ALPHABITFIELDS, + BI_CMYK, + BI_CMYKRLE8, + BI_CMYKRLE4, }; struct Colors { - u8 blue; - u8 green; - u8 red; - u8 reserved; + u8 blue; + u8 green; + u8 red; + u8 reserved; }; struct FASHeader { @@ -59,14 +59,14 @@ struct AnimSequence { }; struct SeqHeader { - le u32 size; - le u32 count; - le u32 strPointers[count*2]; + le u32 size; + le u32 count; + le u32 strPointers[count*2]; }; struct Frame { - u8 colorBitmap[fas.fasHeader.frameSizeColor]; - u8 maskBitmap[fas.fasHeader.frameSizeMask]; + u8 colorBitmap[parent.fasHeader.frameSizeColor] [[sealed]]; + u8 maskBitmap[parent.fasHeader.frameSizeMask] [[sealed]]; }; struct FramesHeader { @@ -80,8 +80,7 @@ struct FAS { SeqHeader seqHeader; AnimSequence sequences[seqHeader.count]; FramesHeader framesHeader; + Frame frames[framesHeader.count]; }; - FAS fas @ 0x00; -Frame frames[fas.framesHeader.count] @ $; diff --git a/patterns/fs/apfs.hexpat b/patterns/fs/apfs.hexpat new file mode 100644 index 00000000..a5db527a --- /dev/null +++ b/patterns/fs/apfs.hexpat @@ -0,0 +1,1479 @@ +#pragma author Hrant Tadevosyan (Axcient, now ConnectWise) +#pragma description Apple File System (APFS) +#pragma endian little + +// refs: +// - https://developer.apple.com/support/downloads/Apple-File-System-Reference.pdf +// - https://github.com/sgan81/apfs-fuse +// - https://github.com/libyal/libfsapfs + +import std.core; +import std.hash; +import std.mem; +import type.guid; +import type.magic; +import type.time; + +using paddr_t = s64; + +struct prange { + paddr_t pr_start_paddr; + u64 pr_block_count; +}; + +using uuid_t = type::GUID; + +#define MAX_CKSUM_SIZE 8 + +using oid_t = u64; +using xid_t = u64; + +#define OID_NX_SUPERBLOCK 1 +#define OID_INVALID 0ULL +#define OID_RESERVED_COUNT 1024 + +#define OBJECT_TYPE_MASK 0x0000ffff +#define OBJECT_TYPE_FLAGS_MASK 0xffff0000 + +#define OBJ_STORAGETYPE_MASK 0xc0000000 +#define OBJECT_TYPE_FLAGS_DEFINED_MASK 0xf8000000 + +enum o_type_id_t : u16 { + OBJECT_TYPE_NX_SUPERBLOCK = 0x0001, + OBJECT_TYPE_BTREE = 0x0002, + OBJECT_TYPE_BTREE_NODE = 0x0003, + OBJECT_TYPE_SPACEMAN = 0x0005, + OBJECT_TYPE_SPACEMAN_CAB = 0x0006, + OBJECT_TYPE_SPACEMAN_CIB = 0x0007, + OBJECT_TYPE_SPACEMAN_BITMAP = 0x0008, + OBJECT_TYPE_SPACEMAN_FREE_QUEUE = 0x0009, + OBJECT_TYPE_EXTENT_LIST_TREE = 0x000a, + OBJECT_TYPE_OMAP = 0x000b, + OBJECT_TYPE_CHECKPOINT_MAP = 0x000c, + OBJECT_TYPE_FS = 0x000d, + OBJECT_TYPE_FSTREE = 0x000e, + OBJECT_TYPE_BLOCKREFTREE = 0x000f, + OBJECT_TYPE_SNAPMETATREE = 0x0010, + OBJECT_TYPE_NX_REAPER = 0x0011, + OBJECT_TYPE_NX_REAP_LIST = 0x0012, + OBJECT_TYPE_OMAP_SNAPSHOT = 0x0013, + OBJECT_TYPE_EFI_JUMPSTART = 0x0014, + OBJECT_TYPE_FUSION_MIDDLE_TREE = 0x0015, + OBJECT_TYPE_NX_FUSION_WBC = 0x0016, + OBJECT_TYPE_NX_FUSION_WBC_LIST = 0x0017, + OBJECT_TYPE_ER_STATE = 0x0018, + OBJECT_TYPE_GBITMAP = 0x0019, + OBJECT_TYPE_GBITMAP_TREE = 0x001a, + OBJECT_TYPE_GBITMAP_BLOCK = 0x001b, + OBJECT_TYPE_ER_RECOVERY_BLOCK = 0x001c, + OBJECT_TYPE_SNAP_META_EXT = 0x001d, + OBJECT_TYPE_INTEGRITY_META = 0x001e, + OBJECT_TYPE_FEXT_TREE = 0x001f, + OBJECT_TYPE_RESERVED_20 = 0x0020, + OBJECT_TYPE_INVALID = 0x0000, + OBJECT_TYPE_TEST = 0x00ff, + OBJECT_TYPE_CONTAINER_KEYBAG = 0x7973, // ys -> keys + OBJECT_TYPE_CONTAINER_KEYBAG_2 = 0x6B65, // ke -> + //OBJECT_TYPE_VOLUME_KEYBAG = "recs", + //OBJECT_TYPE_MEDIA_KEYBAG = "mkey", +}; + +enum o_flag_id_t : u16 { + OBJ_VIRTUAL = 0x0000, + OBJ_EPHEMERAL = 0x8000, + OBJ_PHYSICAL = 0x4000, + OBJ_NOHEADER = 0x2000, + OBJ_ENCRYPTED = 0x1000, + OBJ_NONPERSISTENT = 0x0800, +}; + +struct o_type_t { + o_type_id_t t_type; + o_flag_id_t t_flag; +}; + +struct obj_phys_t { + u8 o_cksum[MAX_CKSUM_SIZE]; + oid_t o_oid; + xid_t o_xid; + o_type_t o_type; + o_type_t o_subtype; +}; + +#define NX_MAGIC_RE "BSXN" +#define NX_MAGIC "NXSB" +#define NX_MAX_FILE_SYSTEMS 100 +#define NX_EPH_INFO_COUNT 4 +#define NX_EPH_MIN_BLOCK_COUNT 8 +#define NX_MAX_FILE_SYSTEM_EPH_STRUCTS 4 +#define NX_TX_MIN_CHECKPOINT_COUNT 4 +#define NX_EPH_INFO_VERSION_1 1 + +#define NX_RESERVED_1 0x00000001LL +#define NX_RESERVED_2 0x00000002LL +#define NX_CRYPTO_SW 0x00000004LL + +#define NX_FEATURE_DEFRAG 0x0000000000000001ULL +#define NX_FEATURE_LCFD 0x0000000000000002ULL +#define NX_SUPPORTED_FEATURES_MASK (NX_FEATURE_DEFRAG | NX_FEATURE_LCFD) + +bitfield nx_features_t { + unsigned defrag : 1; + unsigned lcfd : 1; + padding : 62; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 64)]]; + +#define NX_SUPPORTED_ROCOMPAT_MASK (0x0ULL) + +bitfield nx_rocompat_features_t { + padding : 64; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 64)]]; + +#define NX_INCOMPAT_VERSION1 0x0000000000000001ULL +#define NX_INCOMPAT_VERSION2 0x0000000000000002ULL +#define NX_INCOMPAT_FUSION 0x0000000000000100ULL +#define NX_SUPPORTED_INCOMPAT_MASK (NX_INCOMPAT_VERSION2 | NX_INCOMPAT_FUSION) + +bitfield nx_incompat_features_t { + version1 : 1; + version2 : 1; + padding : 6; + fusion : 1; + padding : 55; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 64)]]; + +#define NX_MINIMUM_BLOCK_SIZE 4096 +#define NX_DEFAULT_BLOCK_SIZE 4096 +#define NX_MAXIMUM_BLOCK_SIZE 65536 + +#define NX_MINIMUM_CONTAINER_SIZE 1048576 + +enum nx_counter_id_t : u64 { + NX_CNTR_OBJ_CKSUM_SET = 0, + NX_CNTR_OBJ_CKSUM_FAIL = 1, + NX_NUM_COUNTERS = 32 +}; + +struct nx_superblock_t { + obj_phys_t nx_o; + type::Magic nx_magic; + u32 nx_block_size; + u64 nx_block_count; + nx_features_t nx_features; + nx_rocompat_features_t nx_readonly_compatible_features; + nx_incompat_features_t nx_incompatible_features; + uuid_t nx_uuid; + oid_t nx_next_oid; + xid_t nx_next_xid; + u32 nx_xp_desc_blocks; + u32 nx_xp_data_blocks; + paddr_t nx_xp_desc_base; + paddr_t nx_xp_data_base; + u32 nx_xp_desc_next; + u32 nx_xp_data_next; + u32 nx_xp_desc_index; + u32 nx_xp_desc_len; + u32 nx_xp_data_index; + u32 nx_xp_data_len; + oid_t nx_spaceman_oid; + oid_t nx_omap_oid; + oid_t nx_reaper_oid; + u32 nx_test_type; + u32 nx_max_file_systems; + oid_t nx_fs_oid[NX_MAX_FILE_SYSTEMS]; + nx_counter_id_t nx_counters[nx_counter_id_t::NX_NUM_COUNTERS]; + prange nx_blocked_out_prange; + oid_t nx_evict_mapping_tree_oid; + u64 nx_flags; + paddr_t nx_efi_jumpstart; + uuid_t nx_fusion_uuid; + prange nx_keylocker; + u64 nx_ephemeral_info[NX_EPH_INFO_COUNT]; + oid_t nx_test_oid; + oid_t nx_fusion_mt_oid; + oid_t nx_fusion_wbc_oid; + prange nx_fusion_wbc; + u64 nx_newest_mounted_version; + prange nx_mkb_locker; +}; + +#define CHECKPOINT_MAP_LAST 0x00000001 + +struct checkpoint_mapping_t { + o_type_t cpm_type; + o_type_t cpm_subtype; + u32 cpm_size; + u32 cpm_pad; + oid_t cpm_fs_oid; + oid_t cpm_oid; + oid_t cpm_paddr; +}; + +struct checkpoint_map_phys_t { + obj_phys_t cpm_o; + u32 cpm_flags; + u32 cpm_count; + + checkpoint_mapping_t cpm_map[cpm_count]; +}; + +struct evict_mapping_val_t { + paddr_t dst_paddr; + u64 len; +}; + +struct omap_phys_t { + obj_phys_t om_o; + u32 om_flags; + u32 om_snap_count; + u32 om_tree_type; + u32 om_snapshot_tree_type; + oid_t om_tree_oid; + oid_t om_snapshot_tree_oid; + xid_t om_most_recent_snap; + xid_t om_pending_revert_min; + xid_t om_pending_revert_max; +}; + +struct omap_key_t { + oid_t ok_oid; + oid_t ok_xid; +}; + +struct omap_val_t { + u32 ov_flags; + u32 ov_size; + paddr_t ov_paddr; +}; + +struct omap_snapshot_t { + u32 oms_flags; + u32 oms_pad; + oid_t oms_oid; +}; + +struct chunk_info_t { + u64 ci_xid; + u64 ci_addr; + u32 ci_block_count; + u32 ci_free_count; + paddr_t ci_bitmap_addr; +}; + +struct chunk_info_block_t { + obj_phys_t cib_o; + u32 cib_index; + u32 cib_chunk_info_count; + chunk_info_t cib_chunk_info[cib_chunk_info_count]; +}; + +struct cib_addr_block_t { + obj_phys_t cab_o; + u32 cab_index; + u32 cab_cib_count; + paddr_t cab_cib_addr[cab_cib_count]; +}; + +struct spaceman_free_queue_key_t { + xid_t sfqk_xid; + paddr_t sfqk_paddr; +}; + +using spaceman_free_queue_val_t = u64; +struct spaceman_free_queue_entry_t { + spaceman_free_queue_key_t sfqe_key; + spaceman_free_queue_val_t sfqe_count; +}; + +struct spaceman_free_queue_t { + u64 sfq_count; + oid_t sfq_tree_oid; + xid_t sfq_oldest_xid; + u16 sfq_tree_node_limit; + u16 sfq_pad16; + u32 sfq_pad32; + u64 sfq_reserved; +}; + +struct spaceman_device_t { + u64 sm_block_count; + u64 sm_chunk_count; + u32 sm_cib_count; + u32 sm_cab_count; + u64 sm_free_count; + u32 sm_addr_offset; + u32 sm_reserved; + u64 sm_reserved2; +}; + +enum smdev_t : u32 { + SD_MAIN = 0, + SD_TIER2 = 1, + SD_COUNT = 2 +}; + +enum sfq_t : u32 { + SFQ_IP = 0, + SFQ_MAIN = 1, + SFQ_TIER2 = 2, + SFQ_COUNT = 3 +}; + +struct spaceman_allocation_zone_boundaries_t { + u64 saz_zone_start; + u64 saz_zone_end; +}; + +#define SM_ALLOCZONE_INVALID_END_BOUNDARY 0 +#define SM_ALLOCZONE_NUM_PREVIOUS_BOUNDARIES 7 +struct spaceman_allocation_zone_info_phys_t { + spaceman_allocation_zone_boundaries_t saz_current_boundaries; + spaceman_allocation_zone_boundaries_t saz_previous_boundaries[SM_ALLOCZONE_NUM_PREVIOUS_BOUNDARIES]; + u16 saz_zone_id; + u16 saz_previous_boundary_index; + u32 saz_reserved; +}; + +struct spaceman_allocation_zones_t { + spaceman_allocation_zone_info_phys_t sdz_allocation_zone_infos[smdev_t::SD_COUNT]; +}; + +#define SM_DATAZONE_ALLOCZONE_COUNT 8 +struct spaceman_datazone_info_phys_t { + spaceman_allocation_zones_t sdz_allocation_zones[SM_DATAZONE_ALLOCZONE_COUNT]; +}; + +struct spaceman_phys_t { + obj_phys_t sm_o; + u32 sm_block_size; + u32 sm_blocks_per_chunk; + u32 sm_chunks_per_cib; + u32 sm_cibs_per_cab; + spaceman_device_t sm_dev[smdev_t::SD_COUNT]; + u32 sm_flags; + u32 sm_ip_bm_tx_multiplier; + u64 sm_ip_block_count; + u32 sm_ip_bm_size_in_blocks; + u32 sm_ip_bm_block_count; + paddr_t sm_ip_bm_base; + paddr_t sm_ip_base; + u64 sm_fs_reserve_block_count; + u64 sm_fs_reserve_alloc_count; + spaceman_free_queue_t sm_fq[sfq_t::SFQ_COUNT]; + u16 sm_ip_bm_free_head; + u16 sm_ip_bm_free_tail; + u32 sm_ip_bm_xid_offset; + u32 sm_ip_bitmap_offset; + u32 sm_ip_bm_free_next_offset; + u32 sm_version; + u32 sm_struct_size; + spaceman_datazone_info_phys_t sm_datazone; +}; + +struct nx_reaper_phys_t { + obj_phys_t nr_o; + u64 nr_next_reap_id; + u64 nr_completed_id; + oid_t nr_head; + oid_t nr_tail; + u32 nr_flags; + u32 nr_rlcount; + u32 nr_type; + u32 nr_size; + oid_t nr_fs_oid; + oid_t nr_oid; + xid_t nr_xid; + u32 nr_nrle_flags; + u32 nr_state_buffer_size; + u8 nr_state_buffer[nr_state_buffer_size]; +}; + +struct nx_reap_list_entry_t { + u32 nrle_next; + u32 nrle_flags; + u32 nrle_type; + u32 nrle_size; + oid_t nrle_fs_oid; + oid_t nrle_oid; + xid_t nrle_xid; +}; + +struct nx_reap_list_phys_t { + obj_phys_t nrl_o; + oid_t nrl_next; + u32 nrl_flags; + u32 nrl_max; + u32 nrl_count; + u32 nrl_first; + u32 nrl_last; + u32 nrl_free; + nx_reap_list_entry_t nrl_entries[nrl_count]; +}; + +enum nx_reap_phase_t : u32 { + APFS_REAP_PHASE_START = 0, + APFS_REAP_PHASE_SNAPSHOTS = 1, + APFS_REAP_PHASE_ACTIVE_FS = 2, + APFS_REAP_PHASE_DESTROY_OMAP = 3, + APFS_REAP_PHASE_DONE = 4, +}; + +struct keybag_entry_t { + uuid_t ke_uuid; + u16 ke_tag; + u16 ke_keylen; + padding[4]; + u8 ke_keydata[]; +}; + +struct kb_locker_t { + u16 kl_version; + u16 kl_nkeys; + u32 kl_nbytes; + padding[8]; + u8 kl_entries[]; +}; + +struct mk_obj_t { + u8 o_cksum[MAX_CKSUM_SIZE]; + oid_t o_oid; + xid_t o_xid; + o_type_id_t o_type; + o_type_id_t o_subtype; +}; + +struct media_keybag_t { + mk_obj_t mk_obj; + kb_locker_t mk_locker; +}; + +using crypto_flags_t = u32; +using cp_key_class_t = u32; +using cp_key_os_version_t = u32; +using cp_key_revision_t = u16; + +struct wrapped_crypto_state_t { + u16 major_version; + u16 minor_version; + crypto_flags_t cpflags; + cp_key_class_t persistent_class; + cp_key_os_version_t key_os_version; + cp_key_revision_t key_revision; + u16 key_len; + u8 persistent_key[0]; +}; + +struct wrapped_meta_crypto_state_t { + u16 major_version; + u16 minor_version; + crypto_flags_t cpflags; + cp_key_class_t persistent_class; + cp_key_os_version_t key_os_version; + cp_key_revision_t key_revision; + u16 unused; +}; + +struct j_crypto_key_t { + // j_key_t hdr; +}; + +struct j_crypto_val_t { + u32 refcnt; + wrapped_crypto_state_t state; +}; + +#define APFS_MODIFIED_NAMELEN 32 +struct apfs_modified_by_t { + u8 id[APFS_MODIFIED_NAMELEN]; + u64 timestamp; + xid_t last_xid; +}; + +bitfield apfs_fs_flags_t { + APFS_FS_UNENCRYPTED : 1; + APFS_FS_RESERVED_2 : 1; + APFS_FS_RESERVED_4 : 1; + APFS_FS_ONEKEY : 1; + APFS_FS_SPILLEDOVER : 1; + APFS_FS_RUN_SPILLOVER_CLEANER : 1; + APFS_FS_ALWAYS_CHECK_EXTENTREF : 1; + APFS_FS_RESERVED_80 : 1; + APFS_FS_RESERVED_100 : 1; + padding : 55; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 64)]]; + +bitfield apfs_incompatible_features_t { + APFS_INCOMPAT_CASE_INSENSITIVE : 1; + APFS_INCOMPAT_DATALESS_SNAPS : 1; + APFS_INCOMPAT_ENC_ROLLED : 1; + APFS_INCOMPAT_NORMALIZATION_INSENSITIVE : 1; + APFS_INCOMPAT_INCOMPLETE_RESTORE : 1; + APFS_INCOMPAT_SEALED_VOLUME : 1; + APFS_INCOMPAT_RESERVED_40 : 1; + padding : 57; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 64)]]; + +apfs_incompatible_features_t apfs_incompatible_features_null; + +#define APFS_MAGIC "BSPA" +#define APFS_MAGIC_LE "APSB" +#define APFS_MAX_HIST 8 +#define APFS_VOLNAME_LEN 256 +struct apfs_superblock_t { + obj_phys_t apfs_o; + type::Magic apfs_magic; + u32 apfs_fs_index; + u64 apfs_features; + u64 apfs_readonly_compatible_features; + apfs_incompatible_features_t apfs_incompatible_features; + u64 apfs_unmount_time; + u64 apfs_fs_reserve_block_count; + u64 apfs_fs_quota_block_count; + u64 apfs_fs_alloc_count; + wrapped_meta_crypto_state_t apfs_meta_crypto; + o_type_t apfs_root_tree_type; + o_type_t apfs_extentref_tree_type; + o_type_t apfs_snap_meta_tree_type; + oid_t apfs_omap_oid; + oid_t apfs_root_tree_oid; + oid_t apfs_extentref_tree_oid; + oid_t apfs_snap_meta_tree_oid; + xid_t apfs_revert_to_xid; + oid_t apfs_revert_to_sblock_oid; + u64 apfs_next_obj_id; + u64 apfs_num_files; + u64 apfs_num_directories; + u64 apfs_num_symlinks; + u64 apfs_num_other_fsobjects; + u64 apfs_num_snapshots; + u64 apfs_total_blocks_alloced; + u64 apfs_total_blocks_freed; + uuid_t apfs_vol_uuid; + u64 apfs_last_mod_time; + apfs_fs_flags_t apfs_fs_flags; + apfs_modified_by_t apfs_formatted_by; + apfs_modified_by_t apfs_modified_by[APFS_MAX_HIST]; + char apfs_volname[APFS_VOLNAME_LEN]; + u32 apfs_next_doc_id; + u16 apfs_role; + u16 reserved; + xid_t apfs_root_to_xid; + oid_t apfs_er_state_oid; + u64 apfs_cloneinfo_id_epoch; + u64 apfs_cloneinfo_xid; + oid_t apfs_snap_meta_ext_oid; + uuid_t apfs_volume_group_id; + oid_t apfs_integrity_meta_oid; + oid_t apfs_fext_tree_oid; + o_type_t apfs_fext_tree_type; + u32 reserved_type; + oid_t reserved_oid; +}; + +#define OBJ_ID_MASK 0x0FFFFFFFFFFFFFFF +#define OBJ_TYPE_MASK 0xF000000000000000 +#define OBJ_TYPE_SHIFT 60 + +struct j_inode_key_t { + // j_key_t hdr; +}; + +bitfield j_inode_flags_t { + INODE_IS_APFS_PRIVATE : 1; + INODE_MAINTAIN_DIR_STATS : 1; + INODE_DIR_STATS_ORIGIN : 1; + INODE_PROT_CLASS_EXPLICIT : 1; + INODE_WAS_CLONED : 1; + INODE_FLAGS_UNUSED : 1; + INODE_HAS_SECURITY_EA : 1; + INODE_BEING_TRUNCATED : 1; + INODE_HAS_FINDER_INFO : 1; + INODE_IS_SPARSE : 1; + INODE_WAS_EVER_CLONED : 1; + INODE_ACTIVE_FILE_TRIMMED : 1; + INODE_PINNED_TO_MAIN : 1; + INODE_PINNED_TO_TIER2 : 1; + INODE_HAS_RSRC_FORK : 1; + INODE_NO_RSRC_FORK : 1; + INODE_ALLOCATION_SPILLEDOVER : 1; + INODE_FAST_PROMOTE : 1; + INODE_HAS_UNCOMPRESSED_SIZE : 1; + INODE_IS_PURGEABLE : 1; + INODE_WANTS_TO_BE_PURGEABLE : 1; + INODE_IS_SYNC_ROOT : 1; + INODE_SNAPSHOT_COW_EXEMPTION : 1; + padding : 41; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 64)]]; + +#define INODE_INHERITED_INTERNAL_FLAGS (INODE_MAINTAIN_DIR_STATS | INODE_SNAPSHOT_COW_EXEMPTION) +#define INODE_CLONED_INTERNAL_FLAGS (INODE_HAS_RSRC_FORK | INODE_NO_RSRC_FORK | INODE_HAS_FINDER_INFO | INODE_SNAPSHOT_COW_EXEMPTION) + +#define OWNING_OBJ_ID_INVALID ~0ULL +#define OWNING_OBJ_ID_UNKNOWN ~1ULL + +#define JOBJ_MAX_KEY_SIZE 832 +#define JOBJ_MAX_VALUE_SIZE 3808 + +#define MIN_DOC_ID = 3; + +#define FEXT_CRYPTO_ID_IS_TWEAK 0x01 + +enum mode_t : u16 { + MODE_S_IFMT = 0o170000, + MODE_S_IFIFO = 0o010000, + MODE_S_IFCHR = 0o020000, + MODE_S_IFDIR = 0o040000, + MODE_S_IFBLK = 0o060000, + MODE_S_IFREG = 0o100000, + MODE_S_IFLNK = 0o120000, + MODE_S_IFSOCK = 0o140000, + MODE_S_IFWHT = 0o160000, +}; + +struct j_inode_val_t { + u64 parent_id; + u64 private_id; + type::time64_t create_time; + type::time64_t mod_time; + type::time64_t change_time; + type::time64_t access_time; + j_inode_flags_t internal_flags; + + u32 nchildren; // or links + + cp_key_class_t default_protection_class; + + u32 write_generation_counter; + u32 bsd_flags; + u32 owner; + u32 group; + mode_t mode; + u16 pad1; + u64 uncompressed_size; + u8 xfields[1]; +}; + +#define J_DREC_LEN_MASK 0x000003FF +#define J_DREC_HASH_MASK 0xFFFFFC00 +#define J_DREC_HASH_SHIFT 10 + +struct j_drec_key_t { + // j_key_t hdr; + u16 name_len; + char name[name_len]; +}; + +bitfield j_drec_hashed_len_t { + length : 10; + hash : 22; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 32)]]; + +struct j_drec_hashed_key_t { + // j_key_t hdr; + j_drec_hashed_len_t name_len_and_hash; + char name[name_len_and_hash.length]; +}; + +enum dir_rec_flags_type_t : u8 { + DT_UNKNOWN = 0, + DT_FIFO = 1, + DT_CHR = 2, + DT_DIR = 4, + DT_BLK = 6, + DT_REG = 8, + DT_LNK = 10, + DT_SOCK = 12, + DT_WHT = 14, +}; + +bitfield dir_rec_flags_t { + DREC_TYPE_MASK : 8; + RESERVED_10 : 1; + padding : 55; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 64)]]; + +struct j_drec_val_t { + u64 file_id; + u64 date_added; + dir_rec_flags_t flags; + u8 xfields[]; +}; + +struct j_dir_stats_key_t { + // j_key_t hdr; +}; + +struct j_dir_stats_val_t { + u64 num_children; + u64 total_size; + u64 chained_key; + u64 gen_count; +}; + +struct j_xattr_key_t { + // j_key_t hdr; + u16 name_len; + char name[name_len]; +}; + +bitfield j_xattr_flags_t { + XATTR_DATA_STREAM : 1; + XATTR_DATA_EMBEDDED : 1; + XATTR_FILE_SYSTEM_OWNED : 1; + XATTR_RESERVED_8 : 1; + padding : 12; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 16)]]; + +#define XATTR_MAX_EMBEDDED_SIZE 3804 +#define SYMLINK_EA_NAME "com.apple.fs.symlink" +#define FIRMLINK_EA_NAME "com.apple.fs.firmlink" +#define APFS_COW_EXEMPT_COUNT_NAME "com.apple.fs.cow-exempt-file-count" + +struct j_xattr_val_t { + j_xattr_flags_t flags; + u16 xdata_len; + u8 xdata[xdata_len]; +}; + +enum j_obj_kinds_t : u8 { + APFS_KIND_ANY = 0, + APFS_KIND_NEW = 1, + APFS_KIND_UPDATE = 2, + APFS_KIND_DEAD = 3, + APFS_KIND_UPDATE_REFCNT = 4, + + APFS_KIND_INVALID = 255 +}; + +bitfield j_inode_bsd_flags { + APFS_UF_NODUMP : 1; + APFS_UF_IMMUTABLE : 1; + APFS_UF_APPEND : 1; + APFS_UF_OPAQUE : 1; + APFS_UF_NOUNLINK : 1; + APFS_UF_COMPRESSED : 1; + APFS_UF_TRACKED : 1; + APFS_UF_DATAVAULT : 1; + reserved : 7; + APFS_UF_HIDDEN : 1; + APFS_SF_ARCHIVED : 1; + APFS_SF_IMMUTABLE : 1; + APFS_SF_APPEND : 1; + APFS_SF_RESTRICTED : 1; + APFS_SF_NOUNLINK : 1; + APFS_SF_SNAPSHOT : 1; + APFS_SF_FIRMLINK : 1; + padding : 6; + APFS_SF_DATALESS : 1; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 64)]]; + +#define INVALID_INO_NUM 0 +#define ROOT_DIR_PARENT 1 +#define ROOT_DIR_INO_NUM 2 +#define PRIV_DIR_INO_NUM 3 +#define SNAP_DIR_INO_NUM 6 +#define PURGEABLE_DIR_INO_NUM 7 +#define MIN_USER_INO_NUM 16 + +#define UNIFIED_ID_SPACE_MARK 0x0800000000000000 + +struct j_phys_ext_key_t { + // j_key_t hdr; +}; + +struct j_phys_ext_val_t { + u64 len_and_kind; + u64 owning_obj_id; + u32 refcnt; +}; + +#define PEXT_LEN_MASK 0x0fffffffffffffffULL +#define PEXT_KIND_MASK 0xf000000000000000ULL +#define PEXT_KIND_SHIFT 60 + +struct j_file_extent_key_t { + // j_key_t hdr; + u64 logical_addr; +}; + +struct j_file_extent_val_t { + u64 len_and_flags; + u64 phys_block_num; + u64 crypto_id; +}; + +#define J_FILE_EXTENT_LEN_MASK 0x00ffffffffffffffULL +#define J_FILE_EXTENT_FLAG_MASK 0xff00000000000000ULL +#define J_FILE_EXTENT_FLAG_SHIFT 56 + +struct j_dstream_id_key_t { + // j_key_t hdr; +}; + +struct j_dstream_id_val_t { + u32 refcnt; +}; + +struct j_dstream_t { + u64 size; + u64 alloced_size; + u64 default_crypto_id; + u64 total_bytes_written; + u64 total_bytes_read; +}; + +struct j_xattr_dstream_t { + u64 xattr_obj_id; + j_dstream_t dstream; +}; + +struct xf_blob_t { + u16 xf_num_exts; + u16 xf_used_data; + u8 xf_data[]; +}; + +enum x_type_t : u8 { + DREC_EXT_TYPE_SIBLING_ID = 1, + + INO_EXT_TYPE_SNAP_XID = 1, + INO_EXT_TYPE_DELTRA_TREE_OID = 2, + INO_EXT_TYPE_DOCUMENT_ID = 3, + INO_EXT_TYPE_NAME = 4, + INO_EXT_TYPE_PREV_FSIZE = 5, + INO_EXT_TYPE_RESERVED_6 = 6, + INO_EXT_TYPE_FINDER_INFO = 7, + INO_EXT_TYPE_DSTREAM = 8, + INO_EXT_TYPE_RESERVED_9 = 9, + INO_EXT_TYPE_DIR_STATS_KEY = 10, + INO_EXT_TYPE_FS_UUID = 11, + INO_EXT_TYPE_RESERVED_12 = 12, + INO_EXT_TYPE_SPARSE_BYTES = 13, + INO_EXT_TYPE_RDEV = 14, + INO_EXT_TYPE_PURGEABLE_FLAGS = 15, + INO_EXT_TYPE_ORIG_SYNC_ROOT_ID = 16 +}; + +bitfield x_flags_t { + XF_DATA_DEPENDENT : 1; + XF_DO_NOT_COPY : 1; + XF_RESERVED_4 : 1; + XF_CHILDREN_INHERIT : 1; + XF_USER_FIELD : 1; + XF_SYSTEM_FIELD : 1; + XF_RESERVED_40 : 1; + XF_RESERVED_80 : 1; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 8)]]; + +struct x_field_t { + x_type_t x_type; + x_flags_t x_flags; + u16 x_size; +}; + +struct j_sibling_key_t { + // j_key_t hdr; + u64 sibling_id; +}; + +struct j_sibling_val_t { + u64 parent_id; + u16 name_len; + char name[name_len]; +}; + +struct j_sibling_map_key_t { + // j_key_t hdr; +}; + +struct j_sibling_map_val_t { + u64 file_id; +}; + +struct j_snap_metadata_key_t { + // j_key_t hdr; +}; + +bitfield snap_meta_flags_t { + SNAP_META_PENDING_DATALESS : 1; + SNAP_META_MERGE_IN_PROGRESS : 1; + padding : 30; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 32)]]; + +struct j_snap_metadata_val_t { + oid_t extentref_tree_oid; + oid_t sblock_oid; + type::time64_t create_time; + type::time64_t change_time; + u64 inum; + o_type_t extentref_tree_type; + snap_meta_flags_t flags; + u16 name_len; + char name[name_len]; +}; + +struct j_snap_name_key_t { + // j_key_t hdr; + u16 name_len; + char name[name_len]; +}; + +struct j_snap_name_val_t { + xid_t snap_xid; +}; + +bitfield j_file_info_lba_t { + lba : 56; + type : 8; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 64)]]; + +#define J_FILE_INFO_LBA_MASK 0x00FFFFFFFFFFFFFF +#define J_FILE_INFO_TYPE_MASK 0xFF00000000000000 +#define J_FILE_INFO_TYPE_SHIFT 56 +struct j_file_info_key_t { + // j_key_t hdr; + j_file_info_lba_t info_and_lba; +}; + +struct j_file_data_hash_val_t { + u16 hashed_len; + u8 hash_size; + u8 hash[hash_size]; +}; + +struct j_file_info_val_t { + j_file_data_hash_val_t dhash; +}; + +enum j_obj_types_t : u8 { + APFS_TYPE_ANY = 0, + APFS_TYPE_SNAP_METADATA = 1, + APFS_TYPE_EXTENT = 2, + APFS_TYPE_INODE = 3, + APFS_TYPE_XATTR = 4, + APFS_TYPE_SIBLING_LINK = 5, + APFS_TYPE_DSTREAM_ID = 6, + APFS_TYPE_CRYPTO_STATE = 7, + APFS_TYPE_FILE_EXTENT = 8, + APFS_TYPE_DIR_REC = 9, + APFS_TYPE_DIR_STATS = 10, + APFS_TYPE_SNAP_NAME = 11, + APFS_TYPE_SIBLING_MAP = 12, + APFS_TYPE_FILE_INFO = 13, + APFS_TYPE_INVALID = 15, +}; + +bitfield j_key_t { + unsigned obj_id : 60; + j_obj_types_t obj_type : 4; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 64)]]; + +struct nloc_t { + u16 off; + u16 len; +}; + +struct kvloc_t { + nloc_t key_loc; + nloc_t val_loc; +}; + +struct kvoff_t { + u16 key_off; + u16 val_off; +}; + +struct kvgen_t { + u16 key_off = 0; + u16 val_off = 0; + if (btn_flags.BTNODE_FIXED_KV_SIZE) { + kvoff_t range; + + key_off = range.key_off; + val_off = range.val_off; + } else { + kvloc_t range; + + key_off = range.key_loc.off; + val_off = range.val_loc.off; + } + + match (subtype) { + (o_type_id_t::OBJECT_TYPE_OMAP): { + omap_key_t key @ key_area + key_off; + + if (btn_level > 0) { + oid_t node_oid @ val_area - val_off; + } else { + omap_val_t val @ val_area - val_off; + } + } + (o_type_id_t::OBJECT_TYPE_FSTREE | o_type_id_t::OBJECT_TYPE_BLOCKREFTREE | o_type_id_t::OBJECT_TYPE_SNAPMETATREE): { + j_key_t key @ key_area + key_off; + match (key.obj_type) { + (j_obj_types_t::APFS_TYPE_SNAP_METADATA): { + j_snap_metadata_key_t snap_meta_key @ key_area + key_off + sizeof (key); + + if (btn_level > 0) { + oid_t node_oid @ val_area - val_off; + } else { + j_snap_metadata_val_t snap_meta_val @ val_area - val_off; + } + } + (j_obj_types_t::APFS_TYPE_EXTENT): { + j_phys_ext_key_t phys_ext_key @ key_area + key_off + sizeof (key); + + if (btn_level > 0) { + oid_t node_oid @ val_area - val_off; + } else { + j_phys_ext_val_t phys_ext_val @ val_area - val_off; + } + } + (j_obj_types_t::APFS_TYPE_INODE): { + j_inode_key_t inode_key @ key_area + key_off + sizeof (key); + + if (btn_level > 0) { + oid_t node_oid @ val_area - val_off; + } else { + j_inode_val_t inode_val @ val_area - val_off; + } + } + (j_obj_types_t::APFS_TYPE_XATTR): { + j_xattr_key_t xattr_key @ key_area + key_off + sizeof (key); + + if (btn_level > 0) { + oid_t node_oid @ val_area - val_off; + } else { + j_xattr_val_t xattr_val @ val_area - val_off; + } + } + (j_obj_types_t::APFS_TYPE_SIBLING_LINK): { + j_sibling_key_t sibling_link_key @ key_area + key_off + sizeof (key); + + if (btn_level > 0) { + oid_t node_oid @ val_area - val_off; + } else { + j_sibling_val_t sibling_link_val @ val_area - val_off; + } + } + (j_obj_types_t::APFS_TYPE_DSTREAM_ID): { + j_dstream_id_key_t dstream_id_key @ key_area + key_off + sizeof (key); + + if (btn_level > 0) { + oid_t node_oid @ val_area - val_off; + } else { + j_dstream_id_val_t dstream_id_val @ val_area - val_off; + } + } + (j_obj_types_t::APFS_TYPE_CRYPTO_STATE): { + j_crypto_key_t crypto_key @ key_area + key_off + sizeof (key); + + if (btn_level > 0) { + oid_t node_oid @ val_area - val_off; + } else { + j_crypto_val_t crypto_val @ val_area - val_off; + } + } + (j_obj_types_t::APFS_TYPE_FILE_EXTENT): { + j_file_extent_key_t file_ext_key @ key_area + key_off + sizeof (key); + + if (btn_level > 0) { + oid_t node_oid @ val_area - val_off; + } else { + j_file_extent_val_t file_ext_val @ val_area - val_off; + } + } + (j_obj_types_t::APFS_TYPE_DIR_REC): { + if (vol_incomp.APFS_INCOMPAT_CASE_INSENSITIVE || + vol_incomp.APFS_INCOMPAT_NORMALIZATION_INSENSITIVE) { + j_drec_hashed_key_t drec_key @ key_area + key_off + sizeof (key); + } else { + j_drec_key_t drec_key @ key_area + key_off + sizeof (key); + } + + if (btn_level > 0) { + oid_t node_oid @ val_area - val_off; + } else { + j_drec_val_t drec_val @ val_area - val_off; + } + } + (j_obj_types_t::APFS_TYPE_DIR_STATS): { + j_dir_stats_key_t dir_stats_key @ key_area + key_off + sizeof (key); + + if (btn_level > 0) { + oid_t node_oid @ val_area - val_off; + } else { + j_dir_stats_val_t dir_stats_val @ val_area - val_off; + } + } + (j_obj_types_t::APFS_TYPE_SNAP_NAME): { + j_snap_name_key_t snap_name_key @ key_area + key_off + sizeof (key); + + if (btn_level > 0) { + oid_t node_oid @ val_area - val_off; + } else { + j_snap_name_val_t snap_name_val @ val_area - val_off; + } + } + (j_obj_types_t::APFS_TYPE_SIBLING_MAP): { + j_sibling_key_t sibling_key @ key_area + key_off + sizeof (key); + + if (btn_level > 0) { + oid_t node_oid @ val_area - val_off; + } else { + j_sibling_val_t sibling_val @ val_area - val_off; + } + } + (j_obj_types_t::APFS_TYPE_FILE_INFO): { + j_file_info_key_t file_info_key @ key_area + key_off + sizeof (key); + + if (btn_level > 0) { + oid_t node_oid @ val_area - val_off; + } else { + j_file_info_val_t file_info_val @ val_area - val_off; + } + } + } + } + } +}; + +bitfield btn_flags_t { + BTNODE_ROOT : 1; + BTNODE_LEAF : 1; + BTNODE_FIXED_KV_SIZE : 1; + BTNODE_HASHED : 1; + BTNODE_NOHEADER : 1; + padding : 10; + BTNODE_CHECK_KOFF_INVAL : 1; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 16)]]; + +bitfield bt_flags_t { + BTREE_UINT64_KEYS : 1; + BTREE_SEQUENTIAL_INSERT : 1; + BTREE_ALLOW_GHOSTS : 1; + BTREE_EPHEMERAL : 1; + BTREE_PHYSICAL : 1; + BTREE_NONPERSISTENT : 1; + BTREE_KV_NONALIGNED : 1; + BTREE_HASHED : 1; + BTREE_NOHEADER : 1; + padding : 23; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 32)]]; + +struct btree_info_fixed_t { + bt_flags_t bt_flags; + u32 bt_node_size; + u32 bt_key_size; + u32 bt_val_size; +}; + +struct btree_info_t { + btree_info_fixed_t bt_fixed; + u32 bt_longest_key; + u32 bt_longest_val; + u64 bt_key_count; + u64 bt_node_count; +}; + +struct btree_node_phys_t { + u32 node_start = $; + + obj_phys_t btn_o; + btn_flags_t btn_flags; + u16 btn_level; + u32 btn_nkeys; + nloc_t btn_table_space; + nloc_t btn_free_space; + nloc_t btn_key_free_list; + nloc_t btn_val_free_list; + + if (btn_table_space.off > $) { + padding[btn_table_space.off - $]; + } + u32 btn_toc_start = $; + + padding[btn_table_space.len]; + u32 key_area = $; + u32 val_area = node_start + block_size; + if (btn_flags.BTNODE_ROOT) { + val_area -= sizeof (btree_info_t); + } + + u8 btn_key_area; + kvgen_t btn_toc[btn_nkeys] @ btn_toc_start; + + padding[btn_free_space.off]; + u8 btn_free_area_start; + + std::mem::AlignTo; + + if (btn_flags.BTNODE_ROOT) { + btree_info_t info @ node_start + block_size - sizeof (btree_info_t); + } +}; + +#define BTREE_NODE_HASH_SIZE_MAX 64 +struct btn_index_node_val_t { + oid_t binv_child_oid; + u8 binv_child_hash[BTREE_NODE_HASH_SIZE_MAX]; +}; + +// ================== HELPERS ================== +fn fletcher64(u64 offset, u64 count, u64 init) { + u64 sum1 = init & 0xFFFFFFFF; + u64 sum2 = init >> 32; + + for (u64 key = 0, key < count, key += 1) { + u32 data @ offset + key * sizeof (u32); + sum1 += data; + sum2 += sum1; + } + + sum1 %= 0xFFFFFFFF; + sum2 %= 0xFFFFFFFF; + + return (sum2 << 32) | sum1; +}; + +fn block_verify(u64 offset, u64 size) { + u64 chk @ offset; + if (chk == 0) + return false; + if (chk == 0xFFFFFFFFFFFFFFFF) + return false; + + u64 cks = fletcher64(offset + MAX_CKSUM_SIZE, size / sizeof (u32) - 2, 0); + cks = fletcher64(offset, MAX_CKSUM_SIZE / sizeof (u32), cks); + + std::assert(cks == 0, std::format("block verification failed, offset: 0x{:X}, size: 0x{:X}", offset, size)); +}; + +fn object_get_latest(u64 offset, u64 count, u64 size, o_type_id_t type, u64 max = 0) { + if (max == 0) { + max = count; + } + + u64 max_xid = 0; + u64 result = 0; + for (u64 address = offset, address <= (offset + count), address += 1) { + if ((address - offset) >= max) { + return result; + } + + obj_phys_t object @ address * size; + + if (object.o_type.t_type != type) { + continue; + } + + if (max_xid < object.o_xid) { + max_xid = object.o_xid; + result = address * size; + } + } + + return result; +}; + +fn checkpoint_map_lookup(checkpoint_map_phys_t checkpoint_map, oid_t oid, o_type_id_t type) { + for (u64 iter = 0, iter < checkpoint_map.cpm_count, iter += 1) { + if (checkpoint_map.cpm_map[iter].cpm_oid == oid && + checkpoint_map.cpm_map[iter].cpm_type.t_type == type) { + return checkpoint_map.cpm_map[iter]; + } + } + + checkpoint_mapping_t empty; + return empty; +}; + +fn keybag_is_encrypted(media_keybag_t keybag) { + bool is_decrypted = + keybag.mk_obj.o_type == o_type_id_t::OBJECT_TYPE_CONTAINER_KEYBAG && + keybag.mk_obj.o_subtype == o_type_id_t::OBJECT_TYPE_CONTAINER_KEYBAG_2; + + std::assert(is_decrypted, "encrypted keybags are not supported"); +}; + +fn omap_node_lookup(paddr_t off, u64 block_size, oid_t oid, xid_t xid) { + while (true) { + btree_node_phys_t node @ off; + if (node.btn_nkeys <= 0) { + return 0; + } + + s64 beg = 0; + s64 end = node.btn_nkeys - 1; + s64 mid = 0; + s64 idx = 0; + while (beg <= end) { + mid = beg + (end - beg) / 2; + + omap_key_t key = node.btn_toc[mid].key; + if (oid > key.ok_oid) { + beg += 1; + } else if (oid < key.ok_oid) { + end -= 1; + } else if (xid > key.ok_xid) { + beg += 1; + idx = mid; + } else if (xid < key.ok_xid) { + end -= 1; + idx = mid; + } else { + idx = mid; + break; + } + } + + if (node.btn_level > 0) { + off = node.btn_toc[idx].node_oid * block_size; + continue; + } + + omap_key_t key = node.btn_toc[idx].key; + if (key.ok_oid == oid) { + return node.btn_toc[idx].val.ov_paddr; + } else { + return 0; + } + } +}; + +fn fstree_inode_lookup( + paddr_t root_off, + paddr_t omap_off, + u64 block_size, + u64 ino, + apfs_incompatible_features_t incomp, + xid_t xid +) { + u64 inode_key = (j_obj_types_t::APFS_TYPE_INODE << OBJ_TYPE_SHIFT) | (ino & OBJ_ID_MASK); + u64 node_off = root_off; + while (true) { + btree_node_phys_t node @ node_off; + + s64 beg = 0; + s64 end = node.btn_nkeys - 1; + s64 mid = 0; + s64 current = 0; + while (beg <= end) { + mid = beg + (end - beg) / 2; + u64 entry_key @ addressof (node.btn_toc[mid].key); + if ((entry_key & OBJ_ID_MASK) > (inode_key & OBJ_ID_MASK)) { + end -= 1; + } else if ((entry_key & OBJ_ID_MASK) < (inode_key & OBJ_ID_MASK)) { + beg += 1; + } else if (((entry_key & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) > ((inode_key & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT)) { + end -= 1; + current = mid; + } else if (((entry_key & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) < ((inode_key & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT)) { + beg += 1; + current = mid; + } else { + current = mid; + break; + } + } + + if (node.btn_level > 0) { + node_off = omap_node_lookup( + omap_off, + block_size, + node.btn_toc[current].node_oid, + xid) * superblock.nx_block_size; + continue; + } + + u64 entry_key @ addressof (node.btn_toc[current].key); + if (entry_key != inode_key) { + return 0; + } + + return addressof (node.btn_toc[current].inode_val); + } +}; + +// ================== PARSE ================== +nx_superblock_t initial_superblock @ 0x00; +nx_superblock_t superblock @ object_get_latest( + initial_superblock.nx_xp_desc_base, + initial_superblock.nx_xp_desc_blocks, + initial_superblock.nx_block_size, + o_type_id_t::OBJECT_TYPE_NX_SUPERBLOCK); + +checkpoint_map_phys_t checkpoint_map @ (superblock.nx_xp_desc_base + superblock.nx_xp_desc_index) * superblock.nx_block_size; + +omap_phys_t object_map @ superblock.nx_omap_oid * superblock.nx_block_size; +btree_node_phys_t object_map_tree @ object_map.om_tree_oid * superblock.nx_block_size; + +apfs_superblock_t volume @ omap_node_lookup( + addressof (object_map_tree), + superblock.nx_block_size, + superblock.nx_fs_oid[0], + superblock.nx_o.o_xid) * superblock.nx_block_size; +omap_phys_t volume_object_map @ volume.apfs_omap_oid * superblock.nx_block_size; +btree_node_phys_t volume_object_map_tree @ volume_object_map.om_tree_oid * superblock.nx_block_size; + +btree_node_phys_t volume_root_tree @ omap_node_lookup( + addressof (volume_object_map_tree), + superblock.nx_block_size, + volume.apfs_root_tree_oid, + volume.apfs_o.o_xid) * superblock.nx_block_size; +j_inode_val_t volume_root_folder @ fstree_inode_lookup( + addressof (volume_root_tree), + addressof (volume_object_map_tree), + superblock.nx_block_size, + ROOT_DIR_INO_NUM, + volume.apfs_incompatible_features, + volume.apfs_o.o_xid +); + +btree_node_phys_t volume_extentref_tree @ volume.apfs_extentref_tree_oid * superblock.nx_block_size; +btree_node_phys_t volume_snapshot_tree @ volume.apfs_snap_meta_tree_oid * superblock.nx_block_size; + +apfs_superblock_t snapshot_volume @ volume_snapshot_tree.btn_toc[0].snap_meta_val.sblock_oid * superblock.nx_block_size; +btree_node_phys_t snapshot_extentref_tree @ volume_snapshot_tree.btn_toc[0].snap_meta_val.extentref_tree_oid * superblock.nx_block_size; + +checkpoint_mapping_t spaceman_cp = checkpoint_map_lookup(checkpoint_map, superblock.nx_spaceman_oid, o_type_id_t::OBJECT_TYPE_SPACEMAN); +spaceman_phys_t spaceman @ spaceman_cp.cpm_paddr * superblock.nx_block_size; + +checkpoint_mapping_t spaceman_freequeue_manager_cp = checkpoint_map_lookup( + checkpoint_map, + spaceman.sm_fq[sfq_t::SFQ_IP].sfq_tree_oid, + o_type_id_t::OBJECT_TYPE_BTREE); +btree_node_phys_t spaceman_freequeue_manager @ spaceman_freequeue_manager_cp.cpm_paddr * superblock.nx_block_size; + +checkpoint_mapping_t spaceman_freequeue_volume_cp = checkpoint_map_lookup( + checkpoint_map, + spaceman.sm_fq[sfq_t::SFQ_MAIN].sfq_tree_oid, + o_type_id_t::OBJECT_TYPE_BTREE); + +btree_node_phys_t spaceman_freequeue_volume @ spaceman_freequeue_volume_cp.cpm_paddr * superblock.nx_block_size; + +paddr_t spaceman_main_cibs_addr @ addressof (spaceman) + spaceman.sm_dev[smdev_t::SD_MAIN].sm_addr_offset; +chunk_info_block_t spaceman_main_cibs[spaceman.sm_dev[smdev_t::SD_MAIN].sm_cib_count] @ spaceman_main_cibs_addr * superblock.nx_block_size; + +paddr_t spaceman_tier2_cibs_addr @ addressof (spaceman) + spaceman.sm_dev[smdev_t::SD_TIER2].sm_addr_offset; +chunk_info_block_t spaceman_tier2_cibs[spaceman.sm_dev[smdev_t::SD_TIER2].sm_cib_count] @ spaceman_tier2_cibs_addr * superblock.nx_block_size; + +checkpoint_mapping_t reaper_cp = checkpoint_map_lookup(checkpoint_map, superblock.nx_reaper_oid, o_type_id_t::OBJECT_TYPE_NX_REAPER); +nx_reaper_phys_t reaper @ reaper_cp.cpm_paddr * superblock.nx_block_size; + +media_keybag_t container_keybag @ superblock.nx_keylocker.pr_start_paddr * superblock.nx_block_size; + +// ================== VERIFY ================== +block_verify(addressof (initial_superblock), initial_superblock.nx_block_size); +block_verify(addressof (superblock), superblock.nx_block_size); +block_verify(addressof (object_map), superblock.nx_block_size); +block_verify(addressof (object_map_tree), superblock.nx_block_size); +block_verify(addressof (volume), superblock.nx_block_size); +block_verify(addressof (volume_object_map), superblock.nx_block_size); +block_verify(addressof (volume_root_tree), superblock.nx_block_size); +block_verify(addressof (spaceman), superblock.nx_block_size); +block_verify(addressof (reaper), superblock.nx_block_size); +block_verify(addressof (spaceman_freequeue_manager), superblock.nx_block_size); +block_verify(addressof (spaceman_freequeue_volume), superblock.nx_block_size); +block_verify(addressof (container_keybag), superblock.nx_block_size); +keybag_is_encrypted(container_keybag); \ No newline at end of file diff --git a/patterns/fs/ext4.hexpat b/patterns/fs/ext4.hexpat index 3e891a9c..26963cb9 100644 --- a/patterns/fs/ext4.hexpat +++ b/patterns/fs/ext4.hexpat @@ -8,7 +8,7 @@ #pragma endian little #pragma magic [53 EF] @ 0x438 -#pragma pattern_limit 0x90000 +#pragma pattern_limit 0x80000000 import type.time; import type.size; @@ -540,4 +540,4 @@ struct ext4_group_descriptors { } }; -ext4_group_descriptors group_descs @ block_to_address(2); \ No newline at end of file +ext4_group_descriptors group_descs @ block_to_address(super_block.s_first_data_block + 1); diff --git a/patterns/gltf.hexpat b/patterns/gltf.hexpat index 85d94b8f..a208b965 100644 --- a/patterns/gltf.hexpat +++ b/patterns/gltf.hexpat @@ -22,13 +22,18 @@ * SOFTWARE. */ -#pragma author H. Utku Maden +#pragma author H. Utku Maden, xZise #pragma description GL Transmission Format binary 3D model (.glb) #pragma MIME model/gltf-binary +#pragma magic [67 6C 54 46] @ 0x00 import std.mem; import std.io; import type.magic; +import std.core; +#ifdef __IMHEX__ +import hex.type.json; +#endif /** * @brief The glTF magic section. @@ -53,7 +58,15 @@ enum gltf_chunk_type_t : u32 { struct gltf_chunk_t { u32 length; /**< Length of this chunk. */ gltf_chunk_type_t type [[format("gltf_format")]]; /**< Type of the chunk. JSON or BIN expected. */ - u8 string[length]; /**< The chunk data. */ +#ifndef __IMHEX__ + u8 data[length]; /**< The chunk data. */ +#endif +#ifdef __IMHEX__ + match (type) { + (gltf_chunk_type_t::JSON): hex::type::Json json; + (gltf_chunk_type_t::BIN): u8 data[length]; + } /**< The chunk data. */ +#endif }; fn gltf_format(gltf_chunk_type_t x) @@ -64,7 +77,162 @@ fn gltf_format(gltf_chunk_type_t x) return ""; }; -gltf_magic_t magic @ 0x00; -gltf_chunk_t chunks[while(!std::mem::eof())] @ $; +struct stride_type_t { + InnerType value [[inline]]; + if (Stride > 0) { + padding[Stride - sizeof(value)]; + } +}; + +enum component_types_t : u64 { + BYTE = 5120, + UNSIGNED_BYTE = 5121, + SHORT = 5122, + UNSIGNED_SHORT = 5123, + UNSIGNED_INT = 5125, + FLOAT = 5126, +}; + +fn component_type_format(component_types_t component_type) +{ + if (component_type == component_types_t::BYTE) return "s8"; +else if (component_type == component_types_t::UNSIGNED_BYTE) return "u8"; +else if (component_type == component_types_t::SHORT) return "s16"; +else if (component_type == component_types_t::UNSIGNED_SHORT) return "u16"; +else if (component_type == component_types_t::UNSIGNED_INT) return "u32"; +else if (component_type == component_types_t::FLOAT) return "float"; + + return std::format("{}", component_type); +}; + +struct component_type_t { + match (component_type) { + (component_types_t::BYTE): s8 value; + (component_types_t::UNSIGNED_BYTE): u8 value; + (component_types_t::SHORT): s16 value; + (component_types_t::UNSIGNED_SHORT): u16 value; + (component_types_t::UNSIGNED_INT): u32 value; + (component_types_t::FLOAT): float value; + } +}; + +struct scalar_t { + component_type_t scalar [[inline]]; +} [[static]]; + +struct vec2_t { + component_type_t x; + component_type_t y; +} [[static]]; + +struct vec3_t { + component_type_t x; + component_type_t y; + component_type_t z; +} [[static]]; + +struct vec4_t { + component_type_t x; + component_type_t y; + component_type_t z; + component_type_t w; +} [[static]]; + +struct mat2_t { + component_type_t a11, a21; + component_type_t a12, a22; +} [[static]]; + +struct mat3_t { + component_type_t a11, a21, a31; + component_type_t a12, a22, a32; + component_type_t a13, a23, a33; +} [[static]]; + +struct mat4_t { + component_type_t a11, a21, a31, a41; + component_type_t a12, a22, a32, a42; + component_type_t a13, a23, a33, a43; + component_type_t a14, a24, a34, a44; +} [[static]]; + +fn mem_cnt(auto value) { + return std::core::member_count(value); +}; + +fn has_mem(auto value, str member) { + return std::core::has_member(value, member); +}; + +struct accessor_t { + u64 accessor_index = std::core::array_index(); + u64 view_index = glb.json_chunk.json.accessors[accessor_index].bufferView [[export]]; + u64 view_offset = glb.json_chunk.json.bufferViews[view_index].byteOffset [[export]]; + if (has_mem(glb.json_chunk.json.bufferViews[view_index], "byteStride")) { + u64 byte_stride = glb.json_chunk.json.bufferViews[view_index].byteStride [[export]]; + } else { + u64 byte_stride = 0 [[export]]; + } + if (has_mem(glb.json_chunk.json.accessors[accessor_index], "byteOffset")) { + u64 accessor_offset = glb.json_chunk.json.accessors[accessor_index].byteOffset [[export]]; + } else { + u64 accessor_offset = 0 [[export]]; + } + view_offset = view_offset + accessor_offset; + u64 count_elements = glb.json_chunk.json.accessors[accessor_index].count; + component_types_t component_type = glb.json_chunk.json.accessors[accessor_index].componentType [[export]]; + str element_type = glb.json_chunk.json.accessors[accessor_index].type [[export]]; + + match (element_type) { + ("SCALAR"): stride_type_t, byte_stride> content[count_elements] @ view_offset + addressof(glb.chunks[0].data); + ("VEC2"): stride_type_t, byte_stride> content[count_elements] @ view_offset + addressof(glb.chunks[0].data); + ("VEC3"): stride_type_t, byte_stride> content[count_elements] @ view_offset + addressof(glb.chunks[0].data); + ("VEC4"): stride_type_t, byte_stride> content[count_elements] @ view_offset + addressof(glb.chunks[0].data); + ("MAT2"): stride_type_t, byte_stride> content[count_elements] @ view_offset + addressof(glb.chunks[0].data); + ("MAT3"): stride_type_t, byte_stride> content[count_elements] @ view_offset + addressof(glb.chunks[0].data); + ("MAT4"): stride_type_t, byte_stride> content[count_elements] @ view_offset + addressof(glb.chunks[0].data); + } +} [[format("accessor_format")]]; + +fn accessor_format(accessor_t accessor) { + return std::format("{}<{}>[{}]", accessor.element_type, accessor.component_type, accessor.count_elements); +}; + +struct image_buffer_t { + u64 image_index = std::core::array_index(); + u64 buffer_view_index = glb.json_chunk.json.images[image_index].bufferView; + u64 byte_offset = glb.json_chunk.json.bufferViews[buffer_view_index].byteOffset; + u64 byte_length = glb.json_chunk.json.bufferViews[buffer_view_index].byteLength; + u8 image[byte_length] @ addressof(glb.chunks[0].data) + byte_offset; +} [[hex::visualize("image", image)]]; + +struct buffer_view_t { + u64 buffer_view_index = std::core::array_index(); + u64 byte_offset = glb.json_chunk.json.bufferViews[buffer_view_index].byteOffset; + u64 byte_length = glb.json_chunk.json.bufferViews[buffer_view_index].byteLength; + u8 data[byte_length] @ addressof(glb.chunks[0].data) + byte_offset; +}; + +struct glb_file_t { + gltf_magic_t magic; + gltf_chunk_t json_chunk; + gltf_chunk_t chunks[while(!std::mem::eof())]; + + std::assert_warn(std::mem::size() == magic.length, "file size mismatch"); +}; + +glb_file_t glb @ 0x00; + +#ifdef __IMHEX__ +struct glb_objects_t { + if (std::core::member_count(glb.chunks) == 1) { + if (has_mem(glb.json_chunk.json, "images")) { + image_buffer_t images[mem_cnt(glb.json_chunk.json.images)]; + } + buffer_view_t buffer_views[mem_cnt(glb.json_chunk.json.bufferViews)]; + accessor_t accessors[mem_cnt(glb.json_chunk.json.accessors)]; + } +}; -std::assert_warn(std::mem::size() == magic.length, "file size mismatch"); +glb_objects_t objects @ 0x00; +#endif \ No newline at end of file diff --git a/patterns/gmf.hexpat b/patterns/gmf.hexpat new file mode 100644 index 00000000..13f4f436 --- /dev/null +++ b/patterns/gmf.hexpat @@ -0,0 +1,229 @@ +#pragma author DmitriLeon2000 +#pragma description Game Maker 3.x data +#pragma endian little + +import std.mem; +import std.io; + +struct Colors { + u8 red [[color("FF0000")]]; + u8 green [[color("00FF00")]]; + u8 blue [[color("0000FF")]]; + padding[1]; +} [[static, color(std::format("{:02X}{:02X}{:02X}", red, green, blue)), + hex::inline_visualize("color", red, green, blue, 255)]]; + +struct PascalString { + u32 length; + char data[length]; +}; + +struct BitmapData { + u8 type[2]; + u32 size; + u8 data[size-6] [[sealed]]; +}; + +enum BackgroundType : s32 { + Solid = 0, + Image = 1, + Gradient = 2, + None = 3 +}; + +enum BackImageStyle : s32 { + Tile = 0, + Stretch = 1, + Scrolling = 2, + LeftTop = 3 +}; + +enum Transition : u32 { + None = 0, + CreateFromLeft = 1, + CreateFromRight = 2, + CreateFromTop = 3, + CreateFromBottom = 4, + CreateFromCenter = 5, + ShiftFromLeft = 6, + ShiftFromRight = 7, + ShiftFromTop = 8, + ShiftFromBottom = 9, + PushFromLeft = 10, + PushFromRight = 11, + PushFromTop = 12, + PushFromBottom = 13 +}; + +struct SpriteBoundingBox { + u32 left, right, bottom, top; +}; + +enum ActionID : s32 { + EnumerateActions = 0, + EndAction = 1, + MoveFixed = 101, + ReverseHorizontal = 102, + ReverseVertical = 103, + JumpPosition = 104, + JumpRandom = 105, + Bounce = 106, + SetSpeedHorizontal = 111, + SetSpeedVertical = 112, + SetGravity = 121, + SetFriction = 122, + MoveFree = 131, + SetAlarm = 201, + InstanceCreate = 311, + InstanceDestroy = 312, + InstanceChange = 313, + PositionDestroy = 321, + Sound = 401, + ShowMessage = 411, + SetScore = 421, + DisplayHighscore = 431, + GoToRoom = 501, + EndGame = 511, + Sleep = 521, + ExitEvent = 601, + CheckPlaceFree = 701, + CheckInstanceNumber = 702, + CheckPlaceObject = 703, + CheckPlaceCollision = 704, + CheckQuestion = 721, + Else = 751, + CheckExpression = 801, + ExecuteCode = 811, + SetVariable = 821, + BlockStart = 901, + BlockEnd = 902, + DrawImage = 1001, + DrawText = 1002, + SetFont = 1003 +}; + +struct GameObjectAction { + s32 version_verify [[hidden]]; + ActionID action_id; + s32 apply_to; // -1 - self; -2 - not available + bool relative; + padding[3]; + u32 param_count; + PascalString params[param_count]; + s32 action_group; +}; + +struct GameObjectSubEvent { + s32 subevent_number; + s32 version_verify [[hidden]]; + u32 action_count; + GameObjectAction actions[action_count]; +}; + +struct GameObjectEvent { + GameObjectSubEvent subevents[while(std::mem::read_unsigned($, 4) != 0xFFFFFFFF)]; + padding[4]; +}; + +struct GameSprite { + s32 version_verify [[hidden]]; + s32 width, height; + SpriteBoundingBox bounding_box1; + u32 image_count; + BitmapData images[image_count]; + SpriteBoundingBox bounding_box2; +}; + +struct GameObject { + s32 version_verify [[hidden]]; + PascalString name; + bool solid; + padding[3]; + bool active; + padding[3]; + if (version_verify >= 320) + u32 parent_id; + GameObjectEvent events[13]; + // Events: 0 - Create, 1 - Destroy, 2 - Alarm, 3 - Step, 4 - Collision, + // 5 - Meeting, 6 - Mouse, 7 - Keyboard, 8 - Other, 9 - Drawing + // 10 - Create duplicate, 11-13 - unused + s32 cap [[hidden]]; + GameSprite sprite; +} [[name(name.data)]]; + +struct GameRoomView { + bool enabled; + padding[3]; + if (parent.version_verify >= 320) + s32 left, top, left_cell, top_cell; + u32 width; + u32 height; + if (parent.version_verify >= 320) { + u32 border_horiz, border_vert; + } else + u32 border; + u32 object_to_follow; +}; + +struct GameRoomInstance { + s32 left, top, object_index; +}; + +struct GameRoom { + u32 version_verify [[hidden]]; + PascalString name; + Colors back_color1; + if (version_verify >= 320) { + Colors back_color2; + bool vertical_gradient; + padding[3]; + } + // specify the name of the external image file from the data folder + PascalString back_image_name; + BackgroundType back_type; + BackImageStyle back_image_style; + s32 back_scroll_speed_horizontal, back_scroll_speed_vertical; + s32 speed, width, height, cell_size; + if (version_verify >= 320) { + bool enable_views; + padding[3]; + GameRoomView views[4]; + } else + GameRoomView view; + if (version_verify >= 320) { + Transition transition; + u32 transition_time, transition_steps; + } + u32 instance_count; + GameRoomInstance instances[instance_count]; +} [[name(name.data)]]; + +struct GameSound { + u32 version_verify [[hidden]]; + PascalString name; + u32 reserved; + // specify the name of the external audio file from the data folder + PascalString filename; + bool allow_for_sound_effects; + padding[3]; + u32 buffer_count; +} [[name(name.data)]]; + +struct GMF { + u32 version; + u32 verification[2]; + u32 version_verify_options [[hidden]]; + u32 option_count; + PascalString options[option_count]; + u32 version_verify_objects [[hidden]]; + u32 object_count; + GameObject objects[object_count]; + u32 version_verify_rooms [[hidden]]; + u32 room_count; + GameRoom rooms[room_count]; + u32 version_verify_sounds [[hidden]]; + u32 sound_count; + GameSound sounds[sound_count]; +}; + +GMF gmf @ 0x0; diff --git a/patterns/ico.hexpat b/patterns/ico.hexpat index 15ab4708..cee8485b 100644 --- a/patterns/ico.hexpat +++ b/patterns/ico.hexpat @@ -4,6 +4,10 @@ #pragma endian little import std.sys; +import std.mem; +import std.string; +import * from bmp as BMP; +import * from png as PNG; #pragma MIME image/vnd.microsoft.icon #pragma MIME image/x-icon @@ -13,38 +17,65 @@ import std.sys; #pragma MIME application/ico enum ImageType : u16 { - Icon = 1, - Cursor = 2 + Icon = 1, + Cursor = 2 }; struct ICONDIR { - u16 reserved [[hidden]]; - ImageType type; - u16 num_images; + u16 reserved [[hidden]]; + ImageType type; + u16 num_images; +}; + +struct BMPData { + u8 data[sizeof(parent.raw_data)] [[hidden, no_unique_address]]; + std::mem::Section section = std::mem::create_section("BMP Image " + std::string::to_string(addressof(this))); + std::mem::set_section_size(section, sizeof(data) + 14); + u8 headerData[14] @ 0x00 in section [[hidden]]; + headerData[0x00] = 0x42; + headerData[0x01] = 0x4D; + u32 size @ 0x02 in section [[hidden]]; + size = std::mem::get_section_size(section); + std::mem::copy_value_to_section(data, section, 0x0E); + BMP image @ 0x00 in section; + u32 offset @ 0x0D in section [[hidden]]; + offset = addressof(image.lineData); }; struct ImageData { - u8 data[parent.image_data_size] [[inline]]; + std::mem::Bytes raw_data [[no_unique_address]]; + be u64 png_magic [[hidden, no_unique_address]]; + if (png_magic == 0x89504e470d0a1a0a) { + PNG png_image @ $ [[inline, highlight_hidden]]; + } + + // TODO: Sections currently can't be properly accessed accross imported types + // TODO: Uncomment this again once that's been fixed + /*else { + BMPData data [[hidden]]; + BMP bmp_image @ addressof(this) - 14 [[inline, highlight_hidden]]; + }*/ }; struct ICONDIRENTRY { - u8 width, height; - u8 num_colors; - u8 reserved [[hidden]]; - - if (header.type == ImageType::Icon) { - u16 color_planes; - u16 bits_per_pixel; - } else if (header.type == ImageType::Cursor) { - u16 horizontal_hotspot_coordinate; - u16 vertical_hotspot_coordinate; - } - - u32 image_data_size; - ImageData *image_data : u32; + u8 width, height; + u8 num_colors; + u8 reserved [[hidden]]; + + if (header.type == ImageType::Icon) { + u16 color_planes; + u16 bits_per_pixel; + } else if (header.type == ImageType::Cursor) { + u16 horizontal_hotspot_coordinate; + u16 vertical_hotspot_coordinate; + } + + u32 image_data_size; + u32 image_data_offset; + ImageData image_data @ image_data_offset; }; ICONDIR header @ 0x00; ICONDIRENTRY images[header.num_images] @ $; -std::assert(header.reserved == 0x00, "Invalid ICO header"); \ No newline at end of file +std::assert(header.reserved == 0x00, "Invalid ICO header"); diff --git a/patterns/ips.hexpat b/patterns/ips.hexpat index bccee99d..91121ae4 100644 --- a/patterns/ips.hexpat +++ b/patterns/ips.hexpat @@ -4,26 +4,25 @@ #pragma endian big -import std.mem; import std.string; struct Hunk { - u24 offset; - u16 length; - if (!length) { - u16 runCount; - u8 payload; - } - else { - u8 payload[length]; - } + u24 offset; + u16 length; + if (!length) { + u16 runCount; + u8 payload; + } + else { + u8 payload[length]; + } }; struct IPS { - char signature[5]; - Hunk hunks[while($ < std::mem::size() - (3 + 3 * (std::mem::read_string(std::mem::size()-3, 3) != "EOF")))]; - char eof[3]; - u24 truncatedSize[3+(std::mem::read_string(std::mem::size()-3, 3) != "EOF")>3]; + char signature[5]; + Hunk hunks[while($ < std::mem::size() - (3 + 3 * (std::mem::read_string(std::mem::size()-3, 3) != "EOF")))]; + char eof[3]; + u24 truncatedSize[std::mem::read_string(std::mem::size()-3, 3) != "EOF"]; }; IPS ips @ 0x00; diff --git a/patterns/java_class.hexpat b/patterns/java_class.hexpat index 47cac0dc..610f555e 100644 --- a/patterns/java_class.hexpat +++ b/patterns/java_class.hexpat @@ -450,6 +450,15 @@ enum major_version : u2 { Java_SE_15 = 59, Java_SE_16 = 60, Java_SE_17 = 61, + Java_SE_18 = 62, + Java_SE_19 = 63, + Java_SE_20 = 64, + Java_SE_21 = 65, + Java_SE_22 = 66, + Java_SE_23 = 67, + Java_SE_24 = 68, + Java_SE_25 = 69, + Java_SE_26 = 70, }; bitfield access_flags_method { diff --git a/patterns/jpeg.hexpat b/patterns/jpeg.hexpat index 1e7269f6..76860bfc 100644 --- a/patterns/jpeg.hexpat +++ b/patterns/jpeg.hexpat @@ -145,6 +145,7 @@ struct Segment { u16 length; if (marker == Marker::APP0) { APP0 data; + u8 xtra_data[length - sizeof(length) - sizeof(data)]; } else if (marker == Marker::APP14) { APP14 data; } else if (marker == Marker::COM) { diff --git a/patterns/ktx.hexpat b/patterns/ktx.hexpat new file mode 100644 index 00000000..3cff6a54 --- /dev/null +++ b/patterns/ktx.hexpat @@ -0,0 +1,231 @@ +#pragma author Lexi Mayfield +#pragma description Khronos TeXture 1.0 +#pragma MIME image/ktx +#pragma magic [ AB 4B 54 58 20 31 31 BB 0D 0A 1A 0A ] @ 0x00 + +import std.io; +import std.mem; +import type.magic; + +enum GLType : u32 { + compressed = 0x0, + UNSIGNED_BYTE = 0x1401, + BYTE = 0x1400, + UNSIGNED_SHORT = 0x1403, + SHORT = 0x1402, + UNSIGNED_INT = 0x1405, + INT = 0x1404, + HALF_FLOAT = 0x140B, + FLOAT = 0x1406, + UNSIGNED_BYTE_3_3_2 = 0x8032, + UNSIGNED_BYTE_2_3_3_REV = 0x8362, + UNSIGNED_SHORT_5_6_5 = 0x8363, + UNSIGNED_SHORT_5_6_5_REV = 0x8364, + UNSIGNED_SHORT_4_4_4_4 = 0x8033, + UNSIGNED_SHORT_4_4_4_4_REV = 0x8365, + UNSIGNED_SHORT_5_5_5_1 = 0x8034, + UNSIGNED_SHORT_1_5_5_5_REV = 0x8366, + UNSIGNED_INT_8_8_8_8 = 0x8035, + UNSIGNED_INT_8_8_8_8_REV = 0x8367, + UNSIGNED_INT_10_10_10_2 = 0x8036, + UNSIGNED_INT_2_10_10_10_REV = 0x8368, + UNSIGNED_INT_24_8 = 0x84FA, + UNSIGNED_INT_10F_11F_11F_REV = 0x8C3B, + UNSIGNED_INT_5_9_9_9_REV = 0x8C3E, + FLOAT_32_UNSIGNED_INT_24_8_REV = 0x8DAD, +}; + +enum GLFormat : u32 { + compressed = 0x0, + RED = 0x1903, + RG = 0x8227, + RGB = 0x1907, + BGR = 0x80E0, + RGBA = 0x1908, + BGRA = 0x80E1, + RED_INTEGER = 0x8D94, + RG_INTEGER = 0x8228, + RGB_INTEGER = 0x8D98, + BGR_INTEGER = 0x8D9A, + RGBA_INTEGER = 0x8D99, + BGRA_INTEGER = 0x8D9B, + STENCIL_INDEX = 0x1901, + DEPTH_COMPONENT = 0x1902, + DEPTH_STENCIL = 0x84F9, +}; + +enum GLInternalFormat : u32 { + R8 = 0x8229, + R8_SNORM = 0x8F94, + R16 = 0x822A, + R16_SNORM = 0x8F98, + RG8 = 0x822B, + RG8_SNORM = 0x8F95, + RG16 = 0x822C, + RG16_SNORM = 0x8F99, + R3_G3_B2 = 0x2A10, + RGB4 = 0x804F, + RGB5 = 0x8050, + RGB8 = 0x8051, + RGB8_SNORM = 0x8F96, + RGB10 = 0x8052, + RGB12 = 0x8053, + RGB16_SNORM = 0x8F9A, + RGBA2 = 0x8055, + RGBA4 = 0x8056, + RGB5_A1 = 0x8057, + RGBA8 = 0x8058, + RGBA8_SNORM = 0x8F97, + RGB10_A2 = 0x8059, + RGB10_A2UI = 0x906F, + RGBA12 = 0x805A, + RGBA16 = 0x805B, + SRGB8 = 0x8C41, + SRGB8_ALPHA8 = 0x8C43, + R16F = 0x822D, + RG16F = 0x822F, + RGB16F = 0x881B, + RGBA16F = 0x881A, + R32F = 0x822E, + RG32F = 0x8230, + RGB32F = 0x8815, + RGBA32F = 0x8814, + R11F_G11F_B10F = 0x8C3A, + RGB9_E5 = 0x8C3D, + R8I = 0x8231, + R8UI = 0x8232, + R16I = 0x8233, + R16UI = 0x8234, + R32I = 0x8235, + R32UI = 0x8236, + RG8I = 0x8237, + RG8UI = 0x8238, + RG16I = 0x8239, + RG16UI = 0x823A, + RG32I = 0x823B, + RG32UI = 0x823C, + RGB8I = 0x8D8F, + RGB8UI = 0x8D7D, + RGB16I = 0x8D89, + RGB16UI = 0x8D77, + RGB32I = 0x8D83, + RGB32UI = 0x8D71, + RGBA8I = 0x8D8E, + RGBA8UI = 0x8D7C, + RGBA16I = 0x8D88, + RGBA16UI = 0x8D76, + RGBA32I = 0x8D82, + RGBA32UI = 0x8D70, + COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0, // BC1 + COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1, // BC1 + COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2, // BC2 + COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3, // BC3 + COMPRESSED_SRGB_S3TC_DXT1_EXT = 0x8C4C, // BC1 + COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT = 0x8C4D, // BC1 + COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT = 0x8C4E, // BC2 + COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT = 0x8C4F, // BC3 + COMPRESSED_RED_RGTC1 = 0x8DBB, // BC4 + COMPRESSED_SIGNED_RED_RGTC1 = 0x8DBC, // BC4 + COMPRESSED_RG_RGTC2 = 0x8DBD, // BC5 + COMPRESSED_SIGNED_RG_RGTC2 = 0x8DBE, // BC5 + COMPRESSED_RGBA_BPTC_UNORM = 0x8E8C, // BC7 + COMPRESSED_SRGB_ALPHA_BPTC_UNORM = 0x8E8D, // BC7 + COMPRESSED_RGB_BPTC_SIGNED_FLOAT = 0x8E8E, // BC6H + COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F, // BC6H + ETC1_RGB8_OES = 0x8D64, // ETC1 + COMPRESSED_R11_EAC = 0x9270, // EAC + COMPRESSED_SIGNED_R11_EAC = 0x9271, // EAC + COMPRESSED_RG11_EAC = 0x9272, // EAC + COMPRESSED_SIGNED_RG11_EAC = 0x9273, // EAC + COMPRESSED_RGB8_ETC2 = 0x9274, // ETC2 + COMPRESSED_SRGB8_ETC2 = 0x9275, // ETC2 + COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276, // ETC2 + COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277, // ETC2 + COMPRESSED_RGBA8_ETC2_EAC = 0x9278, // ETC2 + COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 0x9279, // ETC2 + COMPRESSED_RGBA_ASTC_4x4_KHR = 0x93B0, // ASTC... + COMPRESSED_RGBA_ASTC_5x4_KHR = 0x93B1, + COMPRESSED_RGBA_ASTC_5x5_KHR = 0x93B2, + COMPRESSED_RGBA_ASTC_6x5_KHR = 0x93B3, + COMPRESSED_RGBA_ASTC_6x6_KHR = 0x93B4, + COMPRESSED_RGBA_ASTC_8x5_KHR = 0x93B5, + COMPRESSED_RGBA_ASTC_8x6_KHR = 0x93B6, + COMPRESSED_RGBA_ASTC_8x8_KHR = 0x93B7, + COMPRESSED_RGBA_ASTC_10x5_KHR = 0x93B8, + COMPRESSED_RGBA_ASTC_10x6_KHR = 0x93B9, + COMPRESSED_RGBA_ASTC_10x8_KHR = 0x93BA, + COMPRESSED_RGBA_ASTC_10x10_KHR = 0x93BB, + COMPRESSED_RGBA_ASTC_12x10_KHR = 0x93BC, + COMPRESSED_RGBA_ASTC_12x12_KHR = 0x93BD, + COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR = 0x93D0, + COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR = 0x93D1, + COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR = 0x93D2, + COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR = 0x93D3, + COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR = 0x93D4, + COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR = 0x93D5, + COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR = 0x93D6, + COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR = 0x93D7, + COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR = 0x93D8, + COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR = 0x93D9, + COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR = 0x93DA, + COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR = 0x93DB, + COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR = 0x93DC, + COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR = 0x93DD, +}; + +enum GLBaseInternalFormat : u32 { + RED = 0x1903, + RG = 0x8227, + RGB = 0x1907, + BGR = 0x80E0, + RGBA = 0x1908, + BGRA = 0x80E1, + RED_INTEGER = 0x8D94, + RG_INTEGER = 0x8228, + RGB_INTEGER = 0x8D98, + BGR_INTEGER = 0x8D9A, + RGBA_INTEGER = 0x8D99, + BGRA_INTEGER = 0x8D9B, + SRGB = 0x8C40, + SRGB8 = 0x8C41, + SRGB_ALPHA = 0x8C42, + SRGB8_ALPHA8 = 0x8C43, +}; + +struct KeyValue { + u32 keyAndValueByteSize; + char keyAndValue[keyAndValueByteSize]; + std::mem::AlignTo<4>; +}; + +struct Header { + u32 endianness; + GLType glType; + u32 glTypeSize; + GLFormat glFormat; + GLInternalFormat glInternalFormat; + GLBaseInternalFormat glBaseInternalFormat; + u32 pixelWidth; + u32 pixelHeight; + u32 pixelDepth; + u32 numberOfArrayElements; + u32 numberOfFaces; + u32 numberOfMipmapLevels; + u32 bytesOfKeyValueData; +}; + +struct MipLevel { + u32 imageSize; + u8 imageData[imageSize]; + std::mem::AlignTo<4>; +}; + +struct KhronosTexture { + type::Magic<"\xABKTX 11\xBB\x0D\x0A\x1A\x0A"> identifier; + Header header; + u32 keyValueEnd = $ + header.bytesOfKeyValueData; + KeyValue kv[while($ < keyValueEnd)]; + MipLevel mipLevels[header.numberOfMipmapLevels]; +}; + +KhronosTexture khronosTexture @ 0x00; \ No newline at end of file diff --git a/patterns/lua40.hexpat b/patterns/lua40.hexpat new file mode 100644 index 00000000..6e30e047 --- /dev/null +++ b/patterns/lua40.hexpat @@ -0,0 +1,133 @@ +#pragma description Lua 4.0 bytecode +#pragma magic [ 1B 4C 75 61 40 ] @ 0x00 +// based off of https://www.lua.org/source/4.0/dump.c.html + +import std.io; + +namespace impl { + fn format_LuaString(auto string) { + if (string.size == 0) { + return "None"; + } + return std::format("\"{}\"", string.data); + }; + + fn format_Version(auto ver) { + return std::format("Ver. {}.{}", ver.major, ver.minor); + }; +} +using LuaFunction; + +bitfield Version { + minor : 4; + major : 4; +} [[format("impl::format_Version")]]; + +struct LuaBinaryHeader { + u8 id_chunk; + char magic[3]; + Version version; + u8 endianness; + u8 size_of_int; + u8 size_of_size_t; + u8 size_of_instruction; // ??? + u8 size_INSTRUCTION; // SIZE_INSTRUCTION in Lua 4 source + u8 size_OP; // SIZE_OP + u8 size_B; // SIZE_B + u8 size_number; // sizeof(Number) + + if (size_number == 4) { + u32 TEST_NUMBER; + } else { + u64 TEST_NUMBER; + } +}; + +LuaBinaryHeader header @ 0; + +struct LuaString { + if (header.size_of_size_t == 4) { + u32 size; + } else { + u64 size; + } + + if (size > 0) { + char data[size]; + } +}[[format("impl::format_LuaString")]]; + +struct Vector { + if (header.size_of_int == 4) { + u32 size; + } else { + u64 size; + } + if (size > 0) { + T values[size]; + } +}; + +struct LocalVar { + LuaString varname; + if (header.size_of_int == 4) { + u32 startpc; + u32 endpc; + } else { + u64 startpc; + u64 endpc; + + } +}; + +struct LuaDebugInfo { + Vector localVar; + if (header.size_of_int == 4) { + Vector lineInfo; // i think this is correct + } else { + Vector lineInfo; + } + +}; + +bitfield LuaNumber { + raw : header.size_number; +}; + +struct LuaConstants{ + Vector stringConstants; + if (header.size_of_int == 4) { + Vector intConstants; + } else { + Vector intConstants; + } + Vector protos; +}; + +struct LuaFunction { + LuaString source; + if (header.size_of_int == 4) { + u32 linedefined; + u32 numparams; + } else { + u64 linedefined; + u64 numparams; + } + u8 is_vararg; + + if (header.size_of_int == 4) { + u32 maxstacksize; + } else { + u64 maxstacksize; + } + + LuaDebugInfo debugInfo; + LuaConstants luaConstants; + if (header.size_of_int == 4) { + Vector code; + } else { + Vector code; + } +}; + +LuaFunction toplevelFunction @ sizeof(header);; // Lua header size is not always the same diff --git a/patterns/lua53.hexpat b/patterns/lua53.hexpat index 12ef6851..dd0fbde6 100644 --- a/patterns/lua53.hexpat +++ b/patterns/lua53.hexpat @@ -44,8 +44,15 @@ struct LuaBinaryHeader { struct LuaString { u8 size; if (size > 0) { - char data[size-1]; + if (size == 0xff) { + u64 sizeReal; + char data[sizeReal-1]; + } else { + char data[size-1]; + } } + + }[[format("impl::format_LuaString")]]; struct LuaConstant { diff --git a/patterns/ne.hexpat b/patterns/ne.hexpat index 3cae5c2f..6c825b11 100644 --- a/patterns/ne.hexpat +++ b/patterns/ne.hexpat @@ -1,3 +1,4 @@ +#pragma author gmestanley #pragma description Microsoft DOS NE executable #pragma MIME application/x-ms-ne-executable diff --git a/patterns/nes.hexpat b/patterns/nes.hexpat index 1fdce899..797d8ec2 100644 --- a/patterns/nes.hexpat +++ b/patterns/nes.hexpat @@ -2,87 +2,406 @@ #pragma description Nintendo Entertainment System ROM (.nes) #pragma MIME application/x-nes-rom -import std.mem; import std.string; -bitfield iNES07Flags { - mirroringIsVertical : 1; - batterybackedPRGRAM : 1; - trainerOf512Bytes : 1; - ignoreMirroring : 1; - mapperLowerNybble : 4; - vsUnisystem : 1; - playchoice10 : 1; - nes2Format : 2 [[name("nes2.0Format")]]; - mapperHigherNybble : 4; -} [[name("iNES0.7Flags")]]; - -bitfield Flags9 { - isPAL : 1; - padding : 7; -}; - -bitfield Flags10 { - tvSystem : 2; - padding : 2; - prgRAM : 1; - busConflicts : 1; - padding : 2; -}; - -struct iNESFlags { - iNES07Flags ines07Flags [[name("ines0.7Flags")]]; - if (!ines07Flags.nes2Format) { - u8 prgRAM8KBMultiplier; - Flags9 flags9; - Flags10 flags10; - restLength = 5; - } - else { - restLength = 9; - } -}; - -u8 restLength; +bitfield Flags { + mirroringIsHorizontal : 1; + ignoreMirroring : 1; + batterybackedPRGRAM : 1; + trainerOf512Bytes : 1; + lowerMapperNybble : 4; +}; + +enum ConsoleType : u8 { + Regular, + VsSystem, + PlayChoice10, + ExtendedConsoleType +}; + +fn consoleType(u8 bits) { + ConsoleType type = bits; + return type; +}; + +fn nes2Format(u8 bits) { + return std::string::to_string(bits == 2) + " (" + std::string::to_string(bits) + ")"; +}; + +bitfield iNESFlags7 { + consoleType : 2 [[format("consoleType")]]; + nes2Format : 2 [[format("nes2Format"), name("nes2.0Format")]]; + higherMapperNybble : 4; +}; + +bitfield iNESFlags9 { + tvSystem : 1 [[comment("0 = NTSC, 1 = PAL")]]; + padding : 7; +}; + +fn formatDualTVSystem(u8 bits) { + match (bits) { + (0): return "NTSC"; + (2): return "PAL"; + (1 || 3): return "Dual Compatible"; + } +}; + +bitfield iNESFlags10 { + dualTVSystem : 2 [[format("formatDualTVSystem")]]; + padding : 2; + prgRAM : 1; + busConflicts : 1; +}; + +bitfield MapperExtra { + highestMapperNybble : 4; + submapper : 4; +}; + +bitfield ROMSize { + extraPRGROMSize : 4; + extraCHRROMSize : 4; +}; + +bitfield PRGRAMSize { + prgRAMShiftCount : 4; + eepromShiftCount : 4 [[comment("EEPROM = Non-volatile PRG RAM")]]; +}; + +bitfield CHRRAMSize { + chrRAMSizeShiftCount : 4; + chrNVRAMSizeShiftCount : 4; +}; + +enum TimingList : u8 { + NTSC, + PAL, + MultiRegion, + Dendy +}; + +fn Timing(u8 value) { + TimingList type = value; + return type; +}; + +bitfield Timing { + processorTiming : 2 [[format("Timing")]]; + padding : 6; +}; + +bitfield VsSystemType { + vsPPUType : 4; + vsHardwareType: 4; +}; + +enum ExtendedConsoleType : ConsoleType { + DecimalModeFamiclone = 3, + PlugThrough, + VT01, + VT02, + VT03, + VT09, + VT32, + VT3xx, + UM6578, + FamicomNetworkSystem +}; + +fn formatExtendedConsoleType(u8 nybble) { + ExtendedConsoleType type = nybble; + return type; +}; + +bitfield ExtendedConsoleTypeByte { + type : 4 [[format("formatExtendedConsoleType")]]; + padding : 4; +}; + +bitfield MiscellaneousROMs { + numberOfMiscellaneousROMs : 2; + padding : 6; +}; + +bitfield DefaultExpansionDevice { + defaultExpansionDevice : 6; +}; + +struct NES2Attributes { + MapperExtra mapperExtra; + ROMSize romSize; + PRGRAMSize prgRAMSize; + CHRRAMSize chrRAMSize; + Timing timing; + if (parent.inesFlags7.consoleType == ConsoleType::VsSystem) { + VsSystemType vsSystemType; + } + else if (parent.inesFlags7.consoleType == ConsoleType::ExtendedConsoleType) { + ExtendedConsoleTypeByte ExtendedConsoleTypeByte; + } + else { + padding[1]; + } + MiscellaneousROMs miscellaneousROMs; + DefaultExpansionDevice defaultExpansionDevice; +} [[inline]]; + +fn renderEOF(str string) { + return "\"NES\""; +}; struct Header { - char identifier[4]; - u8 prgROM16KBMultiplier; - u8 chrROM8KBMultiplier; - iNESFlags inesFlags; - char rest[restLength]; + char identifier[4] [[format("renderEOF")]]; + u8 prgROMSizeBy16KiBs; + u8 chrROMSizeBy8KiBs; + Flags flags; + if ($[0x07] & 12 != 4) { + iNESFlags7 inesFlags7; + if (inesFlags7.nes2Format) + NES2Attributes nes2Attributes; + else if ($[0x07] & 12 == 0 && !std::mem::read_unsigned($, 4)) { + u8 prgRAMSizeBy8KiBs; + iNESFlags9 inesFlags9; + iNESFlags10 inesFlags10; + } + } }; Header header @ 0x00; +u8 trainer[512*header.flags.trainerOf512Bytes] @ 0x10; + +enum CHRType : u8 { + CHRROM, + CHRRAM +}; + +fn chrType(u8 value) { + CHRType enumValue = value; + return enumValue; +}; + +fn chrSize(u8 value) { + u24 actualSize; + if (value == 4) actualSize = 262144; + else actualSize = 8192 * header.chrROMSizeBy8KiBs; + return std::string::to_string(value) + " (" + std::string::to_string(actualSize) + ")"; +}; + +bitfield MemorySize { + prgROMSizeBy16KiBs : 4; + chrType : 1 [[format("chrType")]]; + chrSize : 3 [[format("chrSize")]]; +}; + +enum ArrangementList : u8 { + Horizontal, + Vertical +}; + +fn arrangement(u8 value) { + ArrangementList enumValue = value; + return enumValue; +}; + +enum MapperList : u8 { + NROM, + CNROM, + UNROM, + GNROM, + MMC +}; + +fn mapper(u8 value) { + MapperList enumValue = value; + return enumValue; +}; + +bitfield CartridgeType { + nametableArrangement : 1 [[format("arrangement")]]; + mapper : 7 [[format("mapper")]]; +}; + enum EncodingType : u8 { - ASCII = 1 + None, + ASCII, + JIS }; +fn titleLength(u8 value) { return value+1; }; + struct OfficialHeader { - char title[16] [[hex::spec_name("Title Registration Area")]]; - u16 programChecksum; - u16 characterChecksum; - u8 memorySize [[hex::spec_name("Cartridge Memory Size")]]; - u8 cartridgeType; - EncodingType encodingType [[hex::spec_name("Registration Character Type Distinction")]]; - u8 titleLength [[hex::spec_name("Registration Characters Count")]]; - u8 makerID [[hex::spec_name("Maker Code")]]; - u8 complementaryChecksum; + char title[16] [[hex::spec_name("Title Registration Area")]]; + u16 programChecksum; + u16 characterChecksum; + MemorySize memorySize [[hex::spec_name("Cartridge Memory Size")]]; + CartridgeType cartridgeType; + EncodingType encodingType [[hex::spec_name("Registration Characters Type Distinction")]]; + u8 titleLength [[hex::spec_name("Registration Characters Count"), transform("titleLength")]]; + u8 licenseeID [[hex::spec_name("Maker Code")]]; + u8 complementaryChecksum [[hex::spec_name("Checksum for characterChecksum~makerID")]]; }; -union OfficialHeaderUnion { - u8 miscellaneousData[26]; - OfficialHeader officialHeader; +u24 calculatedPRGROMSize = 16384 * ((0x0100 * ($[9] & 0x0F)) * ($[7] & 12 == 8) + header.prgROMSizeBy16KiBs); + +fn hasOfficialHeader() { + u8 sum; + for (u8 i = 0, i < 8, i += 1) { + sum += $[(calculatedPRGROMSize - 14) + i]; + } + return !sum; }; struct PRGROM { - u8 data[16384*header.prgROM16KBMultiplier-32]; - OfficialHeaderUnion officialHeaderUnion; - u16 nmi; - u16 entryPoint; - u16 externalIRQ; + u8 data[calculatedPRGROMSize - 26 * hasOfficialHeader() - 6]; + if (hasOfficialHeader()) + OfficialHeader officialHeader; + u16 nmi; + u16 resetVector [[comment("Entry Point")]]; + u16 externalIRQ; }; -PRGROM prgROM @ sizeof(header); -u8 chrROM[8192*header.chrROM8KBMultiplier] @ sizeof(header)+sizeof(prgROM); \ No newline at end of file +PRGROM prgROM @ 0x10 + sizeof(trainer); +u8 chrROM[8192 * ((0x0100 * ($[9] >> 4)) * ($[7] & 12 == 8) + header.chrROMSizeBy8KiBs)] @ addressof(prgROM) + 16384 * header.prgROMSizeBy16KiBs; + +fn identifyMapper(u16 mapperValue, u8 submapperValue) { + str mapper; + str submapper; + str designation; + match (mapperValue) { + (0): mapper = "NROM"; + (1): mapper = "MMC1B"; + (2): mapper = "UxROM"; + (3): mapper = "CNROM-32"; + (4): mapper = "MMC3"; + (5): mapper = "MMC5"; + (6): mapper = "Front Fareast Magic Card 1/2M RAM Cartridge"; + (7): mapper = "AxROM"; + (8): mapper = "Front Fareast Magic Card 1/2M RAM Cartridge (Initial latch-based banking mode 4)"; + (9): mapper = "MMC2"; + (10): mapper = "MMC4"; + (11): mapper = "Color Dreams"; + (12): mapper = "(See submapper)"; + (13): mapper = "CPROM"; + (14): mapper = "SL-1632"; + (15): mapper = "K-102xx"; + (16): mapper = "Bandai FCG boards"; + (17): mapper = "Front Fareast Super Magic Card RAM cartridge"; + (18): mapper = "SS88006 (Jaleco)"; + (19): mapper = "Namco 129/163"; + (20): mapper = "Famicom Disk System"; + (21): mapper = "VRC4a/VRC4c"; + (22): mapper = "VRC2a"; + (23): mapper = "VRC4e or VRC2b + VRC4f"; + (24): mapper = "VRC6a"; + (25): mapper = "VRC4d or VRC2c + VRC4b"; + (26): mapper = "VRC6b"; + (27): mapper = "World Hero"; + (28): mapper = "Action 53"; + (29): mapper = "RET-CUFROM"; + (30): mapper = "UNROM 512"; + (31): mapper = "NSF"; + (32): mapper = "G-101"; + (33): mapper = "TC0190"; + (34): mapper = "(See submapper)"; + (35): mapper = "J.Y. Company (8KiB WRAM)"; + (36): mapper = "01-22000-400"; + (37): mapper = "SMB+Tetris+NWC"; + (38): mapper = "Crime Busters"; + (39): mapper = "Study & Game 32-in-1"; + (40): mapper = "NTDEC 27xx"; + (41): mapper = "Caltron 6-in-1"; + (42): mapper = "FDS -> NES Hacks"; + (43): mapper = "TONY-I/YS-612"; + (44): mapper = "Super Big 7-in-1"; + (45): mapper = "GA23C"; + (46): mapper = "Rumble Station"; + (47): mapper = "Super Spike V'Ball + NWC"; + (48): mapper = "TC0690"; + (49): mapper = "Super HIK 4-in-1"; + (50): mapper = "761214"; + (51): mapper = "11-in-1 Ball Games"; + (52): mapper = "Realtec 8213"; + (53): mapper = "Supervision 16-in-1"; + (54): mapper = "Novel Diamond 9999999-in-1"; + (55): mapper = "QFJ"; + (56): mapper = "KS202"; + (57): mapper = "GK"; + (58): mapper = "WQ"; + (59): mapper = "T3H53"; + (60): mapper = "Reset-based NROM-128 4-in-1"; + (61): mapper = "(See submapper)"; + (62): mapper = "Super 700-in-1"; + (63): mapper = "(See submapper)"; + (64): mapper = "RAMBO-1"; + (65): mapper = "H3001"; + (66): mapper = "GxROM"; + (67): mapper = "Sunsoft-3"; + (68): mapper = "Sunsoft-4"; + (69): mapper = "Sunsoft FME-7/Sunsoft 5A/Sunsoft 5B"; + (70): mapper = "Family Trainer Mat"; + (71): mapper = "Camerica"; + (72): mapper = "JF-17"; + (73): mapper = "VRC3"; + (74): mapper = "860908C"; + (75): mapper = "VRC1"; + (76): mapper = "NAMCOT-3446"; + (77): mapper = "Napoleon Senki"; + (78): mapper = "74HC161/32"; + (79): mapper = "NINA-003/NINA-006"; + (80): mapper = "X1-005"; + (81): mapper = "N715021"; + (82): mapper = "X1-017"; + } + if (mapperValue == 3) { + match (submapperValue) { + (0): submapper = "Bus conflict"; + (1): submapper = "No bus conflicts"; + (2): submapper = "AND-type bus conflicts"; + } + } + else if (mapperValue == 4) designation = "TxROM"; + else if (mapperValue == 12) { + match (submapperValue) { + (0): submapper = "SL-5020B (Gouder)"; + (1): submapper = "Front Fareast Magic Card 4M RAM Cartridge"; + } + } + else if (mapperValue == 16) { + match (submapperValue) { + (0): submapper = "FCG-1/2 or LZ93D50"; + (4): submapper = "FCG-1/2"; + (5): submapper = "LZ93D50"; + } + } + else if (mapperValue == 34) { + match (submapperValue) { + (0): submapper = "NINA-001/NINA-002"; + (1): submapper = "BNROM"; + } + } + else if (mapperValue == 40) { + match (submapperValue) { + (0): submapper = "NTDEC 2722"; + (1): submapper = "NTDEC 2752"; + } + } + else if (mapperValue == 61) { + match (submapperValue) { + (0): submapper = "NTDEC 0324"; + (1): submapper = "NTDEC BS-N032"; + (_): submapper = "GS-2017"; + } + } + else if (mapperValue == 63) { + match (submapperValue) { + (0): submapper = "TH2291-3"; + (1): submapper = "82AB"; + } + } + std::print("Mapper: " + mapper + "(" + std::string::to_string(mapperValue) + ")"); + if (submapper) std::print("Submapper: " + submapper + "(" + std::string::to_string(submapperValue) + ")"); + if (designation) std::print("Designation: " + designation); +}; +identifyMapper(0x0100 * ($[8] & 0x0F) + 0x10 * ($[7] & 0x0F) + header.flags.lowerMapperNybble, $[8] >> 4); diff --git a/patterns/notepad-state.hexpat b/patterns/notepad-state.hexpat new file mode 100644 index 00000000..36522761 --- /dev/null +++ b/patterns/notepad-state.hexpat @@ -0,0 +1,95 @@ +#pragma author Gal1leo +#pragma description Windows Notepad State Files +#pragma magic [ 4E 50 00 ] @ 0x00 + + +import type.leb128; +import type.time; +import type.magic; +import std.time; + + +std::mem::Section currentSection; + + +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 MoreOptionsBlock { + u8 unknown_spell_1; + u8 unknown_spell_2; + u8 formatting_type; +}; + + +struct ConfigBlock { + bool word_wrap; + bool rtl; + bool show_unicode; + uLEB128 more_options_number; + MoreOptionsBlock more_options_block; +}; + + +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; + currentSection = std::mem::create_section("File Content"); + char16 content[content_length]; + std::mem::copy_value_to_section(content, currentSection, 0); + 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/patterns/nsf.hexpat b/patterns/nsf.hexpat new file mode 100644 index 00000000..b80465e6 --- /dev/null +++ b/patterns/nsf.hexpat @@ -0,0 +1,109 @@ +#pragma author gmestanley +#pragma description NES Sound Format file + +import std.string; + +struct ChunkMetadata { + u32 length; + char ID[4]; +} [[inline]]; + +struct TimeChunkData { + u32 trackLengths[while($\""; +}; + +struct Header { + char signature[5] [[format("renderEOF")]]; + u8 version; + u8 songAmount; + u8 startingSong; + u16 dataLoadAddress; + u16 dataInitAddress; + u16 dataPlayAddress; + char gameName[32] [[format("formatName")]]; + char songwriting[32] [[format("formatName")]]; + char copyrightHolder[32] [[format("formatName")]]; + u16 ntscPlaySpeed; + u8 bankswitchInitValues[8]; + u16 palPlaySpeed; + Region region; + ExtraSoundChipSupport extraSoundChipSupport; + NSF2Flags nsf2flags; + u24 dataLength; +}; + +Header header @ 0x00; + +struct Chunks { + if (header.dataLength) + NSFEChunk chunks[while($ 0) { - AlignmentType enumValue = value; - return enumValue; - } - return value; + if (value) { + AlignmentType enumValue = value; + return enumValue; + } + return value; }; bitfield SectionFlags { - padding : 3; - doNotPad : 1 [[hex::spec_name("IMAGE_SCN_TYPE_NO_PAD")]]; - padding : 1; - containsCode : 1 [[hex::spec_name("IMAGE_SCN_CNT_CODE")]]; - containsInitializedData : 1 [[hex::spec_name("IMAGE_SCN_CNT_INITIALIZED_DATA")]]; - containsUninitializedData : 1 [[hex::spec_name("IMAGE_SCN_CNT_UNINITIALIZED_DATA")]]; - linkOther : 1 [[hex::spec_name("IMAGE_SCN_LNK_OTHER")]]; - linkHasInformation : 1 [[hex::spec_name("IMAGE_SCN_LNK_INFO")]]; - padding : 1; - linkRemove : 1 [[hex::spec_name("IMAGE_SCN_LNK_REMOVE")]]; - linkHasCOMDAT : 1 [[hex::spec_name("IMAGE_SCN_LNK_COMDAT")]]; - padding : 1; - resetSpeculativeExceptions : 1 [[hex::spec_name("")]]; - globalPointerRelocations : 1 [[hex::spec_name("IMAGE_SCN_GPREL")]]; - purgeable : 1 [[hex::spec_name("IMAGE_SCN_MEM_PURGEABLE")]]; - is16Bit : 1 [[hex::spec_name("IMAGE_SCN_MEM_16BIT")]]; - locked : 1 [[hex::spec_name("IMAGE_SCN_MEM_LOCKED")]]; - preloaded : 1 [[hex::spec_name("IMAGE_SCN_MEM_PRELOAD")]]; - dataAlignment : 4 [[format("formatAlignmentBits")]]; - linkExtendedRelocations : 1 [[hex::spec_name("IMAGE_SCN_LNK_NRELOC_OVFL")]]; - discardable : 1 [[hex::spec_name("IMAGE_SCN_MEM_DISCARDABLE")]]; - notCached : 1 [[hex::spec_name("IMAGE_SCN_MEM_NOT_CACHED")]]; - notPageable : 1 [[hex::spec_name("IMAGE_SCN_MEM_NOT_PAGED")]]; - shared : 1 [[hex::spec_name("IMAGE_SCN_MEM_SHARED")]]; - executed : 1 [[hex::spec_name("IMAGE_SCN_MEM_EXECUTE")]]; - read : 1 [[hex::spec_name("IMAGE_SCN_MEM_READ")]]; - writtenOn : 1 [[hex::spec_name("IMAGE_SCN_MEM_WRITE")]]; + padding : 3; + doNotPad : 1 [[hex::spec_name("IMAGE_SCN_TYPE_NO_PAD")]]; + padding : 1; + containsCode : 1 [[hex::spec_name("IMAGE_SCN_CNT_CODE")]]; + containsInitializedData : 1 [[hex::spec_name("IMAGE_SCN_CNT_INITIALIZED_DATA")]]; + containsUninitializedData : 1 [[hex::spec_name("IMAGE_SCN_CNT_UNINITIALIZED_DATA")]]; + linkOther : 1 [[hex::spec_name("IMAGE_SCN_LNK_OTHER")]]; + linkHasInformation : 1 [[hex::spec_name("IMAGE_SCN_LNK_INFO")]]; + padding : 1; + linkRemove : 1 [[hex::spec_name("IMAGE_SCN_LNK_REMOVE")]]; + linkHasCOMDAT : 1 [[hex::spec_name("IMAGE_SCN_LNK_COMDAT")]]; + padding : 1; + resetSpeculativeExceptions : 1 [[hex::spec_name("")]]; + globalPointerRelocations : 1 [[hex::spec_name("IMAGE_SCN_GPREL")]]; + purgeable : 1 [[hex::spec_name("IMAGE_SCN_MEM_PURGEABLE")]]; + is16Bit : 1 [[hex::spec_name("IMAGE_SCN_MEM_16BIT")]]; + locked : 1 [[hex::spec_name("IMAGE_SCN_MEM_LOCKED")]]; + preloaded : 1 [[hex::spec_name("IMAGE_SCN_MEM_PRELOAD")]]; + dataAlignment : 4 [[format("formatAlignmentBits")]]; + linkExtendedRelocations : 1 [[hex::spec_name("IMAGE_SCN_LNK_NRELOC_OVFL")]]; + discardable : 1 [[hex::spec_name("IMAGE_SCN_MEM_DISCARDABLE")]]; + notCached : 1 [[hex::spec_name("IMAGE_SCN_MEM_NOT_CACHED")]]; + notPageable : 1 [[hex::spec_name("IMAGE_SCN_MEM_NOT_PAGED")]]; + shared : 1 [[hex::spec_name("IMAGE_SCN_MEM_SHARED")]]; + executed : 1 [[hex::spec_name("IMAGE_SCN_MEM_EXECUTE")]]; + read : 1 [[hex::spec_name("IMAGE_SCN_MEM_READ")]]; + writtenOn : 1 [[hex::spec_name("IMAGE_SCN_MEM_WRITE")]]; }; fn formatSectionName(str string) { - for (u8 i = 0, i < 8, i += 1) { - if (std::mem::read_unsigned($+i, 1) == 0) { - return "\"" + std::string::substr(string, 0, i) + "\""; - } - } + for (u8 i = 0, i < 8, i += 1) { + if (std::mem::read_unsigned($+i, 1) == 0) { + return "\"" + std::string::substr(string, 0, i) + "\""; + } + } }; struct SectionHeader { - char name[8] [[name("sectionName"), hex::spec_name("name"), format("formatSectionName")]]; - u32 virtualSize; - u32 rva [[hex::spec_name("virtualAddress")]]; - u32 sizeOfRawData; - u32 ptrRawData; - u32 ptrRelocations; - u32 ptrLineNumbers; - u16 numberOfRelocations; - u16 numberOfLineNumbers; - SectionFlags characteristics; + char name[8] [[name("sectionName"), hex::spec_name("name"), format("formatSectionName")]]; + u32 virtualSize; + u32 rva [[hex::spec_name("virtualAddress")]]; + u32 sizeOfRawData; + u32 ptrRawData; + u32 ptrRelocations; + u32 ptrLineNumbers; + u16 numberOfRelocations; + u16 numberOfLineNumbers; + SectionFlags characteristics; }; SectionHeader sectionsTable[coffHeader.numberOfSections] @ addressof(coffHeader.optionalHeader) + coffHeader.sizeOfOptionalHeader; @@ -318,886 +322,887 @@ SectionHeader sectionsTable[coffHeader.numberOfSections] @ addressof(coffHeader. u16 currentSectionIndex; fn relativeVirtualDifference() { - return sectionsTable[currentSectionIndex].rva - sectionsTable[currentSectionIndex].ptrRawData; + return sectionsTable[currentSectionIndex].rva - sectionsTable[currentSectionIndex].ptrRawData; }; fn wordsize() { - return std::mem::read_unsigned(addressof(coffHeader.optionalHeader.magic), 2) / 0x41; -}; - -fn formatNullTerminatedString(str string) { - return "\"" + std::string::substr(string, 0, std::string::length(string)-1) + "\""; + return std::mem::read_unsigned(addressof(coffHeader.optionalHeader.magic), 2) / 0x41; }; // Exception Table bitfield FunctionBitfield { - prologLength : 8; - functionLength : 22; - instructions32Bit : 1; - exceptionHandler : 1; + prologLength : 8; + functionLength : 22; + instructions32Bit : 1; + exceptionHandler : 1; }; struct FunctionTableEntry { - if (coffHeader.architecture == ArchitectureType::MIPSFPU || coffHeader.architecture == ArchitectureType::R3000) { - u32 beginVA; - u32 endVA; - u32 exceptionHandlerPointer; - u32 handlerDataPointer; - u32 prologEndVA; - } else if (coffHeader.architecture == ArchitectureType::ARM || coffHeader.architecture == ArchitectureType::ARM64 || coffHeader.architecture == ArchitectureType::ARMNT || - coffHeader.architecture == ArchitectureType::POWERPC || coffHeader.architecture == ArchitectureType::POWERPCFP || - coffHeader.architecture == ArchitectureType::SH3 || coffHeader.architecture == ArchitectureType::SH3DSP || coffHeader.architecture == ArchitectureType::SH4) { - u32 beginVA; - FunctionBitfield miscellaneousBits; - } else if (coffHeader.architecture == ArchitectureType::AMD64 || coffHeader.architecture == ArchitectureType::IA64) { - u32 beginRVA; - u32 endRVA; - u32 unwindInformationRVA; - } + if (coffHeader.architecture == ArchitectureType::MIPSFPU || coffHeader.architecture == ArchitectureType::R3000) { + u32 beginVA; + u32 endVA; + u32 exceptionHandlerPointer; + u32 handlerDataPointer; + u32 prologEndVA; + } else if (coffHeader.architecture == ArchitectureType::ARM || coffHeader.architecture == ArchitectureType::ARM64 || coffHeader.architecture == ArchitectureType::ARMNT || + coffHeader.architecture == ArchitectureType::POWERPC || coffHeader.architecture == ArchitectureType::POWERPCFP || + coffHeader.architecture == ArchitectureType::SH3 || coffHeader.architecture == ArchitectureType::SH3DSP || coffHeader.architecture == ArchitectureType::SH4) { + u32 beginVA; + FunctionBitfield miscellaneousBits; + } else if (coffHeader.architecture == ArchitectureType::AMD64 || coffHeader.architecture == ArchitectureType::IA64) { + u32 beginRVA; + u32 endRVA; + u32 unwindInformationRVA; + } }; struct ExceptionTable { - FunctionTableEntry functionTableEntries[while($ < (coffHeader.optionalHeader.directories[3].rva - relativeVirtualDifference()) + coffHeader.optionalHeader.directories[3].size)] [[inline]]; + FunctionTableEntry functionTableEntries[while($ < (coffHeader.optionalHeader.directories[3].rva - relativeVirtualDifference()) + coffHeader.optionalHeader.directories[3].size)] [[inline]]; }; // Exports Table struct ExportDirectoryTable { - u32 flags [[name("exportFlags")]]; - type::time32_t timeDateStamp [[name("exportTimeDateStamp")]]; - u16 majorVersion; - u16 minorVersion; - u32 imageNameRVA; - u32 ordinalBase [[name("exportOrdinalBase")]]; - u32 addressesAmount [[name("exportAddressesAmount")]]; - u32 namePointersAmount [[name("exportNamePointersAmount")]]; - u32 addressTableRVA [[name("exportAddressTableRVA")]]; - u32 namePointerTableRVA [[name("exportNamePointerTableRVA")]]; - u32 ordinalTableRVA [[name("exportOrdinalTableRVA")]]; + u32 flags [[name("exportFlags")]]; + type::time32_t timeDateStamp [[name("exportTimeDateStamp")]]; + u16 majorVersion; + u16 minorVersion; + u32 imageNameRVA; + u32 ordinalBase [[name("exportOrdinalBase")]]; + u32 addressesAmount [[name("exportAddressesAmount")]]; + u32 namePointersAmount [[name("exportNamePointersAmount")]]; + u32 addressTableRVA [[name("exportAddressTableRVA")]]; + u32 namePointerTableRVA [[name("exportNamePointerTableRVA")]]; + u32 ordinalTableRVA [[name("exportOrdinalTableRVA")]]; }; struct ExportAddress { - if (sectionsTable[currentSectionIndex].ptrRawData > std::mem::read_unsigned($, 4) > sectionsTable[currentSectionIndex].sizeOfRawData) { - u32 exportRVA; - } - else { - u32 forwarderRVA; - } + if (sectionsTable[currentSectionIndex].ptrRawData > std::mem::read_unsigned($, 4) > sectionsTable[currentSectionIndex].sizeOfRawData) { + u32 exportRVA; + } + else { + u32 forwarderRVA; + } }; struct ExportNamePointer { - u32 exportNameRVA; - char exportName[] @ exportNameRVA - relativeVirtualDifference() [[format("formatNullTerminatedString")]]; + u32 exportNameRVA; + char exportName[] @ exportNameRVA - relativeVirtualDifference() [[format("formatNullTerminatedString")]]; }; struct ExportsTable { - ExportDirectoryTable directoryTable; - ExportAddress exportAddressTable[directoryTable.addressesAmount] @ directoryTable.addressTableRVA - relativeVirtualDifference(); - ExportNamePointer exportNamePointerTable[directoryTable.namePointersAmount] @ directoryTable.namePointerTableRVA - relativeVirtualDifference(); - if (directoryTable.ordinalTableRVA > relativeVirtualDifference()) { - u16 exportOrdinalTable[directoryTable.namePointersAmount] @ directoryTable.ordinalTableRVA - relativeVirtualDifference(); - } - if (directoryTable.imageNameRVA > relativeVirtualDifference()) { - char imageName[] @ directoryTable.imageNameRVA - relativeVirtualDifference() [[format("formatNullTerminatedString")]]; - } - $ = addressof(this)+coffHeader.optionalHeader.directories[0].size; + ExportDirectoryTable directoryTable; + ExportAddress exportAddressTable[directoryTable.addressesAmount] @ directoryTable.addressTableRVA - relativeVirtualDifference(); + ExportNamePointer exportNamePointerTable[directoryTable.namePointersAmount] @ directoryTable.namePointerTableRVA - relativeVirtualDifference(); + if (directoryTable.ordinalTableRVA > relativeVirtualDifference()) { + u16 exportOrdinalTable[directoryTable.namePointersAmount] @ directoryTable.ordinalTableRVA - relativeVirtualDifference(); + } + if (directoryTable.imageNameRVA > relativeVirtualDifference()) { + char imageName[] @ directoryTable.imageNameRVA - relativeVirtualDifference() [[format("formatNullTerminatedString")]]; + } + $ = addressof(this)+coffHeader.optionalHeader.directories[0].size; }; // Imports Table bitfield OrdinalFlagByte { - padding : 7; - flag : 1 [[name("ordinalFlag")]]; + padding : 7; + flag : 1 [[name("ordinalFlag")]]; +}; + +fn formatMangledString(str string) { + return hex::dec::demangle(formatNullTerminatedString(string)); }; struct ImportsName { - u16 hint; - char name[] [[format("formatNullTerminatedString")]]; - if ($ % 2 == 1) { u8 pad; } + u16 hint; + if (std::mem::read_string($, 1) == "?") + char name[] [[format("formatMangledString")]]; + else + char name[] [[format("formatNullTerminatedString")]]; + if ($ % 2 == 1) { u8 pad; } }; struct ImportsAddress { - OrdinalFlagByte ordinalFlagByte @ $+(wordsize()-1); - if (ordinalFlagByte.flag) { - u16 ordinalNumber; - padding[wordsize()-2]; - } else { - u32 nameTableRVA; - if (coffHeader.optionalHeader.magic == PEFormat::PE32Plus) { - padding[4]; - } - } + OrdinalFlagByte ordinalFlagByte @ $+(wordsize()-1); + if (ordinalFlagByte.flag) { + u16 ordinalNumber; + padding[wordsize()-2]; + } else { + u32 nameTableRVA; + if (coffHeader.optionalHeader.magic == PEFormat::PE32Plus) { + padding[4]; + } + } }; struct ImportsLookup : ImportsAddress { - if (!ordinalFlagByte.flag && std::mem::read_unsigned($-wordsize(), wordsize()) > 0) { - ImportsName name @ nameTableRVA - relativeVirtualDifference(); - } + if (!ordinalFlagByte.flag && std::mem::read_unsigned($-wordsize(), wordsize()) > 0) { + ImportsName name @ nameTableRVA - relativeVirtualDifference(); + } }; struct ImportsDirectory { - u32 lookupTableRVA; - u32 timeDateStamp; - u32 forwarderChain; - u32 dllNameRVA; - u32 addressTableRVA; + u32 lookupTableRVA; + u32 timeDateStamp; + u32 forwarderChain; + u32 dllNameRVA; + u32 addressTableRVA; }; struct ImportsStructure { - if (parent.importsDirectoryTable[std::core::array_index()].lookupTableRVA > 0) { - ImportsLookup lookupTable[while(std::mem::read_unsigned($, wordsize()) != 0)] @ parent.importsDirectoryTable[std::core::array_index()].lookupTableRVA - relativeVirtualDifference(); - } - if (parent.importsDirectoryTable[std::core::array_index()].addressTableRVA > 0) { - ImportsAddress addressTable[while(std::mem::read_unsigned($, wordsize()) != 0)] @ parent.importsDirectoryTable[std::core::array_index()].addressTableRVA - relativeVirtualDifference(); - } - if (parent.importsDirectoryTable[std::core::array_index()].dllNameRVA > 0) { - char dllName[] @ parent.importsDirectoryTable[std::core::array_index()].dllNameRVA - relativeVirtualDifference() [[format("formatNullTerminatedString")]]; - } + if (parent.importsDirectoryTable[std::core::array_index()].lookupTableRVA > 0) { + ImportsLookup lookupTable[while(std::mem::read_unsigned($, wordsize()) != 0)] @ parent.importsDirectoryTable[std::core::array_index()].lookupTableRVA - relativeVirtualDifference(); + } + if (parent.importsDirectoryTable[std::core::array_index()].addressTableRVA > 0) { + ImportsAddress addressTable[while(std::mem::read_unsigned($, wordsize()) != 0)] @ parent.importsDirectoryTable[std::core::array_index()].addressTableRVA - relativeVirtualDifference(); + } + if (parent.importsDirectoryTable[std::core::array_index()].dllNameRVA > 0) { + char dllName[] @ parent.importsDirectoryTable[std::core::array_index()].dllNameRVA - relativeVirtualDifference() [[format("formatNullTerminatedString")]]; + } } [[inline]]; struct ImportsTable { - ImportsDirectory importsDirectoryTable[while(std::mem::read_unsigned($, 16) != 0)]; - ImportsStructure importsStructures[sizeof(importsDirectoryTable)/sizeof(importsDirectoryTable[0])] [[inline]]; - $ = addressof(this)+coffHeader.optionalHeader.directories[1].size; + ImportsDirectory importsDirectoryTable[while(std::mem::read_unsigned($, 16) != 0)]; + ImportsStructure importsStructures[sizeof(importsDirectoryTable)/sizeof(importsDirectoryTable[0])] [[inline]]; + $ = addressof(this)+coffHeader.optionalHeader.directories[1].size; }; struct DelayedImportsDirectory { - u32 attributes; - u32 name; - u32 moduleHandle; - u32 delayImportAddressTable; - u32 delayImportNameTable; - u32 boundDelayImportTable; - u32 unloadDelayImportTable; - u32 timeStamp; + u32 attributes; + u32 name; + u32 moduleHandle; + u32 delayImportAddressTable; + u32 delayImportNameTable; + u32 boundDelayImportTable; + u32 unloadDelayImportTable; + u32 timeStamp; }; struct DelayImportsStructure { - if (parent.delayImportsDirectoryTable[std::core::array_index()].delayImportNameTable > 0) { - ImportsLookup delayedLookupTable[while(std::mem::read_unsigned($, wordsize()) != 0)] @ parent.delayImportsDirectoryTable[std::core::array_index()].delayImportNameTable - relativeVirtualDifference(); - } - if (parent.delayImportsDirectoryTable[std::core::array_index()].name > 0) { - char dllName[] @ parent.delayImportsDirectoryTable[std::core::array_index()].name - relativeVirtualDifference() [[format("formatNullTerminatedString")]]; - } + if (parent.delayImportsDirectoryTable[std::core::array_index()].delayImportNameTable > 0) { + ImportsLookup delayedLookupTable[while(std::mem::read_unsigned($, wordsize()) != 0)] @ parent.delayImportsDirectoryTable[std::core::array_index()].delayImportNameTable - relativeVirtualDifference(); + } + if (parent.delayImportsDirectoryTable[std::core::array_index()].name > 0) { + char dllName[] @ parent.delayImportsDirectoryTable[std::core::array_index()].name - relativeVirtualDifference() [[format("formatNullTerminatedString")]]; + } } [[inline]]; // https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#delay-load-import-tables-image-only struct DelayedImportsTable { - DelayedImportsDirectory delayImportsDirectoryTable[while(std::mem::read_unsigned($, 16) != 0)]; - DelayImportsStructure delayImportsStructures[sizeof(delayImportsDirectoryTable)/sizeof(delayImportsDirectoryTable[0])] [[inline]]; - $ = addressof(this)+coffHeader.optionalHeader.directories[1].size; + DelayedImportsDirectory delayImportsDirectoryTable[while(std::mem::read_unsigned($, 16) != 0)]; + DelayImportsStructure delayImportsStructures[sizeof(delayImportsDirectoryTable)/sizeof(delayImportsDirectoryTable[0])] [[inline]]; + $ = addressof(this)+coffHeader.optionalHeader.directories[1].size; }; // General Resource Table things fn formatNullTerminatedString16(str string) { - return "\"" + std::string::substr(string, 0, std::string::length(string)) + "\""; + return "\"" + std::string::substr(string, 0, std::string::length(string)) + "\""; }; // * Bitmap Resource struct BitmapHeader { - u32 size [[name("bitmapSize")]]; - u32 width [[name("bitmapWidth")]]; - u32 height [[name("bitmapHeight")]]; - u16 planes [[name("bitmapPlanes")]]; - u16 bitCount [[name("bitmapBitCount")]]; - u32 compression [[name("bitmapCompression")]]; - u32 imageSize [[name("bitmapImageSize")]]; - u32 xPelsPerMeter [[name("bitmapXPelsPerMeter")]]; - u32 yPelsPerMeter [[name("bitmapYPelsPerMeter")]]; - u32 clrUsed [[name("bitmapClrUsed")]]; - u32 clrImportant [[name("bitmapClrImportant")]]; + u32 size [[name("bitmapSize")]]; + u32 width [[name("bitmapWidth")]]; + u32 height [[name("bitmapHeight")]]; + u16 planes [[name("bitmapPlanes")]]; + u16 bitCount [[name("bitmapBitCount")]]; + u32 compression [[name("bitmapCompression")]]; + u32 imageSize [[name("bitmapImageSize")]]; + u32 xPelsPerMeter [[name("bitmapXPelsPerMeter")]]; + u32 yPelsPerMeter [[name("bitmapYPelsPerMeter")]]; + u32 clrUsed [[name("bitmapClrUsed")]]; + u32 clrImportant [[name("bitmapClrImportant")]]; }; struct Colors { - u8 blue [[color("1F77B4")]]; - u8 green [[color("2CA02C")]]; - u8 red [[color("D62728")]]; - u8 reserved [[color("828282")]]; + u8 blue [[color("1F77B4")]]; + u8 green [[color("2CA02C")]]; + u8 red [[color("D62728")]]; + u8 reserved [[color("828282")]]; }; u32 imageDataSize; struct Bitmap { - BitmapHeader bmh [[name("bitmapHeader")]]; + BitmapHeader bmh [[name("bitmapHeader")]]; - if ((bmh.bitCount != 24) && (bmh.bitCount != 32)) { - Colors rgbq[bmh.clrUsed*((1 << bmh.bitCount)*!bmh.clrUsed)]; - imageDataSize = imageDataSize - sizeof(rgbq); - } + if ((bmh.bitCount != 24) && (bmh.bitCount != 32)) { + Colors rgbq[bmh.clrUsed*((1 << bmh.bitCount)*!bmh.clrUsed)]; + imageDataSize = imageDataSize - sizeof(rgbq); + } - u8 imageData[imageDataSize-sizeof(bmh)]; + u8 imageData[imageDataSize-sizeof(bmh)]; }; // * Cursor Resource struct Cursor { - u16 reserved; - u16 type; - if (!type) { - imageDataSize = parent.size-4; - Bitmap bitmapData [[inline]]; - } + u16 reserved; + u16 type; + if (!type) { + imageDataSize = parent.size-4; + Bitmap bitmapData [[inline]]; + } }; // * Dialog Resource enum AtomType : u16 { - Button = 0x80, - Edit, - Static, - ListBox, - ScrollBar, - ComboBox + Button = 0x80, + Edit, + Static, + ListBox, + ScrollBar, + ComboBox }; struct VLSElement { - if (std::mem::read_unsigned($, 2) == 0xFFFF) { - u16 atomDefiner; - } - else { - AtomType atom; - } + if (std::mem::read_unsigned($, 2) == 0xFFFF) { + u16 atomDefiner; + } + else { + AtomType atom; + } }; struct VariableLengthStructure { - if (std::mem::read_unsigned($, 2) == 0xFFFF) { - VLSElement vlsElement[2]; - } - else { - char16 string[] [[format("formatNullTerminatedString16")]]; - } + if (std::mem::read_unsigned($, 2) == 0xFFFF) { + VLSElement vlsElement[2]; + } + else { + char16 string[] [[format("formatNullTerminatedString16")]]; + } }; struct DialogTemplate { - if (parent.parent.parent.parent.id == 0x411) { - u16 version; - u16 signature; - u32 helpContextIdentifier; - u32 extendedStyles; - u32 style; - } - else { - u32 style; - u32 extendedStyles; - } - u16 numberOfItems; - u16 x; - u16 y; - u16 width; - u16 height; - if (parent.parent.parent.parent.id == 0x409 || parent.parent.parent.parent.id == 0x411) { - VariableLengthStructure dialogMenu; - VariableLengthStructure dialogClass; - char16 dialogTitle[] [[format("formatNullTerminatedString16")]]; - u16 dialogFontSize; - if (parent.parent.parent.parent.id == 0x411) { - u16 weight; - bool italic; - u8 charset; - } - char16 dialogFontTypeface[] [[format("formatNullTerminatedString16")]]; - if (($+2)%16 == 0 || 4 || 8 || 12) - u16 alignment; - } + if (parent.parent.parent.parent.id == 0x411) { + u16 version; + u16 signature; + u32 helpContextIdentifier; + u32 extendedStyles; + u32 style; + } + else { + u32 style; + u32 extendedStyles; + } + u16 numberOfItems; + u16 x; + u16 y; + u16 width; + u16 height; + if (parent.parent.parent.parent.id == 0x409 || parent.parent.parent.parent.id == 0x411) { + VariableLengthStructure dialogMenu; + VariableLengthStructure dialogClass; + char16 dialogTitle[] [[format("formatNullTerminatedString16")]]; + u16 dialogFontSize; + if (parent.parent.parent.parent.id == 0x411) { + u16 weight; + bool italic; + u8 charset; + } + char16 dialogFontTypeface[] [[format("formatNullTerminatedString16")]]; + if (($+2)%16 == 0 || 4 || 8 || 12) + u16 alignment; + } }; struct DialogItemTemplate { - if (parent.parent.parent.parent.parent.id == 0x411) { - u32 helpContextIdentifier; - u32 extendedStyles [[name("itemExtendedStyles")]]; - u32 style [[name("itemStyle")]]; - } - else { - u32 style [[name("itemStyle")]]; - u32 extendedStyles [[name("itemExtendedStyles")]]; - } - u16 x; - u16 y; - u16 width; - u16 height; - if (parent.parent.parent.parent.parent.id == 0x409 || parent.parent.parent.parent.parent.id == 0x411) { - u32 controlID; - VariableLengthStructure windowClass; - VariableLengthStructure dialogItemTitle; - u16 extraCount; - } else { - u16 controlID; - } - if (($+2)%16 == 0 || 4 || 8) - u16 alignment; + if (parent.parent.parent.parent.parent.id == 0x411) { + u32 helpContextIdentifier; + u32 extendedStyles [[name("itemExtendedStyles")]]; + u32 style [[name("itemStyle")]]; + } + else { + u32 style [[name("itemStyle")]]; + u32 extendedStyles [[name("itemExtendedStyles")]]; + } + u16 x; + u16 y; + u16 width; + u16 height; + if (parent.parent.parent.parent.parent.id == 0x409 || parent.parent.parent.parent.parent.id == 0x411) { + u32 controlID; + VariableLengthStructure windowClass; + VariableLengthStructure dialogItemTitle; + u16 extraCount; + } else { + u16 controlID; + } + if (($+2)%16 == 0 || 4 || 8) + u16 alignment; }; struct DialogItem { - DialogItemTemplate template [[inline]]; - /*if (parent.parent.parent.parent.id == 0x409 || parent.parent.parent.parent.id == 0x411) { - u8 itemCreationData[template.extraCount]; - }*/ + DialogItemTemplate template [[inline]]; + /*if (parent.parent.parent.parent.id == 0x409 || parent.parent.parent.parent.id == 0x411) { + u8 itemCreationData[template.extraCount]; + }*/ }; struct Dialog { - DialogTemplate dialogTemplate; - DialogItem items[dialogTemplate.numberOfItems]; + DialogTemplate dialogTemplate; + DialogItem items[dialogTemplate.numberOfItems]; }; // * String Resource struct StringTableResource { - std::string::SizedString16 strings[while($ < (parent.dataRVA - relativeVirtualDifference()) + parent.size)]; + std::string::SizedString16 strings[while($ < (parent.dataRVA - relativeVirtualDifference()) + parent.size)]; }; // * GroupCursor Resource struct GroupCursor { - u16 assorteddata; - u16 colors; - u16 otherassorteddata; - u16 pixels; - u16 assorteddataarray[5]; - u16 ordinalName; + u16 assorteddata; + u16 colors; + u16 otherassorteddata; + u16 pixels; + u16 assorteddataarray[5]; + u16 ordinalName; }; // * GroupIcon Resource struct GroupIconHeader { - u16 reserved; - u16 type; - u16 count; + u16 reserved; + u16 type; + u16 count; }; struct GroupIconEntry { - u8 width; - u8 height; - u8 colorCount; - u8 reserved; - u16 planes; - u16 bitCount; - u32 bytesInResource; - u16 id; + u8 width; + u8 height; + u8 colorCount; + u8 reserved; + u16 planes; + u16 bitCount; + u32 bytesInResource; + u16 id; }; struct GroupIcon { - GroupIconHeader header; - GroupIconEntry entries[header.count]; + GroupIconHeader header; + GroupIconEntry entries[header.count]; }; // * Version Resource struct VersionEntryHeader { - u16 length; - u16 valueLength; - u16 type; - char16 key[] [[format("formatNullTerminatedString16")]]; - padding[while(std::mem::read_unsigned($, 1) == 0 && $ < addressof(key)+sizeof(key)+5)]; + u16 length; + u16 valueLength; + u16 type; + char16 key[] [[format("formatNullTerminatedString16")]]; + padding[while(!$[$] && $ < addressof(key)+sizeof(key)+5)]; }; struct StringInfo { - VersionEntryHeader stringInfoHeader; - if (stringInfoHeader.valueLength > 0) { - char16 string[] [[format("formatNullTerminatedString16")]]; - padding[while(std::mem::read_unsigned($, 1) == 0)]; - } + VersionEntryHeader stringInfoHeader; + if (stringInfoHeader.valueLength > 0) { + char16 string[] [[format("formatNullTerminatedString16")]]; + padding[while(!$[$])]; + } }; struct VersionEntry { - VersionEntryHeader header [[inline]]; - if (header.key == "StringFileInfo") { - VersionEntryHeader stringTableHeader; - StringInfo strings[while($ < addressof(stringTableHeader) + stringTableHeader.length)]; - } - else if (header.key == "VarFileInfo") { - VersionEntryHeader varHeader; - u16 translation[varHeader.valueLength / 2]; - } - else { - u8 value[header.valueLength]; - padding[while(std::mem::read_unsigned($, 1) == 0)]; - } + VersionEntryHeader header [[inline]]; + if (header.key == "StringFileInfo") { + VersionEntryHeader stringTableHeader; + StringInfo strings[while($ < addressof(stringTableHeader) + stringTableHeader.length)]; + } + else if (header.key == "VarFileInfo") { + VersionEntryHeader varHeader; + u16 translation[varHeader.valueLength / 2]; + } + else { + u8 value[header.valueLength]; + padding[while(!$[$])]; + } }; struct Version { - VersionEntryHeader header [[inline]]; - u32 signature; - u16 structVersion[2]; - u16 fileVersion[4]; - u16 productVersion[4]; - u32 fileFlagsMask[2]; - u32 fileFlags; - u32 fileOS; - u32 fileType; - u32 fileSubType; - u32 fileTimestamp; - VersionEntry children[while($ < (parent.dataRVA - relativeVirtualDifference()) + parent.size)]; + VersionEntryHeader header [[inline]]; + u32 signature; + u16 structVersion[2]; + u16 fileVersion[4]; + u16 productVersion[4]; + u32 fileFlagsMask[2]; + u32 fileFlags; + u32 fileOS; + u32 fileType; + u32 fileSubType; + u32 fileTimestamp; + VersionEntry children[while($ < (parent.dataRVA - relativeVirtualDifference()) + parent.size)]; }; // * Resources Using TrueChar fn displayTrueChar(u8 value) { - str notation = "0x"; - if (value < 0x10) - notation += "0"; - if (value == 0) - return "'␀' (" + std::format(notation + "{:X}", value) + ")"; - else - return "'" + char(value) + "' (" + std::format(notation + "{:X}", value) + ")"; + char chr = char(value); + str notation = "0x"; + if (value < 0x10) notation += "0"; + if (!value) chr = "␀"; + return "'" + chr + "' (" + std::format(notation + "{:X}", value) + ")"; }; +using TrueChar = u8 [[format("displayTrueChar")]]; + // Resource Table enum ResourceID : u32 { - Cursor = 0x01, - Bitmap, - Icon, - Menu, - Dialog, - String, - Accelerator = 0x09, - StringData, - GroupCursor = 0x0C, - GroupIcon = 0x0E, - Version = 0x10, - Manifest = 0x18 + Cursor = 0x01, + Bitmap, + Icon, + Menu, + Dialog, + String, + Accelerator = 0x09, + StringData, + GroupCursor = 0x0C, + GroupIcon = 0x0E, + Version = 0x10, + Manifest = 0x18 }; ResourceID resourceIDType; -using TrueChar = u8 [[format("displayTrueChar")]]; - struct DataEntry { - u32 dataRVA; - u32 size; - u32 codepage; - u32 reserved; - - if (resourceIDType == ResourceID::Cursor) { - Cursor cursor @ dataRVA - relativeVirtualDifference(); - } - else if (resourceIDType == ResourceID::Bitmap || (resourceIDType == ResourceID::Icon && std::mem::read_string((dataRVA - relativeVirtualDifference())+1, 3) != "PNG")) { - imageDataSize = size; - Bitmap bitmap @ dataRVA - relativeVirtualDifference(); - } - else if (resourceIDType == ResourceID::Dialog) { - Dialog dialog @ dataRVA - relativeVirtualDifference(); - } - else if (resourceIDType == ResourceID::String) { - StringTableResource stringTableResource @ dataRVA - relativeVirtualDifference(); - } - else if (resourceIDType == ResourceID::StringData) { - TrueChar stringData[size] @ dataRVA - relativeVirtualDifference(); - } - else if (resourceIDType == ResourceID::GroupCursor) { - GroupCursor groupCursor @ dataRVA - relativeVirtualDifference(); - } - else if (resourceIDType == ResourceID::GroupIcon) { - GroupIcon groupIcon @ dataRVA - relativeVirtualDifference(); - } - else if (resourceIDType == ResourceID::Version) { - Version version @ dataRVA - relativeVirtualDifference(); - } - else if (resourceIDType == ResourceID::Manifest) { - TrueChar manifest[size] @ dataRVA - relativeVirtualDifference(); - } - else { - u8 resource[size] @ dataRVA - relativeVirtualDifference(); - } + u32 dataRVA; + u32 size; + u32 codepage; + u32 reserved; + + if (resourceIDType == ResourceID::Cursor) { + Cursor cursor @ dataRVA - relativeVirtualDifference(); + } + else if (resourceIDType == ResourceID::Bitmap || (resourceIDType == ResourceID::Icon && std::mem::read_string((dataRVA - relativeVirtualDifference())+1, 3) != "PNG")) { + imageDataSize = size; + Bitmap bitmap @ dataRVA - relativeVirtualDifference(); + } + else if (resourceIDType == ResourceID::Dialog) { + Dialog dialog @ dataRVA - relativeVirtualDifference(); + } + else if (resourceIDType == ResourceID::String) { + StringTableResource stringTableResource @ dataRVA - relativeVirtualDifference(); + } + else if (resourceIDType == ResourceID::StringData) { + TrueChar stringData[size] @ dataRVA - relativeVirtualDifference(); + } + else if (resourceIDType == ResourceID::GroupCursor) { + GroupCursor groupCursor @ dataRVA - relativeVirtualDifference(); + } + else if (resourceIDType == ResourceID::GroupIcon) { + GroupIcon groupIcon @ dataRVA - relativeVirtualDifference(); + } + else if (resourceIDType == ResourceID::Version) { + Version version @ dataRVA - relativeVirtualDifference(); + } + else if (resourceIDType == ResourceID::Manifest) { + TrueChar manifest[size] @ dataRVA - relativeVirtualDifference(); + } + else { + u8 resource[size] @ dataRVA - relativeVirtualDifference(); + } }; using ResourceDirectory; bitfield OffsetField { - offset : 31; - pointingToDirectory : 1; + offset : 31; + pointingToDirectory : 1; }; struct DataField { - if (std::mem::read_unsigned($+3, 1) >= 0x80) { - OffsetField offsetToData; - ResourceDirectory directory @ coffHeader.optionalHeader.directories[2].rva - relativeVirtualDifference() + offsetToData.offset; - } - else { - u32 offsetToData; - DataEntry dataEntry @ coffHeader.optionalHeader.directories[2].rva - relativeVirtualDifference() + offsetToData; - } + if ($[$+3] >= 0x80) { + OffsetField offsetToData; + ResourceDirectory directory @ coffHeader.optionalHeader.directories[2].rva - relativeVirtualDifference() + offsetToData.offset; + } + else { + u32 offsetToData; + DataEntry dataEntry @ coffHeader.optionalHeader.directories[2].rva - relativeVirtualDifference() + offsetToData; + } } [[inline]]; struct IdDirectoryEntry { - if ($ > coffHeader.optionalHeader.directories[2].rva - relativeVirtualDifference() + 0x10 + 8*(parent.directoryTable.nameEntriesAmount + parent.directoryTable.idEntriesAmount)) { - u32 id; - } - else { - ResourceID id; - resourceIDType = std::mem::read_unsigned(addressof(id), 4); - } + if ($ > coffHeader.optionalHeader.directories[2].rva - relativeVirtualDifference() + 0x10 + 8*(parent.directoryTable.nameEntriesAmount + parent.directoryTable.idEntriesAmount)) { + u32 id; + } + else { + ResourceID id; + resourceIDType = std::mem::read_unsigned(addressof(id), 4); + } - DataField datafield; + DataField datafield; }; struct NameDirectoryEntry { - OffsetField offsetToName; - std::string::SizedString16 name @ coffHeader.optionalHeader.directories[2].rva - relativeVirtualDifference() + offsetToName.offset; + OffsetField offsetToName; + std::string::SizedString16 name @ coffHeader.optionalHeader.directories[2].rva - relativeVirtualDifference() + offsetToName.offset; - DataField datafield; + DataField datafield; }; struct ResourceDirectoryTable { - u32 characteristics; - u32 timeDateStamp; - u16 majorVersion; - u16 minorVersion; - u16 nameEntriesAmount; - u16 idEntriesAmount; + u32 characteristics; + u32 timeDateStamp; + u16 majorVersion; + u16 minorVersion; + u16 nameEntriesAmount; + u16 idEntriesAmount; }; struct ResourceDirectory { - ResourceDirectoryTable directoryTable [[hex::spec_name("resourceDirectoryTable")]]; - NameDirectoryEntry nameEntries[directoryTable.nameEntriesAmount]; - IdDirectoryEntry idEntries[directoryTable.idEntriesAmount]; + ResourceDirectoryTable directoryTable [[hex::spec_name("resourceDirectoryTable")]]; + NameDirectoryEntry nameEntries[directoryTable.nameEntriesAmount]; + IdDirectoryEntry idEntries[directoryTable.idEntriesAmount]; }; struct ResourceTable { - ResourceDirectory rootDirectory; - $ = addressof(this)+coffHeader.optionalHeader.directories[2].size; + ResourceDirectory rootDirectory; + $ = addressof(this)+coffHeader.optionalHeader.directories[2].size; }; // Base Relocations Table enum BaseRelocationType : u8 { - Absolute, - High, - Low, - HighLow, - HighAdjacent, - Reserved = 6, - DIR64 = 10 + Absolute, + High, + Low, + HighLow, + HighAdjacent, + Reserved = 6, + DIR64 = 10 }; enum MIPSBaseRelocationType : u8 { - Absolute, - High, - Low, - HighLow, - HighAdjacent, - MIPSJMPAddress, - Reserved, - MIPSJMPAddress16 = 9, - DIR64 + Absolute, + High, + Low, + HighLow, + HighAdjacent, + MIPSJMPAddress, + Reserved, + MIPSJMPAddress16 = 9, + DIR64 }; enum ARMBaseRelocationType : u8 { - Absolute, - High, - Low, - HighLow, - HighAdjacent, - ARMMOV32, - Reserved, - DIR64 = 10 + Absolute, + High, + Low, + HighLow, + HighAdjacent, + ARMMOV32, + Reserved, + DIR64 = 10 }; enum RISCVBaseRelocationType : u8 { - Absolute, - High, - Low, - HighLow, - HighAdjacent, - RISCVHigh20, - Reserved, - RISCVLow12I, - RISCVLow12S, - DIR64 = 10 + Absolute, + High, + Low, + HighLow, + HighAdjacent, + RISCVHigh20, + Reserved, + RISCVLow12I, + RISCVLow12S, + DIR64 = 10 }; enum THUMBBaseRelocationType : u8 { - Absolute, - High, - Low, - HighLow, - HighAdjacent, - ARMMOV32, - Reserved, - ThumbMOV32, - DIR64 = 10 + Absolute, + High, + Low, + HighLow, + HighAdjacent, + ARMMOV32, + Reserved, + ThumbMOV32, + DIR64 = 10 }; enum LoongarchBaseRelocationType : u8 { - Absolute, - High, - Low, - HighLow, - HighAdjacent, - Reserved = 6, - MarkLA = 8, - DIR64 = 10 + Absolute, + High, + Low, + HighLow, + HighAdjacent, + Reserved = 6, + MarkLA = 8, + DIR64 = 10 }; fn formatBaseRelocationType(u8 value) { - if (coffHeader.architecture == ArchitectureType::MIPS16 || coffHeader.architecture == ArchitectureType::MIPSFPU || coffHeader.architecture == ArchitectureType::MIPSFPU16 || - coffHeader.architecture == ArchitectureType::R3000 || coffHeader.architecture == ArchitectureType::R4000 || coffHeader.architecture == ArchitectureType::R10000) { - MIPSBaseRelocationType mipsTypeBits = value; - return mipsTypeBits; - } - else if (coffHeader.architecture == ArchitectureType::ARM || coffHeader.architecture == ArchitectureType::ARM64 || coffHeader.architecture == ArchitectureType::ARMNT) { - ARMBaseRelocationType armTypeBits = value; - return armTypeBits; - } - else if (coffHeader.architecture == ArchitectureType::RISCV32 || coffHeader.architecture == ArchitectureType::RISCV64 || coffHeader.architecture == ArchitectureType::RISCV128) { - RISCVBaseRelocationType riscvTypeBits = value; - return riscvTypeBits; - } - else if (coffHeader.architecture == ArchitectureType::THUMB) { - THUMBBaseRelocationType thumbTypeBits = value; - return thumbTypeBits; - } - else if (coffHeader.architecture == ArchitectureType::LOONGARCH32 || coffHeader.architecture == ArchitectureType::LOONGARCH64) { - LoongarchBaseRelocationType loongarchTypeBits = value; - return loongarchTypeBits; - } - else { - BaseRelocationType genericTypeBits = value; - return genericTypeBits; - } + if (coffHeader.architecture == ArchitectureType::MIPS16 || coffHeader.architecture == ArchitectureType::MIPSFPU || coffHeader.architecture == ArchitectureType::MIPSFPU16 || + coffHeader.architecture == ArchitectureType::R3000 || coffHeader.architecture == ArchitectureType::R4000 || coffHeader.architecture == ArchitectureType::R10000) { + MIPSBaseRelocationType mipsTypeBits = value; + return mipsTypeBits; + } + else if (coffHeader.architecture == ArchitectureType::ARM || coffHeader.architecture == ArchitectureType::ARM64 || coffHeader.architecture == ArchitectureType::ARMNT) { + ARMBaseRelocationType armTypeBits = value; + return armTypeBits; + } + else if (coffHeader.architecture == ArchitectureType::RISCV32 || coffHeader.architecture == ArchitectureType::RISCV64 || coffHeader.architecture == ArchitectureType::RISCV128) { + RISCVBaseRelocationType riscvTypeBits = value; + return riscvTypeBits; + } + else if (coffHeader.architecture == ArchitectureType::THUMB) { + THUMBBaseRelocationType thumbTypeBits = value; + return thumbTypeBits; + } + else if (coffHeader.architecture == ArchitectureType::LOONGARCH32 || coffHeader.architecture == ArchitectureType::LOONGARCH64) { + LoongarchBaseRelocationType loongarchTypeBits = value; + return loongarchTypeBits; + } + else { + BaseRelocationType genericTypeBits = value; + return genericTypeBits; + } }; bitfield BaseRelocationWord { - offset : 12; - type : 4 [[format("formatBaseRelocationType")]]; + offset : 12; + type : 4 [[format("formatBaseRelocationType")]]; }; struct BaseRelocationBlock { - u32 pageRVA; - u32 blockSize; - BaseRelocationWord word[while($ < addressof(this) + this.blockSize)] [[inline]]; + u32 pageRVA; + u32 blockSize; + BaseRelocationWord word[while($ < addressof(this) + this.blockSize)] [[inline]]; }; struct BaseRelocationTable { - BaseRelocationBlock baseRelocationBlocks[while($ < addressof(this) + coffHeader.optionalHeader.directories[5].size)] [[inline]]; + BaseRelocationBlock baseRelocationBlocks[while($ < addressof(this) + coffHeader.optionalHeader.directories[5].size)] [[inline]]; }; // Debug Table enum DebugType : u32 { - Unknown, - COFF, - Codeview, - FPO, - Misc, - Exception, - Fixup, - OmapToSRC, - OmapFromSRC, - Borland, - Reserved10, - CLSID, - REPRO = 16, - ExtendedDLLCharacteristics = 20 + Unknown, + COFF, + Codeview, + FPO, + Misc, + Exception, + Fixup, + OmapToSRC, + OmapFromSRC, + Borland, + CLSID = 11, + REPRO = 16, + ExtendedDLLCharacteristics = 20 }; struct RSDS { - char signature[4]; - type::GUID guid; - u32 age; - char path[] [[format("formatNullTerminatedString")]]; + char signature[4]; + type::GUID guid; + u32 age; + char path[] [[format("formatNullTerminatedString")]]; }; struct DebugDirectory { - u32 characteristics; - type::time32_t timeDateStamp; - u16 majorVersion; - u16 minorVersion; - DebugType type; - u32 sizeOfData; - u32 virtualAddressOfRawData; - u32 pointerOfRawData; + u32 characteristics; + type::time32_t timeDateStamp; + u16 majorVersion; + u16 minorVersion; + DebugType type; + u32 sizeOfData; + u32 virtualAddressOfRawData; + u32 pointerOfRawData; }; struct DebugData { - DebugDirectory directory; - if (std::mem::read_string(directory.pointerOfRawData, 4) == "RSDS") { - RSDS rsds @ directory.pointerOfRawData; - } - else { - u8 data[directory.sizeOfData] @ directory.pointerOfRawData; - } - $ = addressof(this)+coffHeader.optionalHeader.directories[6].size; + DebugDirectory directory; + if (std::mem::read_string(directory.pointerOfRawData, 4) == "RSDS") { + RSDS rsds @ directory.pointerOfRawData; + } + else { + u8 data[directory.sizeOfData] @ directory.pointerOfRawData; + } + $ = addressof(this)+coffHeader.optionalHeader.directories[6].size; }; // TLS Table struct TLSTable { - if (coffHeader.optionalHeader.magic == PEFormat::PE32Plus) { - u64 rawDataStartVA; - u64 rawDataEndVA; - u64 indexAddress; - u64 callbacksAddress; - } - else { - u32 rawDataStartVA; - u32 rawDataEndVA; - u32 indexAddress; - u32 callbacksAddress; - } - u32 zeroFillSize; - u32 characteristics; - $ = addressof(this)+coffHeader.optionalHeader.directories[9].size; + if (coffHeader.optionalHeader.magic == PEFormat::PE32Plus) { + u64 rawDataStartVA; + u64 rawDataEndVA; + u64 indexAddress; + u64 callbacksAddress; + } + else { + u32 rawDataStartVA; + u32 rawDataEndVA; + u32 indexAddress; + u32 callbacksAddress; + } + u32 zeroFillSize; + u32 characteristics; + $ = addressof(this)+coffHeader.optionalHeader.directories[9].size; }; // CRT Section struct CRTSection { - u64 virtualAddresses[while($ < sectionsTable[currentSectionIndex].ptrRawData + sectionsTable[currentSectionIndex].sizeOfRawData)]; + u64 virtualAddresses[while($ < sectionsTable[currentSectionIndex].ptrRawData + sectionsTable[currentSectionIndex].sizeOfRawData)]; }; // Sections struct LineNumber { - if (std::mem::read_unsigned($+4) > 0) { - u32 virtualAddress; - } else { - u32 symbolTableIndex; - } - u32 lineNumber; + if (std::mem::read_unsigned($+4) > 0) { + u32 virtualAddress; + } else { + u32 symbolTableIndex; + } + u32 lineNumber; }; bool dataDirectoryInSection[coffHeader.optionalHeader.numberOfRVAsAndSizes]; 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() >= $) - dataDirectoryInSection[i] = true; + 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() >= $) + dataDirectoryInSection[i] = true; }; fn noDataDirectories() { - for (u32 i = 0, i < coffHeader.optionalHeader.numberOfRVAsAndSizes, i += 1) - if (dataDirectoryInSection[i]) return false; - return true; + for (u32 i = 0, i < coffHeader.optionalHeader.numberOfRVAsAndSizes, i += 1) + if (dataDirectoryInSection[i]) return false; + return true; }; -fn clearBoolArray() { - for (u32 i = 0, i < coffHeader.optionalHeader.numberOfRVAsAndSizes, i += 1) - dataDirectoryInSection[i] = false; +fn clearDirectoryChecks() { + for (u32 i = 0, i < coffHeader.optionalHeader.numberOfRVAsAndSizes, i += 1) + dataDirectoryInSection[i] = false; }; struct Section { - checkForDataDirectory(); - if (noDataDirectories()) { - if (std::string::starts_with(sectionsTable[currentSectionIndex].name, ".CRT")) // CRT section - CRTSection crtSection; - else - u8 freeformSection[sectionsTable[currentSectionIndex].sizeOfRawData]; // Freeform data section - } - else { - if (dataDirectoryInSection[0]) { - ExportsTable exportTable @ coffHeader.optionalHeader.directories[0].rva - relativeVirtualDifference(); - } - if (dataDirectoryInSection[1]) { - ImportsTable importTable @ coffHeader.optionalHeader.directories[1].rva - relativeVirtualDifference(); - } - if (dataDirectoryInSection[2]) { - ResourceTable resourceTable @ coffHeader.optionalHeader.directories[2].rva - relativeVirtualDifference(); - } - if (dataDirectoryInSection[3]) { - ExceptionTable exceptionTable @ coffHeader.optionalHeader.directories[3].rva - relativeVirtualDifference(); - } - if (dataDirectoryInSection[5]) { - BaseRelocationTable baseRelocationTable @ coffHeader.optionalHeader.directories[5].rva - relativeVirtualDifference(); - } - if (dataDirectoryInSection[6]) { - DebugData debugData @ coffHeader.optionalHeader.directories[6].rva - relativeVirtualDifference(); - } - if (dataDirectoryInSection[7]) { - char copyright[] @ coffHeader.optionalHeader.directories[7].rva - relativeVirtualDifference() [[format("formatNullTerminatedString")]]; - } - if (dataDirectoryInSection[9]) { - TLSTable tlsTable @ coffHeader.optionalHeader.directories[9].rva - relativeVirtualDifference(); - } - if (dataDirectoryInSection[13]) { - DelayedImportsTable delayedImportTable @ coffHeader.optionalHeader.directories[13].rva - relativeVirtualDifference(); - } - } - clearBoolArray(); - - LineNumber lineNumbers[sectionsTable[currentSectionIndex].numberOfLineNumbers] @ sectionsTable[currentSectionIndex].ptrLineNumbers; - - // Next section - if (sectionsTable[currentSectionIndex].sizeOfRawData > 0) // If the size of the next section is bigger than 0 - $ = addressof(this) + sectionsTable[currentSectionIndex].sizeOfRawData; // Put the current offset at the start of it to account for any bytes left - if (currentSectionIndex < coffHeader.numberOfSections-1) // If it's not the last section (to not make $ the address of an inexistent section) - $ = sectionsTable[currentSectionIndex+1].ptrRawData; // Put the current offset at the next section's address - currentSectionIndex += 1; // Make the current section index the next section's index + checkForDataDirectory(); + if (noDataDirectories()) { + if (std::string::starts_with(sectionsTable[currentSectionIndex].name, ".CRT")) // CRT section + CRTSection crtSection; + else + u8 freeformSection[sectionsTable[currentSectionIndex].sizeOfRawData]; // Freeform data section + } + else { + if (dataDirectoryInSection[0]) { + ExportsTable exportTable @ coffHeader.optionalHeader.directories[0].rva - relativeVirtualDifference(); + } + if (dataDirectoryInSection[1]) { + ImportsTable importTable @ coffHeader.optionalHeader.directories[1].rva - relativeVirtualDifference(); + } + if (dataDirectoryInSection[2]) { + ResourceTable resourceTable @ coffHeader.optionalHeader.directories[2].rva - relativeVirtualDifference(); + } + if (dataDirectoryInSection[3]) { + ExceptionTable exceptionTable @ coffHeader.optionalHeader.directories[3].rva - relativeVirtualDifference(); + } + if (dataDirectoryInSection[5]) { + BaseRelocationTable baseRelocationTable @ coffHeader.optionalHeader.directories[5].rva - relativeVirtualDifference(); + } + if (dataDirectoryInSection[6]) { + DebugData debugData @ coffHeader.optionalHeader.directories[6].rva - relativeVirtualDifference(); + } + if (dataDirectoryInSection[7]) { + char copyright[] @ coffHeader.optionalHeader.directories[7].rva - relativeVirtualDifference() [[format("formatNullTerminatedString")]]; + } + if (dataDirectoryInSection[9]) { + TLSTable tlsTable @ coffHeader.optionalHeader.directories[9].rva - relativeVirtualDifference(); + } + if (dataDirectoryInSection[13]) { + DelayedImportsTable delayedImportTable @ coffHeader.optionalHeader.directories[13].rva - relativeVirtualDifference(); + } + } + clearDirectoryChecks(); + + LineNumber lineNumbers[sectionsTable[currentSectionIndex].numberOfLineNumbers] @ sectionsTable[currentSectionIndex].ptrLineNumbers; + + // Next section + if (sectionsTable[currentSectionIndex].sizeOfRawData > 0) // If the size of the next section is bigger than 0 + $ = addressof(this) + sectionsTable[currentSectionIndex].sizeOfRawData; // Put the current offset at the start of it to account for any bytes left + if (currentSectionIndex < coffHeader.numberOfSections-1) // If it's not the last section (to not make $ the address of an inexistent section) + $ = sectionsTable[currentSectionIndex+1].ptrRawData; // Put the current offset at the next section's address + currentSectionIndex += 1; // Make the current section index the next section's index } [[name(sectionsTable[currentSectionIndex-1].name)]]; -Section sections[coffHeader.numberOfSections] @ sectionsTable[0].ptrRawData; +Section sections[coffHeader.numberOfSections] @ coffHeader.optionalHeader.sizeOfHeaders * !sectionsTable[0].sizeOfRawData + + sectionsTable[0].ptrRawData * sectionsTable[0].sizeOfRawData>0; // Symbol & String Tables enum SectionNumberType : s16 { - Undefined = 0, - Absolute = -1, - Debug = -2 + Undefined = 0, + Absolute = -1, + Debug = -2 }; enum SymbolTypeMSB : u8 { - Null = 0x00, - Pointer = 0x10, - Function = 0x20, - Array = 0x30 + Null = 0x00, + Pointer = 0x10, + Function = 0x20, + Array = 0x30 }; enum SymbolTypeLSB : u8 { - Null = 0x00, - Void = 0x01, - Char = 0x02, - Short = 0x03, - Integer = 0x04, - Long = 0x05, - Float = 0x06, - Double = 0x07, - Struct = 0x08, - Union = 0x09, - Enum = 0x0A, - MemberOfEnum = 0x0B, - Byte = 0x0C, - Word = 0x0D, - UInt = 0x0E, - DWord = 0x0F + Null = 0x00, + Void = 0x01, + Char = 0x02, + Short = 0x03, + Integer = 0x04, + Long = 0x05, + Float = 0x06, + Double = 0x07, + Struct = 0x08, + Union = 0x09, + Enum = 0x0A, + MemberOfEnum = 0x0B, + Byte = 0x0C, + Word = 0x0D, + UInt = 0x0E, + DWord = 0x0F }; enum StorageClassType : s8 { - EndOfFunction = -1, - Null = 0, - Automatic = 1, - External = 2, - Static = 3, - Register = 4, - DefinedExternally = 5, - Label = 6, - UndefinedLabel = 7, - MemberOfStruct = 8, - Argument = 9, - StructTag = 10, - MemberOfUnion = 11, - UnionTag = 12, - TypeDefinition = 13, - UndefinedStatic = 14, - EnumTag = 15, - MemberOfEnum = 16, - RegisterParameter = 17, - Bitfield = 18, - Block = 100, - BlockFunction = 101, - EndOfStruct = 102, - File = 103, - Section = 104, - WeakExternal = 105, - CLRToken = 107 + EndOfFunction = -1, + Null = 0, + Automatic = 1, + External = 2, + Static = 3, + Register = 4, + DefinedExternally = 5, + Label = 6, + UndefinedLabel = 7, + MemberOfStruct = 8, + Argument = 9, + StructTag = 10, + MemberOfUnion = 11, + UnionTag = 12, + TypeDefinition = 13, + UndefinedStatic = 14, + EnumTag = 15, + MemberOfEnum = 16, + RegisterParameter = 17, + Bitfield = 18, + Block = 100, + BlockFunction = 101, + EndOfStruct = 102, + File = 103, + Section = 104, + WeakExternal = 105, + CLRToken = 107 }; struct SymbolNameOffset { - padding[4]; - u32 offset [[name("nameOffset")]]; + padding[4]; + u32 offset [[name("nameOffset")]]; } [[inline]]; struct SymbolType { - SymbolTypeMSB msb; - SymbolTypeLSB lsb; + SymbolTypeMSB msb; + SymbolTypeLSB lsb; }; fn formatSymbolType(SymbolType value) { - return "{ " + std::string::to_string(value.msb) + " | " + std::string::to_string(value.lsb) + " }"; + return "{ " + std::string::to_string(value.msb) + " | " + std::string::to_string(value.lsb) + " }"; }; struct Symbol { - if (std::mem::read_unsigned($, 4) == 0) - SymbolNameOffset nameOffset; - else char shortName[8]; - u32 value; - SectionNumberType sectionNumber; - SymbolType type [[format("formatSymbolType")]]; - StorageClassType storageClass; - u8 numberOfAuxSymbols; + if (std::mem::read_unsigned($, 4) == 0) + SymbolNameOffset nameOffset; + else char shortName[8]; + u32 value; + SectionNumberType sectionNumber; + SymbolType type [[format("formatSymbolType")]]; + StorageClassType storageClass; + u8 numberOfAuxSymbols; }; bool checkForSymbols in; @@ -1205,12 +1210,12 @@ bool checkForSymbols in; Symbol symbolTable[checkForSymbols * coffHeader.numberOfSymbols] @ coffHeader.pointerToSymbolTable; struct SymbolString { - char string[] [[format("formatNullTerminatedString")]]; + char string[] [[format("formatNullTerminatedString")]]; } [[inline]]; struct StringTable { - u32 size; - SymbolString strings[while($ < addressof(this) + size)]; + u32 size; + SymbolString strings[while($ < addressof(this) + size)]; } [[inline]]; StringTable stringTable[sizeof(symbolTable)>0] @ addressof(symbolTable) + sizeof(symbolTable); @@ -1220,87 +1225,87 @@ bool checkForRichHeader in; u16 richHeaderEndPosition; u32 richHeaderPosition; fn findRichHeader() { - if (checkForRichHeader) { - for (u16 richEndCursor = peHeader.dosHeader.coffHeaderPointer, richEndCursor > peHeader.dosHeader.headerSizeInParagraphs*16, richEndCursor -= 1) { - if (std::mem::read_string(richEndCursor, 4) == "Rich") { - richHeaderEndPosition = richEndCursor; - //0x18 is the size of a Rich Header body with one product - for (u16 richCursor = richHeaderEndPosition - 0x18, richCursor > peHeader.dosHeader.headerSizeInParagraphs*16, richCursor -= 1) { - if (str(std::mem::read_unsigned(richCursor, 4) ^ std::mem::read_unsigned(richHeaderEndPosition+4, 4)) == "DanS") { - richHeaderPosition = richCursor; - break; - } - } - break; - } - } - } + if (checkForRichHeader) { + for (u16 richEndCursor = peHeader.dosHeader.coffHeaderPointer, richEndCursor > peHeader.dosHeader.headerSizeInParagraphs*16, richEndCursor -= 1) { + if (std::mem::read_string(richEndCursor, 4) == "Rich") { + richHeaderEndPosition = richEndCursor; + //0x18 is the size of a Rich Header body with one product + for (u16 richCursor = richHeaderEndPosition - 0x18, richCursor > peHeader.dosHeader.headerSizeInParagraphs*16, richCursor -= 1) { + if (str(std::mem::read_unsigned(richCursor, 4) ^ std::mem::read_unsigned(richHeaderEndPosition+4, 4)) == "DanS") { + richHeaderPosition = richCursor; + break; + } + } + break; + } + } + } }; findRichHeader(); fn formatHexadecimally(auto value) { - return std::string::to_string(value) + " (" + std::format("0x{:X}", value) + ")"; + return std::string::to_string(value) + " (" + std::format("0x{:X}", value) + ")"; }; fn unmask(u32 value) { - return formatHexadecimally(value ^ std::mem::read_unsigned(richHeaderEndPosition+4, 4)); + return formatHexadecimally(value ^ std::mem::read_unsigned(richHeaderEndPosition+4, 4)); }; fn unmaskBuild(u32 value) { - return formatHexadecimally(value ^ std::mem::read_unsigned(richHeaderEndPosition+4, 2)); + return formatHexadecimally(value ^ std::mem::read_unsigned(richHeaderEndPosition+4, 2)); }; fn unmaskProduct(u16 type) { - str value = "Unknown"; - str notation = "0x"; - if (type ^ std::mem::read_unsigned(richHeaderEndPosition+6, 2) < 0x10) { notation += "0"; } - match(type ^ std::mem::read_unsigned(richHeaderEndPosition+6, 2)) { - (0x00): value = "Unmarked"; - (0x01): value = "Imports"; - (0x04): value = "STDLIBDLL"; - (0x06): value = "VS97CVTRes"; - (0x0A): value = "VS98CCompiler"; - (0x0B): value = "VS98CPPCompiler"; - (0x0C): value = "OldNames"; - (0x0E): value = "MASM613"; - (0x0F): value = "VS2003Assembler"; - (0x16): value = "VC6SP5"; - (0x19): value = "VS2002Linker"; - (0x1C): value = "VS2002CCompiler"; - (0x1D): value = "VS2002CPPCompiler"; - (0x5D): value = "VS2003SDKIMP"; - (0x60): value = "VS2003CPPCompiler"; - (0x6D): value = "VS2005CCompiler"; - (0x6E): value = "VS2005CPPCompiler"; - (0x7B): value = "VS2005Linker"; - (0x93): value = "VS2008Linker"; - (0x9D): value = "Linker12"; - (0x9E): value = "MASM10"; - (0xAA): value = "VS2010CCompiler"; - (0xAB): value = "VS2010CPPCompiler"; - (0xFF): value = "VS2015CVTRes"; - (0x101 | 0x102): value = "VS2015Linker"; - (0x103): value = "VS2015Assembler"; - (0x104): value = "VS2015CCompiler"; - (0x105): value = "VS2015CPPCompiler"; - } - return value + " (" + std::format(notation + "{:X}", type ^ std::mem::read_unsigned(richHeaderEndPosition+6, 2)) + ")"; + str value = "Unknown"; + str notation = "0x"; + if (type ^ std::mem::read_unsigned(richHeaderEndPosition+6, 2) < 0x10) { notation += "0"; } + match(type ^ std::mem::read_unsigned(richHeaderEndPosition+6, 2)) { + (0x00): value = "Unmarked"; + (0x01): value = "Imports"; + (0x04): value = "STDLIBDLL"; + (0x06): value = "VS97CVTRes"; + (0x0A): value = "VS98CCompiler"; + (0x0B): value = "VS98CPPCompiler"; + (0x0C): value = "OldNames"; + (0x0E): value = "MASM613"; + (0x0F): value = "VS2003Assembler"; + (0x16): value = "VC6SP5"; + (0x19): value = "VS2002Linker"; + (0x1C): value = "VS2002CCompiler"; + (0x1D): value = "VS2002CPPCompiler"; + (0x5D): value = "VS2003SDKIMP"; + (0x60): value = "VS2003CPPCompiler"; + (0x6D): value = "VS2005CCompiler"; + (0x6E): value = "VS2005CPPCompiler"; + (0x7B): value = "VS2005Linker"; + (0x93): value = "VS2008Linker"; + (0x9D): value = "Linker12"; + (0x9E): value = "MASM10"; + (0xAA): value = "VS2010CCompiler"; + (0xAB): value = "VS2010CPPCompiler"; + (0xFF): value = "VS2015CVTRes"; + (0x101 | 0x102): value = "VS2015Linker"; + (0x103): value = "VS2015Assembler"; + (0x104): value = "VS2015CCompiler"; + (0x105): value = "VS2015CPPCompiler"; + } + return value + " (" + std::format(notation + "{:X}", type ^ std::mem::read_unsigned(richHeaderEndPosition+6, 2)) + ")"; }; struct Product { - u16 buildNumber [[format("unmaskBuild")]]; - u16 productID [[format("unmaskProduct")]]; - u32 objectCount [[format("unmask")]]; + u16 buildNumber [[format("unmaskBuild")]]; + u16 productID [[format("unmaskProduct")]]; + u32 objectCount [[format("unmask")]]; }; fn formatSignature(auto value) { - return "\"DanS\""; + return "\"DanS\""; }; using NullPadding = u32 [[format("unmask")]]; struct RichHeader { - char maskedSignature[4] [[format("formatSignature")]]; - NullPadding nullPadding[3]; - Product products[while($ < richHeaderEndPosition)]; - char signature[4]; - u32 mask; + char maskedSignature[4] [[format("formatSignature")]]; + NullPadding nullPadding[3]; + Product products[while($ < richHeaderEndPosition)]; + char signature[4]; + u32 mask; }; struct RichHeaderContainer { if (checkForRichHeader) RichHeader richHeader; }; diff --git a/patterns/pef.hexpat b/patterns/pef.hexpat index 6d2b27d9..73e96143 100644 --- a/patterns/pef.hexpat +++ b/patterns/pef.hexpat @@ -1,11 +1,11 @@ -#pragma author Dominik Tamiollo +#pragma author domin568 #pragma description Preffered Executable Format executable (for Mac OS 7.1.2 - Mac OS 10.4 / BeOS) #pragma endian big #pragma pattern_limit 1000000 -#pragma magic [4A 6F 79 21] @ 0x00 import std.core; import std.sys; +import std.math; struct PEFContainerHeader { @@ -435,7 +435,7 @@ u32 relocBegin = loaderOffset + loaderHeader.relocInstrOffset; u32 relocSize = loaderOffset + loaderHeader.loaderStringsOffset - relocBegin; RelocInstruction relocationInstructions[while(($-relocBegin) < relocSize)] @ relocBegin; -u32 exportSlotNum = 2 * loaderHeader.exportHashTablePower; // 2 ^ loaderHeader.exportHashTablePower +u32 exportSlotNum = std::math::pow(2, loaderHeader.exportHashTablePower); u32 exportDataOffset = loaderOffset + loaderHeader.exportHashOffset; u32 exportSymbolKeysOffset = exportDataOffset + sizeof(PEFExportedSymbolHashSlot) * exportSlotNum; u32 exportedSymbolsOffset = exportSymbolKeysOffset + sizeof(PEFExportedSymbolKey) * loaderHeader.exportedSymbolCount; diff --git a/patterns/pickle.hexpat b/patterns/pickle.hexpat new file mode 100644 index 00000000..5252b6df --- /dev/null +++ b/patterns/pickle.hexpat @@ -0,0 +1,357 @@ + +/* + References: + Pickle Source Code: + https://github.com/python/cpython/blob/main/Lib/pickle.py + Pickle Protocol Version Breakdown: + https://docs.python.org/3.13/library/pickle.html#data-stream-format + Pickle OpCode Breakdown: + https://github.com/python/cpython/blob/main/Lib/pickletools.py +*/ + +#pragma author ODeux +#pragma description Python Binary Object Serialization Protocol + +#pragma endian little + +import std.mem; +import std.string; + +#pragma array_limit 524288 + +fn todo(auto message){ + std::error(std::format("@0x{:08X} TODO: " + message, $)); +}; + +fn utf8_fmt(auto s){ + return std::format("{}", s); +}; +#define UTF8_FMT format("utf8_fmt"), transform("utf8_fmt") + +fn utf8_rl_fmt(auto s){ + str new_s = std::string::substr(s, 0, std::string::length(s) - 1); + return std::format("{}", new_s); +}; +#define UTF8_RL_FMT format("utf8_rl_fmt"), transform("utf8_rl_fmt") + +fn int_rl_fmt(auto s){ + if(s == "01\n") return true; /* == TRUE(b'I01\n')[1:] */ + if(s == "00\n") return false; /* == FALSE(b'I00\n')[1:] */ + str new_s = std::string::substr(s, 0, std::string::length(s) - 1); + return std::string::parse_int(new_s, 0); +}; +#define INT_RL_FMT format("int_rl_fmt"), transform("int_rl_fmt") + +fn float_rl_fmt(auto s){ + str new_s = std::string::substr(s, 0, std::string::length(s) - 1); + return std::string::parse_float(new_s); +}; +#define FLOAT_RL_FMT format("float_rl_fmt"), transform("float_rl_fmt") + +fn long_rl_fmt(auto s){ + str new_s = std::string::substr(s, 0, std::string::length(s) - 1); + if(new_s != "" && std::string::at(new_s, std::string::length(new_s) - 1) == "L") + new_s = std::string::substr(new_s, 0, std::string::length(new_s) - 1); + return std::string::parse_int(new_s, 0); +}; +#define LONG_RL_FMT format("long_rl_fmt"), transform("long_rl_fmt") + +fn ascii_rl_fmt(auto s){ + return std::string::substr(s, 0, std::string::length(s) - 1); +}; +#define ASCII_RL_FMT format("ascii_rl_fmt"), transform("ascii_rl_fmt") + +fn integer_rl_fmt(auto s){ + str new_s = std::string::substr(s, 0, std::string::length(s) - 1); + return std::string::parse_int(new_s, 0); +}; +#define INTEGER_RL_FMT format("integer_rl_fmt"), transform("integer_rl_fmt") + +fn string_rl_fmt(auto s){ + str new_s = std::string::substr(s, 0, std::string::length(s) - 1); + auto length = std::string::length(new_s); + if(length >= 2 && new_s[0] == new_s[length - 1] && (new_s[0] == '\'' || new_s[0] == '"')) + new_s = std::string::substr(new_s, 1, length - 1); + else std::error("the STRING opcode argument must be quoted"); + return new_s; +}; +#define STRING_RL_FMT format("string_rl_fmt"), transform("string_rl_fmt") + +enum OpcodesEnum: u8{ + MARK = '(', /* push special markobject on stack */ + STOP = '.', /* every pickle ends with STOP */ + POP = '0', /* discard topmost stack item */ + POP_MARK = '1', /* discard stack top through topmost markobject */ + DUP = '2', /* duplicate top stack item */ + FLOAT = 'F', /* push float object; decimal string argument */ + INT = 'I', /* push integer or bool; decimal string argument */ + BININT = 'J', /* push four-byte signed int */ + BININT1 = 'K', /* push 1-byte unsigned int */ + LONG = 'L', /* push long; decimal string argument */ + BININT2 = 'M', /* push 2-byte unsigned int */ + NONE = 'N', /* push None */ + PERSID = 'P', /* push persistent object; id is taken from string arg */ + BINPERSID = 'Q', /* " " " ; " " " " stack */ + REDUCE = 'R', /* apply callable to argtuple, both on stack */ + STRING = 'S', /* push string; NL-terminated string argument */ + BINSTRING = 'T', /* push string; counted binary string argument */ + SHORT_BINSTRING = 'U', /* " " ; " " " " < 256 bytes */ + UNICODE = 'V', /* push Unicode string; raw-unicode-escaped'd argument */ + BINUNICODE = 'X', /* " " " ; counted UTF-8 string argument */ + APPEND = 'a', /* append stack top to list below it */ + BUILD = 'b', /* call __setstate__ or __dict__.update() */ + GLOBAL = 'c', /* push self.find_class(modname, name); 2 string args */ + DICT = 'd', /* build a dict from stack items */ + EMPTY_DICT = '}', /* push empty dict */ + APPENDS = 'e', /* extend list on stack by topmost stack slice */ + GET = 'g', /* push item from memo on stack; index is string arg */ + BINGET = 'h', /* " " " " " " ; " " 1-byte arg */ + INST = 'i', /* build & push class instance */ + LONG_BINGET = 'j', /* push item from memo on stack; index is 4-byte arg */ + LIST = 'l', /* build list from topmost stack items */ + EMPTY_LIST = ']', /* push empty list */ + OBJ = 'o', /* build & push class instance */ + PUT = 'p', /* store stack top in memo; index is string arg */ + BINPUT = 'q', /* " " " " " ; " " 1-byte arg */ + LONG_BINPUT = 'r', /* " " " " " ; " " 4-byte arg */ + SETITEM = 's', /* add key+value pair to dict */ + TUPLE = 't', /* build tuple from topmost stack items */ + EMPTY_TUPLE = ')', /* push empty tuple */ + SETITEMS = 'u', /* modify dict by adding topmost key+value pairs */ + BINFLOAT = 'G', /* push float; arg is 8-byte float encoding */ + /* ---- Protocol 2 ---- */ + PROTO = 0x80, /* identify pickle protocol */ + NEWOBJ = 0x81, /* build object by applying cls.__new__ to argtuple */ + EXT1 = 0x82, /* push object from extension registry; 1-byte index */ + EXT2 = 0x83, /* ditto, but 2-byte index */ + EXT4 = 0x84, /* ditto, but 4-byte index */ + TUPLE1 = 0x85, /* build 1-tuple from stack top */ + TUPLE2 = 0x86, /* build 2-tuple from two topmost stack items */ + TUPLE3 = 0x87, /* build 3-tuple from three topmost stack items */ + NEWTRUE = 0x88, /* push True */ + NEWFALSE = 0x89, /* push False */ + LONG1 = 0x8A, /* push long from < 256 bytes */ + LONG4 = 0x8B, /* push really big long */ + /* ---- Protocol 3 (Python 3.x) ---- */ + BINBYTES = 'B', /* push bytes; counted binary string argument */ + SHORT_BINBYTES = 'C', /* " " ; " " " " < 256 bytes */ + /* ---- Protocol 4 ---- */ + SHORT_BINUNICODE = 0x8C, /* push short string; UTF-8 length < 256 bytes */ + BINUNICODE8 = 0x8D, /* push very long string */ + BINBYTES8 = 0x8E, /* push very long bytes string */ + EMPTY_SET = 0x8F, /* push empty set on the stack */ + ADDITEMS = 0x90, /* modify set by adding topmost stack items */ + FROZENSET = 0x91, /* build frozenset from topmost stack items */ + NEWOBJ_EX = 0x92, /* like NEWOBJ but work with keyword only arguments */ + STACK_GLOBAL = 0x93, /* same as GLOBAL but using names on the stacks */ + MEMOIZE = 0x94, /* store top of the stack in memo */ + FRAME = 0x95, /* indicate the beginning of a new frame */ + /* ---- Protocol 5 ---- */ + BYTEARRAY8 = 0x96, /* push bytearray */ + NEXT_BUFFER = 0x97, /* push next out-of-band buffer */ + READONLY_BUFFER = 0x98 /* make top of stack readonly */ +}; + +fn readline(){ + auto i = 0; + while(std::mem::read_unsigned($ + i, 1) != '\n') i += 1; + return i + 1; +}; + +struct Opcodes{ + OpcodesEnum opcode; + match(opcode){ + (OpcodesEnum::MARK): {} + (OpcodesEnum::STOP): break; + (OpcodesEnum::POP): {} + (OpcodesEnum::POP_MARK): {} + (OpcodesEnum::DUP): {} + (OpcodesEnum::FLOAT): { + char Float[readline()] [[FLOAT_RL_FMT]]; /* float(readline()[:1]) */ + } + (OpcodesEnum::INT): { + /* == TRUE(b'I01\n')[1:], == FALSE(b'I00\n')[1:], int(readline(), 0) */ + char Int[readline()] [[INT_RL_FMT]]; + } + (OpcodesEnum::BININT): { + s32 Int; + } + (OpcodesEnum::BININT1): { + s8 Int; + } + (OpcodesEnum::LONG): { + /* val = readline()[:-1], val = val and val[-1] == b"L"[0] ? val[:-1]: val */ + char Long[readline()] [[LONG_RL_FMT]]; /* int(val, 0) */ + } + (OpcodesEnum::BININT2): { + u16 Int; + } + (OpcodesEnum::NONE): {} + (OpcodesEnum::PERSID): { + char id[readline()] [[ASCII_RL_FMT]]; /* readline()[:-1].decode("ascii") */ + } + (OpcodesEnum::BINPERSID): {} + (OpcodesEnum::REDUCE): {} + /* + def _decode_string(self, value): + # Used to allow strings from Python 2 to be decoded either as bytes or Unicode strings. + # This should be used only with the STRING, BINSTRING and SHORT_BINSTRING opcodes. + if self.encoding == "bytes": + return value + else: + return value.decode(self.encoding, self.errors) + */ + (OpcodesEnum::STRING): { + /* data must be in quotes ("..." or '...'), dataStripped = stripQuote(readline()[:-1]) */ + /* _decode_string(codecs.escape_decode(dataStripped)[0]) */ + char data[readline()] [[STRING_RL_FMT]]; + } + (OpcodesEnum::BINSTRING): { + s32 length; + char data[length]; /* _decode_string(data) */ + } + (OpcodesEnum::SHORT_BINSTRING): { + u8 length; + char data[length]; /* _decode_string(data) */ + } + (OpcodesEnum::UNICODE): { + /* + "raw-unicode-escape": + Latin-1 encoding with \uXXXX and \UXXXXXXXX for other code points. + Existing backslashes are not escaped in any way. + */ + char data[readline()] [[UTF8_RL_FMT]]; /* str(readline()[:-1], "raw-unicode-escape") */ + } + (OpcodesEnum::BINUNICODE): { + u32 length; + char data[length] [[UTF8_FMT]]; /* str(data, "utf-8", "surrogatepass") */ + } + (OpcodesEnum::APPEND): {} + (OpcodesEnum::BUILD): {} + (OpcodesEnum::GLOBAL): { + char module[readline()] [[UTF8_RL_FMT]]; /* readline()[:-1].decode("utf-8") */ + char name[readline()] [[UTF8_RL_FMT]]; /* readline()[:-1].decode("utf-8") */ + } + (OpcodesEnum::DICT): {} + (OpcodesEnum::EMPTY_DICT): {} + (OpcodesEnum::APPENDS): {} + (OpcodesEnum::GET): { + char index[readline()] [[INTEGER_RL_FMT]]; /* int(readline()[:-1]) */ + } + (OpcodesEnum::BINGET): { + u8 index; + } + (OpcodesEnum::INST): { + char module[readline()] [[ASCII_RL_FMT]]; /* readline()[:-1].decode("ascii") */ + char name[readline()] [[ASCII_RL_FMT]]; /* readline()[:-1].decode("ascii") */ + } + (OpcodesEnum::LONG_BINGET): { + u32 index; + } + (OpcodesEnum::LIST): {} + (OpcodesEnum::EMPTY_LIST): {} + (OpcodesEnum::OBJ): {} + (OpcodesEnum::PUT): { + char index[readline()] [[INTEGER_RL_FMT]]; /* int(readline()[:-1]) */ + } + (OpcodesEnum::BINPUT): { + s8 index; + } + (OpcodesEnum::LONG_BINPUT): { + u32 index; + } + (OpcodesEnum::SETITEM): {} + (OpcodesEnum::TUPLE): {} + (OpcodesEnum::EMPTY_TUPLE): {} + (OpcodesEnum::SETITEMS): {} + (OpcodesEnum::BINFLOAT): { + be double Double; + } + /* ---- Protocol 2 ---- */ + (OpcodesEnum::PROTO): { + u8 version; + } + (OpcodesEnum::NEWOBJ): {} + (OpcodesEnum::EXT1): { + u8 code; + } + (OpcodesEnum::EXT2): { + u16 code; + } + (OpcodesEnum::EXT4): { + s32 code; + } + (OpcodesEnum::TUPLE1): {} + (OpcodesEnum::TUPLE2): {} + (OpcodesEnum::TUPLE3): {} + (OpcodesEnum::NEWTRUE): {} + (OpcodesEnum::NEWFALSE): {} + /* + def decode_long(data): + r"""Decode a long from a two's complement little-endian binary string. + >>> decode_long(b"") => 0 + >>> decode_long(b"\xff\x00") => 255 + >>> decode_long(b"\xff\x7f") => 32767 + >>> decode_long(b"\x00\xff") => -256 + >>> decode_long(b"\x00\x80") => -32768 + >>> decode_long(b"\x80") => -128 + >>> decode_long(b"\x7f") => 127 + """ + return int.from_bytes(data, byteorder="little", signed=True) + */ + (OpcodesEnum::LONG1): { + u8 length; + u8 data[length]; /* decode_long(data) */ + } + (OpcodesEnum::LONG4): { + s32 length; + u8 data[length]; /* decode_long(data) */ + } + /* ---- Protocol 3 (Python 3.x) ---- */ + (OpcodesEnum::BINBYTES): { + u32 length; + u8 bytes[length]; + } + (OpcodesEnum::SHORT_BINBYTES): { + u8 length; + u8 bytes[length]; + } + /* ---- Protocol 4 ---- */ + (OpcodesEnum::SHORT_BINUNICODE): { + u8 length; + char data[length] [[UTF8_FMT]]; /* str(data, "utf-8", "surrogatepass") */ + } + (OpcodesEnum::BINUNICODE8): { + u64 length; + char data[length] [[UTF8_FMT]]; /* str(data, "utf-8", "surrogatepass") */ + } + (OpcodesEnum::BINBYTES8): { + u64 length; + u8 bytes[length]; + } + (OpcodesEnum::EMPTY_SET): {} + (OpcodesEnum::ADDITEMS): {} + (OpcodesEnum::FROZENSET): {} + (OpcodesEnum::NEWOBJ_EX): {} + (OpcodesEnum::STACK_GLOBAL): {} + (OpcodesEnum::MEMOIZE): {} + (OpcodesEnum::FRAME): { + u64 length; + Opcodes opcodes[while($ < addressof(length) + sizeof(length) + length)]; + } + /* ---- Protocol 5 ---- */ + (OpcodesEnum::BYTEARRAY8): { + u64 length; + u8 array[length]; + } + (OpcodesEnum::NEXT_BUFFER): {} + (OpcodesEnum::READONLY_BUFFER): {} + (_): std::error(std::format("Unrecognized {}", opcode)); + } +}; + +struct Pickle{ + Opcodes opcodes[while(!std::mem::eof())]; +}; + +Pickle pickle @ 0x0; diff --git a/patterns/png.hexpat b/patterns/png.hexpat index ebd7122e..787d058f 100644 --- a/patterns/png.hexpat +++ b/patterns/png.hexpat @@ -214,6 +214,11 @@ struct Chunks { chunk_t iend_chunk [[comment("Image End Chunk")]]; }; -u8 visualizer[std::mem::size()] @ 0x00 [[sealed, hex::visualize("image", this)]]; -header_t header @ 0x00 [[comment("PNG file signature"), name("Signature")]]; -Chunks chunks @ 0x08 [[name("Chunks")]]; \ No newline at end of file +struct Png { + header_t header [[comment("PNG file signature"), name("Signature")]]; + Chunks chunks [[name("Chunks")]]; + u128 length = $ - addressof(this); + u8 visualizer[length] @ addressof(this) [[sealed, hex::visualize("image", this), no_unique_address]]; +}; + +Png png @ 0x00; \ No newline at end of file diff --git a/patterns/png2.hexpat b/patterns/png2.hexpat new file mode 100644 index 00000000..787d058f --- /dev/null +++ b/patterns/png2.hexpat @@ -0,0 +1,224 @@ +#pragma description PNG image + +#pragma MIME image/png +#pragma endian big + +import std.mem; + +struct header_t { + u8 highBitByte; + char signature[3]; + char dosLineEnding[2]; + char dosEOF; + char unixLineEnding; +}; + +struct actl_t { + u32 frames [[comment("Total № of frames in animation")]]; + u32 plays [[comment("№ of times animation will loop")]]; +} [[comment("Animation control chunk"), name("acTL")]]; + +enum ColorType: u8 { + Grayscale = 0x0, + RGBTriple = 0x2, + Palette, + GrayscaleAlpha, + RGBA = 0x6 +}; + +enum Interlacing: u8 { + None, + Adam7 +}; + +struct ihdr_t { + u32 width [[comment("Image width")]]; + u32 height [[comment("Image height")]]; + u8 bit_depth; + ColorType color_type [[comment("PNG Image Type")]]; + u8 compression_method [[comment("Only 0x0 = zlib supported by most")]]; + u8 filter_method [[comment("Only 0x0 = adaptive supported by most")]]; + Interlacing interlacing; +}; + +enum sRGB: u8 { + Perceptual = 0x0, + RelativeColorimetric, + Saturation, + AbsoluteColorimetric +}; + +enum Unit: u8 { + Unknown, + Meter +}; + +struct phys_t { + u32 ppu_x [[comment("Pixels per unit, X axis")]]; + u32 ppu_y [[comment("Pixels per unit, Y axis")]]; + Unit unit; +}; + +enum BlendOp: u8 { + Source = 0x0, + Over +}; + +enum DisposeOp: u8 { + None = 0x0, + Background, + Previous +}; + +struct fctl_t { + u32 sequence_no [[comment("Sequence №")]]; + u32 width [[comment("Frame width")]]; + u32 height; + u32 xoff; + u32 yoff; + u16 delay_num; + u16 delay_den; + DisposeOp dispose_op; + BlendOp blend_op; +}; + +struct fdat_t { + u32 sequence_no; +}; + +fn text_len() { + u64 len = parent.parent.length - ($ - addressof(parent.keyword)); + return len; +}; + +struct itxt_t { + char keyword[]; + u8 compression_flag; + u8 compression_method; + char language_tag[]; + char translated_keyword[]; + char text[text_len()]; +}; + +struct ztxt_t { + char keyword[]; + u8 compression_method; + char text[text_len()]; +}; + +struct text_t { + char keyword[]; + char text[text_len()]; +}; + +struct iccp_t { + char keyword[]; + u8 compression_method; + u8 compressed_profile[text_len()]; +}; + +struct palette_entry_t { + u24 color; +} [[inline]]; + +struct chrm_t { + u32 white_point_x; + u32 white_point_y; + u32 red_x; + u32 red_y; + u32 green_x; + u32 green_y; + u32 blue_x; + u32 blue_y; +}; + +struct time_t { + u16 year; + u8 month; + u8 day; + u8 hour; + u8 minute; + u8 second; +}; + +struct chunk_t { + u32 length [[color("17BECF")]]; + char name[4]; + + #define IHDR_k "IHDR" + #define PLTE_k "PLTE" + #define sRGB_k "sRGB" + #define pHYs_k "pHYs" + #define iTXt_k "iTXt" + #define tEXt_k "tEXt" + #define zTXt_k "zTXt" + #define IDAT_k "IDAT" + #define IEND_k "IEND" + #define gAMA_k "gAMA" + #define iCCP_k "iCCP" + #define acTL_k "acTL" + #define fdAT_k "fdAT" + #define fcTL_k "fcTL" + #define cHRM_k "cHRM" + #define tIME_k "tIME" + + if (name == IHDR_k) { + ihdr_t ihdr [[comment("Image Header chunk"), name("IHDR")]]; + } else if (name == PLTE_k) { + palette_entry_t entries[length / 3]; + } else if (name == sRGB_k) { + sRGB srgb; + } else if (name == pHYs_k) { + phys_t phys; + } else if (name == acTL_k) { + actl_t actl [[comment("Animation control chunk")]]; + } else if (name == fcTL_k) { + fctl_t fctl [[comment("Frame control chunk")]]; + } else if (name == iTXt_k) { + itxt_t text; + } else if (name == gAMA_k) { + u32 gamma [[name("image gamma"), comment("4 byte unsigned integer representing gamma times 100000")]]; + } else if (name == iCCP_k) { + iccp_t iccp; + } else if (name == tEXt_k) { + text_t text; + } else if (name == zTXt_k) { + ztxt_t text; + } else if (name == iCCP_k) { + iccp_t iccp; + } else if (name == fdAT_k) { + fdat_t fdat [[comment("Frame data chunk")]]; + u8 data[length-sizeof(u32)]; + } else if (name == cHRM_k) { + chrm_t chrm; + } else if (name == tIME_k) { + time_t time; + } else { + u8 data[length]; + } + + u32 crc; +} [[name(chunkValueName(this))]]; + +fn chunkValueName(ref chunk_t chunk) { + return chunk.name; +}; + +struct chunk_set { + chunk_t chunks[while(builtin::std::mem::read_string($ + 4, 4) != "IEND")] [[inline]]; +} [[inline]]; + +struct Chunks { + chunk_t ihdr_chunk [[comment("PNG Header chunk")]]; + chunk_set set [[comment("PNG Chunks"), name("Chunks"), inline]]; + chunk_t iend_chunk [[comment("Image End Chunk")]]; +}; + +struct Png { + header_t header [[comment("PNG file signature"), name("Signature")]]; + Chunks chunks [[name("Chunks")]]; + u128 length = $ - addressof(this); + u8 visualizer[length] @ addressof(this) [[sealed, hex::visualize("image", this), no_unique_address]]; +}; + +Png png @ 0x00; \ No newline at end of file diff --git a/patterns/popcap_luc.hexpat b/patterns/popcap_luc.hexpat new file mode 100644 index 00000000..dca18cf7 --- /dev/null +++ b/patterns/popcap_luc.hexpat @@ -0,0 +1,174 @@ +#pragma author gluecia +#pragma description PopCap's proprietary Lua bytecode (.luc) +#pragma endian little +#pragma magic [ 1B 4C 75 61 56 ] @ 0x00; + +import std.io; +import std.sys; + +using PLChunk; +using PLConst; +using PLLocal; +using PLOp; +using PLStr; +using cType; + + +struct PLHeader { + char magic[5]; + padding[18]; + + // *technically* this is a part of the first top level chunk, though + // it is essentially just a part of the header since it only appears there. + PLStr filename [[name("Source Name")]]; +}; + + +// LOCALS +fn fmtLocal(PLLocal l) { + return l.name; +}; + +struct PLLocal { + u32 nameLength [[hidden]]; + char16 name[nameLength] [[name("Local")]]; + u32 begin [[name("Begin")]]; + u32 end [[name("End")]]; +} [[format("fmtLocal")]]; + + +// CONSTANTS +// constant types +fn fmtCType(cType t) { + match (t) { + (cType::Nil): return "nil"; + (cType::Int): return "int"; + (cType::Float): return "float"; + (cType::Str): return "str"; + (_): return "unknown"; + } +}; + +enum cType : u8 { + Nil, + Float = 3, + Int, + Str +} [[format("fmtCType")]]; + +// lua strings +fn fmtStr(PLStr s) { + return std::format("\"{}\"", s.val); +}; + + +struct PLStr { + u32 len [[hidden]]; + if (len > 0) + char16 val[len]; +} [[format("fmtStr"), sealed]]; + +// constants struct +fn fmtConst(PLConst c) { + if (c.type == cType::Nil) { + return c.type; + } else { + return std::format("{} ({})", c.val, c.type); + } +}; + +struct PLConst { + cType type; + match (type) { + (cType::Float): double val; + (cType::Int): s32 val; + (cType::Str): PLStr val; + // theres definitely a better way to handle the Nil case + (cType::Nil): u8 val = 0; + (_): std::error("unknown cType given"); + } +} [[format("fmtConst"), sealed]]; + + +// PROTOTYPES +struct PLPrototype { + padding[4]; + PLChunk chunk; +} [[inline]]; + + +// UPVALUES +struct PLUpvalue { + u32 len [[hidden]]; + char name[len] [[name("Upvalue")]]; +} [[inline]]; + + +// OPERANDS +fn fmtPLOp(PLOp o) { + return o.opcode; +}; + +// source: https://github.com/wxarmstrong/PopLua-Disassembler/blob/master/popOp.cpp +enum PLOpType : u8 { + MOVE, LOADK, LOADBOOL, LOADNIL, GETUVPVAL, + GETGLOBAL, GETTABLE, SETGLOBAL, SETUPVAL, + SETTABLE, NEWTABLE, SELF, ADD, SUB, MUL, + DIV, MOD, POW, UNM, NOT, SIZ, CONCAT, JMP, + EQ, LT, LE, TEST, CALL, TAILCALL, RETURN, + FORLOOP, TFORLOOP, TFORPREP, SETLIST, + SETLISTO, CLOSE, ALTSELF, CONSTGLOBAL, + CONSTTABLE, DEFGLOBAL, DEFTABLE, + SETSELFORGLOBAL, GETSELFORGLOBAL, + SELFORGLOBAL, CALLSELFORGLOBAL, + TAILCALLSELFORGLOBAL, INT, BREAK, + CLOSURE +}; + +// this is for a different version of lua and its bytecode, but helped a LOT +// https://archive.org/details/a-no-frills-intro-to-lua-5.1-vm-instructions +struct PLOp { + u32 raw [[hidden]]; + PLOpType opcode = raw & 0x3F [[name("Opcode"), hidden, export]]; + u32 rawOperands = raw >> 6; + u8 opA = rawOperands & 0xFF [[name("Operand A"), export]]; + + match(opcode) { + (PLOpType::LOADK | PLOpType::CLOSURE): u32 Bx = (rawOperands >> 8) [[name("Operand Bx"), export]]; + // 131071 is the bias for sBx to make it signed, formed from (2^18 - 1) >> 1 + // where 2^18 - 1 is the maximum value for an 18 bit number + (PLOpType::JMP | PLOpType::FORLOOP | PLOpType::TFORLOOP): s32 opSBx = (rawOperands >> 8) - 131071 [[name("Operand sBx"), comment("Signed displacement added to the PC."), export]]; + (_): { + u16 opB = (rawOperands >> 8) & 0xFF01 [[name("Operand B"), export]]; + u16 opC = (rawOperands >> 17) & 0xFF01 [[name("Operand C"), export]]; + } + } +} [[format("fmtPLOp")]]; + +// CHUNKS +struct PLChunk { + char intro[0xC] [[name("Chunk Intro"), comment("Holds information on the function, but the format is unknown.")]]; + + u32 sizecode [[name("Instructions Count")]]; + u32 linesArray[sizecode] [[name("Line Numbers"), comment("The line numbers for the given operations, this should be interpreted with the operations array.")]]; + + u32 sizelocvars [[name("Locals Count")]]; + PLLocal localsArray[sizelocvars] [[name("Locals")]]; + + u32 sizeupvalues [[name("Upvalues Count")]]; + PLUpvalue upvalsArray[sizeupvalues] [[name("Upvalues")]]; + + u32 sizek [[name("Constants Count")]]; + PLConst constsArray[sizek] [[name("Constants")]]; + + u32 sizep [[name("Prototype Count")]]; + PLPrototype protoArray[sizep] [[name("Prototypes")]]; + + u32 sizecodeVerify [[hidden]]; + std::assert(sizecode == sizecodeVerify, std::format("sizecode ({}) did not match sizecodeVerify ({})!", sizecode, sizecodeVerify)); + + PLOp opsArray[sizecode] [[name("Instructions"), comment("The raw bytes for operations, should be interpreted with the line numbers array.")]]; +}; + +PLHeader Header @ 0x0; +PLChunk Body @ $; \ No newline at end of file diff --git a/patterns/q3demo.hexpat b/patterns/q3demo.hexpat index 4d013f28..015c224f 100644 --- a/patterns/q3demo.hexpat +++ b/patterns/q3demo.hexpat @@ -118,10 +118,15 @@ fn readString(ref auto data, s32 len, s32 bitindex){ return test; }; +bool corrupted = false; + struct Message { le s32 messageNum; le s32 len; - if(len != FINAL_DEMO_MESSAGE_LENGTH || messageNum != FINAL_DEMO_MESSAGE_NUMBER) { + if(len+$ > std::mem::size()){ + corrupted = true; + } + if(!corrupted && (len != FINAL_DEMO_MESSAGE_LENGTH || messageNum != FINAL_DEMO_MESSAGE_NUMBER)) { u8 data[len]; if( len>=10){ // should usually be true unless corrupted @@ -189,7 +194,7 @@ namespace format { } struct Q3Demo { - Message messages[while(!std::mem::eof())]; + Message messages[while(!std::mem::eof() && !corrupted)]; }; diff --git a/patterns/roblox_pack.hexpat b/patterns/roblox_pack.hexpat new file mode 100644 index 00000000..2c63493e --- /dev/null +++ b/patterns/roblox_pack.hexpat @@ -0,0 +1,33 @@ +#pragma author marduk.ru +#pragma description Roblox .pack shader archive format +#pragma magic [ 52 42 58 53 ] @ 0x00 + +import std.string; +import hex.core; +import type.magic; + +struct Header { + type::Magic<"RBXS"> magic; + u32 fileCount; +}; + +struct FileIndex { + char name[0x40]; + u128 md5Hash; + u32 offset; + u32 size; + u64 reserved; + + u8 file[size] @ offset; + + #ifdef __IMHEX__ + hex::core::add_virtual_file(name, file); + #endif +} [[name(name)]]; + +struct RBXPack { + Header header; + FileIndex files[header.fileCount]; +}; + +RBXPack pack @ 0x0; \ No newline at end of file diff --git a/patterns/sdb.hexpat b/patterns/sdb.hexpat new file mode 100644 index 00000000..e097c3cf --- /dev/null +++ b/patterns/sdb.hexpat @@ -0,0 +1,87 @@ +#pragma description SDB File (Shim DataBase file) +#pragma author learn_more +#pragma magic [ 73 64 62 66 ] @ 0x08 +#pragma endian little + +import std.mem; +import std.io; +import std.string; +import type.base; + +enum TagType : u8 { + Null = 0x1, + Byte = 0x2, + Word = 0x3, + DWord = 0x4, + QWord = 0x5, + StringRef = 0x6, + List = 0x7, + String = 0x8, + Binary = 0x9, +}; + +bitfield TypeId { + id : 12; + type : 4; +}; + +fn get_tag(TypeId tag) { + return std::format("{:x}{:03x}", tag.type, tag.id); +}; + +fn get_type(TypeId tag) { + TagType tmp = tag.type; + str name = std::format("{}", tmp); + u32 len = std::string::length(name); + return std::string::substr(name, 9, len-9); +}; + +struct Tag { + TypeId tag[[format_read("get_tag")]]; + match (tag.type) { + (TagType::Null) : { + // All set + } + (TagType::Byte) : { + u8 value; + u8 pad[[hidden]]; + } + (TagType::Word) : { + u16 value; + } + (TagType::DWord) : { + u32 value; + } + (TagType::QWord) : { + u64 value; + } + (TagType::StringRef) : { + u32 str_offset; + char value @ str_offset; + } + (TagType::List) : { + u32 size ; + Tag tags[while($ < ((addressof(size) + size + 4) ))]; + } + (TagType::String) : { + u32 size; + char16 value[size/2]; + } + (TagType::Binary) : { + u32 size; + u8 value[size]; + if (size & 1 ) { + u8 pad[[hidden]]; + } + } + } +}[[name(get_tag(tag)), comment(get_type(tag))]]; + +struct Header { + u32 Major; + u32 Minor; + char Magic[4]; + Tag tags[while(!std::mem::eof())]; +}; + +Header file @ 0x0; \ No newline at end of file diff --git a/patterns/snes.hexpat b/patterns/snes.hexpat new file mode 100644 index 00000000..4a6b690f --- /dev/null +++ b/patterns/snes.hexpat @@ -0,0 +1,110 @@ +#pragma author gmestanley +#pragma description Super Nintendo Entertainment System ROM header +#pragma sources snes.nesdev.org/wiki/ROM_header CPU_vectors en.wikibooks.org/wiki/Super_NES_Programming/SNES_memory_map + +import std.string; + +u24 headerPosition = 0x7FC0; + +fn calculateHeaderPosition() { + if (std::mem::size() > 0x20000 && std::mem::size() < 0x600000) headerPosition += 0x8000; + else if (std::mem::size() >= 0x600000) headerPosition += 0x400000; +}; +calculateHeaderPosition(); + +enum ChipsetSubtype : u8 { + SPC7110, + ST01x, + ST018, + CX4 +}; + +struct ExpandedHeader { + char makerID[2]; + char gameID[4]; + padding[6]; + u8 expansionFlashSize [[comment("1 << N")]]; + u8 expansionRAMSize [[comment("1 << N")]]; + u8 specialVersion; + ChipsetSubtype chipsetSubtype; +}; + +struct ConditionalStruct { + if ($[headerPosition+0x1A] == 0x33) ExpandedHeader expandedHeader @ headerPosition - 0x10; + else if (!$[headerPosition+0x14]) ChipsetSubtype chipsetSubtype @ headerPosition - 1; +} [[inline]]; + +ConditionalStruct conditionalStruct @ $; + +enum MappingMode : u8 { + LoROM, + HiROM, + ExHiROM = 5 +}; + +fn formatMappingMode(u8 value) { + MappingMode enumValue = value; + return enumValue; +}; + +bitfield ROMType { + mappingMode : 4 [[format("formatMappingMode")]]; + speed : 1; + unknown : 1; +}; + +enum CoprocessorType : u8 { + DSP, + GSU, + OBC1, + SA1, + SDD1, + SRTC, + Other = 0x0E, + Custom +}; + +fn formatExtraHardwareType(u8 value) { + str valueMeaning = " (ROM"; + if (!value) valueMeaning += " only"; + else if (value) { + if (value > 3) valueMeaning += " + coprocessor"; + if (value != 3 || value != 6) valueMeaning += " + RAM"; + if (value == 2 || value > 5) valueMeaning += " + battery"; + } + return std::string::to_string(value) + valueMeaning + ")"; +}; + +fn formatCoprocessorType(u8 value) { + CoprocessorType enumValue = value; + return enumValue; +}; + +bitfield ExtraHardware { + extraHardwareType : 4 [[format("formatExtraHardwareType")]]; + coprocessorType : 4 [[format("formatCoprocessorType")]]; +}; + +enum Country : u8 { + NTSC = 1, + PAL +}; + +struct Header { + char title[21]; + ROMType romType; + ExtraHardware extraHardware; + u8 romSize [[comment("1 << N, rounded up")]]; + u8 ramSize [[comment("1 << N")]]; + Country country; + u8 developerID; + u8 romVersion; + u16 checksumComplement; + u16 checksum; + padding[4]; + u16 vectors[6]; + padding[4]; + u16 emulationModeVectors[6]; +}; + +Header header @ headerPosition; diff --git a/patterns/tarc.hexpat b/patterns/tarc.hexpat new file mode 100644 index 00000000..93ce6ad3 --- /dev/null +++ b/patterns/tarc.hexpat @@ -0,0 +1,50 @@ +#pragma author Lexi Mayfield +#pragma description KEX Engine TARC format +#pragma magic [0x54 0x41 0x52 0x43] @ 0x00 + +enum PixelFormat : u16 { + DXT1 = 14, + DXT5 = 16, + BC7 = 33, +}; + +struct TARCEntry1 { + char fileName[64]; + u64 offset; + u32 size; + u16 width; + u16 height; + u16 numMipMaps; + PixelFormat pixelFormat; +}; + +struct TARCEntry2 { + char fileName[64]; + u64 offset; + u32 size; + u16 x; + u16 y; + u16 width; + u16 height; + u16 numMipMaps; + PixelFormat pixelFormat; + u16 origWidth; + u16 origHeight; + s16 offsetX; + s16 offsetY; +}; + +struct TARC { + char id[4]; + u32 version; + u32 numEntries; + u64 pixelDataOffset; + + if (version == 1) { + TARCEntry1 entries[numEntries]; + } else if (version == 2) { + TARCEntry2 entries[numEntries]; + } +}; + +TARC tarc @ 0x00; \ No newline at end of file diff --git a/patterns/terminfo.hexpat b/patterns/terminfo.hexpat new file mode 100644 index 00000000..63bc5201 --- /dev/null +++ b/patterns/terminfo.hexpat @@ -0,0 +1,348 @@ +/* info */ +#pragma author Nightowl286 +#pragma description Compiled term info entry + +#pragma endian little + +#pragma magic [1e 02] @ 0x00 +#pragma MIME application/x-terminfo2 + +#pragma magic [1a 01] @ 0x00 +#pragma MIME application/x-terminfo + +/* + Format has been primarily taken from: + https://www.man7.org/linux/man-pages/man5/term.5.html + + Technically you can't count on the binary formats being compatible + on different unix versions, however *(as I've been told)* those versions + are pretty much dead nowadays so this format is common enough to work + in like the majority of the cases. +*/ + +/* imports */ +import std.array; +import std.mem; +import std.string; +import std.ctype; + + +/* utility */ +fn is_char(char ch) { + char value = std::mem::read_unsigned($, 1); + return value == ch; +}; + +fn is_not_char(char ch) { + char value = std::mem::read_unsigned($, 1); + return value != ch; +}; + + +/* types */ +enum number_format : s16 { + legacy = 0o432, + extended = 0o1036 +} +[[format("number_format_to_string")]]; + +struct boolean_capability { s8 value; } +[[ + sealed, + format("boolean_capability_format"), + comment("Indicates the presence of a boolean capability.\n\n(An empty value indicates that the capability is missing).") +]]; + +struct numerical_capability { + if (parent.header.format == number_format::extended) + s32 value; + else + s16 value; +} +[[ + sealed, + format("numerical_capability_format"), + comment("Indicates the presence of a numerical capability.\n\n(An empty value indicates that the capability is missing).") +]]; + +struct string_table_value_offset { s16 value; } +[[ + sealed, + format("string_table_value_offset_format"), + comment("Indicates the offset of a string capability value in the string table.\n\n(An empty value indicates that the capability is missing).") +]]; + +struct string_table_name_offset { s16 value; } +[[ + sealed, + format("string_table_name_offset_format"), + comment("Indicates the offset of a name string in the string table.\n\n(This value is relative to the name portion of the string table).") +]]; + +struct terminal_name { + char name[while(is_not_char('|') && is_not_char(0x00))]; + padding[1]; + + if (is_char('|')) $ += 1; + if (is_char(0)) break; +} +[[ + sealed, + comment("Represents a terminal name/alias.") +]]; + +struct table_string { + char value[while(is_not_char(0x00))]; + if (is_char(0)) + $ += 1; + +} +[[ + sealed, + format("string_table_value_format"), + comment("A null-terminated string in the string table.") +]]; + +struct string_table { + std::ByteSizedArray array [[inline]]; +}; + +struct legacy_header { + number_format format + [[comment("The number format of the terminfo entry.")]]; + + s16 name_size + [[ + name("name size"), + comment("The size of the terminal names in bytes.") + ]]; + + s16 boolean_count + [[ + name("boolean capability count"), + comment("The amount of known boolean capabilities in the file.") + ]]; + + s16 numerical_count + [[ + name("numerical capability count"), + comment("The amount of known numerical capabilities in the file.") + ]]; + + s16 string_count + [[ + name("string capability count"), + comment("The amount of known string capabilities in the file.") + ]]; + + s16 string_table_size + [[ + name("string table size"), + comment("The size of the string table (in bytes).") + ]]; +}; + +struct extended_header { + s16 boolean_count + [[ + name("boolean capability count"), + comment("The amount of extended boolean capabilities in the file.") + ]]; + + s16 numerical_count + [[ + name("numerical capability count"), + comment("The amount of extended numerical capabilities in the file.") + ]]; + + s16 string_count + [[ + name("string capability count"), + comment("The amount of extended string capabilities in the file.") + ]]; + + s16 table_item_count + [[ + name("table item count"), + comment("The amount of items in the string table.") + ]]; + + s16 string_table_size + [[ + name("string table size"), + comment("The size of the extended string table (in bytes).") + ]]; +}; + +struct extended_data { + extended_header header + [[comment("The metadata about the extended section of the file.")]]; + + boolean_capability booleans[header.boolean_count] + [[ + name("boolean capabilities"), + comment("The extended boolean capabilities.") + ]]; + + if ($ % 2 != 0) $ += 1; // align to even boundary before numerical capabilities are read; + + numerical_capability numerical[header.numerical_count] + [[ + name("numerical capabilities"), + comment("The extended numerical capabilities.") + ]]; + + string_table_value_offset string_offsets[header.string_count] + [[ + name("string table value offsets"), + comment("The string table offsets for the extended string capability values.") + ]]; + + string_table_name_offset name_offsets[header.table_item_count - header.string_count] + [[ + name("string table name offsets"), + comment("The string table offsets for all of the extended capability names.\n\n(These values are relative to the name portion of the string table).") + ]]; + + string_table string_table + [[ + name("string table"), + comment("Contains all of the string values used in the extended section of the file.") + ]]; +}; + +struct file { + legacy_header header + [[comment("The metadata about the file.")]]; + + terminal_name terminal_names[1000] + [[ + format_entries("terminal_name_format"), + name("terminal names"), + comment("The names and aliases that the terminfo file is describing.") + ]]; + + boolean_capability booleans[header.boolean_count] + [[ + name("boolean capabilities"), + comment("The known boolean capabilities.") + ]]; + + if ($ % 2 != 0) $ += 1; // align to even boundary before numerical capabilities are read; + + numerical_capability numerical[header.numerical_count] + [[ + name("numerical capabilities"), + comment("The known numerical capabilities.") + ]]; + + string_table_value_offset string_offsets[header.string_count] + [[ + name("string table value offsets"), + comment("The string table offsets for the known string capability values.") + ]]; + + string_table string_table + [[ + name("string table"), + comment("Contains all of the string values used in the (legacy storage format section of) file.") + ]]; + + if (std::mem::eof() == false) { + extended_data extended + [[comment("Contains the information from extended storage format section.")]]; + } + +} [[inline]]; + + +/* formatting */ +fn terminal_name_format(terminal_name name) { + return '"' + name.name + '"'; +}; + +fn boolean_capability_format(boolean_capability value) { + match (value.value) { + (-2): return "cancelled"; + (-1 | 0): return ""; + (1): return "present"; + (_): return value.value; + } + return value; +}; + +fn numerical_capability_format(numerical_capability value) { + match (value.value) { + (-2): return "cancelled"; + (-1): return ""; + (_): return value.value; + } + return value; +}; + +fn string_table_value_offset_format(string_table_value_offset value) { + match (value.value) { + (-2): return "cancelled"; + (-1): return ""; + (_): return value.value; + } + return value; +}; + +fn string_table_name_offset_format(string_table_name_offset value) { + return value.value; +}; + +fn string_table_value_format(ref table_string value) { + str view = "\""; + + for (s16 i = 0, i < sizeof(value.value), i += 1) { + char ch = value.value[i]; + if (ch == '\\') view += "\\\\"; + else if (ch == '"') view += "\\\""; + else if (std::ctype::isgraph(ch)) view += ch; + else view += to_hex_ch(ch); + } + + view += "\""; + return view; +}; + +fn to_hex_ch(char ch) { + if (ch == '\x1b') return "\\e"; + if (ch == ' ') return " "; + if (ch == '\r') return "\\r"; + if (ch == '\n') return "\\n"; + if (ch == '\a') return "\\a"; + if (ch == '\b') return "\\b"; + if (ch == '\t') return "\\t"; + if (ch == 11) return "\\v"; + if (ch == '\f') return "\\f"; + + s8 second = ch % 16; + s8 first = ch / 16; + + return "\\x" + to_hex_digit(first) + to_hex_digit(second); +}; + +fn to_hex_digit(u8 num) { + if (num == 10) return 'A'; + if (num == 11) return 'B'; + if (num == 12) return 'C'; + if (num == 13) return 'D'; + if (num == 14) return 'E'; + if (num == 15) return 'F'; + + return std::string::to_string(num); +}; + +fn number_format_to_string(number_format format) { + if (format == number_format::extended) return "extended"; + if (format == number_format::legacy) return "legacy"; + + return "unknown (" + std::string::to_string(s16(format)) + ")"; +}; + + +/* actual data */ +file file @ 0x00; \ No newline at end of file diff --git a/patterns/uefi_fv_varstore.hexpat b/patterns/uefi_fv_varstore.hexpat new file mode 100644 index 00000000..e5b3248c --- /dev/null +++ b/patterns/uefi_fv_varstore.hexpat @@ -0,0 +1,212 @@ +/* + * ImHex Pattern for UEFI Firmware Volume Variable Store + * + * This file type is commonly used with virtual machine UEFI variable files, like OVMF.fd + * used with QEMU. You could also extract a UEFI firmware binary from a flash device, + * search for the FV Variable Store, and set this pattern to the FV address. + * + * A 'custom_vars.fd' can be generated with these tools: + * + * https://gitlab.com/kraxel/virt-firmware + * https://github.com/rhuefi/qemu-ovmf-secureboot/tree/master + * https://github.com/LongSoft/UEFITool + * + * 1. Generate a blank .fd file with ovmfvartool. + * + * $ ovmfvartool generate-blank blank.fd + * + * 2. Enroll the Redhat and Microsoft keys with virt-fw-vars in custom_vars.fd. + * + * $ virt-fw-vars -i blank.fd -o custom_vars.fd --enroll-redhat --secure-boot + * + * 3. Dump custom_vars.fd contents + * + * $virt-fw-vars -i custom_vars.fd -pvx + * + * or + * + * $ uefitool custom_vars.fd + * + * or use this pattern with ImHex! + */ + +#pragma author Marc Jones +#pragma description UEFI Firmware Volume Variable Store +// #pragma debug + +import std.core; +import std.mem; +import type.guid; + + +// --- GUIDs --- + +#define NVRAM_FV "{FFF12B8D-7696-4C8B-A985-2747075B4F50}" +#define NVRAM_VARSTORE "{AAF32C78-947B-439A-A180-2E144EC37792}" + + +// --- Enumerations and Bitfields --- + +// Describes the type of a file within the Firmware File System. +enum FfsFileType : u8 { + RAW = 0x01, + FREEFORM = 0x02, + SECURITY_CORE = 0x03, + PEI_CORE = 0x04, + DXE_CORE = 0x05, + PEIM = 0x06, + DRIVER = 0x07, + COMBINED_PEIM_DRIVER = 0x08, + APPLICATION = 0x09, + SMM = 0x0A, + FIRMWARE_VOLUME_IMAGE = 0x0B, + COMBINED_SMM_DXE = 0x0C, + SMM_CORE = 0x0D, + FFS_PAD = 0xF0, +}; + +// Attributes for a UEFI variable, indicating its properties and accessibility. +bitfield VariableAttributes{ + NON_VOLATILE : 1; + BOOTSERVICE_ACCESS : 1; + RUNTIME_ACCESS : 1; + HARDWARE_ERROR_RECORD : 1; + AUTHENTICATED_WRITE_ACCESS : 1; + TIME_BASED_AUTHENTICATED_WRITE_ACCESS : 1; + APPEND_WRITE : 1; + RSVD: 25; +}; + +// +// Variable Store Header Format & State flags +// +enum VariableStoreFormat : u8 { + VARIABLE_STORE_FORMATTED = 0x5a, +}; + +enum VariableStoreState : u8 { + VARIABLE_STORE_HEALTHY = 0xfe, +}; + + +// +// Variable State flags. See https://countchu.blogspot.com/2014/09/the-life-cycle-of-uefi-variable-in.html +// +enum VariableState : u8 { + VAR_IN_DELETED_TRANSITION = 0xfe, + VAR_DELETED = 0xfd, + VAR_HEADER_VALID_ONLY = 0x7f, + VAR_ADDED = 0x3f, + VAR_ADDED__VAR_IN_DELETED_TRANSITION__VAR_DELETED = 0x3c, + VAR_ADDED__VAR_IN_DELETED_TRANSITION = 0x3e, + VAR_ADDED__VAR_DELETED = 0x3d, +}; + + +// --- Other Structures --- + +struct EFI_TIME { + u16 Year; + u8 Month; + u8 Day; + u8 Hour; + u8 Minute; + u8 Second; + u8 Pad1; + u32 Nanosecond; + u16 TimeZone; + u8 Daylight; + u8 Pad2; +}; + + +// --- Firmware Volume Structures --- + +// Header for a block in the firmware volume map. +struct EFI_FV_BLOCK_MAP_ENTRY { + u32 NumBlocks; + u32 Length; +}; + +// The main header for a Firmware Volume. +struct EFI_FIRMWARE_VOLUME_HEADER { + u128 ZeroVector; + type::GUID FileSystemGuid; + u64 FvLength; + u32 Signature; + u32 Attributes; + u16 HeaderLength; + u16 Checksum; + u16 ExtHeaderOffset; + u8 Reserved; + u8 Revision; + EFI_FV_BLOCK_MAP_ENTRY BlockMap[while(std::mem::read_unsigned($, 4) != 0 || std::mem::read_unsigned($ + 4, 4) != 0)]; + EFI_FV_BLOCK_MAP_ENTRY BlockMapTerminator; // After the loop, explicitly parse the (0,0) terminator entry +}[[single_color]]; + + +// --- UEFI Variable Structures --- + +struct VARIABLE_STORE_HEADER { + type::GUID Signature; + u32 Size; + VariableStoreFormat Format; + VariableStoreState State; + u16 Reserved; + u32 Reserved1; +}[[single_color]]; + + +#define VAR_START_ID 0x55AA + +struct VARIABLE_HEADER { + u16 StartId; + VariableState State; + u8 Reserved; + VariableAttributes Attributes; + u32 NameSize; + u32 DataSize; + type::GUID VendorGuid; +}; + +struct AUTHENTICATED_VARIABLE_HEADER { + u16 StartId; + VariableState State; + u8 Reserved; + VariableAttributes Attributes; + u64 MonotonicCount; + EFI_TIME TimeStamp; + u32 PubKeyIndex; + u32 NameSize; + u32 DataSize; + type::GUID VendorGuid; +}; + +struct UEFI_VARIABLE { + AUTHENTICATED_VARIABLE_HEADER Header; // TODO: Check authenticated vs normal variable... + char16 Name[Header.NameSize / 2]; // Name is a UTF-16 string + u8 Data[Header.DataSize]; + // Align to the next 4-byte boundary for the next variable. + u8 pad[std::mem::align_to(4, sizeof(this)) - sizeof(this)]; +} [[name(this.Name), single_color]]; + + +// --- Main Pattern Entry Point --- + +EFI_FIRMWARE_VOLUME_HEADER FV_Header @ 0; + +if (std::core::formatted_value(FV_Header.FileSystemGuid) != "{FFF12B8D-7696-4C8B-A985-2747075B4F50}") { + std::error(std::format("Unknown FV_Header.FileSystemGuid {}", std::core::formatted_value(FV_Header.FileSystemGuid))); +} + +// The next structure should be the Variable Store Header +VARIABLE_STORE_HEADER VarStore @ $; + +if (std::core::formatted_value(VarStore.Signature) != NVRAM_VARSTORE) { + std::error(std::format("Unknown VarStore.Signature {}", std::core::formatted_value(VarStore.Signature))); +} + +// Index through the Uefi variables until we don't find a Variable Signature 0x55AA +UEFI_VARIABLE UefiVars[while(std::mem::read_unsigned($, 2) == VAR_START_ID)] @ $; + +// TODO: grey out the Uefi variables that are in the non-active state, != VAR_ADDED. diff --git a/patterns/unity-asset-bundle.hexpat b/patterns/unity-asset-bundle.hexpat new file mode 100644 index 00000000..2ef7e09a --- /dev/null +++ b/patterns/unity-asset-bundle.hexpat @@ -0,0 +1,177 @@ +#pragma author Khoo Hao Yit +#pragma description Unity Asset Bundle + +#pragma magic [ "UnityWeb\0" ] @ 0x00 +#pragma magic [ "UnityRaw\0" ] @ 0x00 +#pragma magic [ "UnityArchive\0" ] @ 0x00 +#pragma magic [ "UnityFS\0" ] @ 0x00 +#pragma endian big + +// Reference: +// https://archive.vg-resource.com/thread-43269.html +// https://github.com/K0lb3/UnityPy +// https://github.com/Perfare/AssetStudio +// https://docs.unity3d.com/550/Documentation/Manual/AssetBundleInternalStructure.html + +import std.core; +import std.sys; +import hex.dec; + +enum Compression: u8 { + None = 0, + Lzma = 1, + Lz4 = 2, + Lz4hc = 3, + Lzham = 4, +}; + +enum Flag : u32 { + CompressionMask = 0b0000111111, + BlocksAndDirectoryInfoCombined = 0b0001000000, + BlockInfoAtTheEnd = 0b0010000000, + OldWebPluginCompatibility = 0b0100000000, + BlockInfoNeedPaddingAtStart = 0b1000000000, +}; + +std::mem::Section blockData; +auto cursor = -1; + +namespace v1 { + struct Level { + u32 compressedSize; + u32 decompressedSize; + }; + + struct DirectoryRecord { + char path[]; + u32 offset; + u32 size; + + std::mem::Bytes data @ offset in blockData; + }; + + struct Header { + u32 nodeCount; + v1::DirectoryRecord nodes[nodeCount]; + }; +} + +namespace v2 { + struct BlockInfo { + u32 decompressedSize; + u32 compressedSize; + u16 flags; + + u8 compressedData[compressedSize] @ cursor in 0 [[sealed, name(std::format("blockInfo{:d}", std::core::array_index()))]]; + cursor += compressedSize; + + std::mem::Section decompressedData = std::mem::create_section("decompressedData"); + match (flags & Flag::CompressionMask) { + (_): std::unimplemented(); + (Compression::None): std::mem::copy_value_to_section(compressedData, decompressedData, 0); + (Compression::Lzma): hex::dec::lzma_decompress(compressedData, decompressedData); + (Compression::Lz4): hex::dec::lz4_decompress(compressedData, decompressedData, false); + (Compression::Lz4hc): hex::dec::lz4_decompress(compressedData, decompressedData, false); + (Compression::Lzham): std::unimplemented(); + } + std::mem::copy_section_to_section( + decompressedData, + 0, + blockData, + std::mem::get_section_size(blockData), + std::mem::get_section_size(decompressedData) + ); + std::mem::delete_section(decompressedData); + }; + + struct DirectoryRecord { + s64 offset; + s64 size; + u32 flags; + char path[]; + + std::mem::Bytes data @ offset in blockData; + }; + + struct Header { + blockData = std::mem::create_section("BlockData"); + std::mem::Bytes<16> dataHash; + s32 blockInfoCount; + v2::BlockInfo blockInfos[blockInfoCount]; + u32 nodeCount; + v2::DirectoryRecord nodes[nodeCount]; + }; +} + +struct AssetBundle { + char signature[]; + u32 version; + char unityVersion[]; + char unityRevision[]; + if (signature == "UnityArchive\0") { + std::unimplemented(); + } + if (signature != "UnityFS\0" && version != 6) { + if (version >= 4) { + std::mem::Bytes<16> hash; + u32 crc; + } + u32 minimumStreamedBytes; + u32 size; + u32 numberOfLevelsToDownloadBeforeStreaming; + s32 levelCount; + v1::Level levels[levelCount]; + if (version >= 2) { + u32 completeFileSize; + } + if (version >= 3) { + u32 fileInfoHeaderSize; + } + $ = size; + u8 compressedBlockInfo[levels[std::core::member_count(levels) - 1].compressedSize] [[sealed]]; + blockData = std::mem::create_section("BlockInfoAndData"); + if (signature == "UnityWeb\0") { + hex::dec::lzma_decompress(compressedBlockInfo, blockData); + } else { + std::mem::copy_value_to_section(compressedBlockInfo, blockData, 0); + } + v1::Header header @ 0 in blockData; + return; + } + s64 size; + u32 compressedBlockInfoSize; + u32 decompressedBlockInfoSize; + u32 flags; + if (signature != "UnityFS\0") { + $ += 1; + } + if (version >= 7) { + $ += -$ & 0b1111; + } + + if (flags & Flag::BlockInfoAtTheEnd) { + u8 compressedBlockInfo[compressedBlockInfoSize] @ std::mem::size() - compressedBlockInfo [[sealed]]; + } else { + std::assert_warn(flags & Flag::BlocksAndDirectoryInfoCombined, "Expected BlocksAndDirectoryInfoCombined to be true"); + u8 compressedBlockInfo[compressedBlockInfoSize] [[sealed]]; + } + + if (flags & Flag::BlockInfoNeedPaddingAtStart) { + $ += -$ & 0b1111; + } + + std::mem::Section blockInfo = std::mem::create_section("BlockInfo"); + match (flags & Flag::CompressionMask) { + (_): std::unimplemented(); + (Compression::None): std::mem::copy_value_to_section(compressedBlockInfo, blockInfo, 0); + (Compression::Lzma): hex::dec::lzma_decompress(compressedBlockInfo, blockInfo); + (Compression::Lz4): hex::dec::lz4_decompress(compressedBlockInfo, blockInfo, false); + (Compression::Lz4hc): hex::dec::lz4_decompress(compressedBlockInfo, blockInfo, false); + (Compression::Lzham): std::unimplemented(); + } + + cursor = $; + v2::Header header @ 0 in blockInfo; +}; + +AssetBundle assetBundle @ 0; diff --git a/patterns/upk-ue3.hexpat b/patterns/upk-ue3.hexpat index b0c2a559..6f8674a4 100644 --- a/patterns/upk-ue3.hexpat +++ b/patterns/upk-ue3.hexpat @@ -1,5 +1,6 @@ #pragma description Unreal Engine 3 UPK #pragma endian little +#pragma magic [ c1 83 2a 9e ] @ 0x00 import std.io; diff --git a/patterns/was_oskasoftware.hexpat b/patterns/was_oskasoftware.hexpat index 9feed166..ccdfe707 100644 --- a/patterns/was_oskasoftware.hexpat +++ b/patterns/was_oskasoftware.hexpat @@ -7,8 +7,8 @@ import type.size; struct Sound { char name[]; u32 length; - u8 data[length]; -}; + u8 data[length] [[sealed]]; +} [[name(name), hex::visualize("sound", data, 1, 8000)]]; struct Header { type::Size size; @@ -21,4 +21,4 @@ struct WAS { Sound sounds[header.count]; }; -WAS was @ 0x00; \ No newline at end of file +WAS was @ 0x00; diff --git a/tests/patterns/source/main.cpp b/tests/patterns/source/main.cpp index 7d12f034..0d6abeb7 100644 --- a/tests/patterns/source/main.cpp +++ b/tests/patterns/source/main.cpp @@ -72,7 +72,7 @@ int main(int argc, char **argv) { } runtime.setDangerousFunctionCallHandler([]{ return true; }); - runtime.setIncludePaths({ includePath }); + runtime.setIncludePaths({ includePath, patternFilePath.parent_path() }); runtime.addPragma("MIME", DummyPragmaHandler); runtime.addPragma("description", DescPragmaHandler); runtime.addDefine("__PL_UNIT_TESTS__"); diff --git a/tests/patterns/test_data/SHR_animation#C20000 b/tests/patterns/test_data/SHR_animation#C20000 new file mode 100644 index 00000000..bcc964d5 Binary files /dev/null and b/tests/patterns/test_data/SHR_animation#C20000 differ diff --git a/tests/patterns/test_data/commodore_basic.hexpat.prg b/tests/patterns/test_data/commodore_basic.hexpat.prg new file mode 100644 index 00000000..9ad2826a Binary files /dev/null and b/tests/patterns/test_data/commodore_basic.hexpat.prg differ diff --git a/tests/patterns/test_data/dos.hexpat.exe b/tests/patterns/test_data/dos.hexpat.exe new file mode 100644 index 00000000..5e20b67c Binary files /dev/null and b/tests/patterns/test_data/dos.hexpat.exe differ diff --git a/tests/patterns/test_data/dotnet_binaryformatter.hexpat.bin b/tests/patterns/test_data/dotnet_binaryformatter.hexpat.bin new file mode 100644 index 00000000..a3bb135c Binary files /dev/null and b/tests/patterns/test_data/dotnet_binaryformatter.hexpat.bin differ diff --git a/tests/patterns/test_data/esp32_image.hexpat.bin b/tests/patterns/test_data/esp32_image.hexpat.bin new file mode 100644 index 00000000..fcf755eb Binary files /dev/null and b/tests/patterns/test_data/esp32_image.hexpat.bin differ diff --git a/tests/patterns/test_data/gmf.hexpat/gmf.hexpat.gmf b/tests/patterns/test_data/gmf.hexpat/gmf.hexpat.gmf new file mode 100644 index 00000000..ddb8bd64 Binary files /dev/null and b/tests/patterns/test_data/gmf.hexpat/gmf.hexpat.gmf differ diff --git a/tests/patterns/test_data/gmf.hexpat/gmf.hexpat_data/help b/tests/patterns/test_data/gmf.hexpat/gmf.hexpat_data/help new file mode 100644 index 00000000..5fba4b39 Binary files /dev/null and b/tests/patterns/test_data/gmf.hexpat/gmf.hexpat_data/help differ diff --git a/tests/patterns/test_data/ktx.hexpat/rgb-mipmap-reference.ktx b/tests/patterns/test_data/ktx.hexpat/rgb-mipmap-reference.ktx new file mode 100644 index 00000000..622a9477 Binary files /dev/null and b/tests/patterns/test_data/ktx.hexpat/rgb-mipmap-reference.ktx differ diff --git a/tests/patterns/test_data/pickle.hexpat.bin b/tests/patterns/test_data/pickle.hexpat.bin new file mode 100644 index 00000000..1931e649 Binary files /dev/null and b/tests/patterns/test_data/pickle.hexpat.bin differ diff --git a/tests/patterns/test_data/popcap_luc.hexpat.luc b/tests/patterns/test_data/popcap_luc.hexpat.luc new file mode 100644 index 00000000..7d95d371 Binary files /dev/null and b/tests/patterns/test_data/popcap_luc.hexpat.luc differ diff --git a/tests/patterns/test_data/roblox_pack.hexpat.pack b/tests/patterns/test_data/roblox_pack.hexpat.pack new file mode 100644 index 00000000..99be9630 Binary files /dev/null and b/tests/patterns/test_data/roblox_pack.hexpat.pack differ diff --git a/tests/patterns/test_data/sdb.hexpat.sdb b/tests/patterns/test_data/sdb.hexpat.sdb new file mode 100644 index 00000000..e98a40e1 Binary files /dev/null and b/tests/patterns/test_data/sdb.hexpat.sdb differ diff --git a/tests/patterns/test_data/terminfo.hexpat.bin b/tests/patterns/test_data/terminfo.hexpat.bin new file mode 100644 index 00000000..2a9f42b4 Binary files /dev/null and b/tests/patterns/test_data/terminfo.hexpat.bin differ diff --git a/tests/patterns/test_data/uefi_fv_varstore.hexpat.fd b/tests/patterns/test_data/uefi_fv_varstore.hexpat.fd new file mode 100644 index 00000000..b3ee1048 Binary files /dev/null and b/tests/patterns/test_data/uefi_fv_varstore.hexpat.fd differ diff --git a/themes/Nocturne.json b/themes/Nocturne.json index 253ea5fe..9db4d5ba 100644 --- a/themes/Nocturne.json +++ b/themes/Nocturne.json @@ -1,6 +1,6 @@ { - "base": "Nocturne (Dark)", - "name": "Nocture", + "base": "Dark", + "name": "Nocturne", "image_theme": "dark", "authors": "JeanxPereira", "description": "minimal dark theme based on Dracula but more darker", @@ -162,7 +162,7 @@ "default": "#7F7F7FFF", "doc-comment": "#206020FF", "error-marker": "#D83545FF", - "global-doc-comment": "#2B9C89FF", + "doc-global-comment": "#2B9C89FF", "identifier": "#AFAFAFFF", "keyword": "#F87AC4FF", "known-identifier": "#6CF982FF", diff --git a/themes/catppuccin-frappe.json b/themes/catppuccin-frappe.json index 7ff19966..46c8bbdc 100644 --- a/themes/catppuccin-frappe.json +++ b/themes/catppuccin-frappe.json @@ -143,8 +143,8 @@ "error-marker": "#E782847F", "unknown-identifier": "#E782847F", "error-text": "#E782847F", - "debug-text": "#E782847F", - "warning-text": "#E782847F", + "debug-text": "#737994FF", + "warning-text": "#A5ADCEFF", "pattern-variable": "#949CBBFF", "function-parameter": "#949CBBFF", "function-variable": "#949CBBFF", @@ -160,13 +160,13 @@ "typedef": "#949CBBFF", "user-defined-type": "#949CBBFF", "keyword": "#CA9EE6FF", - "builtin-type": "#E78284FF", + "known-identifier": "#E78284FF", "line-number": "#838BA7FF", - "block-comment": "#737994FF", + "multi-line--comment": "#737994FF", "number": "#EF9F76FF", - "preprocessor-identifier": "#BABBF1FF", - "directive": "#A5ADCEFF", - "operator": "#C6D0F5FF", + "preproc-identifier": "#BABBF1FF", + "preprocessor": "#A5ADCEFF", + "punctuation": "#C6D0F5FF", "separator": "#C6D0F5FF", "selection": "#6268807F", "string": "#A6D189FF" diff --git a/themes/catppuccin-latte.json b/themes/catppuccin-latte.json index 29e66922..af4f42f5 100644 --- a/themes/catppuccin-latte.json +++ b/themes/catppuccin-latte.json @@ -143,8 +143,8 @@ "error-marker": "#D20F397F", "unknown-identifier": "#D20F397F", "error-text": "#D20F397F", - "debug-text": "#D20F397F", - "warning-text": "#D20F397F", + "debug-text": "#9CA0B0FF", + "warning-text": "#6C6F85FF", "pattern-variable": "#7C7F93FF", "function-parameter": "#7C7F93FF", "function-variable": "#7C7F93FF", @@ -160,13 +160,13 @@ "typedef": "#7C7F93FF", "user-defined-type": "#7C7F93FF", "keyword": "#8839EFFF", - "builtin-type": "#D20F39FF", + "known=identifier": "#D20F39FF", "line-number": "#8C8FA1FF", - "block-comment": "#9CA0B0FF", + "multi-line-comment": "#9CA0B0FF", "number": "#FE640BFF", - "preprocessor-identifier": "#7287FDFF", - "directive": "#6C6F85FF", - "operator": "#4C4F69FF", + "preproc-identifier": "#7287FDFF", + "preprocessor": "#6C6F85FF", + "punctuation": "#4C4F69FF", "separator": "#4C4F69FF", "selection": "#ACB0BE7F", "string": "#40A02BFF" diff --git a/themes/catppuccin-macchiato.json b/themes/catppuccin-macchiato.json index e056cc22..f2886b05 100644 --- a/themes/catppuccin-macchiato.json +++ b/themes/catppuccin-macchiato.json @@ -143,8 +143,8 @@ "error-marker": "#ED87967F", "unknown-identifier": "#ED87967F", "error-text": "#ED87967F", - "debug-text": "#ED87967F", - "warning-text": "#ED87967F", + "debug-text": "#6E738DFF", + "warning-text": "#A5ADCBFF", "pattern-variable": "#939AB7FF", "function-parameter": "#939AB7FF", "function-variable": "#939AB7FF", @@ -160,13 +160,13 @@ "typedef": "#939AB7FF", "user-defined-type": "#939AB7FF", "keyword": "#C6A0F6FF", - "builtin-type": "#ED8796FF", + "known-identifier": "#ED8796FF", "line-number": "#8087A2FF", - "block-comment": "#6E738DFF", + "multi-line-comment": "#6E738DFF", "number": "#F5A97FFF", - "preprocessor-identifier": "#B7BDF8FF", - "directive": "#A5ADCBFF", - "operator": "#CAD3F5FF", + "preproc-identifier": "#B7BDF8FF", + "preprocessor": "#A5ADCBFF", + "punctuation": "#CAD3F5FF", "separator": "#CAD3F5FF", "selection": "#5B60787F", "string": "#A6DA95FF" diff --git a/themes/catppuccin-mocha.json b/themes/catppuccin-mocha.json index 67652505..4bddd267 100644 --- a/themes/catppuccin-mocha.json +++ b/themes/catppuccin-mocha.json @@ -143,8 +143,8 @@ "error-marker": "#F38BA87F", "unknown-identifier": "#F38BA87F", "error-text": "#F38BA87F", - "debug-text": "#F38BA87F", - "warning-text": "#F38BA87F", + "debug-text": "#6C7086FF", + "warning-text": "#A6ADC8FF", "pattern-variable": "#9399B2FF", "function-parameter": "#9399B2FF", "function-variable": "#9399B2FF", @@ -160,13 +160,13 @@ "typedef": "#9399B2FF", "user-defined-type": "#9399B2FF", "keyword": "#CBA6F7FF", - "builtin-type": "#F38BA8FF", + "known-identifier": "#F38BA8FF", "line-number": "#7F849CFF", - "block-comment": "#6C7086FF", + "multi-line-comment": "#6C7086FF", "number": "#FAB387FF", - "preprocessor-identifier": "#B4BEFEFF", - "directive": "#A6ADC8FF", - "operator": "#CDD6F4FF", + "preproc-identifier": "#B4BEFEFF", + "preprocessor": "#A6ADC8FF", + "punctuation": "#CDD6F4FF", "separator": "#CDD6F4FF", "selection": "#585B707F", "string": "#A6E3A1FF" diff --git a/themes/one_dark.json b/themes/one_dark.json index 545c904c..11c16f67 100644 --- a/themes/one_dark.json +++ b/themes/one_dark.json @@ -158,7 +158,7 @@ "default": "#DADADAFF", "doc-comment": "#206858FF", "error-marker": "#C01A2BFF", - "global-doc-comment": "#208070FF", + "doc-global-comment": "#208070FF", "identifier": "#E4E4E4FB", "keyword": "#C46DE6FF", "known-identifier": "#C46DE6FF", diff --git a/themes/retina_dark.json b/themes/retina_dark.json new file mode 100644 index 00000000..5aa8ad49 --- /dev/null +++ b/themes/retina_dark.json @@ -0,0 +1,364 @@ +{ + "base": "Dark", + "colors": { + "imgui": { + "border": "#6D6D7F7F", + "border-shadow": "#00000000", + "button": "#4296F966", + "button-active": "#0F87F9FF", + "button-hovered": "#4296F9FF", + "check-mark": "#4296F9FF", + "child-background": "#00000000", + "docking-empty-background": "#0F0F0FEF", + "docking-preview": "#4296F9B2", + "drag-drop-target": "#FFFF00E5", + "frame-background": "#28497A89", + "frame-background-active": "#4296F9AA", + "frame-background-hovered": "#4296F966", + "header": "#4296F94F", + "header-active": "#4296F9FF", + "header-hovered": "#4296F9CC", + "menu-bar-background": "#232323FF", + "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": "#141414FF", + "resize-grip": "#4296F933", + "resize-grip-active": "#4296F9F2", + "resize-grip-hovered": "#4296F9AA", + "scrollbar-background": "#05050587", + "scrollbar-grab": "#4F4F4FFF", + "scrollbar-grab-active": "#828282FF", + "scrollbar-grab-hovered": "#686868FF", + "separator": "#6D6D7F7F", + "separator-active": "#1966BFFF", + "separator-hovered": "#1966BFC6", + "slider-grab": "#3D84E0FF", + "slider-grab-active": "#4296F9FF", + "tab": "#2D5993DB", + "tab-active": "#3268ADFF", + "tab-hovered": "#4296F9CC", + "tab-unfocused": "#111A25F7", + "tab-unfocused-active": "#22426CFF", + "table-border-light": "#3A3A3FFF", + "table-border-strong": "#4F4F59FF", + "table-header-background": "#303033FF", + "table-row-background": "#00000000", + "table-row-background-alt": "#FFFFFF0F", + "text": "#FFFFFFFF", + "text-disabled": "#7F7F7FFF", + "text-selected-background": "#4296F959", + "title-background": "#232323FF", + "title-background-active": "#232323FF", + "title-background-collapse": "#232323FF", + "window-background": "#0F0F0FFF", + "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": "#141414FF", + "desc-button-active": "#3C3C3CFF", + "desc-button-hovered": "#282828FF", + "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": "#06539BFF", + "logger-warning": "#F1C40FFF", + "patches": "#E74C3CFF", + "pattern-selected": "#3683CBFF", + "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": { + "attribute": "#C09494FF", + "background": "#101010FF", + "breakpoint": "#89140340", + "calculated-pointer": "#AAAAAAFF", + "char-literal": "#936848FF", + "comment": "#206020FF", + "current-line-edge": "#A0A0A040", + "current-line-fill": "#00000040", + "current-line-fill-inactive": "#80808040", + "cursor": "#E0E0E0FF", + "debug-text": "#8A8A8AFF", + "default": "#7F7F7FFF", + "default-text": "#7F7F7FFF", + "doc-block-comment": "#73B473FF", + "doc-comment": "#406020FF", + "doc-global-comment": "#506050FF", + "error-marker": "#90140280", + "error-text": "#96130080", + "function": "#C3C283FF", + "function-parameter": "#DADADAFF", + "function-variable": "#B7B7B7FF", + "global-variable": "#CECECEFF", + "identifier": "#AAAAAAFF", + "keyword": "#4880AFFF", + "known-identifier": "#45B28CFF", + "line-number": "#02A7A7FF", + "local-variable": "#C1C1C1FF", + "multi-line-comment": "#206040FF", + "namespace": "#A96226FF", + "number": "#287928FF", + "pattern-variable": "#D3D3D3FF", + "placed-variable": "#C8C6C6FF", + "preproc-identifier": "#BCB999FF", + "preprocessor": "#C0C04CFF", + "preprocessor-deactivated": "#4F4F4F45", + "punctuation": "#B6B6B6FF", + "selection": "#2060A080", + "separator": "#C4C4C4FF", + "string": "#7E3D3DFF", + "template-variable": "#7C7C7CFF", + "typedef": "#75C34CFF", + "unknown-identifier": "#BC2323FE", + "user-defined-type": "#73BCB2FF", + "view": "#7D7D7DFF", + "warning-text": "#A9A906FF" + } + }, + "image_theme": "dark", + "name": "Retina 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.6499999761581421, + "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 + ] + } + } +} \ No newline at end of file diff --git a/themes/solarized_dark.json b/themes/solarized_dark.json index 9b075646..9c5ae3a5 100644 --- a/themes/solarized_dark.json +++ b/themes/solarized_dark.json @@ -143,8 +143,8 @@ "error-marker": "#FF200080", "unknown-identifier": "#FF200080", "error-text": "#FF200080", - "debug-text": "#FF200080", - "warning-text": "#FF200080", + "debug-text": "#206020FF", + "warning-text": "#808040FF", "identifier": "#AAAAAAFF", "pattern-variable": "#AAAAAAFF", "function-parameter": "#AAAAAAFF", @@ -161,14 +161,14 @@ "typedef": "#AAAAAAFF", "user-defined-type": "#AAAAAAFF", "keyword": "#569CD6FF", - "builtin-type": "#4DC69BFF", + "known-identifier": "#4DC69BFF", "line-number": "#007070FF", - "block-comment": "#206040FF", + "multi-line-comment": "#206040FF", "number": "#00FF00FF", - "preprocessor-identifier": "#A040C0FF", - "directive": "#808040FF", - "operator": "#FFFFFFFF", - "separator": "#FFFFFFFF", + "preproc-identifier": "#A040C0FF", + "preprocessor": "#808040FF", + "punctuation": "#7F7F7FFF", + "separator": "#7F7F7FFF", "selection": "#2060A080", "string": "#E07070FF" } diff --git a/themes/theme_lion.json b/themes/theme_lion.json new file mode 100644 index 00000000..33d2796f --- /dev/null +++ b/themes/theme_lion.json @@ -0,0 +1,364 @@ +{ + "base": "Dark", + "colors": { + "imgui": { + "border": "#6D6D7F7F", + "border-shadow": "#2B2B2B00", + "button": "#21BEA866", + "button-active": "#0FF9E8FF", + "button-hovered": "#25BAB2FF", + "check-mark": "#1AC3BAFF", + "child-background": "#2B2B2B00", + "docking-empty-background": "#2B2B2BEF", + "docking-preview": "#108691B2", + "drag-drop-target": "#FFFF00E5", + "frame-background": "#28717A89", + "frame-background-active": "#24AAB6AA", + "frame-background-hovered": "#10B3B566", + "header": "#20999F4F", + "header-active": "#269DA5FF", + "header-hovered": "#33A2AECC", + "menu-bar-background": "#313537FF", + "modal-window-dim-background": "#CCCCCC59", + "nav-highlight": "#42F9E7FF", + "nav-windowing-background": "#CCCCCC33", + "nav-windowing-highlight": "#FFFFFFB2", + "plot-histogram": "#E5B200FF", + "plot-histogram-hovered": "#FF9900FF", + "plot-lines": "#9B9B9BFF", + "plot-lines-hovered": "#FF6D59FF", + "popup-background": "#3B3F41FF", + "resize-grip": "#42F9D433", + "resize-grip-active": "#42F9E4F2", + "resize-grip-hovered": "#42F9E7AA", + "scrollbar-background": "#2B2B2B87", + "scrollbar-grab": "#4F4F4FFF", + "scrollbar-grab-active": "#828282FF", + "scrollbar-grab-hovered": "#686868FF", + "separator": "#6D747F7F", + "separator-active": "#19BFA4FF", + "separator-hovered": "#19BFACC6", + "slider-grab": "#2C8990FF", + "slider-grab-active": "#267C83FF", + "tab": "#2D8F93DB", + "tab-active": "#158891FF", + "tab-hovered": "#36A3C1CC", + "tab-unfocused": "#1E2D31F7", + "tab-unfocused-active": "#226C6CFF", + "table-border-light": "#3A3A3FFF", + "table-border-strong": "#4F4F59FF", + "table-header-background": "#2B2B2BFF", + "table-row-background": "#2B2B2B6A", + "table-row-background-alt": "#FFFFFF0F", + "text": "#A9B7C6FF", + "text-disabled": "#7F7F7FFF", + "text-selected-background": "#19B8A259", + "title-background": "#3C3F41FF", + "title-background-active": "#232323FF", + "title-background-collapse": "#232323FF", + "window-background": "#2B2B2BFF", + "window-shadow": "#1B1B1BFF" + }, + "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": "#22B2B2B0", + "desc-button": "#2B2B2BFF", + "desc-button-active": "#3C3C3CFF", + "desc-button-hovered": "#4B4B4BFF", + "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": "#06539BFF", + "logger-warning": "#F1C40FFF", + "patches": "#E74C3CFF", + "pattern-selected": "#06539BFF", + "toolbar-blue": "#06539BFF", + "toolbar-brown": "#DBB377FF", + "toolbar-gray": "#E6E6E6FF", + "toolbar-green": "#388B42FF", + "toolbar-purple": "#672A78FF", + "toolbar-red": "#E74C3CFF", + "toolbar-yellow": "#F1C40FFF" + }, + "imnodes": { + "box-selector": "#3D78E01E", + "box-selector-outline": "#3D78E096", + "grid-background": "#282832C8", + "grid-line": "#C8C8C828", + "grid-line-primary": "#F0F0F03C", + "link": "#3D85E0C8", + "link-hovered": "#4285FAFF", + "link-selected": "#4285FAFF", + "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": "#4285FAFF", + "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": "#29467AFF", + "title-bar-hovered": "#4285FAFF", + "title-bar-selected": "#4285FAFF" + }, + "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": { + "attribute": "#30A090FF", + "background": "#2B2B2BFF", + "multi-line-comment": "#B0B0B0FF", + "breakpoint": "#3A2323FF", + "known-identifier": "#F09A3BFF", + "calculated-pointer": "#F2C7FEFF", + "char-literal": "#38E517FF", + "comment": "#808080FF", + "current-line-edge": "#A0A0A040", + "current-line-fill": "#00000040", + "current-line-fill-inactive": "#80808040", + "cursor": "#E0E0E0FF", + "debug-text": "#8A8A8AFF", + "default": "#7F7F7FFF", + "default-text": "#7F7F7FFF", + "preprocessor": "#BBB529FF", + "doc-block-comment": "#416438FF", + "doc-comment": "#629755FF", + "doc-global-comment": "#74B265FF", + "error-marker": "#FF0C0080", + "error-text": "#FF200080", + "function": "#FFC66DFF", + "function-parameter": "#A9B7C6FF", + "function-variable": "#798CADFF", + "global-variable": "#B8C5BFFF", + "identifier": "#AAAAAAFF", + "keyword": "#CC7832FF", + "line-number": "#676767FF", + "local-variable": "#FF7FCAFF", + "namespace": "#B5B6E3FF", + "number": "#6897BBFF", + "punctuation": "#ABD374FF", + "pattern-variable": "#FCADDCFF", + "placed-variable": "#A9B7C6FF", + "preprocessor-deactivated": "#686A4BB8", + "preproc-identifier": "#908B25FF", + "selection": "#209CA080", + "separator": "#AA9C89FF", + "string": "#6A8759FF", + "template-variable": "#9876AAFF", + "typedef": "#B9BCD1FF", + "unknown-identifier": "#BC3F3CFF", + "user-defined-type": "#B1B2FFFF", + "view": "#C7CAFFFF", + "warning-text": "#FFFF00FF" + } + }, + "image_postfix": "dark", + "name": "Theme Lion", + "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.6499999761581421, + "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 + ] + } + } +} \ No newline at end of file diff --git a/themes/vs_dark.json b/themes/vs_dark.json index 92b41bf0..bf028663 100644 --- a/themes/vs_dark.json +++ b/themes/vs_dark.json @@ -145,8 +145,8 @@ "error-marker": "#FF200080", "unknown-identifier": "#FF200080", "error-text": "#FF200080", - "debug-text": "#FF200080", - "warning-text": "#FF200080", + "debug-text": "#206020FF", + "warning-text": "#808040FF", "pattern-variable": "#AAAAAAFF", "function-parameter": "#AAAAAAFF", "function-variable": "#AAAAAAFF", @@ -162,14 +162,14 @@ "typedef": "#AAAAAAFF", "user-defined-type": "#AAAAAAFF", "keyword": "#569CD6FF", - "builtin-type": "#4DC69BFF", + "known-identifier": "#4DC69BFF", "line-number": "#007070FF", - "block-comment": "#206040FF", + "multi-line-comment": "#206040FF", "number": "#00FF00FF", - "preprocessor-identifier": "#A040C0FF", - "directive": "#808040FF", - "operator": "#FFFFFFFF", - "separator": "#FFFFFFFF", + "preproc-identifier": "#A040C0FF", + "preprocessor": "#808040FF", + "punctuation": "#7F7F7FFF", + "separator": "#7F7F7FFF", "selection": "#2060A080", "string": "#E07070FF" }