writeFsceneb function

Uint8List writeFsceneb(
  1. SceneDocument document
)

Serializes document to a .fsceneb container: the document's JSON manifest followed by one chunk per payload.

Every payload in the document must carry its PayloadSpec.bytes; a manifest-only payload (no bytes) cannot be embedded and throws a FscenebFormatException. Payloads referenced by external ref rather than an embedded chunk are not payloads, so they are unaffected.

Implementation

Uint8List writeFsceneb(SceneDocument document) {
  final body = BytesBuilder();

  void addChunk(String type, Uint8List data) {
    final preamble = ByteData(8)..setUint32(0, data.length, Endian.little);
    final typeBytes = ascii.encode(type);
    for (var i = 0; i < 4; i++) {
      preamble.setUint8(4 + i, typeBytes[i]);
    }
    body.add(preamble.buffer.asUint8List());
    body.add(data);
    final remainder = data.length % _alignment;
    if (remainder != 0) body.add(Uint8List(_alignment - remainder));
  }

  addChunk(_chunkJson, utf8.encode(writeFscene(document)));

  // Emit payloads in a deterministic (id-sorted) order, matching the JSON
  // manifest's enumeration so two writes of the same document are identical.
  final payloads = document.payloads.entries.toList()
    ..sort((a, b) => a.key.toToken().compareTo(b.key.toToken()));
  for (final entry in payloads) {
    final bytes = entry.value.bytes;
    if (bytes == null) {
      throw FscenebFormatException(
        'Payload ${entry.key.toToken()} has no bytes to embed',
      );
    }
    addChunk(_chunkBlob, _encodeBlob(entry.key, bytes));
  }

  final bodyBytes = body.toBytes();
  final total = _headerByteLength + bodyBytes.length;
  final out = Uint8List(total);
  for (var i = 0; i < 4; i++) {
    out[i] = _magic[i];
  }
  ByteData.sublistView(out)
    ..setUint32(4, kFscenebVersion, Endian.little)
    ..setUint32(8, total, Endian.little)
    ..setUint32(12, 0, Endian.little);
  out.setRange(_headerByteLength, total, bodyBytes);
  return out;
}