@series-inc/stowkit-reader 0.1.41 → 0.1.43
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/LICENSE.txt +6 -6
- package/README.md +330 -330
- package/dist/stowkit_reader.wasm +0 -0
- package/package.json +32 -28
package/LICENSE.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
By downloading, copying, or using this template, you agree to the
|
|
2
|
-
RUN.game Terms of Service:
|
|
3
|
-
|
|
4
|
-
https://policy.run.game/eula.html
|
|
5
|
-
|
|
6
|
-
© Series Inc. All rights reserved.
|
|
1
|
+
By downloading, copying, or using this template, you agree to the
|
|
2
|
+
RUN.game Terms of Service:
|
|
3
|
+
|
|
4
|
+
https://policy.run.game/eula.html
|
|
5
|
+
|
|
6
|
+
© Series Inc. All rights reserved.
|
package/README.md
CHANGED
|
@@ -1,330 +1,330 @@
|
|
|
1
|
-
# @series-inc/stowkit-reader
|
|
2
|
-
|
|
3
|
-
WebAssembly-based reader for StowKit (.stow) asset pack files. Low-level API for reading and parsing binary asset data.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- 🚀 **Pure WASM** - All binary parsing done in WebAssembly (Zig)
|
|
8
|
-
- 📦 **Zero Dependencies** - Just WebAssembly, no external libraries
|
|
9
|
-
- 🔍 **WASM Metadata Parsing** - Parse complex structures in WASM, not JavaScript
|
|
10
|
-
- 🎮 **Game-Ready** - Supports all asset types (meshes, textures, audio, animations)
|
|
11
|
-
- 📝 **TypeScript Support** - Full type definitions included
|
|
12
|
-
- ⚡ **Fast** - WASM parsing is 10-50x faster than JavaScript DataView
|
|
13
|
-
|
|
14
|
-
## Installation
|
|
15
|
-
|
|
16
|
-
```bash
|
|
17
|
-
npm install @series-inc/stowkit-reader
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## Usage
|
|
21
|
-
|
|
22
|
-
```typescript
|
|
23
|
-
import { StowKitReader } from '@series-inc/stowkit-reader';
|
|
24
|
-
|
|
25
|
-
// Initialize the reader
|
|
26
|
-
const reader = new StowKitReader();
|
|
27
|
-
await reader.init();
|
|
28
|
-
|
|
29
|
-
// Open a .stow file
|
|
30
|
-
const response = await fetch('assets.stow');
|
|
31
|
-
const buffer = await response.arrayBuffer();
|
|
32
|
-
await reader.open(buffer);
|
|
33
|
-
|
|
34
|
-
// List all assets
|
|
35
|
-
const assets = reader.listAssets();
|
|
36
|
-
console.log(`Found ${assets.length} assets`);
|
|
37
|
-
|
|
38
|
-
// Find asset by path
|
|
39
|
-
const index = reader.findAssetByPath('models/character.mesh');
|
|
40
|
-
|
|
41
|
-
// Read asset data
|
|
42
|
-
const data = reader.readAssetData(index);
|
|
43
|
-
|
|
44
|
-
// Parse metadata (done in WASM!)
|
|
45
|
-
const texInfo = reader.parseTextureMetadata(index);
|
|
46
|
-
console.log(`Texture: ${texInfo.width}x${texInfo.height}`);
|
|
47
|
-
|
|
48
|
-
const audioInfo = reader.parseAudioMetadata(index);
|
|
49
|
-
console.log(`Audio: ${audioInfo.sampleRate}Hz, ${audioInfo.durationMs}ms`);
|
|
50
|
-
|
|
51
|
-
const animInfo = reader.parseAnimationMetadata(index);
|
|
52
|
-
console.log(`Animation: ${animInfo.duration}s, ${animInfo.boneCount} bones`);
|
|
53
|
-
|
|
54
|
-
// Clean up
|
|
55
|
-
reader.close();
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Asset Types
|
|
59
|
-
|
|
60
|
-
| Type | ID | Description |
|
|
61
|
-
|------|-------|-------------|
|
|
62
|
-
| Static Mesh | 1 | Draco-compressed 3D models |
|
|
63
|
-
| Texture 2D | 2 | KTX2/Basis Universal textures |
|
|
64
|
-
| Audio | 3 | OGG/MP3 audio files |
|
|
65
|
-
| Material Schema | 4 | Material template definitions |
|
|
66
|
-
| Skinned Mesh | 5 | Skeletal meshes with bones |
|
|
67
|
-
| Animation Clip | 6 | Bone animation keyframes |
|
|
68
|
-
|
|
69
|
-
## API Reference
|
|
70
|
-
|
|
71
|
-
### Exports
|
|
72
|
-
|
|
73
|
-
```typescript
|
|
74
|
-
export { StowKitReader } from '@series-inc/stowkit-reader';
|
|
75
|
-
export { PerfLogger } from '@series-inc/stowkit-reader';
|
|
76
|
-
export { AssetType } from '@series-inc/stowkit-reader';
|
|
77
|
-
|
|
78
|
-
// Interfaces
|
|
79
|
-
export type { AssetInfo, AssetListItem, TextureMetadata, AudioMetadata } from '@series-inc/stowkit-reader';
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### Asset Types Enum
|
|
83
|
-
|
|
84
|
-
```typescript
|
|
85
|
-
enum AssetType {
|
|
86
|
-
UNKNOWN = 0,
|
|
87
|
-
STATIC_MESH = 1,
|
|
88
|
-
TEXTURE_2D = 2,
|
|
89
|
-
AUDIO = 3,
|
|
90
|
-
MATERIAL_SCHEMA = 4,
|
|
91
|
-
SKINNED_MESH = 5,
|
|
92
|
-
ANIMATION_CLIP = 6,
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### Initialization
|
|
97
|
-
|
|
98
|
-
#### `new StowKitReader(wasmUrl?: string)`
|
|
99
|
-
Create a new reader instance. Optionally specify the WASM file URL (default: `'/stowkit/stowkit_reader.wasm'`).
|
|
100
|
-
|
|
101
|
-
#### `async init(): Promise<StowKitReader>`
|
|
102
|
-
Initialize the WebAssembly module. Must be called before using the reader.
|
|
103
|
-
|
|
104
|
-
```typescript
|
|
105
|
-
const reader = new StowKitReader('/path/to/stowkit_reader.wasm');
|
|
106
|
-
await reader.init();
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### Opening Files
|
|
110
|
-
|
|
111
|
-
#### `async open(file: ArrayBuffer | File): Promise<boolean>`
|
|
112
|
-
Open a .stow file from memory.
|
|
113
|
-
|
|
114
|
-
```typescript
|
|
115
|
-
// From fetch
|
|
116
|
-
const response = await fetch('assets.stow');
|
|
117
|
-
const buffer = await response.arrayBuffer();
|
|
118
|
-
await reader.open(buffer);
|
|
119
|
-
|
|
120
|
-
// From file input
|
|
121
|
-
const file = input.files[0];
|
|
122
|
-
await reader.open(file);
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
### Asset Access
|
|
126
|
-
|
|
127
|
-
#### `getAssetCount(): number`
|
|
128
|
-
Get the total number of assets in the pack.
|
|
129
|
-
|
|
130
|
-
#### `listAssets(): AssetListItem[]`
|
|
131
|
-
Get information about all assets.
|
|
132
|
-
|
|
133
|
-
```typescript
|
|
134
|
-
const assets = reader.listAssets();
|
|
135
|
-
assets.forEach(asset => {
|
|
136
|
-
console.log(`${asset.name} (type ${asset.type}): ${asset.dataSize} bytes`);
|
|
137
|
-
});
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
#### `findAssetByPath(path: string): number`
|
|
141
|
-
Find an asset index by its canonical path. Returns -1 if not found.
|
|
142
|
-
|
|
143
|
-
```typescript
|
|
144
|
-
const index = reader.findAssetByPath('models/character.mesh');
|
|
145
|
-
if (index >= 0) {
|
|
146
|
-
const data = reader.readAssetData(index);
|
|
147
|
-
}
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
#### `getAssetInfo(index: number): AssetInfo | null`
|
|
151
|
-
Get detailed information about a specific asset.
|
|
152
|
-
|
|
153
|
-
#### `readAssetData(index: number): Uint8Array | null`
|
|
154
|
-
Read the binary data of an asset.
|
|
155
|
-
|
|
156
|
-
#### `readAssetMetadata(index: number): Uint8Array | null`
|
|
157
|
-
Read the raw metadata bytes of an asset.
|
|
158
|
-
|
|
159
|
-
### WASM Metadata Parsing
|
|
160
|
-
|
|
161
|
-
All metadata parsing is done in WASM for maximum performance. No manual DataView manipulation needed!
|
|
162
|
-
|
|
163
|
-
#### `parseTextureMetadata(index: number): TextureMetadata | null`
|
|
164
|
-
Parse texture metadata from WASM.
|
|
165
|
-
|
|
166
|
-
```typescript
|
|
167
|
-
const info = reader.parseTextureMetadata(2);
|
|
168
|
-
// { stringId, width, height, channels, channelFormat, tags }
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
#### `parseAudioMetadata(index: number): AudioMetadata | null`
|
|
172
|
-
Parse audio metadata from WASM.
|
|
173
|
-
|
|
174
|
-
```typescript
|
|
175
|
-
const info = reader.parseAudioMetadata(3);
|
|
176
|
-
// { stringId, sampleRate, channels, durationMs, tags }
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
#### `parseMeshMetadata(index: number): object | null`
|
|
180
|
-
Parse static mesh metadata from WASM.
|
|
181
|
-
|
|
182
|
-
```typescript
|
|
183
|
-
const info = reader.parseMeshMetadata(1);
|
|
184
|
-
// { stringId, tags }
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
#### `parseAnimationMetadata(index: number): object | null`
|
|
188
|
-
Parse animation clip metadata from WASM.
|
|
189
|
-
|
|
190
|
-
```typescript
|
|
191
|
-
const info = reader.parseAnimationMetadata(9);
|
|
192
|
-
// { stringId, targetMeshId, duration, ticksPerSecond, channelCount, boneCount }
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
#### `parseMaterialSchemaMetadata(index: number): object | null`
|
|
196
|
-
Parse material schema metadata from WASM.
|
|
197
|
-
|
|
198
|
-
```typescript
|
|
199
|
-
const schema = reader.parseMaterialSchemaMetadata(4);
|
|
200
|
-
// { stringId, schemaName, fieldCount, fields: [...] }
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
#### `parseSkinnedMeshMetadata(index: number): object | null`
|
|
204
|
-
Parse skinned mesh metadata from WASM (returns bone hierarchy and stringId).
|
|
205
|
-
|
|
206
|
-
```typescript
|
|
207
|
-
const info = reader.parseSkinnedMeshMetadata(8);
|
|
208
|
-
// { stringId, boneCount, bones: [{name, parentIndex}, ...], tags }
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
#### `parseSkinnedMeshGeometryFast(metadataBlob: Uint8Array, dataBlob: Uint8Array): object | null`
|
|
212
|
-
Parse skinned mesh geometry using WASM (10-50x faster than JavaScript!). Returns combined vertex/index buffers ready for rendering.
|
|
213
|
-
|
|
214
|
-
```typescript
|
|
215
|
-
const metadata = reader.readAssetMetadata(index);
|
|
216
|
-
const data = reader.readAssetData(index);
|
|
217
|
-
|
|
218
|
-
const geometry = reader.parseSkinnedMeshGeometryFast(metadata, data);
|
|
219
|
-
if (geometry) {
|
|
220
|
-
const { vertexCount, indexCount, positions, normals, uvs, skinIndices, skinWeights, indices } = geometry;
|
|
221
|
-
// Use with Three.js BufferGeometry, etc.
|
|
222
|
-
}
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
### Utility Methods
|
|
226
|
-
|
|
227
|
-
#### `setAssetName(index: number, name: string): void`
|
|
228
|
-
Set a custom name for an asset (for UI display purposes).
|
|
229
|
-
|
|
230
|
-
```typescript
|
|
231
|
-
reader.setAssetName(5, 'Custom Asset Name');
|
|
232
|
-
const assets = reader.listAssets();
|
|
233
|
-
// Asset at index 5 will now have name 'Custom Asset Name'
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
#### `static formatBytes(bytes: number): string`
|
|
237
|
-
Format byte count to human-readable string.
|
|
238
|
-
|
|
239
|
-
```typescript
|
|
240
|
-
console.log(StowKitReader.formatBytes(1024)); // "1 KB"
|
|
241
|
-
console.log(StowKitReader.formatBytes(1048576)); // "1 MB"
|
|
242
|
-
console.log(StowKitReader.formatBytes(524288000)); // "500 MB"
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
### Cleanup
|
|
246
|
-
|
|
247
|
-
#### `close(): void`
|
|
248
|
-
Close the current file and free resources.
|
|
249
|
-
|
|
250
|
-
```typescript
|
|
251
|
-
reader.close();
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
## Why WASM Parsing?
|
|
255
|
-
|
|
256
|
-
**Before (JavaScript):**
|
|
257
|
-
```typescript
|
|
258
|
-
// 50+ lines of manual DataView parsing
|
|
259
|
-
const view = new DataView(metadata.buffer, metadata.byteOffset);
|
|
260
|
-
const stringIdBytes = metadata.slice(0, 128);
|
|
261
|
-
const decoder = new TextDecoder();
|
|
262
|
-
const stringId = decoder.decode(stringIdBytes.slice(0, stringIdBytes.indexOf(0)));
|
|
263
|
-
const sampleRate = view.getUint32(128, true);
|
|
264
|
-
// ... 40 more lines ...
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
**After (WASM):**
|
|
268
|
-
```typescript
|
|
269
|
-
// 1 line!
|
|
270
|
-
const info = reader.parseAudioMetadata(index);
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
**Benefits:**
|
|
274
|
-
- ✅ Single source of truth (C header defines all structs)
|
|
275
|
-
- ✅ 10-50x faster parsing
|
|
276
|
-
- ✅ No sync issues between reader and parser
|
|
277
|
-
- ✅ Less prone to offset calculation bugs
|
|
278
|
-
- ✅ Cleaner, more maintainable code
|
|
279
|
-
|
|
280
|
-
## Examples
|
|
281
|
-
|
|
282
|
-
### Inspect a Pack
|
|
283
|
-
|
|
284
|
-
```typescript
|
|
285
|
-
import { StowKitReader, AssetType } from '@series-inc/stowkit-reader';
|
|
286
|
-
|
|
287
|
-
const reader = new StowKitReader();
|
|
288
|
-
await reader.init();
|
|
289
|
-
|
|
290
|
-
const response = await fetch('game.stow');
|
|
291
|
-
await reader.open(await response.arrayBuffer());
|
|
292
|
-
|
|
293
|
-
const assets = reader.listAssets();
|
|
294
|
-
console.log(`Pack contains ${assets.length} assets`);
|
|
295
|
-
|
|
296
|
-
assets.forEach(asset => {
|
|
297
|
-
const typeName = ['Unknown', 'Mesh', 'Texture', 'Audio', 'Schema', 'Skinned Mesh', 'Animation'][asset.type] || 'Unknown';
|
|
298
|
-
console.log(`[${asset.index}] ${asset.name} - ${typeName} (${StowKitReader.formatBytes(asset.dataSize)})`);
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
reader.close();
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
### Extract Assets
|
|
305
|
-
|
|
306
|
-
```typescript
|
|
307
|
-
const reader = new StowKitReader();
|
|
308
|
-
await reader.init();
|
|
309
|
-
await reader.open(buffer);
|
|
310
|
-
|
|
311
|
-
// Find and extract a specific asset
|
|
312
|
-
const index = reader.findAssetByPath('models/character.mesh');
|
|
313
|
-
if (index >= 0) {
|
|
314
|
-
const data = reader.readAssetData(index);
|
|
315
|
-
const metadata = reader.readAssetMetadata(index);
|
|
316
|
-
|
|
317
|
-
// Save to file
|
|
318
|
-
const blob = new Blob([data]);
|
|
319
|
-
const url = URL.createObjectURL(blob);
|
|
320
|
-
const a = document.createElement('a');
|
|
321
|
-
a.href = url;
|
|
322
|
-
a.download = 'character.mesh';
|
|
323
|
-
a.click();
|
|
324
|
-
}
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
## Requirements
|
|
328
|
-
|
|
329
|
-
- WebAssembly support in the browser
|
|
330
|
-
- The `stowkit_reader.wasm` file must be accessible at runtime
|
|
1
|
+
# @series-inc/stowkit-reader
|
|
2
|
+
|
|
3
|
+
WebAssembly-based reader for StowKit (.stow) asset pack files. Low-level API for reading and parsing binary asset data.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🚀 **Pure WASM** - All binary parsing done in WebAssembly (Zig)
|
|
8
|
+
- 📦 **Zero Dependencies** - Just WebAssembly, no external libraries
|
|
9
|
+
- 🔍 **WASM Metadata Parsing** - Parse complex structures in WASM, not JavaScript
|
|
10
|
+
- 🎮 **Game-Ready** - Supports all asset types (meshes, textures, audio, animations)
|
|
11
|
+
- 📝 **TypeScript Support** - Full type definitions included
|
|
12
|
+
- ⚡ **Fast** - WASM parsing is 10-50x faster than JavaScript DataView
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @series-inc/stowkit-reader
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { StowKitReader } from '@series-inc/stowkit-reader';
|
|
24
|
+
|
|
25
|
+
// Initialize the reader
|
|
26
|
+
const reader = new StowKitReader();
|
|
27
|
+
await reader.init();
|
|
28
|
+
|
|
29
|
+
// Open a .stow file
|
|
30
|
+
const response = await fetch('assets.stow');
|
|
31
|
+
const buffer = await response.arrayBuffer();
|
|
32
|
+
await reader.open(buffer);
|
|
33
|
+
|
|
34
|
+
// List all assets
|
|
35
|
+
const assets = reader.listAssets();
|
|
36
|
+
console.log(`Found ${assets.length} assets`);
|
|
37
|
+
|
|
38
|
+
// Find asset by path
|
|
39
|
+
const index = reader.findAssetByPath('models/character.mesh');
|
|
40
|
+
|
|
41
|
+
// Read asset data
|
|
42
|
+
const data = reader.readAssetData(index);
|
|
43
|
+
|
|
44
|
+
// Parse metadata (done in WASM!)
|
|
45
|
+
const texInfo = reader.parseTextureMetadata(index);
|
|
46
|
+
console.log(`Texture: ${texInfo.width}x${texInfo.height}`);
|
|
47
|
+
|
|
48
|
+
const audioInfo = reader.parseAudioMetadata(index);
|
|
49
|
+
console.log(`Audio: ${audioInfo.sampleRate}Hz, ${audioInfo.durationMs}ms`);
|
|
50
|
+
|
|
51
|
+
const animInfo = reader.parseAnimationMetadata(index);
|
|
52
|
+
console.log(`Animation: ${animInfo.duration}s, ${animInfo.boneCount} bones`);
|
|
53
|
+
|
|
54
|
+
// Clean up
|
|
55
|
+
reader.close();
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Asset Types
|
|
59
|
+
|
|
60
|
+
| Type | ID | Description |
|
|
61
|
+
|------|-------|-------------|
|
|
62
|
+
| Static Mesh | 1 | Draco-compressed 3D models |
|
|
63
|
+
| Texture 2D | 2 | KTX2/Basis Universal textures |
|
|
64
|
+
| Audio | 3 | OGG/MP3 audio files |
|
|
65
|
+
| Material Schema | 4 | Material template definitions |
|
|
66
|
+
| Skinned Mesh | 5 | Skeletal meshes with bones |
|
|
67
|
+
| Animation Clip | 6 | Bone animation keyframes |
|
|
68
|
+
|
|
69
|
+
## API Reference
|
|
70
|
+
|
|
71
|
+
### Exports
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
export { StowKitReader } from '@series-inc/stowkit-reader';
|
|
75
|
+
export { PerfLogger } from '@series-inc/stowkit-reader';
|
|
76
|
+
export { AssetType } from '@series-inc/stowkit-reader';
|
|
77
|
+
|
|
78
|
+
// Interfaces
|
|
79
|
+
export type { AssetInfo, AssetListItem, TextureMetadata, AudioMetadata } from '@series-inc/stowkit-reader';
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Asset Types Enum
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
enum AssetType {
|
|
86
|
+
UNKNOWN = 0,
|
|
87
|
+
STATIC_MESH = 1,
|
|
88
|
+
TEXTURE_2D = 2,
|
|
89
|
+
AUDIO = 3,
|
|
90
|
+
MATERIAL_SCHEMA = 4,
|
|
91
|
+
SKINNED_MESH = 5,
|
|
92
|
+
ANIMATION_CLIP = 6,
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Initialization
|
|
97
|
+
|
|
98
|
+
#### `new StowKitReader(wasmUrl?: string)`
|
|
99
|
+
Create a new reader instance. Optionally specify the WASM file URL (default: `'/stowkit/stowkit_reader.wasm'`).
|
|
100
|
+
|
|
101
|
+
#### `async init(): Promise<StowKitReader>`
|
|
102
|
+
Initialize the WebAssembly module. Must be called before using the reader.
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
const reader = new StowKitReader('/path/to/stowkit_reader.wasm');
|
|
106
|
+
await reader.init();
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Opening Files
|
|
110
|
+
|
|
111
|
+
#### `async open(file: ArrayBuffer | File): Promise<boolean>`
|
|
112
|
+
Open a .stow file from memory.
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
// From fetch
|
|
116
|
+
const response = await fetch('assets.stow');
|
|
117
|
+
const buffer = await response.arrayBuffer();
|
|
118
|
+
await reader.open(buffer);
|
|
119
|
+
|
|
120
|
+
// From file input
|
|
121
|
+
const file = input.files[0];
|
|
122
|
+
await reader.open(file);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Asset Access
|
|
126
|
+
|
|
127
|
+
#### `getAssetCount(): number`
|
|
128
|
+
Get the total number of assets in the pack.
|
|
129
|
+
|
|
130
|
+
#### `listAssets(): AssetListItem[]`
|
|
131
|
+
Get information about all assets.
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
const assets = reader.listAssets();
|
|
135
|
+
assets.forEach(asset => {
|
|
136
|
+
console.log(`${asset.name} (type ${asset.type}): ${asset.dataSize} bytes`);
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### `findAssetByPath(path: string): number`
|
|
141
|
+
Find an asset index by its canonical path. Returns -1 if not found.
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
const index = reader.findAssetByPath('models/character.mesh');
|
|
145
|
+
if (index >= 0) {
|
|
146
|
+
const data = reader.readAssetData(index);
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
#### `getAssetInfo(index: number): AssetInfo | null`
|
|
151
|
+
Get detailed information about a specific asset.
|
|
152
|
+
|
|
153
|
+
#### `readAssetData(index: number): Uint8Array | null`
|
|
154
|
+
Read the binary data of an asset.
|
|
155
|
+
|
|
156
|
+
#### `readAssetMetadata(index: number): Uint8Array | null`
|
|
157
|
+
Read the raw metadata bytes of an asset.
|
|
158
|
+
|
|
159
|
+
### WASM Metadata Parsing
|
|
160
|
+
|
|
161
|
+
All metadata parsing is done in WASM for maximum performance. No manual DataView manipulation needed!
|
|
162
|
+
|
|
163
|
+
#### `parseTextureMetadata(index: number): TextureMetadata | null`
|
|
164
|
+
Parse texture metadata from WASM.
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
const info = reader.parseTextureMetadata(2);
|
|
168
|
+
// { stringId, width, height, channels, channelFormat, tags }
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### `parseAudioMetadata(index: number): AudioMetadata | null`
|
|
172
|
+
Parse audio metadata from WASM.
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
const info = reader.parseAudioMetadata(3);
|
|
176
|
+
// { stringId, sampleRate, channels, durationMs, tags }
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
#### `parseMeshMetadata(index: number): object | null`
|
|
180
|
+
Parse static mesh metadata from WASM.
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
const info = reader.parseMeshMetadata(1);
|
|
184
|
+
// { stringId, tags }
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
#### `parseAnimationMetadata(index: number): object | null`
|
|
188
|
+
Parse animation clip metadata from WASM.
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
const info = reader.parseAnimationMetadata(9);
|
|
192
|
+
// { stringId, targetMeshId, duration, ticksPerSecond, channelCount, boneCount }
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
#### `parseMaterialSchemaMetadata(index: number): object | null`
|
|
196
|
+
Parse material schema metadata from WASM.
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
const schema = reader.parseMaterialSchemaMetadata(4);
|
|
200
|
+
// { stringId, schemaName, fieldCount, fields: [...] }
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
#### `parseSkinnedMeshMetadata(index: number): object | null`
|
|
204
|
+
Parse skinned mesh metadata from WASM (returns bone hierarchy and stringId).
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
const info = reader.parseSkinnedMeshMetadata(8);
|
|
208
|
+
// { stringId, boneCount, bones: [{name, parentIndex}, ...], tags }
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
#### `parseSkinnedMeshGeometryFast(metadataBlob: Uint8Array, dataBlob: Uint8Array): object | null`
|
|
212
|
+
Parse skinned mesh geometry using WASM (10-50x faster than JavaScript!). Returns combined vertex/index buffers ready for rendering.
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
const metadata = reader.readAssetMetadata(index);
|
|
216
|
+
const data = reader.readAssetData(index);
|
|
217
|
+
|
|
218
|
+
const geometry = reader.parseSkinnedMeshGeometryFast(metadata, data);
|
|
219
|
+
if (geometry) {
|
|
220
|
+
const { vertexCount, indexCount, positions, normals, uvs, skinIndices, skinWeights, indices } = geometry;
|
|
221
|
+
// Use with Three.js BufferGeometry, etc.
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Utility Methods
|
|
226
|
+
|
|
227
|
+
#### `setAssetName(index: number, name: string): void`
|
|
228
|
+
Set a custom name for an asset (for UI display purposes).
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
reader.setAssetName(5, 'Custom Asset Name');
|
|
232
|
+
const assets = reader.listAssets();
|
|
233
|
+
// Asset at index 5 will now have name 'Custom Asset Name'
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
#### `static formatBytes(bytes: number): string`
|
|
237
|
+
Format byte count to human-readable string.
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
console.log(StowKitReader.formatBytes(1024)); // "1 KB"
|
|
241
|
+
console.log(StowKitReader.formatBytes(1048576)); // "1 MB"
|
|
242
|
+
console.log(StowKitReader.formatBytes(524288000)); // "500 MB"
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Cleanup
|
|
246
|
+
|
|
247
|
+
#### `close(): void`
|
|
248
|
+
Close the current file and free resources.
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
reader.close();
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Why WASM Parsing?
|
|
255
|
+
|
|
256
|
+
**Before (JavaScript):**
|
|
257
|
+
```typescript
|
|
258
|
+
// 50+ lines of manual DataView parsing
|
|
259
|
+
const view = new DataView(metadata.buffer, metadata.byteOffset);
|
|
260
|
+
const stringIdBytes = metadata.slice(0, 128);
|
|
261
|
+
const decoder = new TextDecoder();
|
|
262
|
+
const stringId = decoder.decode(stringIdBytes.slice(0, stringIdBytes.indexOf(0)));
|
|
263
|
+
const sampleRate = view.getUint32(128, true);
|
|
264
|
+
// ... 40 more lines ...
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**After (WASM):**
|
|
268
|
+
```typescript
|
|
269
|
+
// 1 line!
|
|
270
|
+
const info = reader.parseAudioMetadata(index);
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**Benefits:**
|
|
274
|
+
- ✅ Single source of truth (C header defines all structs)
|
|
275
|
+
- ✅ 10-50x faster parsing
|
|
276
|
+
- ✅ No sync issues between reader and parser
|
|
277
|
+
- ✅ Less prone to offset calculation bugs
|
|
278
|
+
- ✅ Cleaner, more maintainable code
|
|
279
|
+
|
|
280
|
+
## Examples
|
|
281
|
+
|
|
282
|
+
### Inspect a Pack
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
import { StowKitReader, AssetType } from '@series-inc/stowkit-reader';
|
|
286
|
+
|
|
287
|
+
const reader = new StowKitReader();
|
|
288
|
+
await reader.init();
|
|
289
|
+
|
|
290
|
+
const response = await fetch('game.stow');
|
|
291
|
+
await reader.open(await response.arrayBuffer());
|
|
292
|
+
|
|
293
|
+
const assets = reader.listAssets();
|
|
294
|
+
console.log(`Pack contains ${assets.length} assets`);
|
|
295
|
+
|
|
296
|
+
assets.forEach(asset => {
|
|
297
|
+
const typeName = ['Unknown', 'Mesh', 'Texture', 'Audio', 'Schema', 'Skinned Mesh', 'Animation'][asset.type] || 'Unknown';
|
|
298
|
+
console.log(`[${asset.index}] ${asset.name} - ${typeName} (${StowKitReader.formatBytes(asset.dataSize)})`);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
reader.close();
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Extract Assets
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
const reader = new StowKitReader();
|
|
308
|
+
await reader.init();
|
|
309
|
+
await reader.open(buffer);
|
|
310
|
+
|
|
311
|
+
// Find and extract a specific asset
|
|
312
|
+
const index = reader.findAssetByPath('models/character.mesh');
|
|
313
|
+
if (index >= 0) {
|
|
314
|
+
const data = reader.readAssetData(index);
|
|
315
|
+
const metadata = reader.readAssetMetadata(index);
|
|
316
|
+
|
|
317
|
+
// Save to file
|
|
318
|
+
const blob = new Blob([data]);
|
|
319
|
+
const url = URL.createObjectURL(blob);
|
|
320
|
+
const a = document.createElement('a');
|
|
321
|
+
a.href = url;
|
|
322
|
+
a.download = 'character.mesh';
|
|
323
|
+
a.click();
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Requirements
|
|
328
|
+
|
|
329
|
+
- WebAssembly support in the browser
|
|
330
|
+
- The `stowkit_reader.wasm` file must be accessible at runtime
|
package/dist/stowkit_reader.wasm
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,28 +1,32 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@series-inc/stowkit-reader",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "WebAssembly-based reader for StowKit (.stow) asset pack files",
|
|
5
|
-
"main": "dist/stowkit-reader.js",
|
|
6
|
-
"module": "dist/stowkit-reader.esm.js",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
8
|
-
"files": [
|
|
9
|
-
"dist",
|
|
10
|
-
"README.md"
|
|
11
|
-
],
|
|
12
|
-
"scripts": {
|
|
13
|
-
"build": "npm run copy:wasm && rollup -c",
|
|
14
|
-
"copy:wasm": "node scripts/copy-wasm.js",
|
|
15
|
-
"prepublishOnly": "npm run build"
|
|
16
|
-
},
|
|
17
|
-
"keywords": [],
|
|
18
|
-
"devDependencies": {
|
|
19
|
-
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
20
|
-
"@rollup/plugin-typescript": "^11.1.5",
|
|
21
|
-
"rollup": "^4.9.2",
|
|
22
|
-
"tslib": "^2.8.1",
|
|
23
|
-
"typescript": "^5.3.3"
|
|
24
|
-
},
|
|
25
|
-
"publishConfig": {
|
|
26
|
-
"access": "public"
|
|
27
|
-
}
|
|
28
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@series-inc/stowkit-reader",
|
|
3
|
+
"version": "0.1.43",
|
|
4
|
+
"description": "WebAssembly-based reader for StowKit (.stow) asset pack files",
|
|
5
|
+
"main": "dist/stowkit-reader.js",
|
|
6
|
+
"module": "dist/stowkit-reader.esm.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "npm run copy:wasm && rollup -c",
|
|
14
|
+
"copy:wasm": "node scripts/copy-wasm.js",
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [],
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
20
|
+
"@rollup/plugin-typescript": "^11.1.5",
|
|
21
|
+
"rollup": "^4.9.2",
|
|
22
|
+
"tslib": "^2.8.1",
|
|
23
|
+
"typescript": "^5.3.3"
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/series-ai/stowkit-2.git"
|
|
31
|
+
}
|
|
32
|
+
}
|