From 10028be6ce6d944566877516a70557e291dba29f Mon Sep 17 00:00:00 2001 From: Thomas Barlow Date: Sun, 24 Sep 2023 02:39:39 +0100 Subject: [PATCH 1/2] Fix #104684: json2fbx.py script creates invalid FBX v7500+ files As of FBX version 7500, the element metadata values are 64-bit as opposed to being 32-bit in earlier versions. FBX exported by Blender are currently always FBX version 7400, so the fbx binary writing code was only set up to handle writing version 7400 files and earlier. The json2fbx.py script however, can create FBX files with whatever version is defined in the .json. Because the script uses the same code as exporting an FBX with Blender, it would create invalid FBX version 7500+ files with 32-bit metadata values. Attempting to read one of these files with Blender or external software would cause errors. This patch sets global variables based on the version of the file being exported and then uses those global variables when exporting to export with the correct metadata values for the file version being exported. This process is very similar to what is already done in parse_fbx.py when importing FBX files. There are no expected changes to FBX files exported by Blender with this patch, only the json2fbx.py script should be affected. --- io_scene_fbx/encode_bin.py | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/io_scene_fbx/encode_bin.py b/io_scene_fbx/encode_bin.py index fe2bef09d..00f81bf76 100644 --- a/io_scene_fbx/encode_bin.py +++ b/io_scene_fbx/encode_bin.py @@ -12,8 +12,10 @@ import array import numpy as np import zlib -_BLOCK_SENTINEL_LENGTH = 13 -_BLOCK_SENTINEL_DATA = (b'\0' * _BLOCK_SENTINEL_LENGTH) +_BLOCK_SENTINEL_LENGTH = ... +_BLOCK_SENTINEL_DATA = ... +_ELEM_META_FORMAT = ... +_ELEM_META_SIZE = ... _IS_BIG_ENDIAN = (__import__("sys").byteorder != 'little') _HEAD_MAGIC = b'Kaydara FBX Binary\x20\x20\x00\x1a\x00' @@ -219,7 +221,7 @@ class FBXElem: assert(self._end_offset == -1) assert(self._props_length == -1) - offset += 12 # 3 uints + offset += _ELEM_META_SIZE # 3 uints (or 3 ulonglongs for FBX 7500 and later) offset += 1 + len(self.id) # len + idname props_length = 0 @@ -250,7 +252,7 @@ class FBXElem: assert(self._end_offset != -1) assert(self._props_length != -1) - write(pack('<3I', self._end_offset, len(self.props), self._props_length)) + write(pack(_ELEM_META_FORMAT, self._end_offset, len(self.props), self._props_length)) write(bytes((len(self.id),))) write(self.id) @@ -263,7 +265,7 @@ class FBXElem: if tell() != self._end_offset: raise IOError("scope length not reached, " - "something is wrong (%d)" % (end_offset - tell())) + "something is wrong (%d)" % (self._end_offset - tell())) def _write_children(self, write, tell, is_last): if self.elems: @@ -308,6 +310,27 @@ def _write_timedate_hack(elem_root): print("Missing fields!") +# FBX 7500 (aka FBX2016) introduces incompatible changes at binary level: +# * The NULL block marking end of nested stuff switches from 13 bytes long to 25 bytes long. +# * The FBX element metadata (end_offset, prop_count and prop_length) switch from uint32 to uint64. +def init_version(fbx_version): + global _BLOCK_SENTINEL_LENGTH, _BLOCK_SENTINEL_DATA, _ELEM_META_FORMAT, _ELEM_META_SIZE + + _BLOCK_SENTINEL_LENGTH = ... + _BLOCK_SENTINEL_DATA = ... + _ELEM_META_FORMAT = ... + _ELEM_META_SIZE = ... + + if fbx_version < 7500: + _ELEM_META_FORMAT = '<3I' + _ELEM_META_SIZE = 12 + else: + _ELEM_META_FORMAT = '<3Q' + _ELEM_META_SIZE = 24 + _BLOCK_SENTINEL_LENGTH = _ELEM_META_SIZE + 1 + _BLOCK_SENTINEL_DATA = (b'\0' * _BLOCK_SENTINEL_LENGTH) + + def write(fn, elem_root, version): assert(elem_root.id == b'') @@ -315,6 +338,8 @@ def write(fn, elem_root, version): write = f.write tell = f.tell + init_version(version) + write(_HEAD_MAGIC) write(pack(' Date: Tue, 26 Sep 2023 02:47:13 +0100 Subject: [PATCH 2/2] Increase FBX IO version --- io_scene_fbx/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_scene_fbx/__init__.py b/io_scene_fbx/__init__.py index 75a4ab720..44b964174 100644 --- a/io_scene_fbx/__init__.py +++ b/io_scene_fbx/__init__.py @@ -5,7 +5,7 @@ bl_info = { "name": "FBX format", "author": "Campbell Barton, Bastien Montagne, Jens Restemeier, @Mysteryem", - "version": (5, 8, 2), + "version": (5, 8, 3), "blender": (3, 6, 0), "location": "File > Import-Export", "description": "FBX IO meshes, UVs, vertex colors, materials, textures, cameras, lamps and actions", -- 2.30.2