@gmvjs/core 0.1.0 → 0.1.1
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/README.md +41 -35
- package/dist/archives/common.d.ts +9 -9
- package/dist/archives/common.js.map +1 -1
- package/dist/archives/folder.d.ts +1 -1
- package/dist/archives/folder.js +1 -1
- package/dist/archives/folder.js.map +1 -1
- package/dist/archives/zip.js.map +1 -1
- package/dist/binary.d.ts +7 -7
- package/dist/binary.js.map +1 -1
- package/dist/builtin-en.js.map +1 -1
- package/dist/builtin.js +1 -1
- package/dist/builtin.js.map +1 -1
- package/dist/codecs/zlib.d.ts +2 -2
- package/dist/codecs/zlib.js.map +1 -1
- package/dist/diagnostics.js.map +1 -1
- package/dist/geometry.d.ts +5 -5
- package/dist/geometry.js.map +1 -1
- package/dist/i18n.d.ts +4 -4
- package/dist/i18n.js.map +1 -1
- package/dist/images/bc45.d.ts +1 -1
- package/dist/images/bc45.js.map +1 -1
- package/dist/images/bc7.d.ts +1 -1
- package/dist/images/bc7.js.map +1 -1
- package/dist/images/dds.d.ts +1 -1
- package/dist/images/dds.js.map +1 -1
- package/dist/images/pcx.js.map +1 -1
- package/dist/images/tga.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/levelbuilder.d.ts +3 -3
- package/dist/levelbuilder.js +1 -1
- package/dist/levelbuilder.js.map +1 -1
- package/dist/math.d.ts +6 -6
- package/dist/math.js.map +1 -1
- package/dist/normals.d.ts +5 -5
- package/dist/normals.js.map +1 -1
- package/dist/plugin.d.ts +57 -57
- package/dist/plugin.js.map +1 -1
- package/dist/registry.d.ts +14 -14
- package/dist/registry.js +13 -13
- package/dist/registry.js.map +1 -1
- package/dist/scene.d.ts +14 -14
- package/dist/scene.js.map +1 -1
- package/dist/session.d.ts +34 -34
- package/dist/session.js +33 -33
- package/dist/session.js.map +1 -1
- package/dist/skeleton.d.ts +3 -3
- package/dist/skeleton.js.map +1 -1
- package/dist/surface.d.ts +4 -4
- package/dist/surface.js.map +1 -1
- package/dist/textures.d.ts +12 -12
- package/dist/textures.js +9 -9
- package/dist/textures.js.map +1 -1
- package/dist/types.d.ts +103 -103
- package/dist/types.js.map +1 -1
- package/dist/writers/dds.d.ts +1 -1
- package/dist/writers/dds.js.map +1 -1
- package/dist/writers/obj.js.map +1 -1
- package/dist/writers/zip.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
# @gmvjs/core —
|
|
1
|
+
# @gmvjs/core — the Game Model Viewer core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.patreon.com/cw/shibisty)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The core of a library for reading game models. It has no game formats at all: only the plugin API, shared model types, a registry with format detection, a viewing session, archives, textures and helper math. Games are added with engine packages (`@gmvjs/xray-v1`, `@gmvjs/source1`…), rendering with the `@gmvjs/renderer` package.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Runs in the browser, in a Web Worker (no DOM) and in Node ≥ 20. ESM only, `sideEffects: false`.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
8
10
|
|
|
9
11
|
```sh
|
|
10
12
|
npm i @gmvjs/core
|
|
11
13
|
```
|
|
12
14
|
|
|
13
|
-
##
|
|
15
|
+
## Quick start
|
|
14
16
|
|
|
15
17
|
```ts
|
|
16
18
|
import { createRegistry, ViewerSession } from '@gmvjs/core';
|
|
@@ -28,7 +30,7 @@ await session.addArchives([meshesBsa, texturesBsa]);
|
|
|
28
30
|
await session.openEntry(session.archiveModels('chair').items[0]);
|
|
29
31
|
```
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
Parsing a file only, without a session (Node, Worker):
|
|
32
34
|
|
|
33
35
|
```ts
|
|
34
36
|
import { createRegistry } from '@gmvjs/core';
|
|
@@ -39,7 +41,7 @@ const det = await reg.detect(file); // { format, confidence, candidates }
|
|
|
39
41
|
const model = await det!.format.load(file, { files: [file], archives: [], sibling: async () => null, readPath: async () => null });
|
|
40
42
|
```
|
|
41
43
|
|
|
42
|
-
##
|
|
44
|
+
## Your own engine
|
|
43
45
|
|
|
44
46
|
```ts
|
|
45
47
|
import { defineEngine, createRegistry, binary, type Model } from '@gmvjs/core';
|
|
@@ -52,50 +54,54 @@ export const myEngine = defineEngine({
|
|
|
52
54
|
sniff: (head) => (binary.hasMagic(head, 'MYM1') ? 1 : 0),
|
|
53
55
|
load: async (main) => (await import('./parser')).parse(await main.arrayBuffer()) as Model,
|
|
54
56
|
}],
|
|
55
|
-
games: [{ id: 'my-game', group: '
|
|
57
|
+
games: [{ id: 'my-game', group: 'Games', title: 'My Game', engine: 'my', formats: ['my-mesh'] }],
|
|
56
58
|
i18n: { en: { title: 'My Engine', presets: { 'my-game': { title: 'My Game' } } } },
|
|
57
59
|
});
|
|
58
60
|
```
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
By convention an engine package exports `<id>Engine` from its root and a subpath per game: `@gmvjs/<engine>/<game>` → `{ preset, engine }`, where `engine` is the same engine limited to that game's preset (`pickGames(engine, id)`).
|
|
61
63
|
|
|
62
|
-
##
|
|
64
|
+
## What's inside
|
|
63
65
|
|
|
64
|
-
|
|
|
66
|
+
| Module | Purpose |
|
|
65
67
|
|---|---|
|
|
66
|
-
| `types` | `Model`, `Mesh`, `Motion` (`NodeMotion`, `VertexMotion`,
|
|
67
|
-
| `plugin` | `defineEngine`, `EnginePlugin`, `FormatPlugin`, `ArchivePlugin`, `ImageCodec`, `TextureDictionaryPlugin`, `MaterialResolver`, `ExporterPlugin`, `GamePreset
|
|
68
|
-
| `registry` | `Registry`, `createRegistry()`:
|
|
69
|
-
| `session` | `ViewerSession`:
|
|
70
|
-
| `textures` | `TextureLibrary`:
|
|
71
|
-
| `normals`, `surface` |
|
|
72
|
-
| `skeleton` | `NodeSkeleton
|
|
73
|
-
| `scene`, `levelbuilder` |
|
|
74
|
-
| `archives` | `createArchive`,
|
|
68
|
+
| `types` | `Model`, `Mesh`, `Motion` (`NodeMotion`, `VertexMotion`, extensible by engines), `SkeletonRuntime`, `Lightmap`, `ScreenFx`, `InstancedScene`, `Archive`, `VFile` |
|
|
69
|
+
| `plugin` | `defineEngine`, `EnginePlugin`, `FormatPlugin`, `ArchivePlugin`, `ImageCodec`, `TextureDictionaryPlugin`, `MaterialResolver`, `ExporterPlugin`, `GamePreset`, `pickGames()` |
|
|
70
|
+
| `registry` | `Registry`, `createRegistry()`: plugin collection, detection by extension, path and first bytes, preset by format and folder markers |
|
|
71
|
+
| `session` | `ViewerSession`: files, "Game folder", archives, model loading, textures, normals, PBR, external animations, `status` / `model` / `preset` events; `loadSameDirMotions` |
|
|
72
|
+
| `textures` | `TextureLibrary`: texture lookup by name across all open archives, decoding through engine codecs |
|
|
73
|
+
| `normals`, `surface` | tangents, normal map packing detection (RGB / RG / AG), combining PBR maps |
|
|
74
|
+
| `skeleton` | `NodeSkeleton`: poses for node hierarchies: keys, layers, additive tracks, grid blending |
|
|
75
|
+
| `scene`, `levelbuilder` | scene tree, ray picking, moving instances; building a level from instances by material |
|
|
76
|
+
| `archives` | `createArchive`, folder (`openFolder`), ZIP (`openZip`; PK3/PK4/IWD/PAK) |
|
|
75
77
|
| `codecs` | zlib / raw deflate (`DecompressionStream`) |
|
|
76
|
-
| `images` | DDS (DXT1–5,
|
|
77
|
-
| `writers` | ZIP, DDS (RGBA), OBJ + MTL (
|
|
78
|
-
| `i18n` | `setLocale('ru' / 'en')`, `tr()`, `presetText()`, `engineTitle()`, `exporterText()`:
|
|
79
|
-
| `diagnostics` | `buildDiagnostics`, `hexDump
|
|
80
|
-
| `binary`, `math`, `geometry` |
|
|
81
|
-
| `builtin` | `coreEngine`:
|
|
78
|
+
| `images` | DDS (DXT1–5, uncompressed), TGA, PCX; BC4/BC5 (`decodeBC45`) and BC7 (`decodeBC7`) decoders for engines |
|
|
79
|
+
| `writers` | ZIP, DDS (RGBA), OBJ + MTL (the `obj` export) |
|
|
80
|
+
| `i18n` | `setLocale('ru' / 'en')`, `tr()`, `presetText()`, `engineTitle()`, `exporterText()`: English preset texts come from `EnginePlugin.i18n` |
|
|
81
|
+
| `diagnostics` | `buildDiagnostics`, `hexDump`: a report about a failed load |
|
|
82
|
+
| `binary`, `math`, `geometry` | reading binary data, matrices and quaternions, bounds and normals |
|
|
83
|
+
| `builtin` | `coreEngine`: the ZIP archive, DDS/TGA/PCX codecs, OBJ export; `createRegistry` always attaches it |
|
|
82
84
|
|
|
83
|
-
##
|
|
85
|
+
## API notes
|
|
84
86
|
|
|
85
|
-
- **`EnginePlugin.i18n`** — `{ en?: { title?, groups?, presets?: Record<id, { title?, hint?, archiveLabel?, motionLabel? }>, exporters?: Record<id, { title?, description?, fields? }> } }`.
|
|
86
|
-
- **`MotionKinds`** —
|
|
87
|
+
- **`EnginePlugin.i18n`** — `{ en?: { title?, groups?, presets?: Record<id, { title?, hint?, archiveLabel?, motionLabel? }>, exporters?: Record<id, { title?, description?, fields? }> } }`. The registry attaches the texts together with the engine; after `setLocale('en')`, `presetText` / `engineTitle` / `exporterText` return English texts.
|
|
88
|
+
- **`MotionKinds`** — animation kinds by the `kind` field (the core has `node` and `vertex`). An engine adds its own kind by extending the interface, and narrowing by `m.kind` works in application code:
|
|
87
89
|
|
|
88
90
|
```ts
|
|
89
91
|
declare module '@gmvjs/core/types' { interface MotionKinds { xray: XrayMotion } }
|
|
90
92
|
```
|
|
91
|
-
- **`pickGames(engine, ...ids)`** —
|
|
92
|
-
- **`Registry.presetAvailable(preset)`** —
|
|
93
|
-
-
|
|
93
|
+
- **`pickGames(engine, ...ids)`** — an engine with only the chosen presets and the formats they reference (archives, codecs and export: all of them). The `@gmvjs/<engine>/<game>` subpaths are built on it. Two slices of one engine in the same registry are merged.
|
|
94
|
+
- **`Registry.presetAvailable(preset)`** — whether the registry has the preset's format. A bridge preset with another package's format (`@gmvjs/source2/gltf`, `@gmvjs/cryengine3/dae` → `@gmvjs/assimp`) is unavailable without it, but the registry does not break.
|
|
95
|
+
- **Module subpaths** `@gmvjs/core/<module>` (`binary`, `geometry`, `math`, `levelbuilder`, `images/dds`, `archives/common`…) are for engine packages.
|
|
94
96
|
|
|
95
|
-
##
|
|
97
|
+
## Dependencies
|
|
96
98
|
|
|
97
|
-
|
|
99
|
+
None.
|
|
98
100
|
|
|
99
|
-
##
|
|
101
|
+
## License
|
|
100
102
|
|
|
101
103
|
MIT
|
|
104
|
+
|
|
105
|
+
[](https://www.patreon.com/cw/shibisty)
|
|
106
|
+
|
|
107
|
+
If this project helps you, consider supporting its development on Patreon ❤️
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Archive, ArchiveEntry, BlobFile } from '../types';
|
|
2
|
-
/**
|
|
2
|
+
/** Extensions that put an entry into the texture index (path without extension). */
|
|
3
3
|
export declare const TEXTURE_RE: RegExp;
|
|
4
4
|
export interface ArchiveSpec<E extends ArchiveEntry> {
|
|
5
5
|
name: string;
|
|
@@ -7,22 +7,22 @@ export interface ArchiveSpec<E extends ArchiveEntry> {
|
|
|
7
7
|
version: number;
|
|
8
8
|
entries: E[];
|
|
9
9
|
read(entry: E): Promise<Uint8Array>;
|
|
10
|
-
/**
|
|
10
|
+
/** Custom lookup function (for example, by path suffix). */
|
|
11
11
|
lookup?(path: string): E | null;
|
|
12
12
|
byBase?: Map<string, E>;
|
|
13
|
-
/**
|
|
13
|
+
/** Extra texture keys (default: the full path without extension). */
|
|
14
14
|
textureKeys?(entry: E): string[];
|
|
15
|
-
/**
|
|
15
|
+
/** Display file name (default: the last path segment). */
|
|
16
16
|
displayName?(entry: E): string;
|
|
17
|
-
/**
|
|
17
|
+
/** On a duplicate texture key keep the first entry (default: the last one, as in game archives). */
|
|
18
18
|
keepFirstTextureKey?: boolean;
|
|
19
|
-
/**
|
|
19
|
+
/** Custom file contents (without a copy via read). */
|
|
20
20
|
fileBytes?(entry: E): Promise<ArrayBuffer>;
|
|
21
|
-
/**
|
|
21
|
+
/** Source file of the entry (folder). */
|
|
22
22
|
blobOf?(entry: E): BlobFile | null;
|
|
23
|
-
/**
|
|
23
|
+
/** Texture at an exact path that is not in the entry list. */
|
|
24
24
|
findTexture?(path: string): E | null;
|
|
25
25
|
}
|
|
26
26
|
export declare function createArchive<E extends ArchiveEntry>(spec: ArchiveSpec<E>): Archive;
|
|
27
|
-
/**
|
|
27
|
+
/** Read a byte range of the file. */
|
|
28
28
|
export declare const readRange: (file: Blob, start: number, end: number) => Promise<Uint8Array>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.js","sources":["../../src/archives/common.ts"],"sourcesContent":["//
|
|
1
|
+
{"version":3,"file":"common.js","sources":["../../src/archives/common.ts"],"sourcesContent":["// Common archive plumbing: path and texture indexes, file-like entry objects.\nimport { baseName, stripExt, toArrayBuffer } from '../binary';\nimport type { Archive, ArchiveEntry, BlobFile, VFile } from '../types';\n\n/** Extensions that put an entry into the texture index (path without extension). */\nexport const TEXTURE_RE = /\\.(dds|tga|png|jpe?g|pcx|bmp|webp|paa|pac|pct|tex|xbt)$/;\n\nexport interface ArchiveSpec<E extends ArchiveEntry> {\n name: string;\n kind: string;\n version: number;\n entries: E[];\n read(entry: E): Promise<Uint8Array>;\n /** Custom lookup function (for example, by path suffix). */\n lookup?(path: string): E | null;\n byBase?: Map<string, E>;\n /** Extra texture keys (default: the full path without extension). */\n textureKeys?(entry: E): string[];\n /** Display file name (default: the last path segment). */\n displayName?(entry: E): string;\n /** On a duplicate texture key keep the first entry (default: the last one, as in game archives). */\n keepFirstTextureKey?: boolean;\n /** Custom file contents (without a copy via read). */\n fileBytes?(entry: E): Promise<ArrayBuffer>;\n /** Source file of the entry (folder). */\n blobOf?(entry: E): BlobFile | null;\n /** Texture at an exact path that is not in the entry list. */\n findTexture?(path: string): E | null;\n}\n\nexport function createArchive<E extends ArchiveEntry>(spec: ArchiveSpec<E>): Archive {\n const index = new Map<string, ArchiveEntry>(), byKey = new Map<string, ArchiveEntry>(), files = new Map<ArchiveEntry, VFile>();\n for (const e of spec.entries) {\n index.set(e.path, e); // duplicate path (mods, appended archives): the last entry wins\n if (TEXTURE_RE.test(e.path)) {\n for (const k of spec.textureKeys ? spec.textureKeys(e) : [stripExt(e.path)]) if (!spec.keepFirstTextureKey || !byKey.has(k)) byKey.set(k, e);\n }\n }\n const arch: Archive = {\n name: spec.name,\n kind: spec.kind,\n version: spec.version,\n entries: spec.entries,\n index,\n byKey,\n byBase: spec.byBase,\n lookup: (p) => (spec.lookup ? spec.lookup(p) : (index.get(p) as E | undefined) ?? null),\n read: (e) => spec.read(e as E),\n blobOf: spec.blobOf ? (e) => spec.blobOf!(e as E) : undefined,\n findTexture: spec.findTexture ? (p) => spec.findTexture!(p) : undefined,\n fileOf(e) {\n let f = files.get(e);\n if (!f) {\n f = {\n name: spec.displayName ? spec.displayName(e as E) : baseName(e.path),\n path: e.path,\n archive: arch,\n arrayBuffer: spec.fileBytes ? () => spec.fileBytes!(e as E) : async () => toArrayBuffer(await spec.read(e as E)),\n };\n files.set(e, f);\n }\n return f;\n },\n };\n return arch;\n}\n\n/** Read a byte range of the file. */\nexport const readRange = async (file: Blob, start: number, end: number): Promise<Uint8Array> =>\n new Uint8Array(await file.slice(start, end).arrayBuffer());\n"],"names":[],"mappings":";AAKO,MAAM,aAAa;AAyBnB,SAAS,cAAsC,MAA+B;AACnF,QAAM,QAAQ,oBAAI,OAA6B,4BAAY,IAAA,GAA6B,QAAQ,oBAAI,IAAA;AACpG,aAAW,KAAK,KAAK,SAAS;AAC5B,UAAM,IAAI,EAAE,MAAM,CAAC;AACnB,QAAI,WAAW,KAAK,EAAE,IAAI,GAAG;AAC3B,iBAAW,KAAK,KAAK,cAAc,KAAK,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,EAAG,KAAI,CAAC,KAAK,uBAAuB,CAAC,MAAM,IAAI,CAAC,EAAG,OAAM,IAAI,GAAG,CAAC;AAAA,IAC7I;AAAA,EACF;AACA,QAAM,OAAgB;AAAA,IACpB,MAAM,KAAK;AAAA,IACX,MAAM,KAAK;AAAA,IACX,SAAS,KAAK;AAAA,IACd,SAAS,KAAK;AAAA,IACd;AAAA,IACA;AAAA,IACA,QAAQ,KAAK;AAAA,IACb,QAAQ,CAAC,MAAO,KAAK,SAAS,KAAK,OAAO,CAAC,IAAK,MAAM,IAAI,CAAC,KAAuB;AAAA,IAClF,MAAM,CAAC,MAAM,KAAK,KAAK,CAAM;AAAA,IAC7B,QAAQ,KAAK,SAAS,CAAC,MAAM,KAAK,OAAQ,CAAM,IAAI;AAAA,IACpD,aAAa,KAAK,cAAc,CAAC,MAAM,KAAK,YAAa,CAAC,IAAI;AAAA,IAC9D,OAAO,GAAG;AACR,UAAI,IAAI,MAAM,IAAI,CAAC;AACnB,UAAI,CAAC,GAAG;AACN,YAAI;AAAA,UACF,MAAM,KAAK,cAAc,KAAK,YAAY,CAAM,IAAI,SAAS,EAAE,IAAI;AAAA,UACnE,MAAM,EAAE;AAAA,UACR,SAAS;AAAA,UACT,aAAa,KAAK,YAAY,MAAM,KAAK,UAAW,CAAM,IAAI,YAAY,cAAc,MAAM,KAAK,KAAK,CAAM,CAAC;AAAA,QAAA;AAEjH,cAAM,IAAI,GAAG,CAAC;AAAA,MAChB;AACA,aAAO;AAAA,IACT;AAAA,EAAA;AAEF,SAAO;AACT;AAGO,MAAM,YAAY,OAAO,MAAY,OAAe,QACzD,IAAI,WAAW,MAAM,KAAK,MAAM,OAAO,GAAG,EAAE,aAAa;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { Archive, BlobFile } from '../types';
|
|
2
2
|
export declare function openFolder(files: readonly BlobFile[]): Archive;
|
|
3
|
-
/**
|
|
3
|
+
/** The same, but in chunks of `chunk` files, yielding to the browser (large game folders have hundreds of thousands of files). */
|
|
4
4
|
export declare function openFolderAsync(files: readonly BlobFile[], onProgress?: (done: number, total: number) => void, chunk?: number): Promise<Archive>;
|
package/dist/archives/folder.js
CHANGED
|
@@ -34,7 +34,7 @@ class FolderIndex {
|
|
|
34
34
|
blobOf: (e) => e.src,
|
|
35
35
|
displayName: (e) => e.src.name,
|
|
36
36
|
keepFirstTextureKey: true,
|
|
37
|
-
//
|
|
37
|
+
// keys for every path suffix: a/b/c, b/c, c — not tied to the folder root
|
|
38
38
|
textureKeys: (e) => {
|
|
39
39
|
const parts = stripExt(e.path).split("/");
|
|
40
40
|
return TEXTURE_RE.test(e.path) ? parts.map((_, i) => parts.slice(i).join("/")) : [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"folder.js","sources":["../../src/archives/folder.ts"],"sourcesContent":["//
|
|
1
|
+
{"version":3,"file":"folder.js","sources":["../../src/archives/folder.ts"],"sourcesContent":["// \"Game folder\": loose files of the selected folder as a virtual archive (lookup by path suffix, without reading contents).\nimport { stripExt } from '../binary';\nimport type { Archive, ArchiveEntry, BlobFile } from '../types';\nimport { createArchive, TEXTURE_RE } from './common';\n\ninterface FolderEntry extends ArchiveEntry { src: BlobFile }\n\nconst relPath = (f: BlobFile): string => (f.webkitRelativePath || f.name).replace(/\\\\/g, '/');\n\n/** Folder index: paths, full and by suffix (built in chunks, see openFolderAsync). */\nclass FolderIndex {\n entries: FolderEntry[] = []; full = new Map<string, FolderEntry>(); byBase = new Map<string, FolderEntry>(); bySuffix = new Map<string, FolderEntry>(); root = '';\n add(f: BlobFile): void {\n const rel = relPath(f), path = rel.toLowerCase();\n if (!this.root && rel.includes('/')) this.root = rel.split('/')[0]!;\n const e: FolderEntry = { path, size: f.size, src: f };\n this.entries.push(e);\n if (!this.full.has(path)) this.full.set(path, e);\n const sp = path.split('/'), base = sp[sp.length - 1]!;\n if (!this.byBase.has(base)) this.byBase.set(base, e);\n for (let i = 0; i < sp.length; i++) { const k = sp.slice(i).join('/'); if (!this.bySuffix.has(k)) this.bySuffix.set(k, e); }\n }\n archive(): Archive {\n const { full, bySuffix } = this;\n return createArchive<FolderEntry>({\n name: this.root || 'папка',\n kind: 'folder',\n version: 0,\n entries: this.entries,\n byBase: this.byBase,\n lookup: (p) => full.get(p) ?? bySuffix.get(p) ?? null,\n read: async (e) => new Uint8Array(await e.src.arrayBuffer()),\n fileBytes: (e) => e.src.arrayBuffer(),\n blobOf: (e) => e.src,\n displayName: (e) => e.src.name,\n keepFirstTextureKey: true,\n // keys for every path suffix: a/b/c, b/c, c — not tied to the folder root\n textureKeys: (e) => {\n const parts = stripExt(e.path).split('/');\n return TEXTURE_RE.test(e.path) ? parts.map((_, i) => parts.slice(i).join('/')) : [];\n },\n });\n }\n}\n\nexport function openFolder(files: readonly BlobFile[]): Archive {\n const ix = new FolderIndex();\n for (const f of files) ix.add(f);\n return ix.archive();\n}\n\n/** The same, but in chunks of `chunk` files, yielding to the browser (large game folders have hundreds of thousands of files). */\nexport async function openFolderAsync(files: readonly BlobFile[], onProgress?: (done: number, total: number) => void, chunk = 5000): Promise<Archive> {\n const ix = new FolderIndex();\n for (let i = 0; i < files.length; i += chunk) {\n onProgress?.(i, files.length);\n await new Promise((r) => setTimeout(r, 0));\n for (let k = i; k < Math.min(files.length, i + chunk); k++) ix.add(files[k]!);\n }\n onProgress?.(files.length, files.length);\n await new Promise((r) => setTimeout(r, 0));\n return ix.archive();\n}\n"],"names":[],"mappings":";;AAOA,MAAM,UAAU,CAAC,OAAyB,EAAE,sBAAsB,EAAE,MAAM,QAAQ,OAAO,GAAG;AAG5F,MAAM,YAAY;AAAA,EAChB,UAAyB,CAAA;AAAA,EAAI,2BAAW,IAAA;AAAA,EAA4B,6BAAa,IAAA;AAAA,EAA4B,+BAAe,IAAA;AAAA,EAA4B,OAAO;AAAA,EAC/J,IAAI,GAAmB;AACrB,UAAM,MAAM,QAAQ,CAAC,GAAG,OAAO,IAAI,YAAA;AACnC,QAAI,CAAC,KAAK,QAAQ,IAAI,SAAS,GAAG,EAAG,MAAK,OAAO,IAAI,MAAM,GAAG,EAAE,CAAC;AACjE,UAAM,IAAiB,EAAE,MAAM,MAAM,EAAE,MAAM,KAAK,EAAA;AAClD,SAAK,QAAQ,KAAK,CAAC;AACnB,QAAI,CAAC,KAAK,KAAK,IAAI,IAAI,EAAG,MAAK,KAAK,IAAI,MAAM,CAAC;AAC/C,UAAM,KAAK,KAAK,MAAM,GAAG,GAAG,OAAO,GAAG,GAAG,SAAS,CAAC;AACnD,QAAI,CAAC,KAAK,OAAO,IAAI,IAAI,EAAG,MAAK,OAAO,IAAI,MAAM,CAAC;AACnD,aAAS,IAAI,GAAG,IAAI,GAAG,QAAQ,KAAK;AAAE,YAAM,IAAI,GAAG,MAAM,CAAC,EAAE,KAAK,GAAG;AAAG,UAAI,CAAC,KAAK,SAAS,IAAI,CAAC,EAAG,MAAK,SAAS,IAAI,GAAG,CAAC;AAAA,IAAG;AAAA,EAC7H;AAAA,EACA,UAAmB;AACjB,UAAM,EAAE,MAAM,SAAA,IAAa;AAC3B,WAAO,cAA2B;AAAA,MAChC,MAAM,KAAK,QAAQ;AAAA,MACnB,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS,KAAK;AAAA,MACd,QAAQ,KAAK;AAAA,MACb,QAAQ,CAAC,MAAM,KAAK,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK;AAAA,MACjD,MAAM,OAAO,MAAM,IAAI,WAAW,MAAM,EAAE,IAAI,aAAa;AAAA,MAC3D,WAAW,CAAC,MAAM,EAAE,IAAI,YAAA;AAAA,MACxB,QAAQ,CAAC,MAAM,EAAE;AAAA,MACjB,aAAa,CAAC,MAAM,EAAE,IAAI;AAAA,MAC1B,qBAAqB;AAAA;AAAA,MAErB,aAAa,CAAC,MAAM;AAClB,cAAM,QAAQ,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG;AACxC,eAAO,WAAW,KAAK,EAAE,IAAI,IAAI,MAAM,IAAI,CAAC,GAAG,MAAM,MAAM,MAAM,CAAC,EAAE,KAAK,GAAG,CAAC,IAAI,CAAA;AAAA,MACnF;AAAA,IAAA,CACD;AAAA,EACH;AACF;AAEO,SAAS,WAAW,OAAqC;AAC9D,QAAM,KAAK,IAAI,YAAA;AACf,aAAW,KAAK,MAAO,IAAG,IAAI,CAAC;AAC/B,SAAO,GAAG,QAAA;AACZ;AAGA,eAAsB,gBAAgB,OAA4B,YAAoD,QAAQ,KAAwB;AACpJ,QAAM,KAAK,IAAI,YAAA;AACf,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,OAAO;AAC5C,iBAAa,GAAG,MAAM,MAAM;AAC5B,UAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC;AACzC,aAAS,IAAI,GAAG,IAAI,KAAK,IAAI,MAAM,QAAQ,IAAI,KAAK,GAAG,IAAK,IAAG,IAAI,MAAM,CAAC,CAAE;AAAA,EAC9E;AACA,eAAa,MAAM,QAAQ,MAAM,MAAM;AACvC,QAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC;AACzC,SAAO,GAAG,QAAA;AACZ;"}
|
package/dist/archives/zip.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zip.js","sources":["../../src/archives/zip.ts"],"sourcesContent":["// ZIP (pk3/pk4
|
|
1
|
+
{"version":3,"file":"zip.js","sources":["../../src/archives/zip.ts"],"sourcesContent":["// ZIP (id Tech pk3/pk4, CryEngine .pak, .zip): the central directory is read, data on demand (store / deflate).\nimport { normPath } from '../binary';\nimport { inflateRaw } from '../codecs/zlib';\nimport type { Archive, ArchiveEntry, BlobFile } from '../types';\nimport { createArchive, readRange } from './common';\n\ninterface ZipEntry extends ArchiveEntry { method: number; comp: number; off: number }\n\nexport async function openZip(file: BlobFile): Promise<Archive> {\n const tailLen = Math.min(file.size, 65557), tail = await readRange(file, file.size - tailLen, file.size);\n const tdv = new DataView(tail.buffer, tail.byteOffset, tail.byteLength);\n let e = -1;\n for (let i = tail.length - 22; i >= 0; i--) if (tdv.getUint32(i, true) === 0x06054b50) { e = i; break; }\n if (e < 0) throw new Error(`${file.name}: не ZIP (нет конца каталога)`);\n const cdSize = tdv.getUint32(e + 12, true), cdOff = tdv.getUint32(e + 16, true);\n if (cdOff === 0xffffffff) throw new Error(`${file.name}: ZIP64 не поддерживается`);\n const u8 = await readRange(file, cdOff, cdOff + cdSize), dv = new DataView(u8.buffer, u8.byteOffset, u8.byteLength), entries: ZipEntry[] = [];\n for (let p = 0; p + 46 <= u8.length && dv.getUint32(p, true) === 0x02014b50;) {\n const method = dv.getUint16(p + 10, true), comp = dv.getUint32(p + 20, true), size = dv.getUint32(p + 24, true);\n const nl = dv.getUint16(p + 28, true), xl = dv.getUint16(p + 30, true), cl = dv.getUint16(p + 32, true), off = dv.getUint32(p + 42, true);\n let n = '';\n for (let i = 0; i < nl; i++) n += String.fromCharCode(u8[p + 46 + i]!);\n if (!n.endsWith('/')) entries.push({ path: normPath(n), method, comp, size, off });\n p += 46 + nl + xl + cl;\n }\n return createArchive<ZipEntry>({\n name: file.name,\n kind: 'zip',\n version: 0,\n entries,\n async read(en) {\n const lh = new DataView(await file.slice(en.off, en.off + 30).arrayBuffer());\n const start = en.off + 30 + lh.getUint16(26, true) + lh.getUint16(28, true);\n const data = await readRange(file, start, start + en.comp);\n if (en.method === 0) return data;\n if (en.method === 8) return inflateRaw(data);\n throw new Error('метод сжатия ZIP ' + en.method + ' не поддерживается');\n },\n });\n}\n"],"names":[],"mappings":";;;AAQA,eAAsB,QAAQ,MAAkC;AAC9D,QAAM,UAAU,KAAK,IAAI,KAAK,MAAM,KAAK,GAAG,OAAO,MAAM,UAAU,MAAM,KAAK,OAAO,SAAS,KAAK,IAAI;AACvG,QAAM,MAAM,IAAI,SAAS,KAAK,QAAQ,KAAK,YAAY,KAAK,UAAU;AACtE,MAAI,IAAI;AACR,WAAS,IAAI,KAAK,SAAS,IAAI,KAAK,GAAG,IAAK,KAAI,IAAI,UAAU,GAAG,IAAI,MAAM,WAAY;AAAE,QAAI;AAAG;AAAA,EAAO;AACvG,MAAI,IAAI,EAAG,OAAM,IAAI,MAAM,GAAG,KAAK,IAAI,+BAA+B;AACtE,QAAM,SAAS,IAAI,UAAU,IAAI,IAAI,IAAI,GAAG,QAAQ,IAAI,UAAU,IAAI,IAAI,IAAI;AAC9E,MAAI,UAAU,WAAY,OAAM,IAAI,MAAM,GAAG,KAAK,IAAI,2BAA2B;AACjF,QAAM,KAAK,MAAM,UAAU,MAAM,OAAO,QAAQ,MAAM,GAAG,KAAK,IAAI,SAAS,GAAG,QAAQ,GAAG,YAAY,GAAG,UAAU,GAAG,UAAsB,CAAA;AAC3I,WAAS,IAAI,GAAG,IAAI,MAAM,GAAG,UAAU,GAAG,UAAU,GAAG,IAAI,MAAM,YAAa;AAC5E,UAAM,SAAS,GAAG,UAAU,IAAI,IAAI,IAAI,GAAG,OAAO,GAAG,UAAU,IAAI,IAAI,IAAI,GAAG,OAAO,GAAG,UAAU,IAAI,IAAI,IAAI;AAC9G,UAAM,KAAK,GAAG,UAAU,IAAI,IAAI,IAAI,GAAG,KAAK,GAAG,UAAU,IAAI,IAAI,IAAI,GAAG,KAAK,GAAG,UAAU,IAAI,IAAI,IAAI,GAAG,MAAM,GAAG,UAAU,IAAI,IAAI,IAAI;AACxI,QAAI,IAAI;AACR,aAAS,IAAI,GAAG,IAAI,IAAI,IAAK,MAAK,OAAO,aAAa,GAAG,IAAI,KAAK,CAAC,CAAE;AACrE,QAAI,CAAC,EAAE,SAAS,GAAG,WAAW,KAAK,EAAE,MAAM,SAAS,CAAC,GAAG,QAAQ,MAAM,MAAM,KAAK;AACjF,SAAK,KAAK,KAAK,KAAK;AAAA,EACtB;AACA,SAAO,cAAwB;AAAA,IAC7B,MAAM,KAAK;AAAA,IACX,MAAM;AAAA,IACN,SAAS;AAAA,IACT;AAAA,IACA,MAAM,KAAK,IAAI;AACb,YAAM,KAAK,IAAI,SAAS,MAAM,KAAK,MAAM,GAAG,KAAK,GAAG,MAAM,EAAE,EAAE,aAAa;AAC3E,YAAM,QAAQ,GAAG,MAAM,KAAK,GAAG,UAAU,IAAI,IAAI,IAAI,GAAG,UAAU,IAAI,IAAI;AAC1E,YAAM,OAAO,MAAM,UAAU,MAAM,OAAO,QAAQ,GAAG,IAAI;AACzD,UAAI,GAAG,WAAW,EAAG,QAAO;AAC5B,UAAI,GAAG,WAAW,EAAG,QAAO,WAAW,IAAI;AAC3C,YAAM,IAAI,MAAM,sBAAsB,GAAG,SAAS,oBAAoB;AAAA,IACxE;AAAA,EAAA,CACD;AACH;"}
|
package/dist/binary.d.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
/** ASCII
|
|
1
|
+
/** ASCII string up to a zero byte or n bytes. */
|
|
2
2
|
export declare function cstr(u8: Uint8Array, start: number, maxLen: number): string;
|
|
3
|
-
/** ASCII
|
|
3
|
+
/** ASCII string in [a, b) up to the first zero byte. */
|
|
4
4
|
export declare function cstrRange(u8: Uint8Array, a: number, b: number): string;
|
|
5
|
-
/** ASCII
|
|
5
|
+
/** ASCII in [a, b) without stopping at zero bytes. */
|
|
6
6
|
export declare function ascii(u8: Uint8Array, a: number, b: number): string;
|
|
7
|
-
/**
|
|
7
|
+
/** Four characters as a little-endian uint32. */
|
|
8
8
|
export declare const fourCC: (s: string) => number;
|
|
9
9
|
/** IEEE 754 half -> number. */
|
|
10
10
|
export declare function half(x: number): number;
|
|
11
|
-
/**
|
|
11
|
+
/** Copy of a Uint8Array's contents as a separate ArrayBuffer. */
|
|
12
12
|
export declare function toArrayBuffer(u8: Uint8Array): ArrayBuffer;
|
|
13
|
-
/**
|
|
13
|
+
/** First n bytes of a file (for format detection). */
|
|
14
14
|
export declare function readHead(file: Blob | {
|
|
15
15
|
arrayBuffer(): Promise<ArrayBuffer>;
|
|
16
16
|
}, n?: number): Promise<Uint8Array>;
|
|
17
|
-
/**
|
|
17
|
+
/** Whether the first bytes match an ASCII signature. */
|
|
18
18
|
export declare function hasMagic(head: Uint8Array, magic: string, offset?: number): boolean;
|
|
19
19
|
export declare const u32le: (u8: Uint8Array, o: number) => number;
|
|
20
20
|
export declare const extOf: (name: string) => string;
|
package/dist/binary.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"binary.js","sources":["../src/binary.ts"],"sourcesContent":["//
|
|
1
|
+
{"version":3,"file":"binary.js","sources":["../src/binary.ts"],"sourcesContent":["// Small helpers for reading binary data.\n\n/** ASCII string up to a zero byte or n bytes. */\nexport function cstr(u8: Uint8Array, start: number, maxLen: number): string {\n let s = '';\n for (let i = 0; i < maxLen && u8[start + i]; i++) s += String.fromCharCode(u8[start + i]!);\n return s;\n}\n\n/** ASCII string in [a, b) up to the first zero byte. */\nexport function cstrRange(u8: Uint8Array, a: number, b: number): string {\n let s = '';\n for (let i = a; i < b && u8[i]; i++) s += String.fromCharCode(u8[i]!);\n return s;\n}\n\n/** ASCII in [a, b) without stopping at zero bytes. */\nexport function ascii(u8: Uint8Array, a: number, b: number): string {\n let s = '';\n for (let i = a; i < b; i++) s += String.fromCharCode(u8[i]!);\n return s;\n}\n\n/** Four characters as a little-endian uint32. */\nexport const fourCC = (s: string): number =>\n (s.charCodeAt(0) | (s.charCodeAt(1) << 8) | (s.charCodeAt(2) << 16) | (s.charCodeAt(3) << 24)) >>> 0;\n\n/** IEEE 754 half -> number. */\nexport function half(x: number): number {\n const s = x & 0x8000 ? -1 : 1, e = (x >> 10) & 31, f = x & 1023;\n if (e === 0) return s * 5.960464477539063e-8 * f;\n if (e === 31) return f ? NaN : s * Infinity;\n return s * 2 ** (e - 15) * (1 + f / 1024);\n}\n\n/** Copy of a Uint8Array's contents as a separate ArrayBuffer. */\nexport function toArrayBuffer(u8: Uint8Array): ArrayBuffer {\n return u8.buffer.slice(u8.byteOffset, u8.byteOffset + u8.byteLength) as ArrayBuffer;\n}\n\n/** First n bytes of a file (for format detection). */\nexport async function readHead(file: Blob | { arrayBuffer(): Promise<ArrayBuffer> }, n = 64): Promise<Uint8Array> {\n if (typeof Blob !== 'undefined' && file instanceof Blob) return new Uint8Array(await file.slice(0, n).arrayBuffer());\n const buf = await file.arrayBuffer();\n return new Uint8Array(buf, 0, Math.min(n, buf.byteLength));\n}\n\n/** Whether the first bytes match an ASCII signature. */\nexport function hasMagic(head: Uint8Array, magic: string, offset = 0): boolean {\n if (head.length < offset + magic.length) return false;\n for (let i = 0; i < magic.length; i++) if (head[offset + i] !== magic.charCodeAt(i)) return false;\n return true;\n}\n\nexport const u32le = (u8: Uint8Array, o: number): number =>\n u8.length >= o + 4 ? (u8[o]! | (u8[o + 1]! << 8) | (u8[o + 2]! << 16) | (u8[o + 3]! << 24)) >>> 0 : -1;\n\nexport const extOf = (name: string): string => {\n const i = name.lastIndexOf('.');\n return i < 0 ? '' : name.slice(i + 1).toLowerCase();\n};\nexport const normPath = (p: string): string => p.replace(/\\\\/g, '/').toLowerCase();\nexport const stripExt = (p: string): string => p.replace(/\\.[^./]+$/, '');\nexport const baseName = (p: string): string => p.replace(/\\\\/g, '/').split('/').pop() ?? p;\n\nconst decoder = new TextDecoder();\nexport const decodeText = (data: Uint8Array | ArrayBuffer): string => decoder.decode(data);\n"],"names":[],"mappings":"AAGO,SAAS,KAAK,IAAgB,OAAe,QAAwB;AAC1E,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,UAAU,GAAG,QAAQ,CAAC,GAAG,UAAU,OAAO,aAAa,GAAG,QAAQ,CAAC,CAAE;AACzF,SAAO;AACT;AAGO,SAAS,UAAU,IAAgB,GAAW,GAAmB;AACtE,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,KAAK,GAAG,CAAC,GAAG,IAAK,MAAK,OAAO,aAAa,GAAG,CAAC,CAAE;AACpE,SAAO;AACT;AAGO,SAAS,MAAM,IAAgB,GAAW,GAAmB;AAClE,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,GAAG,UAAU,OAAO,aAAa,GAAG,CAAC,CAAE;AAC3D,SAAO;AACT;AAGO,MAAM,SAAS,CAAC,OACpB,EAAE,WAAW,CAAC,IAAK,EAAE,WAAW,CAAC,KAAK,IAAM,EAAE,WAAW,CAAC,KAAK,KAAO,EAAE,WAAW,CAAC,KAAK,QAAS;AAG9F,SAAS,KAAK,GAAmB;AACtC,QAAM,IAAI,IAAI,QAAS,KAAK,GAAG,IAAK,KAAK,KAAM,IAAI,IAAI,IAAI;AAC3D,MAAI,MAAM,EAAG,QAAO,IAAI,uBAAuB;AAC/C,MAAI,MAAM,GAAI,QAAO,IAAI,MAAM,IAAI;AACnC,SAAO,IAAI,MAAM,IAAI,OAAO,IAAI,IAAI;AACtC;AAGO,SAAS,cAAc,IAA6B;AACzD,SAAO,GAAG,OAAO,MAAM,GAAG,YAAY,GAAG,aAAa,GAAG,UAAU;AACrE;AAGA,eAAsB,SAAS,MAAsD,IAAI,IAAyB;AAChH,MAAI,OAAO,SAAS,eAAe,gBAAgB,KAAM,QAAO,IAAI,WAAW,MAAM,KAAK,MAAM,GAAG,CAAC,EAAE,aAAa;AACnH,QAAM,MAAM,MAAM,KAAK,YAAA;AACvB,SAAO,IAAI,WAAW,KAAK,GAAG,KAAK,IAAI,GAAG,IAAI,UAAU,CAAC;AAC3D;AAGO,SAAS,SAAS,MAAkB,OAAe,SAAS,GAAY;AAC7E,MAAI,KAAK,SAAS,SAAS,MAAM,OAAQ,QAAO;AAChD,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,IAAK,KAAI,KAAK,SAAS,CAAC,MAAM,MAAM,WAAW,CAAC,EAAG,QAAO;AAC5F,SAAO;AACT;AAEO,MAAM,QAAQ,CAAC,IAAgB,MACpC,GAAG,UAAU,IAAI,KAAK,GAAG,CAAC,IAAM,GAAG,IAAI,CAAC,KAAM,IAAM,GAAG,IAAI,CAAC,KAAM,KAAO,GAAG,IAAI,CAAC,KAAM,QAAS,IAAI;AAE/F,MAAM,QAAQ,CAAC,SAAyB;AAC7C,QAAM,IAAI,KAAK,YAAY,GAAG;AAC9B,SAAO,IAAI,IAAI,KAAK,KAAK,MAAM,IAAI,CAAC,EAAE,YAAA;AACxC;AACO,MAAM,WAAW,CAAC,MAAsB,EAAE,QAAQ,OAAO,GAAG,EAAE,YAAA;AAC9D,MAAM,WAAW,CAAC,MAAsB,EAAE,QAAQ,aAAa,EAAE;AACjE,MAAM,WAAW,CAAC,MAAsB,EAAE,QAAQ,OAAO,GAAG,EAAE,MAAM,GAAG,EAAE,IAAA,KAAS;AAEzF,MAAM,UAAU,IAAI,YAAA;AACb,MAAM,aAAa,CAAC,SAA2C,QAAQ,OAAO,IAAI;"}
|
package/dist/builtin-en.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builtin-en.js","sources":["../src/builtin-en.ts"],"sourcesContent":["//
|
|
1
|
+
{"version":3,"file":"builtin-en.js","sources":["../src/builtin-en.ts"],"sourcesContent":["// English texts of the core engine (Russian ones live in the plugins themselves).\nimport type { EngineTexts } from './plugin';\n\nexport const EN: EngineTexts = {\n \"title\": \"Common formats\",\n \"exporters\": {\n \"obj\": {\n \"title\": \"OBJ + MTL (Blender, 3ds Max, any 3D editor)\",\n \"description\": \"Bind-pose geometry, UVs, normals and materials with DDS textures — one ZIP. OBJ does not store bones or animations.\",\n \"fields\": {\n \"name\": {\n \"label\": \"File name\"\n },\n \"scale\": {\n \"label\": \"Scale\"\n }\n }\n }\n },\n \"groups\": {\n \"Игры\": \"Games\"\n }\n};\n"],"names":[],"mappings":"AAGO,MAAM,KAAkB;AAAA,EAC7B,SAAS;AAAA,EACT,aAAa;AAAA,IACX,OAAO;AAAA,MACL,SAAS;AAAA,MACT,eAAe;AAAA,MACf,UAAU;AAAA,QACR,QAAQ;AAAA,UACN,SAAS;AAAA,QAAA;AAAA,QAEX,SAAS;AAAA,UACP,SAAS;AAAA,QAAA;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAAA,EAEF,UAAU;AAAA,IACR,QAAQ;AAAA,EAAA;AAEZ;"}
|
package/dist/builtin.js
CHANGED
|
@@ -16,7 +16,7 @@ const coreEngine = defineEngine({
|
|
|
16
16
|
engine: "core",
|
|
17
17
|
extensions: ["zip", "pk3", "pk4", "iwd", "pak"],
|
|
18
18
|
sniff: (h) => hasMagic(h, "PK") || hasMagic(h, "PK") ? 1 : 0,
|
|
19
|
-
// .pak
|
|
19
|
+
// CryEngine .pak files (Far Cry, Crysis, KCD) open by themselves in the "Game folder": only the ZIP central directory is read
|
|
20
20
|
openFromFolder: (_h, name) => /\.pak$/i.test(name),
|
|
21
21
|
open: (file) => openZip(file)
|
|
22
22
|
}
|
package/dist/builtin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builtin.js","sources":["../src/builtin.ts"],"sourcesContent":["//
|
|
1
|
+
{"version":3,"file":"builtin.js","sources":["../src/builtin.ts"],"sourcesContent":["// The core's built-in \"engine\": shared archives (ZIP) and codecs (DDS, TGA, PCX) used by many games.\nimport { hasMagic } from './binary';\nimport { openZip } from './archives/zip';\nimport { decodeDDS } from './images/dds';\nimport { decodePCX } from './images/pcx';\nimport { decodeTGA } from './images/tga';\nimport { defineEngine } from './plugin';\nimport { EN } from './builtin-en';\n\nexport const coreEngine = defineEngine({\n id: 'core',\n title: 'Общие форматы',\n i18n: { en: EN },\n archives: [\n {\n id: 'zip',\n title: 'ZIP (pk3, pk4, iwd, CryEngine .pak)',\n engine: 'core',\n extensions: ['zip', 'pk3', 'pk4', 'iwd', 'pak'],\n sniff: (h) => (hasMagic(h, 'PK\\x03\\x04') || hasMagic(h, 'PK\\x05\\x06') ? 1 : 0),\n // CryEngine .pak files (Far Cry, Crysis, KCD) open by themselves in the \"Game folder\": only the ZIP central directory is read\n openFromFolder: (_h, name) => /\\.pak$/i.test(name),\n open: (file) => openZip(file),\n },\n ],\n exporters: [\n {\n id: 'obj',\n title: 'OBJ + MTL (Blender, 3ds Max, любой 3D-редактор)',\n target: 'obj',\n description: 'Геометрия в bind-позе, UV, нормали и материалы с текстурами DDS — одним ZIP. Кости и анимации OBJ не хранит.',\n fields: [\n { id: 'name', label: 'Имя файла', type: 'text', default: (_m, base) => base },\n { id: 'scale', label: 'Масштаб', type: 'number', min: 0, step: 0.01, default: 1 },\n ],\n run: async (model, values, ctx) => (await import('./writers/obj')).exportObj(model, values, ctx),\n },\n ],\n codecs: [\n { id: 'dds', extensions: ['dds'], decode: (b) => decodeDDS(b) },\n { id: 'tga', extensions: ['tga'], decode: (b) => decodeTGA(b) },\n { id: 'pcx', extensions: ['pcx'], decode: (b) => decodePCX(b) },\n ],\n});\n"],"names":[],"mappings":";;;;;;;AASO,MAAM,aAAa,aAAa;AAAA,EACrC,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,MAAM,EAAE,IAAI,GAAA;AAAA,EACZ,UAAU;AAAA,IACR;AAAA,MACE,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,YAAY,CAAC,OAAO,OAAO,OAAO,OAAO,KAAK;AAAA,MAC9C,OAAO,CAAC,MAAO,SAAS,GAAG,MAAY,KAAK,SAAS,GAAG,MAAY,IAAI,IAAI;AAAA;AAAA,MAE5E,gBAAgB,CAAC,IAAI,SAAS,UAAU,KAAK,IAAI;AAAA,MACjD,MAAM,CAAC,SAAS,QAAQ,IAAI;AAAA,IAAA;AAAA,EAC9B;AAAA,EAEF,WAAW;AAAA,IACT;AAAA,MACE,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,QAAQ;AAAA,QACN,EAAE,IAAI,QAAQ,OAAO,aAAa,MAAM,QAAQ,SAAS,CAAC,IAAI,SAAS,KAAA;AAAA,QACvE,EAAE,IAAI,SAAS,OAAO,WAAW,MAAM,UAAU,KAAK,GAAG,MAAM,MAAM,SAAS,EAAA;AAAA,MAAE;AAAA,MAElF,KAAK,OAAO,OAAO,QAAQ,SAAS,MAAM,OAAO,kBAAe,GAAG,UAAU,OAAO,QAAQ,GAAG;AAAA,IAAA;AAAA,EACjG;AAAA,EAEF,QAAQ;AAAA,IACN,EAAE,IAAI,OAAO,YAAY,CAAC,KAAK,GAAG,QAAQ,CAAC,MAAM,UAAU,CAAC,EAAA;AAAA,IAC5D,EAAE,IAAI,OAAO,YAAY,CAAC,KAAK,GAAG,QAAQ,CAAC,MAAM,UAAU,CAAC,EAAA;AAAA,IAC5D,EAAE,IAAI,OAAO,YAAY,CAAC,KAAK,GAAG,QAAQ,CAAC,MAAM,UAAU,CAAC,EAAA;AAAA,EAAE;AAElE,CAAC;"}
|
package/dist/codecs/zlib.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** zlib
|
|
1
|
+
/** zlib stream (with a 78 xx header). */
|
|
2
2
|
export declare const inflateZlib: (u8: Uint8Array) => Promise<Uint8Array>;
|
|
3
|
-
/**
|
|
3
|
+
/** Raw deflate (ZIP, method 8). */
|
|
4
4
|
export declare const inflateRaw: (u8: Uint8Array) => Promise<Uint8Array>;
|
package/dist/codecs/zlib.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zlib.js","sources":["../../src/codecs/zlib.ts"],"sourcesContent":["//
|
|
1
|
+
{"version":3,"file":"zlib.js","sources":["../../src/codecs/zlib.ts"],"sourcesContent":["// zlib / raw deflate decompression via the standard DecompressionStream (browsers, Node 18+).\nasync function run(u8: Uint8Array, format: 'deflate' | 'deflate-raw'): Promise<Uint8Array> {\n const stream = new Blob([u8 as BlobPart]).stream().pipeThrough(new DecompressionStream(format));\n return new Uint8Array(await new Response(stream).arrayBuffer());\n}\n/** zlib stream (with a 78 xx header). */\nexport const inflateZlib = (u8: Uint8Array): Promise<Uint8Array> => run(u8, 'deflate');\n/** Raw deflate (ZIP, method 8). */\nexport const inflateRaw = (u8: Uint8Array): Promise<Uint8Array> => run(u8, 'deflate-raw');\n"],"names":[],"mappings":"AACA,eAAe,IAAI,IAAgB,QAAwD;AACzF,QAAM,SAAS,IAAI,KAAK,CAAC,EAAc,CAAC,EAAE,OAAA,EAAS,YAAY,IAAI,oBAAoB,MAAM,CAAC;AAC9F,SAAO,IAAI,WAAW,MAAM,IAAI,SAAS,MAAM,EAAE,aAAa;AAChE;AAEO,MAAM,cAAc,CAAC,OAAwC,IAAI,IAAI,SAAS;AAE9E,MAAM,aAAa,CAAC,OAAwC,IAAI,IAAI,aAAa;"}
|
package/dist/diagnostics.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diagnostics.js","sources":["../src/diagnostics.ts"],"sourcesContent":["//
|
|
1
|
+
{"version":3,"file":"diagnostics.js","sources":["../src/diagnostics.ts"],"sourcesContent":["// Diagnostics text for an error report: file, format, signature and a hex dump of the header.\nimport { readHead } from './binary';\nimport type { BlobFile, VFile } from './types';\n\nexport function hexDump(u8: Uint8Array, width = 16): string {\n const lines: string[] = [];\n for (let o = 0; o < u8.length; o += width) {\n const row = u8.subarray(o, o + width);\n const hex = Array.from(row, (b) => b.toString(16).padStart(2, '0')).join(' ').padEnd(width * 3 - 1);\n const txt = Array.from(row, (b) => (b >= 0x20 && b < 0x7f ? String.fromCharCode(b) : '.')).join('');\n lines.push(`${o.toString(16).padStart(4, '0')} ${hex} ${txt}`);\n }\n return lines.join('\\n');\n}\n\nexport interface DiagnosticsInput {\n file: VFile | BlobFile;\n format?: string;\n preset?: string;\n error: string;\n appVersion?: string;\n}\n\nexport async function buildDiagnostics(d: DiagnosticsInput, headBytes = 64): Promise<string> {\n let head: Uint8Array = new Uint8Array(0);\n try { head = await readHead(d.file, headBytes); } catch { /* file unavailable */ }\n const size = typeof Blob !== 'undefined' && d.file instanceof Blob ? d.file.size : undefined;\n return [\n `gmv ${d.appVersion ?? ''}`.trim(),\n `Файл: ${('path' in d.file && d.file.path) || d.file.name}${size !== undefined ? ` (${size} байт)` : ''}`,\n d.preset ? `Пресет: ${d.preset}` : '',\n d.format ? `Формат: ${d.format}` : '',\n `Ошибка: ${d.error}`,\n `Сигнатура: ${Array.from(head.subarray(0, 4), (b) => (b >= 0x20 && b < 0x7f ? String.fromCharCode(b) : '.')).join('')}`,\n `Заголовок (${head.length} байт):`,\n hexDump(head),\n ].filter(Boolean).join('\\n');\n}\n"],"names":[],"mappings":";AAIO,SAAS,QAAQ,IAAgB,QAAQ,IAAY;AAC1D,QAAM,QAAkB,CAAA;AACxB,WAAS,IAAI,GAAG,IAAI,GAAG,QAAQ,KAAK,OAAO;AACzC,UAAM,MAAM,GAAG,SAAS,GAAG,IAAI,KAAK;AACpC,UAAM,MAAM,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,GAAG,EAAE,OAAO,QAAQ,IAAI,CAAC;AAClG,UAAM,MAAM,MAAM,KAAK,KAAK,CAAC,MAAO,KAAK,MAAQ,IAAI,MAAO,OAAO,aAAa,CAAC,IAAI,GAAI,EAAE,KAAK,EAAE;AAClG,UAAM,KAAK,GAAG,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,KAAK,GAAG,KAAK,GAAG,EAAE;AAAA,EACjE;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAUA,eAAsB,iBAAiB,GAAqB,YAAY,IAAqB;AAC3F,MAAI,OAAmB,IAAI,WAAW,CAAC;AACvC,MAAI;AAAE,WAAO,MAAM,SAAS,EAAE,MAAM,SAAS;AAAA,EAAG,QAAQ;AAAA,EAAyB;AACjF,QAAM,OAAO,OAAO,SAAS,eAAe,EAAE,gBAAgB,OAAO,EAAE,KAAK,OAAO;AACnF,SAAO;AAAA,IACL,OAAO,EAAE,cAAc,EAAE,GAAG,KAAA;AAAA,IAC5B,SAAU,UAAU,EAAE,QAAQ,EAAE,KAAK,QAAS,EAAE,KAAK,IAAI,GAAG,SAAS,SAAY,KAAK,IAAI,WAAW,EAAE;AAAA,IACvG,EAAE,SAAS,WAAW,EAAE,MAAM,KAAK;AAAA,IACnC,EAAE,SAAS,WAAW,EAAE,MAAM,KAAK;AAAA,IACnC,WAAW,EAAE,KAAK;AAAA,IAClB,cAAc,MAAM,KAAK,KAAK,SAAS,GAAG,CAAC,GAAG,CAAC,MAAO,KAAK,MAAQ,IAAI,MAAO,OAAO,aAAa,CAAC,IAAI,GAAI,EAAE,KAAK,EAAE,CAAC;AAAA,IACrH,cAAc,KAAK,MAAM;AAAA,IACzB,QAAQ,IAAI;AAAA,EAAA,EACZ,OAAO,OAAO,EAAE,KAAK,IAAI;AAC7B;"}
|
package/dist/geometry.d.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import type { Bounds, Mesh, SkeletonRuntime } from './types';
|
|
2
|
-
/**
|
|
2
|
+
/** Smoothed normals from triangles (area-weighted). */
|
|
3
3
|
export declare function computeNormals(pos: ArrayLike<number>, idx: ArrayLike<number>): Float32Array;
|
|
4
4
|
export declare function accumulateNormals(pos: ArrayLike<number>, idx: ArrayLike<number>, nor: Float32Array): void;
|
|
5
5
|
export declare function normalize3(a: Float32Array): void;
|
|
6
|
-
/**
|
|
6
|
+
/** Index buffer of the smallest sufficient width. */
|
|
7
7
|
export declare function makeIndices(vertexCount: number, list: ArrayLike<number>): Uint16Array | Uint32Array;
|
|
8
8
|
export declare function emptyBounds(): Bounds;
|
|
9
|
-
/**
|
|
9
|
+
/** Bounds from raw vertex positions. */
|
|
10
10
|
export declare function boundsOf(meshes: ReadonlyArray<{
|
|
11
11
|
pos: ArrayLike<number>;
|
|
12
12
|
}>): Bounds;
|
|
13
13
|
export declare function extendBounds(b: Bounds, pos: ArrayLike<number>): void;
|
|
14
|
-
/**
|
|
14
|
+
/** Model bounds in the bind pose: skeleton skin matrices are applied to the vertices. */
|
|
15
15
|
export declare function skinnedBounds(sk: SkeletonRuntime, meshes: readonly Mesh[]): Bounds;
|
|
16
|
-
/**
|
|
16
|
+
/** Triangles from a strip, alternating winding and skipping degenerate ones. */
|
|
17
17
|
export declare function stripToTris(idx: ArrayLike<number>): number[];
|
|
18
18
|
export declare const sumTris: (meshes: readonly Mesh[]) => number;
|
|
19
19
|
export declare const sumVerts: (meshes: readonly Mesh[]) => number;
|
package/dist/geometry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"geometry.js","sources":["../src/geometry.ts"],"sourcesContent":["//
|
|
1
|
+
{"version":3,"file":"geometry.js","sources":["../src/geometry.ts"],"sourcesContent":["// Common geometry: normals, bounds, indices.\nimport type { Bounds, Mesh, SkeletonRuntime } from './types';\n\n/** Smoothed normals from triangles (area-weighted). */\nexport function computeNormals(pos: ArrayLike<number>, idx: ArrayLike<number>): Float32Array {\n const nor = new Float32Array(pos.length);\n accumulateNormals(pos, idx, nor);\n normalize3(nor);\n return nor;\n}\n\nexport function accumulateNormals(pos: ArrayLike<number>, idx: ArrayLike<number>, nor: Float32Array): void {\n for (let t = 0; t + 2 < idx.length; t += 3) {\n const a = idx[t]! * 3, b = idx[t + 1]! * 3, c = idx[t + 2]! * 3;\n const ux = pos[b]! - pos[a]!, uy = pos[b + 1]! - pos[a + 1]!, uz = pos[b + 2]! - pos[a + 2]!;\n const vx = pos[c]! - pos[a]!, vy = pos[c + 1]! - pos[a + 1]!, vz = pos[c + 2]! - pos[a + 2]!;\n const x = uy * vz - uz * vy, y = uz * vx - ux * vz, z = ux * vy - uy * vx;\n for (const o of [a, b, c]) { nor[o] += x; nor[o + 1] += y; nor[o + 2] += z; }\n }\n}\n\nexport function normalize3(a: Float32Array): void {\n for (let k = 0; k < a.length; k += 3) {\n const l = Math.hypot(a[k]!, a[k + 1]!, a[k + 2]!) || 1;\n a[k] /= l; a[k + 1] /= l; a[k + 2] /= l;\n }\n}\n\n/** Index buffer of the smallest sufficient width. */\nexport function makeIndices(vertexCount: number, list: ArrayLike<number>): Uint16Array | Uint32Array {\n return vertexCount > 65535 ? Uint32Array.from(list) : Uint16Array.from(list);\n}\n\nexport function emptyBounds(): Bounds {\n return { min: [Infinity, Infinity, Infinity], max: [-Infinity, -Infinity, -Infinity] };\n}\n\n/** Bounds from raw vertex positions. */\nexport function boundsOf(meshes: ReadonlyArray<{ pos: ArrayLike<number> }>): Bounds {\n const b = emptyBounds();\n for (const m of meshes) extendBounds(b, m.pos);\n return b;\n}\n\nexport function extendBounds(b: Bounds, pos: ArrayLike<number>): void {\n for (let i = 0; i < pos.length; i++) {\n const k = i % 3, v = pos[i]!;\n if (v < b.min[k]!) b.min[k] = v;\n if (v > b.max[k]!) b.max[k] = v;\n }\n}\n\n/** Model bounds in the bind pose: skeleton skin matrices are applied to the vertices. */\nexport function skinnedBounds(sk: SkeletonRuntime, meshes: readonly Mesh[]): Bounds {\n sk.pose(null, 0, false);\n const { min, max } = emptyBounds();\n for (const m of meshes)\n for (let v = 0; v < m.vertexCount; v++) {\n const x = m.pos[v * 3]!, y = m.pos[v * 3 + 1]!, z = m.pos[v * 3 + 2]!, o = [0, 0, 0];\n for (let k = 0; k < 4; k++) {\n const w = m.weight[v * 4 + k]!;\n if (!w) continue;\n const M = sk.skinV[m.bone[v * 4 + k]!]!;\n o[0] += w * (M[0]! * x + M[4]! * y + M[8]! * z + M[12]!);\n o[1] += w * (M[1]! * x + M[5]! * y + M[9]! * z + M[13]!);\n o[2] += w * (M[2]! * x + M[6]! * y + M[10]! * z + M[14]!);\n }\n for (let k = 0; k < 3; k++) {\n if (o[k]! < min[k]!) min[k] = o[k]!;\n if (o[k]! > max[k]!) max[k] = o[k]!;\n }\n }\n return { min, max };\n}\n\n/** Triangles from a strip, alternating winding and skipping degenerate ones. */\nexport function stripToTris(idx: ArrayLike<number>): number[] {\n const o: number[] = [];\n for (let k = 2; k < idx.length; k++) {\n const a = idx[k - 2]!, b = idx[k - 1]!, c = idx[k]!;\n if (a === b || b === c || a === c) continue;\n if (k & 1) o.push(a, c, b); else o.push(a, b, c);\n }\n return o;\n}\n\nexport const sumTris = (meshes: readonly Mesh[]): number => meshes.reduce((a, m) => a + m.indices.length / 3, 0);\nexport const sumVerts = (meshes: readonly Mesh[]): number => meshes.reduce((a, m) => a + m.vertexCount, 0);\n"],"names":[],"mappings":"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"}
|
package/dist/i18n.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { EnginePlugin, ExportField, ExporterPlugin, GamePreset } from './plugin';
|
|
2
|
-
/**
|
|
2
|
+
/** Add an engine's texts (called by the registry when the engine is attached). */
|
|
3
3
|
export declare function registerTexts(e: EnginePlugin): void;
|
|
4
4
|
export type Locale = 'ru' | 'en';
|
|
5
5
|
export declare function setLocale(l: Locale): void;
|
|
6
6
|
export declare function getLocale(): Locale;
|
|
7
|
-
/**
|
|
7
|
+
/** Text by key with {parameter} substitution. */
|
|
8
8
|
export declare function tr(key: string, params?: Record<string, string | number>): string;
|
|
9
|
-
/**
|
|
9
|
+
/** Game preset texts in the current language. */
|
|
10
10
|
export declare function presetText(p: GamePreset): {
|
|
11
11
|
title: string;
|
|
12
12
|
hint: string;
|
|
@@ -15,7 +15,7 @@ export declare function presetText(p: GamePreset): {
|
|
|
15
15
|
motionLabel?: string;
|
|
16
16
|
};
|
|
17
17
|
export declare function engineTitle(e: EnginePlugin | undefined, id?: string): string;
|
|
18
|
-
/**
|
|
18
|
+
/** Name, description and field labels of an export option in the current language. */
|
|
19
19
|
export declare function exporterText(x: ExporterPlugin): {
|
|
20
20
|
title: string;
|
|
21
21
|
description: string;
|
package/dist/i18n.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"i18n.js","sources":["../src/i18n.ts"],"sourcesContent":["//
|
|
1
|
+
{"version":3,"file":"i18n.js","sources":["../src/i18n.ts"],"sourcesContent":["// Language of library texts: session statuses, texture labels, scene layers, names of presets, engines and export options.\n// Russian by default (plugin texts are written in Russian); English comes from EnginePlugin.i18n (attached by the registry).\n// Model details (Model.info) and format error messages are still in Russian.\nimport type { EngineTexts, EnginePlugin, ExportField, ExporterPlugin, GamePreset } from './plugin';\n\n/** English texts of attached engines (filled by Registry.use). */\nconst EN: Required<Pick<EngineTexts, 'groups' | 'presets' | 'exporters'>> = { groups: {}, presets: {}, exporters: {} };\n/** Add an engine's texts (called by the registry when the engine is attached). */\nexport function registerTexts(e: EnginePlugin): void {\n const t = e.i18n?.en;\n if (!t) return;\n Object.assign(EN.groups, t.groups);\n Object.assign(EN.presets, t.presets);\n Object.assign(EN.exporters, t.exporters);\n}\n\nexport type Locale = 'ru' | 'en';\nlet locale: Locale = 'ru';\n\nexport function setLocale(l: Locale): void { locale = l; }\nexport function getLocale(): Locale { return locale; }\n\nconst MSG: Record<string, { ru: string; en: string }> = {\n 'session.readingArchive': { ru: 'Чтение архива {name}…', en: 'Reading archive {name}…' },\n 'session.archiveError': { ru: 'Ошибка архива: {error}', en: 'Archive error: {error}' },\n 'session.archives': { ru: 'Архивов: {n}', en: 'Archives: {n}' },\n 'session.archivesSkipped': { ru: 'Архивов: {n}; пропущено файлов: {k} ({list})', en: 'Archives: {n}; files skipped: {k} ({list})' },\n 'session.indexing': { ru: 'Индексация папки: файлов {n}', en: 'Indexing folder: {n} files' },\n 'session.folderTextures': { ru: 'Индексация текстур папки…', en: 'Indexing folder textures…' },\n 'session.findArchives': { ru: 'Поиск архивов в папке…', en: 'Looking for archives in the folder…' },\n 'session.loadingName': { ru: 'Загрузка {name}…', en: 'Loading {name}…' },\n 'session.companions': { ru: 'Запомнено: {list} — теперь выберите сами архивы', en: 'Remembered: {list} — now choose the archives themselves' },\n 'session.folder': { ru: 'Папка «{name}»: файлов {n}', en: 'Folder \"{name}\": {n} files' },\n 'session.dict': { ru: '{id} {name}: текстур {n}', en: '{id} {name}: {n} textures' },\n 'session.dictErrors': { ru: ', ошибок {n} ({first})', en: ', {n} errors ({first})' },\n 'session.dictError': { ru: 'Ошибка {id}: {error}', en: '{id} error: {error}' },\n 'session.noModel': { ru: 'Нет файла модели, подходящего под выбранный формат', en: 'No model file matches the selected format' },\n 'session.loading': { ru: 'Загрузка…', en: 'Loading…' },\n 'session.loaded': { ru: 'Загружено: {name}', en: 'Loaded: {name}' },\n 'session.error': { ru: 'Ошибка: {error}', en: 'Error: {error}' },\n 'session.noMotionModel': { ru: 'Сначала загрузите модель, поддерживающую внешние анимации', en: 'Load a model that supports external animations first' },\n 'session.motionsAdded': { ru: 'Добавлено анимаций: {n} ({name})', en: 'Animations added: {n} ({name})' },\n 'session.motionError': { ru: 'Ошибка анимации: {error}', en: 'Animation error: {error}' },\n 'tex.noFile': { ru: 'нет файла', en: 'no file' },\n 'tex.error': { ru: 'ошибка: {error}', en: 'error: {error}' },\n 'tex.normalNoFile': { ru: 'N нет файла', en: 'N no file' },\n 'tex.pbrFactors': { ru: ' (множители)', en: ' (factors)' },\n 'scene.model': { ru: 'Модель', en: 'Model' },\n 'scene.geometry': { ru: 'Геометрия уровня', en: 'Level geometry' },\n 'scene.objects': { ru: 'Объекты', en: 'Objects' },\n 'scene.otherObjects': { ru: 'объекты', en: 'objects' },\n 'scene.mesh': { ru: 'меш {i}', en: 'mesh {i}' },\n 'scene.meshes': { ru: '{n} меш.', en: '{n} meshes' },\n 'scene.tris': { ru: '{n} тр.', en: '{n} tris' },\n};\n\n/** Text by key with {parameter} substitution. */\nexport function tr(key: string, params: Record<string, string | number> = {}): string {\n const m = MSG[key];\n const s = m ? m[locale] : key;\n return s.replace(/\\{(\\w+)\\}/g, (_, k: string) => (params[k] !== undefined ? String(params[k]) : `{${k}}`));\n}\n\n/** Game preset texts in the current language. */\nexport function presetText(p: GamePreset): { title: string; hint: string; group: string; archiveLabel?: string; motionLabel?: string } {\n const en = locale === 'en' ? EN.presets[p.id] : undefined;\n return {\n title: en?.title ?? p.title,\n hint: en?.hint ?? p.hint ?? '',\n group: locale === 'en' ? EN.groups[p.group] ?? p.group : p.group,\n archiveLabel: en?.archiveLabel ?? p.ui?.archiveLabel,\n motionLabel: en?.motionLabel ?? p.ui?.motionLabel,\n };\n}\n\nexport function engineTitle(e: EnginePlugin | undefined, id = ''): string {\n if (!e) return id;\n return (locale === 'en' && e.i18n?.en?.title) || e.title;\n}\n\n/** Name, description and field labels of an export option in the current language. */\nexport function exporterText(x: ExporterPlugin): { title: string; description: string; field(f: ExportField): { label: string; hint?: string; placeholder?: string } } {\n const en = locale === 'en' ? EN.exporters[x.id] : undefined;\n return {\n title: en?.title ?? x.title,\n description: en?.description ?? x.description,\n field: (f) => { const e = en?.fields?.[f.id]; return { label: e?.label ?? f.label, hint: e?.hint ?? f.hint, placeholder: e?.placeholder ?? f.placeholder }; },\n };\n}\n"],"names":[],"mappings":"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"}
|
package/dist/images/bc45.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/** BC4 →
|
|
1
|
+
/** BC4 → grey, BC5 (two) → normal: R, G from the blocks, B = Z. */
|
|
2
2
|
export declare function decodeBC45(u8: Uint8Array, off: number, w: number, h: number, two: boolean): Uint8Array;
|