mirror of
https://github.com/WerWolv/ImHex-Patterns.git
synced 2026-03-27 23:37:04 -05:00
patterns/fbx: Improved the FBX pattern (#302)
* patterns/fbx: Improved the FBX pattern -Added pragma magic -Fixed sizing of compressed contents array -Added references -Cleaned up whitespace * Added missing ] bracket in README.md
This commit is contained in:
@@ -1,6 +1,12 @@
|
|||||||
#pragma description Kaydara FBX Binary
|
#pragma description Kaydara FBX Binary
|
||||||
|
#pragma magic [4B 61 79 64 61 72 61 20 46 42 58 20 42 69 6E 61 72 79 20 20 00 1A 00] @ 0x00
|
||||||
|
|
||||||
// Based on Blenders implementation of FBX import/export
|
/*
|
||||||
|
* Based on Blenders implementation of FBX import/export, see:
|
||||||
|
* (incomplete) https://code.blender.org/2013/08/fbx-binary-file-format-specification/
|
||||||
|
* https://projects.blender.org/blender/blender/src/branch/main/scripts/addons_core/io_scene_fbx/parse_fbx.py
|
||||||
|
* https://projects.blender.org/blender/blender/src/branch/main/scripts/addons_core/io_scene_fbx/encode_bin.py
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma endian little
|
#pragma endian little
|
||||||
|
|
||||||
@@ -17,9 +23,9 @@ 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];
|
||||||
@@ -31,7 +37,11 @@ struct Array<E> {
|
|||||||
#ifdef __IMHEX__
|
#ifdef __IMHEX__
|
||||||
std::mem::Section contentsSection = std::mem::create_section(std::format("contentsSection @ {:#x}", pos));
|
std::mem::Section contentsSection = std::mem::create_section(std::format("contentsSection @ {:#x}", pos));
|
||||||
hex::dec::zlib_decompress(compressedContents, contentsSection, 15);
|
hex::dec::zlib_decompress(compressedContents, contentsSection, 15);
|
||||||
E contents[] @ 0x00 in contentsSection;
|
auto contentsSectionSize = std::mem::get_section_size(contentsSection);
|
||||||
|
auto contentsElementSize = sizeof(E);
|
||||||
|
std::assert_warn((contentsSectionSize % contentsElementSize) == 0,
|
||||||
|
"The size of the contentsSection must be an integer multiple of sizeof(E) !");
|
||||||
|
E contents[contentsSectionSize / contentsElementSize] @ 0x00 in contentsSection;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -57,7 +67,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;
|
||||||
@@ -90,15 +100,15 @@ struct NodeRecord32 {
|
|||||||
u32 numProperties;
|
u32 numProperties;
|
||||||
u32 propertyListLen;
|
u32 propertyListLen;
|
||||||
u8 nameLen;
|
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;
|
||||||
@@ -113,15 +123,15 @@ struct NodeRecord64 {
|
|||||||
u64 numProperties;
|
u64 numProperties;
|
||||||
u64 propertyListLen;
|
u64 propertyListLen;
|
||||||
u8 nameLen;
|
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;
|
||||||
@@ -133,27 +143,27 @@ struct NodeRecord64 {
|
|||||||
|
|
||||||
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 {
|
||||||
type::Magic<"\xFA\xBC\xAB\x09\xD0\xC8\xD4\x66\xB1\x76\xFB\x83\x1C\xF7\x26\x7E"> footerId;
|
type::Magic<"\xFA\xBC\xAB\x09\xD0\xC8\xD4\x66\xB1\x76\xFB\x83\x1C\xF7\x26\x7E"> 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;
|
||||||
@@ -162,7 +172,6 @@ struct Footer{
|
|||||||
type::Magic<"\xF8\x5A\x8C\x6A\xDE\xF5\xD9\x7E\xEC\xE9\x0C\xE3\x75\x8F\x29\x0B"> footerMagic;
|
type::Magic<"\xF8\x5A\x8C\x6A\xDE\xF5\xD9\x7E\xEC\xE9\x0C\xE3\x75\x8F\x29\x0B"> footerMagic;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct Header {
|
struct Header {
|
||||||
type::Magic<"Kaydara FBX Binary \x00\x1A\x00"> magic;
|
type::Magic<"Kaydara FBX Binary \x00\x1A\x00"> magic;
|
||||||
u32 version;
|
u32 version;
|
||||||
@@ -170,15 +179,15 @@ struct Header {
|
|||||||
|
|
||||||
struct FBX {
|
struct FBX {
|
||||||
Header header;
|
Header header;
|
||||||
|
|
||||||
if (header.version < 7500) {
|
if (header.version < 7500) {
|
||||||
NodeRecord32 rootRecords[while(true)];
|
NodeRecord32 rootRecords[while(true)];
|
||||||
} else {
|
} else {
|
||||||
NodeRecord64 rootRecords[while(true)];
|
NodeRecord64 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!");
|
||||||
};
|
};
|
||||||
|
|
||||||
FBX fbx @ 0x00;
|
FBX fbx @ 0x00;
|
||||||
Reference in New Issue
Block a user