diff --git a/patterns/fbx.hexpat b/patterns/fbx.hexpat index 810f5ba..30b6cd3 100644 --- a/patterns/fbx.hexpat +++ b/patterns/fbx.hexpat @@ -4,18 +4,22 @@ #pragma endian little -import hex.dec; +#ifdef __IMHEX__ + import hex.dec; +#endif + import std.mem; import std.string; import std.sys; +import type.magic; struct Array { u32 arrayLength; u32 encoding; u32 compressedLength; - + std::assert(encoding < 2, "Invalid array encoding!"); - + if (encoding == 0) { // Uncompressed E contents[arrayLength]; @@ -23,9 +27,12 @@ struct Array { // Compressed (zlib) u128 pos = $; u8 compressedContents[compressedLength]; - std::mem::Section contentsSection = std::mem::create_section(std::format("contentsSection @ {:#x}", pos)); - hex::dec::zlib_decompress(compressedContents, contentsSection, 15); - E contents[] @ 0x00 in contentsSection; + + #ifdef __IMHEX__ + std::mem::Section contentsSection = std::mem::create_section(std::format("contentsSection @ {:#x}", pos)); + hex::dec::zlib_decompress(compressedContents, contentsSection, 15); + E contents[] @ 0x00 in contentsSection; + #endif } }; @@ -50,7 +57,7 @@ enum PropertyTypeCode : char { struct PropertyRecord { PropertyTypeCode typeCode; - + match (typeCode) { (PropertyTypeCode::BYTE): s8 data; (PropertyTypeCode::SHORT): s16 data; @@ -78,78 +85,148 @@ struct PropertyRecord { } }; -struct NodeRecord { - T endOffset; - T numProperties; - T propertyListLen; - u8 nameLen; +using NodeRecord32B; +struct NodeRecord32A { + u32 endOffset; + u32 numProperties; + u32 propertyListLen; + u8 nameLen; + // Detect sentinel record which marks the end of a list of node records - if (endOffset == 0 - && numProperties == 0 - && propertyListLen == 0 + if (endOffset == 0 + && numProperties == 0 + && propertyListLen == 0 && nameLen == 0) { break; } - + char name[nameLen]; auto posBeforePropertyRecords = $; auto posAfterPropertyRecords = posBeforePropertyRecords + propertyListLen; PropertyRecord propertyRecords[numProperties]; - $ = posAfterPropertyRecords; - NodeRecord nestedList[while(true)]; - $ = endOffset; + std::assert($ == posAfterPropertyRecords, std::format("Invalid size of propertyRecords @ {:#x} !", posBeforePropertyRecords)); + NodeRecord32B nestedList[while($ < endOffset)]; + std::assert($ == endOffset, std::format("Invalid size of nestedList @ {:#x} !", posAfterPropertyRecords)); +}; + +struct NodeRecord32B { + u32 endOffset; + u32 numProperties; + u32 propertyListLen; + u8 nameLen; + + // Detect sentinel record which marks the end of a list of node records + if (endOffset == 0 + && numProperties == 0 + && propertyListLen == 0 + && nameLen == 0) { + break; + } + + char name[nameLen]; + auto posBeforePropertyRecords = $; + auto posAfterPropertyRecords = posBeforePropertyRecords + propertyListLen; + PropertyRecord propertyRecords[numProperties]; + std::assert($ == posAfterPropertyRecords, std::format("Invalid size of propertyRecords @ {:#x} !", posBeforePropertyRecords)); + NodeRecord32A nestedList[while($ < endOffset)]; + std::assert($ == endOffset, std::format("Invalid size of nestedList @ {:#x} !", posAfterPropertyRecords)); +}; + +using NodeRecord64B; + +struct NodeRecord64A { + u64 endOffset; + u64 numProperties; + u64 propertyListLen; + u8 nameLen; + + // Detect sentinel record which marks the end of a list of node records + if (endOffset == 0 + && numProperties == 0 + && propertyListLen == 0 + && nameLen == 0) { + break; + } + + char name[nameLen]; + auto posBeforePropertyRecords = $; + auto posAfterPropertyRecords = posBeforePropertyRecords + propertyListLen; + PropertyRecord propertyRecords[numProperties]; + std::assert($ == posAfterPropertyRecords, std::format("Invalid size of propertyRecords @ {:#x} !", posBeforePropertyRecords)); + NodeRecord64B nestedList[while($ < endOffset)]; + std::assert($ == endOffset, std::format("Invalid size of nestedList @ {:#x} !", posAfterPropertyRecords)); +}; + +struct NodeRecord64B { + u64 endOffset; + u64 numProperties; + u64 propertyListLen; + u8 nameLen; + + // Detect sentinel record which marks the end of a list of node records + if (endOffset == 0 + && numProperties == 0 + && propertyListLen == 0 + && nameLen == 0) { + break; + } + + char name[nameLen]; + auto posBeforePropertyRecords = $; + auto posAfterPropertyRecords = posBeforePropertyRecords + propertyListLen; + PropertyRecord propertyRecords[numProperties]; + std::assert($ == posAfterPropertyRecords, std::format("Invalid size of propertyRecords @ {:#x} !", posBeforePropertyRecords)); + NodeRecord64A nestedList[while($ < endOffset)]; + std::assert($ == endOffset, std::format("Invalid size of nestedList @ {:#x} !", posAfterPropertyRecords)); }; fn assertZero (auto array, u128 size, auto message) { bool nonzeroPadding = false; - + for (u8 i = 0, i < size, i = i + 1) { if (array[i] != 0) { nonzeroPadding = true; } } - + std::assert_warn(!nonzeroPadding, message); }; struct Footer{ - char footerId[16]; - std::assert_warn(footerId == "\xFA\xBC\xAB\x09\xD0\xC8\xD4\x66\xB1\x76\xFB\x83\x1C\xF7\x26\x7E", "Invalid footerId!"); + type::Magic<"\xFA\xBC\xAB\x09\xD0\xC8\xD4\x66\xB1\x76\xFB\x83\x1C\xF7\x26\x7E"> footerId; char zeroes[4]; assertZero(zeroes, 4, "Found non-zero values in footer after footerId!"); u128 ofs = $; u8 alignmentPaddingSize = ((ofs + 15) & ~15) - ofs; - + if (alignmentPaddingSize == 0) { alignmentPaddingSize = 16; } - + char alignmentPadding[alignmentPaddingSize]; assertZero(alignmentPadding, alignmentPaddingSize, "Found non-zero bytes in alignmentPadding!"); u32 version; char staticPadding[120]; assertZero(staticPadding, 120, "Found non-zero bytes in staticPadding!"); - char footerMagic[16]; - std::assert_warn(footerMagic == "\xF8\x5A\x8C\x6A\xDE\xF5\xD9\x7E\xEC\xE9\x0C\xE3\x75\x8F\x29\x0B", "Invalid footerMagic!"); + type::Magic<"\xF8\x5A\x8C\x6A\xDE\xF5\xD9\x7E\xEC\xE9\x0C\xE3\x75\x8F\x29\x0B"> footerMagic; }; struct Header { - char magic[23]; - std::assert(magic == "Kaydara FBX Binary \x00\x1A\x00", "File is not a valid FBX!"); + type::Magic<"Kaydara FBX Binary \x00\x1A\x00"> magic; u32 version; }; struct FBX { Header header; - + if (header.version < 7500) { - NodeRecord rootRecords[while(true)]; + NodeRecord32A rootRecords[while(true)]; } else { - NodeRecord rootRecords[while(true)]; + NodeRecord64A rootRecords[while(true)]; } - + Footer footer; std::assert_warn(header.version == footer.version, "Version numbers in header and footer do not match!"); };