@doki-land/live2d-renderer 0.0.0 → 0.0.12

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.
@@ -0,0 +1,464 @@
1
+ /**
2
+ * moc2 object graph types + deserializer.
3
+ * Type tags and field order match the public moc2 stream layout.
4
+ */
5
+
6
+ import { MOC2_REF_TYPE, Moc2Reader } from "./moc2-reader.js";
7
+
8
+ export const Moc2Type = {
9
+ Null: 0,
10
+ String: 1,
11
+ ObjectArray: 15,
12
+ Int32Array: 16,
13
+ RectInt: 21,
14
+ PointInt: 22,
15
+ Int32ArrayAlt: 25,
16
+ Float64Array: 26,
17
+ Float32Array: 27,
18
+ DrawDataId: 50,
19
+ BaseDataId: 51,
20
+ ParamId: 60,
21
+ MeshDeformer: 65,
22
+ PivotManager: 66,
23
+ Pivot: 67,
24
+ AffineDeformer: 68,
25
+ Affine: 69,
26
+ DrawableMesh: 70,
27
+ ParamDefFloat: 131,
28
+ PartsData: 133,
29
+ ModelImpl: 136,
30
+ ParamDefSet: 137,
31
+ AvatarParts: 142,
32
+ PartsDataId: 134,
33
+ } as const;
34
+
35
+ export interface Moc2ParamDef {
36
+ readonly kind: "paramDef";
37
+ readonly min: number;
38
+ readonly max: number;
39
+ readonly defaultValue: number;
40
+ readonly id: string;
41
+ }
42
+
43
+ export interface Moc2ParamDefSet {
44
+ readonly kind: "paramDefSet";
45
+ readonly params: readonly Moc2ParamDef[];
46
+ }
47
+
48
+ export interface Moc2Pivot {
49
+ readonly kind: "pivot";
50
+ readonly paramId: string;
51
+ readonly pivotCount: number;
52
+ readonly pivotValues: Float32Array;
53
+ }
54
+
55
+ export interface Moc2PivotManager {
56
+ readonly kind: "pivotManager";
57
+ readonly pivots: readonly Moc2Pivot[];
58
+ }
59
+
60
+ export interface Moc2Affine {
61
+ readonly kind: "affine";
62
+ readonly originX: number;
63
+ readonly originY: number;
64
+ readonly scaleX: number;
65
+ readonly scaleY: number;
66
+ readonly rotation: number;
67
+ readonly reflectX: boolean;
68
+ readonly reflectY: boolean;
69
+ }
70
+
71
+ export interface Moc2BaseDeformer {
72
+ readonly kind: "meshDeformer" | "affineDeformer";
73
+ readonly id: string;
74
+ readonly targetBaseId: string | null;
75
+ readonly pivotManager: Moc2PivotManager | null;
76
+ /** Mesh deformer: rows/cols + keyform point arrays. */
77
+ readonly rows?: number;
78
+ readonly cols?: number;
79
+ readonly keyforms?: readonly Float32Array[];
80
+ /** Affine deformer: keyform affines. */
81
+ readonly affines?: readonly Moc2Affine[];
82
+ readonly opacities?: Float32Array | null;
83
+ }
84
+
85
+ export interface Moc2DrawableMesh {
86
+ readonly kind: "drawableMesh";
87
+ readonly id: string;
88
+ readonly targetBaseId: string | null;
89
+ readonly pivotManager: Moc2PivotManager | null;
90
+ readonly averageDrawOrder: number;
91
+ readonly drawOrders: Int32Array;
92
+ readonly opacities: Float32Array;
93
+ readonly clipId: string | null;
94
+ readonly textureIndex: number;
95
+ readonly numPoints: number;
96
+ readonly numPolygons: number;
97
+ readonly indices: Uint16Array;
98
+ readonly keyforms: readonly Float32Array[];
99
+ readonly uvs: Float32Array;
100
+ readonly optionFlags: number;
101
+ /**
102
+ * moc2 color-composition enum when optionFlags bit0 set:
103
+ * 0 normal, 1 additive, 2 multiplicative (Cubism 2).
104
+ */
105
+ readonly colorComposition: number;
106
+ }
107
+
108
+ export interface Moc2PartsData {
109
+ readonly kind: "parts";
110
+ readonly locked: boolean;
111
+ readonly visible: boolean;
112
+ readonly id: string;
113
+ readonly baseData: readonly Moc2BaseDeformer[];
114
+ readonly drawData: readonly Moc2DrawableMesh[];
115
+ }
116
+
117
+ export interface Moc2ModelImpl {
118
+ readonly kind: "model";
119
+ readonly paramDefSet: Moc2ParamDefSet;
120
+ readonly parts: readonly Moc2PartsData[];
121
+ readonly canvasWidth: number;
122
+ readonly canvasHeight: number;
123
+ }
124
+
125
+ function asId(value: unknown): string {
126
+ if (typeof value === "string") return value;
127
+ if (value == null) return "";
128
+ return String(value);
129
+ }
130
+
131
+ function asParamDefs(value: unknown): Moc2ParamDef[] {
132
+ if (!Array.isArray(value)) return [];
133
+ return value.filter(
134
+ (v): v is Moc2ParamDef =>
135
+ !!v &&
136
+ typeof v === "object" &&
137
+ (v as Moc2ParamDef).kind === "paramDef",
138
+ );
139
+ }
140
+
141
+ function asPivots(value: unknown): Moc2Pivot[] {
142
+ if (!Array.isArray(value)) return [];
143
+ return value.filter(
144
+ (v): v is Moc2Pivot =>
145
+ !!v && typeof v === "object" && (v as Moc2Pivot).kind === "pivot",
146
+ );
147
+ }
148
+
149
+ function asFloat32Arrays(value: unknown): Float32Array[] {
150
+ if (!Array.isArray(value)) {
151
+ if (value instanceof Float32Array) return [value];
152
+ return [];
153
+ }
154
+ return value.filter((v): v is Float32Array => v instanceof Float32Array);
155
+ }
156
+
157
+ function asAffines(value: unknown): Moc2Affine[] {
158
+ if (!Array.isArray(value)) return [];
159
+ return value.filter(
160
+ (v): v is Moc2Affine =>
161
+ !!v && typeof v === "object" && (v as Moc2Affine).kind === "affine",
162
+ );
163
+ }
164
+
165
+ function asBaseList(value: unknown): Moc2BaseDeformer[] {
166
+ if (!Array.isArray(value)) return [];
167
+ return value.filter(
168
+ (v): v is Moc2BaseDeformer =>
169
+ !!v &&
170
+ typeof v === "object" &&
171
+ ((v as Moc2BaseDeformer).kind === "meshDeformer" ||
172
+ (v as Moc2BaseDeformer).kind === "affineDeformer"),
173
+ );
174
+ }
175
+
176
+ function asDrawList(value: unknown): Moc2DrawableMesh[] {
177
+ if (!Array.isArray(value)) return [];
178
+ return value.filter(
179
+ (v): v is Moc2DrawableMesh =>
180
+ !!v &&
181
+ typeof v === "object" &&
182
+ (v as Moc2DrawableMesh).kind === "drawableMesh",
183
+ );
184
+ }
185
+
186
+ function asPartsList(value: unknown): Moc2PartsData[] {
187
+ if (!Array.isArray(value)) return [];
188
+ return value.filter(
189
+ (v): v is Moc2PartsData =>
190
+ !!v &&
191
+ typeof v === "object" &&
192
+ (v as Moc2PartsData).kind === "parts",
193
+ );
194
+ }
195
+
196
+ function readPivotManager(r: Moc2Parser): Moc2PivotManager {
197
+ return {
198
+ kind: "pivotManager",
199
+ pivots: asPivots(r.readObject()),
200
+ };
201
+ }
202
+
203
+ function readV2Opacity(r: Moc2Reader): Float32Array | null {
204
+ if (r.getFormatVersion() >= 10) {
205
+ return r.readFloat32Array();
206
+ }
207
+ return null;
208
+ }
209
+
210
+ /** Parse one typed object body (caller owns object-table registration). */
211
+ export function readMoc2ObjectBody(r: Moc2Parser, type: number): unknown {
212
+ switch (type) {
213
+ case Moc2Type.Null:
214
+ return null;
215
+ case Moc2Type.String:
216
+ return r.readString();
217
+ case Moc2Type.DrawDataId:
218
+ case Moc2Type.BaseDataId:
219
+ case Moc2Type.ParamId:
220
+ case Moc2Type.PartsDataId:
221
+ return r.readString();
222
+ case Moc2Type.ObjectArray: {
223
+ const n = r.readVarint();
224
+ const arr: unknown[] = new Array(n);
225
+ for (let i = 0; i < n; i++) arr[i] = r.readObject();
226
+ return arr;
227
+ }
228
+ case Moc2Type.Int32Array:
229
+ case Moc2Type.Int32ArrayAlt:
230
+ return r.readInt32Array();
231
+ case Moc2Type.Float32Array:
232
+ return r.readFloat32Array();
233
+ case Moc2Type.Float64Array:
234
+ return r.readFloat64Array();
235
+ case Moc2Type.RectInt:
236
+ return {
237
+ kind: "rectInt",
238
+ a: r.readInt32(),
239
+ b: r.readInt32(),
240
+ c: r.readInt32(),
241
+ d: r.readInt32(),
242
+ };
243
+ case Moc2Type.PointInt:
244
+ return { kind: "pointInt", x: r.readInt32(), y: r.readInt32() };
245
+ case Moc2Type.ParamDefFloat: {
246
+ const def: Moc2ParamDef = {
247
+ kind: "paramDef",
248
+ min: r.readFloat32(),
249
+ max: r.readFloat32(),
250
+ defaultValue: r.readFloat32(),
251
+ id: asId(r.readObject()),
252
+ };
253
+ return def;
254
+ }
255
+ case Moc2Type.ParamDefSet: {
256
+ const set: Moc2ParamDefSet = {
257
+ kind: "paramDefSet",
258
+ params: asParamDefs(r.readObject()),
259
+ };
260
+ return set;
261
+ }
262
+ case Moc2Type.ModelImpl: {
263
+ const paramDefSet = r.readObject() as Moc2ParamDefSet;
264
+ const parts = asPartsList(r.readObject());
265
+ const model: Moc2ModelImpl = {
266
+ kind: "model",
267
+ paramDefSet,
268
+ parts,
269
+ canvasWidth: r.readInt32(),
270
+ canvasHeight: r.readInt32(),
271
+ };
272
+ return model;
273
+ }
274
+ case Moc2Type.PartsData: {
275
+ const locked = r.readBit();
276
+ const visible = r.readBit();
277
+ const parts: Moc2PartsData = {
278
+ kind: "parts",
279
+ locked,
280
+ visible,
281
+ id: asId(r.readObject()),
282
+ baseData: asBaseList(r.readObject()),
283
+ drawData: asDrawList(r.readObject()),
284
+ };
285
+ return parts;
286
+ }
287
+ case Moc2Type.AvatarParts: {
288
+ // Same payload shape as parts lists without lock/visible bits.
289
+ return {
290
+ kind: "avatarParts",
291
+ id: asId(r.readObject()),
292
+ drawData: r.readObject(),
293
+ baseData: r.readObject(),
294
+ };
295
+ }
296
+ case Moc2Type.PivotManager:
297
+ return readPivotManager(r);
298
+ case Moc2Type.Pivot: {
299
+ const pivot: Moc2Pivot = {
300
+ kind: "pivot",
301
+ paramId: asId(r.readObject()),
302
+ pivotCount: r.readInt32(),
303
+ pivotValues: (() => {
304
+ const v = r.readObject();
305
+ return v instanceof Float32Array ? v : new Float32Array();
306
+ })(),
307
+ };
308
+ return pivot;
309
+ }
310
+ case Moc2Type.Affine: {
311
+ const affine: Moc2Affine = {
312
+ kind: "affine",
313
+ originX: r.readFloat32(),
314
+ originY: r.readFloat32(),
315
+ scaleX: r.readFloat32(),
316
+ scaleY: r.readFloat32(),
317
+ rotation: r.readFloat32(),
318
+ reflectX: r.getFormatVersion() >= 10 ? r.readBool() : false,
319
+ reflectY: r.getFormatVersion() >= 10 ? r.readBool() : false,
320
+ };
321
+ return affine;
322
+ }
323
+ case Moc2Type.MeshDeformer: {
324
+ const id = asId(r.readObject());
325
+ const targetBaseId = asId(r.readObject()) || null;
326
+ const cols = r.readInt32();
327
+ const rows = r.readInt32();
328
+ const pivotManager = r.readObject() as Moc2PivotManager | null;
329
+ const keyforms = asFloat32Arrays(r.readObject());
330
+ const opacities = readV2Opacity(r);
331
+ const def: Moc2BaseDeformer = {
332
+ kind: "meshDeformer",
333
+ id,
334
+ targetBaseId,
335
+ pivotManager,
336
+ cols,
337
+ rows,
338
+ keyforms,
339
+ opacities,
340
+ };
341
+ return def;
342
+ }
343
+ case Moc2Type.AffineDeformer: {
344
+ const id = asId(r.readObject());
345
+ const targetBaseId = asId(r.readObject()) || null;
346
+ const pivotManager = r.readObject() as Moc2PivotManager | null;
347
+ const affines = asAffines(r.readObject());
348
+ const opacities = readV2Opacity(r);
349
+ const def: Moc2BaseDeformer = {
350
+ kind: "affineDeformer",
351
+ id,
352
+ targetBaseId,
353
+ pivotManager,
354
+ affines,
355
+ opacities,
356
+ };
357
+ return def;
358
+ }
359
+ case Moc2Type.DrawableMesh: {
360
+ const id = asId(r.readObject());
361
+ const targetBaseId = asId(r.readObject()) || null;
362
+ const pivotManager = r.readObject() as Moc2PivotManager | null;
363
+ const averageDrawOrder = r.readInt32();
364
+ const drawOrders = r.readInt32Array();
365
+ const opacities = r.readFloat32Array();
366
+ let clipId: string | null = null;
367
+ if (r.getFormatVersion() >= 11) {
368
+ clipId = asId(r.readObject()) || null;
369
+ }
370
+ const textureIndex = r.readInt32();
371
+ const numPoints = r.readInt32();
372
+ const numPolygons = r.readInt32();
373
+ const indexSrc = r.readObject();
374
+ const indexArr =
375
+ indexSrc instanceof Int32Array ? indexSrc : new Int32Array(0);
376
+ const indices = new Uint16Array(numPolygons * 3);
377
+ for (let i = 0; i < indices.length; i++) {
378
+ indices[i] = indexArr[i] ?? 0;
379
+ }
380
+ const keyforms = asFloat32Arrays(r.readObject());
381
+ const uvsRaw = r.readObject();
382
+ const uvs =
383
+ uvsRaw instanceof Float32Array
384
+ ? uvsRaw
385
+ : new Float32Array(numPoints * 2);
386
+ let optionFlags = 0;
387
+ let colorComposition = 0;
388
+ if (r.getFormatVersion() >= 8) {
389
+ optionFlags = r.readInt32();
390
+ if (optionFlags !== 0) {
391
+ if ((optionFlags & 1) !== 0) {
392
+ // Cubism 2 color composition: 0 normal, 1 add, 2 multiply.
393
+ colorComposition = r.readInt32();
394
+ }
395
+ // Remaining bits: blend hints / culling (bit 5 = 32).
396
+ }
397
+ }
398
+ const mesh: Moc2DrawableMesh = {
399
+ kind: "drawableMesh",
400
+ id,
401
+ targetBaseId,
402
+ pivotManager,
403
+ averageDrawOrder,
404
+ drawOrders,
405
+ opacities,
406
+ clipId,
407
+ textureIndex,
408
+ numPoints,
409
+ numPolygons,
410
+ indices,
411
+ keyforms,
412
+ uvs,
413
+ optionFlags,
414
+ colorComposition,
415
+ };
416
+ return mesh;
417
+ }
418
+ default:
419
+ throw new Error(
420
+ `@doki-land/live2d-renderer: unsupported moc2 type tag ${type}`,
421
+ );
422
+ }
423
+ }
424
+
425
+ /** Parser that owns the object table on top of {@link Moc2Reader}. */
426
+ export class Moc2Parser extends Moc2Reader {
427
+ readObject(typeHint = -1): unknown {
428
+ this.alignBits();
429
+ const type = typeHint < 0 ? this.readVarint() : typeHint;
430
+ if (type === MOC2_REF_TYPE) {
431
+ const index = this.readInt32();
432
+ if (index < 0 || index >= this.objects.length) {
433
+ throw new Error(
434
+ `@doki-land/live2d-renderer: moc2 bad back-ref ${index}`,
435
+ );
436
+ }
437
+ return this.objects[index];
438
+ }
439
+ const value = readMoc2ObjectBody(this, type);
440
+ this.objects.push(value);
441
+ return value;
442
+ }
443
+
444
+ parseModel(): Moc2ModelImpl {
445
+ const version = this.readHeader();
446
+ if (version > 11) {
447
+ throw new Error(
448
+ `@doki-land/live2d-renderer: moc2 version ${version} newer than supported (11)`,
449
+ );
450
+ }
451
+ const root = this.readObject();
452
+ this.readEofGuard();
453
+ if (
454
+ !root ||
455
+ typeof root !== "object" ||
456
+ (root as Moc2ModelImpl).kind !== "model"
457
+ ) {
458
+ throw new Error(
459
+ "@doki-land/live2d-renderer: moc2 root is not ModelImpl",
460
+ );
461
+ }
462
+ return root as Moc2ModelImpl;
463
+ }
464
+ }
@@ -0,0 +1,224 @@
1
+ /**
2
+ * moc2 binary stream reader (7-bit varints, big-endian ints/floats).
3
+ * Clean-room layout from observed `.moc` bytes + public format notes — no Core code.
4
+ */
5
+
6
+ export const MOC2_REF_TYPE = 33;
7
+ export const MOC2_EOF_MARKER = -30584;
8
+
9
+ export class Moc2Reader {
10
+ private readonly view: DataView;
11
+ private offset = 0;
12
+ /** Bit cursor within the current bit-pack byte (0 = aligned). */
13
+ private bitPos = 0;
14
+ private bitByte = 0;
15
+ private formatVersion = 0;
16
+ /** Object table for type-33 back-references (push order = identity). */
17
+ readonly objects: unknown[] = [];
18
+
19
+ constructor(bytes: ArrayBuffer) {
20
+ this.view = new DataView(bytes);
21
+ }
22
+
23
+ get byteLength(): number {
24
+ return this.view.byteLength;
25
+ }
26
+
27
+ getFormatVersion(): number {
28
+ return this.formatVersion;
29
+ }
30
+
31
+ /** Read magic `"moc"` + version; set format version. */
32
+ readHeader(): number {
33
+ const m = this.readInt8();
34
+ const o = this.readInt8();
35
+ const c = this.readInt8();
36
+ if (m !== 0x6d || o !== 0x6f || c !== 0x63) {
37
+ throw new Error(
38
+ `@doki-land/live2d-renderer: expected moc2 magic "moc", got ${JSON.stringify(
39
+ String.fromCharCode(m & 0xff, o & 0xff, c & 0xff),
40
+ )}`,
41
+ );
42
+ }
43
+ const version = this.readInt8() & 0xff;
44
+ this.formatVersion = version;
45
+ return version;
46
+ }
47
+
48
+ /** Version ≥ 8 ends with two int16 EOF markers. */
49
+ readEofGuard(): void {
50
+ if (this.formatVersion < 8) return;
51
+ const a = this.readInt16();
52
+ const b = this.readInt16();
53
+ if (a !== MOC2_EOF_MARKER || b !== MOC2_EOF_MARKER) {
54
+ throw new Error(
55
+ `@doki-land/live2d-renderer: moc2 EOF marker mismatch (${a}, ${b})`,
56
+ );
57
+ }
58
+ }
59
+
60
+ alignBits(): void {
61
+ this.bitPos = 0;
62
+ }
63
+
64
+ readVarint(): number {
65
+ this.alignBits();
66
+ const b0 = this.readInt8();
67
+ if ((b0 & 0x80) === 0) return b0 & 0xff;
68
+ const b1 = this.readInt8();
69
+ if ((b1 & 0x80) === 0) {
70
+ return ((b0 & 0x7f) << 7) | (b1 & 0x7f);
71
+ }
72
+ const b2 = this.readInt8();
73
+ if ((b2 & 0x80) === 0) {
74
+ return ((b0 & 0x7f) << 14) | ((b1 & 0x7f) << 7) | (b2 & 0xff);
75
+ }
76
+ const b3 = this.readInt8();
77
+ if ((b3 & 0x80) === 0) {
78
+ return (
79
+ ((b0 & 0x7f) << 21) |
80
+ ((b1 & 0x7f) << 14) |
81
+ ((b2 & 0x7f) << 7) |
82
+ (b3 & 0xff)
83
+ );
84
+ }
85
+ throw new Error(
86
+ "@doki-land/live2d-renderer: moc2 varint overflow (>28 bits)",
87
+ );
88
+ }
89
+
90
+ readBit(): boolean {
91
+ if (this.bitPos === 0 || this.bitPos === 8) {
92
+ this.bitByte = this.readInt8() & 0xff;
93
+ this.bitPos = 0;
94
+ }
95
+ const bit = ((this.bitByte >> (7 - this.bitPos)) & 1) === 1;
96
+ this.bitPos++;
97
+ return bit;
98
+ }
99
+
100
+ readInt8(): number {
101
+ this.alignBits();
102
+ if (this.offset >= this.view.byteLength) {
103
+ throw new Error(
104
+ "@doki-land/live2d-renderer: moc2 truncated (int8)",
105
+ );
106
+ }
107
+ return this.view.getInt8(this.offset++);
108
+ }
109
+
110
+ readInt16(): number {
111
+ this.alignBits();
112
+ if (this.offset + 2 > this.view.byteLength) {
113
+ throw new Error(
114
+ "@doki-land/live2d-renderer: moc2 truncated (int16)",
115
+ );
116
+ }
117
+ const v = this.view.getInt16(this.offset); // BE
118
+ this.offset += 2;
119
+ return v;
120
+ }
121
+
122
+ readInt32(): number {
123
+ this.alignBits();
124
+ if (this.offset + 4 > this.view.byteLength) {
125
+ throw new Error(
126
+ "@doki-land/live2d-renderer: moc2 truncated (int32)",
127
+ );
128
+ }
129
+ const v = this.view.getInt32(this.offset); // BE
130
+ this.offset += 4;
131
+ return v;
132
+ }
133
+
134
+ readFloat32(): number {
135
+ this.alignBits();
136
+ if (this.offset + 4 > this.view.byteLength) {
137
+ throw new Error(
138
+ "@doki-land/live2d-renderer: moc2 truncated (float32)",
139
+ );
140
+ }
141
+ const v = this.view.getFloat32(this.offset); // BE
142
+ this.offset += 4;
143
+ return v;
144
+ }
145
+
146
+ readFloat64(): number {
147
+ this.alignBits();
148
+ if (this.offset + 8 > this.view.byteLength) {
149
+ throw new Error(
150
+ "@doki-land/live2d-renderer: moc2 truncated (float64)",
151
+ );
152
+ }
153
+ const v = this.view.getFloat64(this.offset); // BE
154
+ this.offset += 8;
155
+ return v;
156
+ }
157
+
158
+ readBool(): boolean {
159
+ return this.readInt8() !== 0;
160
+ }
161
+
162
+ /** Latin-1 / byte string (moc2 IDs are ASCII). */
163
+ readString(): string {
164
+ const len = this.readVarint();
165
+ if (this.offset + len > this.view.byteLength) {
166
+ throw new Error(
167
+ "@doki-land/live2d-renderer: moc2 truncated (string)",
168
+ );
169
+ }
170
+ const chars: number[] = [];
171
+ for (let i = 0; i < len; i++) {
172
+ chars.push(this.view.getUint8(this.offset++));
173
+ }
174
+ return String.fromCharCode(...chars);
175
+ }
176
+
177
+ readInt32Array(): Int32Array {
178
+ this.alignBits();
179
+ const len = this.readVarint();
180
+ if (this.offset + len * 4 > this.view.byteLength) {
181
+ throw new Error(
182
+ "@doki-land/live2d-renderer: moc2 truncated (int32[])",
183
+ );
184
+ }
185
+ const out = new Int32Array(len);
186
+ for (let i = 0; i < len; i++) {
187
+ out[i] = this.view.getInt32(this.offset); // BE
188
+ this.offset += 4;
189
+ }
190
+ return out;
191
+ }
192
+
193
+ readFloat32Array(): Float32Array {
194
+ this.alignBits();
195
+ const len = this.readVarint();
196
+ if (this.offset + len * 4 > this.view.byteLength) {
197
+ throw new Error(
198
+ "@doki-land/live2d-renderer: moc2 truncated (float32[])",
199
+ );
200
+ }
201
+ const out = new Float32Array(len);
202
+ for (let i = 0; i < len; i++) {
203
+ out[i] = this.view.getFloat32(this.offset); // BE
204
+ this.offset += 4;
205
+ }
206
+ return out;
207
+ }
208
+
209
+ readFloat64Array(): Float64Array {
210
+ this.alignBits();
211
+ const len = this.readVarint();
212
+ if (this.offset + len * 8 > this.view.byteLength) {
213
+ throw new Error(
214
+ "@doki-land/live2d-renderer: moc2 truncated (float64[])",
215
+ );
216
+ }
217
+ const out = new Float64Array(len);
218
+ for (let i = 0; i < len; i++) {
219
+ out[i] = this.view.getFloat64(this.offset); // BE
220
+ this.offset += 8;
221
+ }
222
+ return out;
223
+ }
224
+ }