readFsceneb function
Parses a .fsceneb container from bytes into a SceneDocument with each
embedded payload's PayloadSpec.bytes attached.
Tolerates the same JSONC superset and runs the same version migration as readFscene for the manifest. Throws a FscenebFormatException on a bad magic, an unsupported container version, or a missing manifest.
Implementation
SceneDocument readFsceneb(Uint8List bytes) {
if (bytes.length < _headerByteLength) {
throw const FscenebFormatException('Truncated container (no header)');
}
for (var i = 0; i < 4; i++) {
if (bytes[i] != _magic[i]) {
throw const FscenebFormatException(
'Not a .fsceneb container (bad magic)',
);
}
}
final view = ByteData.sublistView(bytes);
final version = view.getUint32(4, Endian.little);
if (version > kFscenebVersion) {
throw FscenebFormatException(
'Container version $version is newer than supported $kFscenebVersion',
);
}
final total = view.getUint32(8, Endian.little);
if (total > bytes.length) {
throw const FscenebFormatException('Container length exceeds the data');
}
String? manifest;
final blobs = <LocalId, Uint8List>{};
var offset = _headerByteLength;
while (offset + 8 <= total) {
final dataLength = view.getUint32(offset, Endian.little);
final type = ascii.decode(
Uint8List.sublistView(bytes, offset + 4, offset + 8),
);
final dataStart = offset + 8;
final dataEnd = dataStart + dataLength;
if (dataEnd > total) {
throw const FscenebFormatException('Chunk extends past the container');
}
final data = Uint8List.sublistView(bytes, dataStart, dataEnd);
switch (type) {
case _chunkJson:
manifest = utf8.decode(data);
case _chunkBlob:
final (id, payload) = _decodeBlob(data);
blobs[id] = payload;
default:
break; // Skip unrecognized chunk types.
}
final padded = dataLength + ((-dataLength) & (_alignment - 1));
offset = dataStart + padded;
}
if (manifest == null) {
throw const FscenebFormatException('Container has no JSON manifest chunk');
}
final document = readFscene(manifest);
blobs.forEach((id, payload) {
document.payload(id)?.bytes = payload;
});
return document;
}