patterns/fbx: Fixed the fbx hexpat (#276)

* patterns: FBX - revised hexpat for fbx files but still broken

* patterns: FBX - implemented a workaround for the previously broken hexpat

* Disable ImHex-specific functions outside of ImHex

---------

Co-authored-by: Nik <werwolv98@gmail.com>
This commit is contained in:
Hikodroid
2024-07-25 00:00:11 +02:00
committed by GitHub
parent 0e17355db0
commit 120bc84548

View File

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