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