rmapi-js 12.0.2 → 13.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +182 -36
- package/dist/index.js +730 -236
- package/dist/lru.d.ts +4 -4
- package/dist/lru.js +4 -4
- package/dist/raw.d.ts +172 -36
- package/dist/raw.js +220 -75
- package/dist/rm5.d.ts +1 -1
- package/dist/rm6.d.ts +38 -13
- package/dist/rm6.js +418 -39
- package/dist/rmapi-js.esm.min.js +13 -9
- package/package.json +2 -1
package/dist/rm6.js
CHANGED
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* of length-prefixed, tagged blocks. This module reads every block faithfully
|
|
6
6
|
* — preserving `CrdtId`s, `LwwValue` wrappers, and each block's unread tail —
|
|
7
7
|
* into an {@link RmScene | `RmScene`}, whose methods resolve the CRDT into
|
|
8
|
-
* ordered layers, strokes, and text.
|
|
9
|
-
*
|
|
8
|
+
* ordered layers, strokes, and text. Nothing is dropped, so
|
|
9
|
+
* {@link serializeRmScene | `serializeRmScene`} reproduces the original bytes
|
|
10
|
+
* exactly, including blocks it could not parse.
|
|
10
11
|
*
|
|
11
12
|
* @packageDocumentation
|
|
12
13
|
*/
|
|
@@ -146,7 +147,11 @@ class Reader {
|
|
|
146
147
|
const subEnd = this.#offset + length;
|
|
147
148
|
this.#bounds.push(subEnd);
|
|
148
149
|
try {
|
|
149
|
-
|
|
150
|
+
const value = fn();
|
|
151
|
+
if (this.#offset !== subEnd) {
|
|
152
|
+
throw new Error(`subblock ${index} left ${subEnd - this.#offset} bytes unread`);
|
|
153
|
+
}
|
|
154
|
+
return value;
|
|
150
155
|
}
|
|
151
156
|
finally {
|
|
152
157
|
this.#bounds.pop();
|
|
@@ -201,6 +206,125 @@ class Reader {
|
|
|
201
206
|
});
|
|
202
207
|
}
|
|
203
208
|
}
|
|
209
|
+
/** the tagged-write counterpart to {@link Reader | `Reader`} */
|
|
210
|
+
class Writer {
|
|
211
|
+
#buffer = new Uint8Array(4096);
|
|
212
|
+
#view = new DataView(this.#buffer.buffer);
|
|
213
|
+
#length = 0;
|
|
214
|
+
#ensure(extra) {
|
|
215
|
+
if (this.#length + extra <= this.#buffer.length)
|
|
216
|
+
return;
|
|
217
|
+
let size = this.#buffer.length;
|
|
218
|
+
while (size < this.#length + extra)
|
|
219
|
+
size *= 2;
|
|
220
|
+
const grown = new Uint8Array(size);
|
|
221
|
+
grown.set(this.#buffer);
|
|
222
|
+
this.#buffer = grown;
|
|
223
|
+
this.#view = new DataView(grown.buffer);
|
|
224
|
+
}
|
|
225
|
+
get length() {
|
|
226
|
+
return this.#length;
|
|
227
|
+
}
|
|
228
|
+
u8(value) {
|
|
229
|
+
this.#ensure(1);
|
|
230
|
+
this.#view.setUint8(this.#length, value);
|
|
231
|
+
this.#length += 1;
|
|
232
|
+
}
|
|
233
|
+
u16(value) {
|
|
234
|
+
this.#ensure(2);
|
|
235
|
+
this.#view.setUint16(this.#length, value, true);
|
|
236
|
+
this.#length += 2;
|
|
237
|
+
}
|
|
238
|
+
u32(value) {
|
|
239
|
+
this.#ensure(4);
|
|
240
|
+
this.#view.setUint32(this.#length, value, true);
|
|
241
|
+
this.#length += 4;
|
|
242
|
+
}
|
|
243
|
+
f32(value) {
|
|
244
|
+
this.#ensure(4);
|
|
245
|
+
this.#view.setFloat32(this.#length, value, true);
|
|
246
|
+
this.#length += 4;
|
|
247
|
+
}
|
|
248
|
+
f64(value) {
|
|
249
|
+
this.#ensure(8);
|
|
250
|
+
this.#view.setFloat64(this.#length, value, true);
|
|
251
|
+
this.#length += 8;
|
|
252
|
+
}
|
|
253
|
+
varuint(value) {
|
|
254
|
+
let rest = value;
|
|
255
|
+
do {
|
|
256
|
+
let byte = rest % 128;
|
|
257
|
+
rest = Math.floor(rest / 128);
|
|
258
|
+
if (rest > 0)
|
|
259
|
+
byte |= 0x80;
|
|
260
|
+
this.u8(byte);
|
|
261
|
+
} while (rest > 0);
|
|
262
|
+
}
|
|
263
|
+
crdtId(id) {
|
|
264
|
+
this.u8(id.authorId);
|
|
265
|
+
this.varuint(id.counter);
|
|
266
|
+
}
|
|
267
|
+
bytes(data) {
|
|
268
|
+
this.#ensure(data.length);
|
|
269
|
+
this.#buffer.set(data, this.#length);
|
|
270
|
+
this.#length += data.length;
|
|
271
|
+
}
|
|
272
|
+
patchU32(at, value) {
|
|
273
|
+
this.#view.setUint32(at, value, true);
|
|
274
|
+
}
|
|
275
|
+
finish() {
|
|
276
|
+
return this.#buffer.slice(0, this.#length);
|
|
277
|
+
}
|
|
278
|
+
tag(index, type) {
|
|
279
|
+
this.varuint(index * 16 + type);
|
|
280
|
+
}
|
|
281
|
+
writeInt(index, value) {
|
|
282
|
+
this.tag(index, TAG_BYTE4);
|
|
283
|
+
this.u32(value);
|
|
284
|
+
}
|
|
285
|
+
writeFloat(index, value) {
|
|
286
|
+
this.tag(index, TAG_BYTE4);
|
|
287
|
+
this.f32(value);
|
|
288
|
+
}
|
|
289
|
+
writeDouble(index, value) {
|
|
290
|
+
this.tag(index, TAG_BYTE8);
|
|
291
|
+
this.f64(value);
|
|
292
|
+
}
|
|
293
|
+
writeId(index, id) {
|
|
294
|
+
this.tag(index, TAG_ID);
|
|
295
|
+
this.crdtId(id);
|
|
296
|
+
}
|
|
297
|
+
writeBool(index, value) {
|
|
298
|
+
this.tag(index, TAG_BYTE1);
|
|
299
|
+
this.u8(value ? 1 : 0);
|
|
300
|
+
}
|
|
301
|
+
writeByte(index, value) {
|
|
302
|
+
this.tag(index, TAG_BYTE1);
|
|
303
|
+
this.u8(value);
|
|
304
|
+
}
|
|
305
|
+
subblock(index, write) {
|
|
306
|
+
this.tag(index, TAG_LENGTH4);
|
|
307
|
+
const at = this.length;
|
|
308
|
+
this.u32(0);
|
|
309
|
+
write();
|
|
310
|
+
this.patchU32(at, this.length - at - 4);
|
|
311
|
+
}
|
|
312
|
+
writeLww(index, lww, write) {
|
|
313
|
+
this.subblock(index, () => {
|
|
314
|
+
this.writeId(1, lww.timestamp);
|
|
315
|
+
write(lww.value);
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
writeString(index, text) {
|
|
319
|
+
this.subblock(index, () => {
|
|
320
|
+
const encoded = new TextEncoder().encode(text);
|
|
321
|
+
this.varuint(encoded.length);
|
|
322
|
+
// the device writes this flag as 1 even for non-ascii text
|
|
323
|
+
this.u8(1);
|
|
324
|
+
this.bytes(encoded);
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
}
|
|
204
328
|
/** read the `SceneItem` envelope shared by item blocks */
|
|
205
329
|
function readItemEnvelope(reader, readValue) {
|
|
206
330
|
const parentId = reader.readId(1);
|
|
@@ -236,7 +360,7 @@ function readLineValue(reader, version) {
|
|
|
236
360
|
y,
|
|
237
361
|
speed: reader.f32() * 4,
|
|
238
362
|
direction: (reader.f32() * 255) / (2 * Math.PI),
|
|
239
|
-
width:
|
|
363
|
+
width: reader.f32() * 4,
|
|
240
364
|
pressure: reader.f32() * 255,
|
|
241
365
|
};
|
|
242
366
|
}
|
|
@@ -257,12 +381,10 @@ function readLineValue(reader, version) {
|
|
|
257
381
|
startingLength,
|
|
258
382
|
points,
|
|
259
383
|
};
|
|
260
|
-
// optional trailing timestamp / move id / color are skipped by seeking to the
|
|
261
|
-
// subblock end; only the highlighter rgba color is captured
|
|
262
384
|
if (reader.hasTag(6, TAG_ID))
|
|
263
|
-
reader.readId(6);
|
|
385
|
+
line.timestampId = reader.readId(6);
|
|
264
386
|
if (reader.hasTag(7, TAG_ID))
|
|
265
|
-
reader.readId(7);
|
|
387
|
+
line.moveId = reader.readId(7);
|
|
266
388
|
if (reader.hasTag(8, TAG_BYTE4))
|
|
267
389
|
line.colorRgba = reader.readInt(8);
|
|
268
390
|
return line;
|
|
@@ -367,10 +489,12 @@ function readBlockBody(reader, blockType, version) {
|
|
|
367
489
|
visible,
|
|
368
490
|
};
|
|
369
491
|
if (reader.bytesRemaining() > 0 && reader.hasTag(7, TAG_LENGTH4)) {
|
|
370
|
-
node.
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
492
|
+
node.anchor = {
|
|
493
|
+
id: reader.readLww(7, () => reader.readId(2)),
|
|
494
|
+
type: reader.readLww(8, () => reader.readByte(2)),
|
|
495
|
+
threshold: reader.readLww(9, () => reader.readFloat(2)),
|
|
496
|
+
originX: reader.readLww(10, () => reader.readFloat(2)),
|
|
497
|
+
};
|
|
374
498
|
}
|
|
375
499
|
return node;
|
|
376
500
|
}
|
|
@@ -416,15 +540,19 @@ function readBlockBody(reader, blockType, version) {
|
|
|
416
540
|
}
|
|
417
541
|
return { type: "authorIds", authors };
|
|
418
542
|
}
|
|
419
|
-
case 0x0a:
|
|
420
|
-
|
|
543
|
+
case 0x0a: {
|
|
544
|
+
const info = {
|
|
421
545
|
type: "pageInfo",
|
|
422
546
|
loadsCount: reader.readInt(1),
|
|
423
547
|
mergesCount: reader.readInt(2),
|
|
424
548
|
textCharsCount: reader.readInt(3),
|
|
425
549
|
textLinesCount: reader.readInt(4),
|
|
426
|
-
typeFolioUseCount: reader.hasTag(5, TAG_BYTE4) ? reader.readInt(5) : 0,
|
|
427
550
|
};
|
|
551
|
+
if (reader.hasTag(5, TAG_BYTE4)) {
|
|
552
|
+
info.typeFolioUseCount = reader.readInt(5);
|
|
553
|
+
}
|
|
554
|
+
return info;
|
|
555
|
+
}
|
|
428
556
|
case 0x0d: {
|
|
429
557
|
const info = {
|
|
430
558
|
type: "sceneInfo",
|
|
@@ -445,29 +573,22 @@ function readBlockBody(reader, blockType, version) {
|
|
|
445
573
|
throw new Error(`unknown v6 block type 0x${blockType.toString(16)}`);
|
|
446
574
|
}
|
|
447
575
|
}
|
|
576
|
+
/**
|
|
577
|
+
* the byte order of a little-endian (bytes_le) uuid
|
|
578
|
+
*
|
|
579
|
+
* The permutation is its own inverse, so one table serves both directions.
|
|
580
|
+
*/
|
|
581
|
+
const UUID_LE_ORDER = [3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15];
|
|
582
|
+
function swapUuidBytes(bytes) {
|
|
583
|
+
const out = new Uint8Array(16);
|
|
584
|
+
for (let index = 0; index < 16; index++) {
|
|
585
|
+
out[index] = bytes[UUID_LE_ORDER[index]] ?? 0;
|
|
586
|
+
}
|
|
587
|
+
return out;
|
|
588
|
+
}
|
|
448
589
|
function uuidToString(bytes) {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
const le = [
|
|
452
|
-
b[3],
|
|
453
|
-
b[2],
|
|
454
|
-
b[1],
|
|
455
|
-
b[0],
|
|
456
|
-
b[5],
|
|
457
|
-
b[4],
|
|
458
|
-
b[7],
|
|
459
|
-
b[6],
|
|
460
|
-
b[8],
|
|
461
|
-
b[9],
|
|
462
|
-
b[10],
|
|
463
|
-
b[11],
|
|
464
|
-
b[12],
|
|
465
|
-
b[13],
|
|
466
|
-
b[14],
|
|
467
|
-
b[15],
|
|
468
|
-
];
|
|
469
|
-
const hex = le.map((byte) => (byte ?? 0).toString(16).padStart(2, "0"));
|
|
470
|
-
return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10, 16).join("")}`;
|
|
590
|
+
const hex = swapUuidBytes(bytes).toHex();
|
|
591
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
471
592
|
}
|
|
472
593
|
/** parse the raw block list of a version 6 `.rm` file */
|
|
473
594
|
function parseV6Blocks(data) {
|
|
@@ -481,16 +602,36 @@ function parseV6Blocks(data) {
|
|
|
481
602
|
if (reader.bytesRemaining() < 8)
|
|
482
603
|
break;
|
|
483
604
|
const length = reader.u32();
|
|
484
|
-
reader.u8();
|
|
605
|
+
const reserved = reader.u8();
|
|
485
606
|
const minVersion = reader.u8();
|
|
486
607
|
const currentVersion = reader.u8();
|
|
487
608
|
const blockType = reader.u8();
|
|
488
609
|
const blockStart = reader.offset;
|
|
610
|
+
if (length > reader.bytesRemaining()) {
|
|
611
|
+
// the block overruns the file; keep what's left verbatim and stop
|
|
612
|
+
blocks.push({
|
|
613
|
+
type: "unknown",
|
|
614
|
+
blockType,
|
|
615
|
+
data: reader.bytes(reader.bytesRemaining()),
|
|
616
|
+
declaredLength: length,
|
|
617
|
+
reserved,
|
|
618
|
+
minVersion,
|
|
619
|
+
currentVersion,
|
|
620
|
+
extraData: new Uint8Array(),
|
|
621
|
+
});
|
|
622
|
+
break;
|
|
623
|
+
}
|
|
489
624
|
const blockEnd = blockStart + length;
|
|
490
625
|
let block;
|
|
491
626
|
try {
|
|
492
627
|
const [body, extraData] = reader.bounded(blockEnd, () => readBlockBody(reader, blockType, currentVersion));
|
|
493
|
-
block = {
|
|
628
|
+
block = {
|
|
629
|
+
...body,
|
|
630
|
+
reserved,
|
|
631
|
+
minVersion,
|
|
632
|
+
currentVersion,
|
|
633
|
+
extraData,
|
|
634
|
+
};
|
|
494
635
|
}
|
|
495
636
|
catch {
|
|
496
637
|
// couldn't parse this block; keep its raw bytes so the file still
|
|
@@ -500,6 +641,7 @@ function parseV6Blocks(data) {
|
|
|
500
641
|
type: "unknown",
|
|
501
642
|
blockType,
|
|
502
643
|
data: reader.bytes(length),
|
|
644
|
+
reserved,
|
|
503
645
|
minVersion,
|
|
504
646
|
currentVersion,
|
|
505
647
|
extraData: new Uint8Array(),
|
|
@@ -722,6 +864,243 @@ export class RmScene {
|
|
|
722
864
|
return this.#text;
|
|
723
865
|
}
|
|
724
866
|
}
|
|
867
|
+
function writeItemEnvelope(writer, parentId, item, itemType, writeValue) {
|
|
868
|
+
writer.writeId(1, parentId);
|
|
869
|
+
writer.writeId(2, item.itemId);
|
|
870
|
+
writer.writeId(3, item.leftId);
|
|
871
|
+
writer.writeId(4, item.rightId);
|
|
872
|
+
writer.writeInt(5, item.deletedLength);
|
|
873
|
+
const { value } = item;
|
|
874
|
+
if (value !== undefined) {
|
|
875
|
+
writer.subblock(6, () => {
|
|
876
|
+
writer.u8(itemType);
|
|
877
|
+
writeValue(value);
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
function writeLineValue(writer, line, version) {
|
|
882
|
+
writer.writeInt(1, line.tool);
|
|
883
|
+
writer.writeInt(2, line.color);
|
|
884
|
+
writer.writeDouble(3, line.thicknessScale);
|
|
885
|
+
writer.writeFloat(4, line.startingLength);
|
|
886
|
+
writer.subblock(5, () => {
|
|
887
|
+
for (const point of line.points) {
|
|
888
|
+
writer.f32(point.x);
|
|
889
|
+
writer.f32(point.y);
|
|
890
|
+
if (version === 1) {
|
|
891
|
+
writer.f32(point.speed / 4);
|
|
892
|
+
writer.f32((point.direction * 2 * Math.PI) / 255);
|
|
893
|
+
writer.f32(point.width / 4);
|
|
894
|
+
writer.f32(point.pressure / 255);
|
|
895
|
+
}
|
|
896
|
+
else {
|
|
897
|
+
writer.u16(point.speed);
|
|
898
|
+
writer.u16(point.width);
|
|
899
|
+
writer.u8(point.direction);
|
|
900
|
+
writer.u8(point.pressure);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
});
|
|
904
|
+
if (line.timestampId !== undefined)
|
|
905
|
+
writer.writeId(6, line.timestampId);
|
|
906
|
+
if (line.moveId !== undefined)
|
|
907
|
+
writer.writeId(7, line.moveId);
|
|
908
|
+
if (line.colorRgba !== undefined)
|
|
909
|
+
writer.writeInt(8, line.colorRgba);
|
|
910
|
+
}
|
|
911
|
+
function writeGlyphValue(writer, glyph) {
|
|
912
|
+
if (glyph.start !== undefined)
|
|
913
|
+
writer.writeInt(2, glyph.start);
|
|
914
|
+
writer.writeInt(3, glyph.length);
|
|
915
|
+
writer.writeInt(4, glyph.color);
|
|
916
|
+
writer.writeString(5, glyph.text);
|
|
917
|
+
writer.subblock(6, () => {
|
|
918
|
+
writer.varuint(glyph.rectangles.length);
|
|
919
|
+
for (const rect of glyph.rectangles) {
|
|
920
|
+
writer.f64(rect.x);
|
|
921
|
+
writer.f64(rect.y);
|
|
922
|
+
writer.f64(rect.w);
|
|
923
|
+
writer.f64(rect.h);
|
|
924
|
+
}
|
|
925
|
+
});
|
|
926
|
+
if (glyph.colorRgba !== undefined)
|
|
927
|
+
writer.writeInt(10, glyph.colorRgba);
|
|
928
|
+
}
|
|
929
|
+
function writeText(writer, text) {
|
|
930
|
+
writer.subblock(2, () => {
|
|
931
|
+
writer.subblock(1, () => {
|
|
932
|
+
writer.subblock(1, () => {
|
|
933
|
+
writer.varuint(text.items.length);
|
|
934
|
+
for (const item of text.items) {
|
|
935
|
+
writer.subblock(0, () => {
|
|
936
|
+
writer.writeId(2, item.itemId);
|
|
937
|
+
writer.writeId(3, item.leftId);
|
|
938
|
+
writer.writeId(4, item.rightId);
|
|
939
|
+
writer.writeInt(5, item.deletedLength);
|
|
940
|
+
const { value } = item;
|
|
941
|
+
if (value !== "" && value !== undefined) {
|
|
942
|
+
writer.subblock(6, () => {
|
|
943
|
+
if (typeof value === "number") {
|
|
944
|
+
writer.varuint(0);
|
|
945
|
+
writer.u8(1);
|
|
946
|
+
writer.writeInt(2, value);
|
|
947
|
+
}
|
|
948
|
+
else {
|
|
949
|
+
const encoded = new TextEncoder().encode(value);
|
|
950
|
+
writer.varuint(encoded.length);
|
|
951
|
+
writer.u8(1);
|
|
952
|
+
writer.bytes(encoded);
|
|
953
|
+
}
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
});
|
|
959
|
+
});
|
|
960
|
+
writer.subblock(2, () => {
|
|
961
|
+
writer.subblock(1, () => {
|
|
962
|
+
writer.varuint(text.styles.size);
|
|
963
|
+
for (const [key, style] of text.styles) {
|
|
964
|
+
const [authorId, counter] = key.split(":");
|
|
965
|
+
writer.crdtId({
|
|
966
|
+
authorId: Number(authorId),
|
|
967
|
+
counter: Number(counter),
|
|
968
|
+
});
|
|
969
|
+
writer.writeId(1, style.timestamp);
|
|
970
|
+
writer.subblock(2, () => {
|
|
971
|
+
writer.u8(17);
|
|
972
|
+
writer.u8(style.value);
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
});
|
|
977
|
+
});
|
|
978
|
+
writer.subblock(3, () => {
|
|
979
|
+
writer.f64(text.posX);
|
|
980
|
+
writer.f64(text.posY);
|
|
981
|
+
});
|
|
982
|
+
writer.writeFloat(4, text.width);
|
|
983
|
+
}
|
|
984
|
+
function uuidToBytes(uuid) {
|
|
985
|
+
return swapUuidBytes(Uint8Array.fromHex(uuid.replaceAll("-", "")));
|
|
986
|
+
}
|
|
987
|
+
const BLOCK_TYPES = {
|
|
988
|
+
migrationInfo: 0x00,
|
|
989
|
+
sceneTree: 0x01,
|
|
990
|
+
treeNode: 0x02,
|
|
991
|
+
sceneGlyphItem: 0x03,
|
|
992
|
+
sceneGroupItem: 0x04,
|
|
993
|
+
sceneLineItem: 0x05,
|
|
994
|
+
sceneTextItem: 0x06,
|
|
995
|
+
rootText: 0x07,
|
|
996
|
+
sceneTombstone: 0x08,
|
|
997
|
+
authorIds: 0x09,
|
|
998
|
+
pageInfo: 0x0a,
|
|
999
|
+
sceneInfo: 0x0d,
|
|
1000
|
+
};
|
|
1001
|
+
function writeBlockBody(writer, block) {
|
|
1002
|
+
switch (block.type) {
|
|
1003
|
+
case "migrationInfo":
|
|
1004
|
+
writer.writeId(1, block.migrationId);
|
|
1005
|
+
writer.writeBool(2, block.isDevice);
|
|
1006
|
+
break;
|
|
1007
|
+
case "sceneTree":
|
|
1008
|
+
writer.writeId(1, block.treeId);
|
|
1009
|
+
writer.writeId(2, block.nodeId);
|
|
1010
|
+
writer.writeBool(3, block.isUpdate);
|
|
1011
|
+
writer.subblock(4, () => writer.writeId(1, block.parentId));
|
|
1012
|
+
break;
|
|
1013
|
+
case "treeNode":
|
|
1014
|
+
writer.writeId(1, block.nodeId);
|
|
1015
|
+
writer.writeLww(2, block.label, (value) => writer.writeString(2, value));
|
|
1016
|
+
writer.writeLww(3, block.visible, (value) => writer.writeBool(2, value));
|
|
1017
|
+
if (block.anchor !== undefined) {
|
|
1018
|
+
const { id, type, threshold, originX } = block.anchor;
|
|
1019
|
+
writer.writeLww(7, id, (value) => writer.writeId(2, value));
|
|
1020
|
+
writer.writeLww(8, type, (value) => writer.writeByte(2, value));
|
|
1021
|
+
writer.writeLww(9, threshold, (value) => writer.writeFloat(2, value));
|
|
1022
|
+
writer.writeLww(10, originX, (value) => writer.writeFloat(2, value));
|
|
1023
|
+
}
|
|
1024
|
+
break;
|
|
1025
|
+
case "sceneGlyphItem":
|
|
1026
|
+
writeItemEnvelope(writer, block.parentId, block.item, 0x01, (glyph) => writeGlyphValue(writer, glyph));
|
|
1027
|
+
break;
|
|
1028
|
+
case "sceneGroupItem":
|
|
1029
|
+
writeItemEnvelope(writer, block.parentId, block.item, 0x02, (id) => writer.writeId(2, id));
|
|
1030
|
+
break;
|
|
1031
|
+
case "sceneLineItem":
|
|
1032
|
+
writeItemEnvelope(writer, block.parentId, block.item, 0x03, (line) => writeLineValue(writer, line, block.currentVersion));
|
|
1033
|
+
break;
|
|
1034
|
+
case "sceneTextItem":
|
|
1035
|
+
case "sceneTombstone":
|
|
1036
|
+
writeItemEnvelope(writer, block.parentId, block.item, 0x00, () => undefined);
|
|
1037
|
+
break;
|
|
1038
|
+
case "rootText":
|
|
1039
|
+
writer.writeId(1, block.blockId);
|
|
1040
|
+
writeText(writer, block.text);
|
|
1041
|
+
break;
|
|
1042
|
+
case "authorIds":
|
|
1043
|
+
writer.varuint(block.authors.size);
|
|
1044
|
+
for (const [authorId, uuid] of block.authors) {
|
|
1045
|
+
writer.subblock(0, () => {
|
|
1046
|
+
const bytes = uuidToBytes(uuid);
|
|
1047
|
+
writer.varuint(bytes.length);
|
|
1048
|
+
writer.bytes(bytes);
|
|
1049
|
+
writer.u16(authorId);
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
break;
|
|
1053
|
+
case "pageInfo":
|
|
1054
|
+
writer.writeInt(1, block.loadsCount);
|
|
1055
|
+
writer.writeInt(2, block.mergesCount);
|
|
1056
|
+
writer.writeInt(3, block.textCharsCount);
|
|
1057
|
+
writer.writeInt(4, block.textLinesCount);
|
|
1058
|
+
if (block.typeFolioUseCount !== undefined) {
|
|
1059
|
+
writer.writeInt(5, block.typeFolioUseCount);
|
|
1060
|
+
}
|
|
1061
|
+
break;
|
|
1062
|
+
case "sceneInfo":
|
|
1063
|
+
writer.writeLww(1, block.currentLayer, (value) => writer.writeId(2, value));
|
|
1064
|
+
if (block.backgroundVisible !== undefined) {
|
|
1065
|
+
writer.writeLww(2, block.backgroundVisible, (value) => writer.writeBool(2, value));
|
|
1066
|
+
}
|
|
1067
|
+
if (block.rootDocumentVisible !== undefined) {
|
|
1068
|
+
writer.writeLww(3, block.rootDocumentVisible, (value) => writer.writeBool(2, value));
|
|
1069
|
+
}
|
|
1070
|
+
if (block.paperSize !== undefined) {
|
|
1071
|
+
writer.subblock(5, () => {
|
|
1072
|
+
writer.u32(block.paperSize[0]);
|
|
1073
|
+
writer.u32(block.paperSize[1]);
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
break;
|
|
1077
|
+
case "unknown":
|
|
1078
|
+
writer.bytes(block.data);
|
|
1079
|
+
break;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
/** serialize a parsed scene back to version 6 `.rm` bytes */
|
|
1083
|
+
export function serializeRmScene(scene) {
|
|
1084
|
+
const writer = new Writer();
|
|
1085
|
+
const header = V6_HEADER.padEnd(HEADER_LENGTH, " ");
|
|
1086
|
+
writer.bytes(new TextEncoder().encode(header));
|
|
1087
|
+
for (const block of scene.blocks) {
|
|
1088
|
+
const lengthAt = writer.length;
|
|
1089
|
+
writer.u32(0);
|
|
1090
|
+
writer.u8(block.reserved);
|
|
1091
|
+
writer.u8(block.minVersion);
|
|
1092
|
+
writer.u8(block.currentVersion);
|
|
1093
|
+
writer.u8(block.type === "unknown" ? block.blockType : BLOCK_TYPES[block.type]);
|
|
1094
|
+
const bodyStart = writer.length;
|
|
1095
|
+
writeBlockBody(writer, block);
|
|
1096
|
+
if (block.type !== "unknown")
|
|
1097
|
+
writer.bytes(block.extraData);
|
|
1098
|
+
writer.patchU32(lengthAt, block.type === "unknown" && block.declaredLength !== undefined
|
|
1099
|
+
? block.declaredLength
|
|
1100
|
+
: writer.length - bodyStart);
|
|
1101
|
+
}
|
|
1102
|
+
return writer.finish();
|
|
1103
|
+
}
|
|
725
1104
|
/** parse a version 6 `.rm` file into a resolvable scene */
|
|
726
1105
|
export function parseRmScene(data) {
|
|
727
1106
|
return new RmScene(parseV6Blocks(data));
|