stormlib-js 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +254 -0
- package/dist/index.d.mts +420 -0
- package/dist/index.d.ts +420 -0
- package/dist/index.js +2979 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2920 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# stormlib-js
|
|
2
|
+
|
|
3
|
+
Pure TypeScript implementation of [StormLib](https://github.com/ladislav-zezula/StormLib) for reading MPQ (Mo'PaQ) archives. No native bindings, no WASM — just TypeScript.
|
|
4
|
+
|
|
5
|
+
MPQ is the archive format used by Blizzard Entertainment games including Diablo, StarCraft, Warcraft III, and World of Warcraft.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Read-only** MPQ archive support
|
|
10
|
+
- **MPQ V1 through V4** format support
|
|
11
|
+
- **Pure TypeScript** — no native addons or WASM compilation required
|
|
12
|
+
- **Synchronous API** — simple, straightforward file extraction
|
|
13
|
+
- **Single runtime dependency** — only [pako](https://github.com/nickolaskgas/pako) for zlib
|
|
14
|
+
- **Dual module output** — ships both CommonJS and ESM builds with full type declarations
|
|
15
|
+
|
|
16
|
+
### Compression algorithms
|
|
17
|
+
|
|
18
|
+
| Algorithm | Status |
|
|
19
|
+
|---|---|
|
|
20
|
+
| zlib (Deflate) | Supported |
|
|
21
|
+
| PKWARE DCL (Implode) | Supported |
|
|
22
|
+
| Blizzard Huffman | Supported |
|
|
23
|
+
| ADPCM Mono / Stereo | Supported |
|
|
24
|
+
| Sparse (RLE) | Supported |
|
|
25
|
+
| BZIP2 | Not supported |
|
|
26
|
+
| LZMA | Not supported |
|
|
27
|
+
|
|
28
|
+
> BZIP2 and LZMA are rarely used in practice (only some SC2/HotS archives). If you need them, contributions are welcome.
|
|
29
|
+
|
|
30
|
+
### Table formats
|
|
31
|
+
|
|
32
|
+
| Table | Status |
|
|
33
|
+
|---|---|
|
|
34
|
+
| Classic Hash Table | Supported |
|
|
35
|
+
| Classic Block Table | Supported |
|
|
36
|
+
| Hi-Block Table (V2+) | Supported |
|
|
37
|
+
| HET Table (V3+) | Supported |
|
|
38
|
+
| BET Table (V3+) | Supported |
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install stormlib-js
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Quick start
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { MpqArchive } from 'stormlib-js';
|
|
50
|
+
|
|
51
|
+
// Open an archive
|
|
52
|
+
const archive = MpqArchive.open('game.mpq');
|
|
53
|
+
|
|
54
|
+
// List known files (from internal listfile)
|
|
55
|
+
const files = archive.getFileList();
|
|
56
|
+
console.log(files);
|
|
57
|
+
|
|
58
|
+
// Extract a file
|
|
59
|
+
const data = archive.extractFile('war3map.j');
|
|
60
|
+
console.log(data.toString('utf-8'));
|
|
61
|
+
|
|
62
|
+
// Search with wildcards
|
|
63
|
+
const results = archive.findFiles('*.blp');
|
|
64
|
+
for (const entry of results) {
|
|
65
|
+
console.log(entry.fileName, entry.fileSize);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Clean up
|
|
69
|
+
archive.close();
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## API
|
|
73
|
+
|
|
74
|
+
### `MpqArchive`
|
|
75
|
+
|
|
76
|
+
The main class for working with MPQ archives.
|
|
77
|
+
|
|
78
|
+
#### `MpqArchive.open(path, options?)`
|
|
79
|
+
|
|
80
|
+
Opens an MPQ archive from a file path.
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
const archive = MpqArchive.open('archive.mpq');
|
|
84
|
+
|
|
85
|
+
// With options
|
|
86
|
+
const archive = MpqArchive.open('archive.mpq', {
|
|
87
|
+
noListfile: false, // Skip loading internal (listfile)
|
|
88
|
+
noAttributes: false, // Skip loading internal (attributes)
|
|
89
|
+
noHeaderSearch: false, // Don't search for MPQ header (assume offset 0)
|
|
90
|
+
forceMpqV1: false, // Force reading as MPQ V1
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
#### `archive.hasFile(name)`
|
|
95
|
+
|
|
96
|
+
Returns `true` if the file exists in the archive.
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
if (archive.hasFile('war3map.j')) {
|
|
100
|
+
// ...
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
#### `archive.extractFile(name)`
|
|
105
|
+
|
|
106
|
+
Extracts a file and returns its contents as a `Buffer`.
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
const buf = archive.extractFile('war3map.w3i');
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
#### `archive.openFile(name)`
|
|
113
|
+
|
|
114
|
+
Opens a file handle for more control over reading.
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
const file = archive.openFile('units.doo');
|
|
118
|
+
console.log(file.size, file.compressedSize, file.flags);
|
|
119
|
+
const data = file.read();
|
|
120
|
+
file.close();
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
#### `archive.getFileList()`
|
|
124
|
+
|
|
125
|
+
Returns an array of known file names (populated from the internal listfile).
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
const names = archive.getFileList();
|
|
129
|
+
// ['war3map.j', 'war3map.w3e', ...]
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
#### `archive.findFiles(mask?)`
|
|
133
|
+
|
|
134
|
+
Searches for files matching a wildcard pattern. Supports `*` and `?`.
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
const textures = archive.findFiles('*.blp');
|
|
138
|
+
for (const f of textures) {
|
|
139
|
+
console.log(f.fileName, f.fileSize, f.compSize);
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### `archive.enumerateFiles()`
|
|
144
|
+
|
|
145
|
+
Lists all file entries including unnamed ones. Unnamed entries are given synthetic names like `File00000001.xxx`.
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
const all = archive.enumerateFiles();
|
|
149
|
+
console.log(`Total: ${all.length} files`);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### `archive.addListfile(names)`
|
|
153
|
+
|
|
154
|
+
Applies an external list of file names to resolve unnamed entries. Useful for archives without an internal `(listfile)`.
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
const resolved = archive.addListfile([
|
|
158
|
+
'war3map.j', 'war3map.w3e', 'war3map.w3i',
|
|
159
|
+
]);
|
|
160
|
+
console.log(`${resolved} new names resolved`);
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
#### `archive.getHeader()`
|
|
164
|
+
|
|
165
|
+
Returns the parsed MPQ header.
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
const header = archive.getHeader();
|
|
169
|
+
console.log(`Format: V${header.wFormatVersion + 1}`);
|
|
170
|
+
console.log(`Sector size: ${512 << header.wSectorSize}`);
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
#### `archive.close()`
|
|
174
|
+
|
|
175
|
+
Closes the archive and releases the underlying file handle.
|
|
176
|
+
|
|
177
|
+
### Error classes
|
|
178
|
+
|
|
179
|
+
All errors extend `MpqError`:
|
|
180
|
+
|
|
181
|
+
| Class | Description |
|
|
182
|
+
|---|---|
|
|
183
|
+
| `MpqNotFoundError` | File not found in the archive |
|
|
184
|
+
| `MpqCorruptError` | Archive data is corrupt |
|
|
185
|
+
| `MpqUnsupportedError` | Unsupported feature (e.g. BZIP2) |
|
|
186
|
+
| `MpqEncryptionError` | Decryption failure |
|
|
187
|
+
| `MpqCompressionError` | Decompression failure |
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
import { MpqNotFoundError } from 'stormlib-js';
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
archive.extractFile('nonexistent.txt');
|
|
194
|
+
} catch (err) {
|
|
195
|
+
if (err instanceof MpqNotFoundError) {
|
|
196
|
+
console.log('File does not exist');
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Supported games
|
|
202
|
+
|
|
203
|
+
This library can read MPQ archives from:
|
|
204
|
+
|
|
205
|
+
- **Diablo** / **Diablo II** (V1, Huffman/PKWARE compression)
|
|
206
|
+
- **StarCraft** / **Brood War** (V1, Huffman/PKWARE compression)
|
|
207
|
+
- **Warcraft III** / **The Frozen Throne** (V1, zlib compression)
|
|
208
|
+
- **World of Warcraft** (V2, 64-bit offsets)
|
|
209
|
+
- **StarCraft II** (V3/V4, HET/BET tables)
|
|
210
|
+
|
|
211
|
+
## Advanced usage
|
|
212
|
+
|
|
213
|
+
Low-level crypto and hashing functions are exported for specialized use cases:
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
import {
|
|
217
|
+
hashString, jenkinsHash,
|
|
218
|
+
decryptBlock, encryptBlock,
|
|
219
|
+
getStormBuffer,
|
|
220
|
+
MPQ_HASH_TABLE_INDEX,
|
|
221
|
+
MPQ_HASH_NAME_A,
|
|
222
|
+
} from 'stormlib-js';
|
|
223
|
+
|
|
224
|
+
// Compute MPQ hash
|
|
225
|
+
const idx = hashString('(listfile)', MPQ_HASH_TABLE_INDEX);
|
|
226
|
+
|
|
227
|
+
// Jenkins hash for HET tables
|
|
228
|
+
const hash = jenkinsHash('war3map.j');
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Development
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
# Install dependencies
|
|
235
|
+
npm install
|
|
236
|
+
|
|
237
|
+
# Run tests
|
|
238
|
+
npm test
|
|
239
|
+
|
|
240
|
+
# Type check
|
|
241
|
+
npm run typecheck
|
|
242
|
+
|
|
243
|
+
# Build
|
|
244
|
+
npm run build
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Acknowledgments
|
|
248
|
+
|
|
249
|
+
- [Ladislav Zezula](https://github.com/ladislav-zezula) for the original [StormLib](https://github.com/ladislav-zezula/StormLib) C/C++ library and the comprehensive MPQ format documentation
|
|
250
|
+
- [The StormLib documentation](http://www.zezula.net/en/mpq/stormlib.html) for format specifications
|
|
251
|
+
|
|
252
|
+
## License
|
|
253
|
+
|
|
254
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
/** Raw MPQ header as parsed from disk (normalized to V4 superset) */
|
|
2
|
+
interface MpqHeader {
|
|
3
|
+
/** 'MPQ\x1A' signature */
|
|
4
|
+
dwID: number;
|
|
5
|
+
/** Size of the header */
|
|
6
|
+
dwHeaderSize: number;
|
|
7
|
+
/** 32-bit archive size (V1) */
|
|
8
|
+
dwArchiveSize: number;
|
|
9
|
+
/** Format version: 0=V1, 1=V2, 2=V3, 3=V4 */
|
|
10
|
+
wFormatVersion: number;
|
|
11
|
+
/** Sector size shift (sector size = 512 << wSectorSize) */
|
|
12
|
+
wSectorSize: number;
|
|
13
|
+
/** Hash table offset (relative to archive start) */
|
|
14
|
+
dwHashTablePos: number;
|
|
15
|
+
/** Block table offset (relative to archive start) */
|
|
16
|
+
dwBlockTablePos: number;
|
|
17
|
+
/** Number of hash table entries */
|
|
18
|
+
dwHashTableSize: number;
|
|
19
|
+
/** Number of block table entries */
|
|
20
|
+
dwBlockTableSize: number;
|
|
21
|
+
/** 64-bit hi-block table offset */
|
|
22
|
+
hiBlockTablePos64: bigint;
|
|
23
|
+
/** High 16 bits of hash table offset */
|
|
24
|
+
wHashTablePosHi: number;
|
|
25
|
+
/** High 16 bits of block table offset */
|
|
26
|
+
wBlockTablePosHi: number;
|
|
27
|
+
/** 64-bit archive size */
|
|
28
|
+
archiveSize64: bigint;
|
|
29
|
+
/** 64-bit BET table position */
|
|
30
|
+
betTablePos64: bigint;
|
|
31
|
+
/** 64-bit HET table position */
|
|
32
|
+
hetTablePos64: bigint;
|
|
33
|
+
/** Compressed hash table size */
|
|
34
|
+
hashTableSize64: bigint;
|
|
35
|
+
/** Compressed block table size */
|
|
36
|
+
blockTableSize64: bigint;
|
|
37
|
+
/** Compressed hi-block table size */
|
|
38
|
+
hiBlockTableSize64: bigint;
|
|
39
|
+
/** Compressed HET table size */
|
|
40
|
+
hetTableSize64: bigint;
|
|
41
|
+
/** Compressed BET table size */
|
|
42
|
+
betTableSize64: bigint;
|
|
43
|
+
/** Raw chunk size for MD5 */
|
|
44
|
+
dwRawChunkSize: number;
|
|
45
|
+
/** MD5 checksums (V4) */
|
|
46
|
+
md5BlockTable: Uint8Array;
|
|
47
|
+
md5HashTable: Uint8Array;
|
|
48
|
+
md5HiBlockTable: Uint8Array;
|
|
49
|
+
md5BetTable: Uint8Array;
|
|
50
|
+
md5HetTable: Uint8Array;
|
|
51
|
+
md5MpqHeader: Uint8Array;
|
|
52
|
+
}
|
|
53
|
+
/** User data header that may precede the MPQ header */
|
|
54
|
+
interface MpqUserData {
|
|
55
|
+
dwID: number;
|
|
56
|
+
cbUserDataSize: number;
|
|
57
|
+
dwHeaderOffs: number;
|
|
58
|
+
cbUserDataHeader: number;
|
|
59
|
+
}
|
|
60
|
+
/** Hash table entry (16 bytes on disk) */
|
|
61
|
+
interface MpqHashEntry {
|
|
62
|
+
/** Hash of file path (method A) */
|
|
63
|
+
dwName1: number;
|
|
64
|
+
/** Hash of file path (method B) */
|
|
65
|
+
dwName2: number;
|
|
66
|
+
/** Windows LANGID locale */
|
|
67
|
+
lcLocale: number;
|
|
68
|
+
/** Platform (always 0) */
|
|
69
|
+
platform: number;
|
|
70
|
+
/** Index into block table (0xFFFFFFFF=free, 0xFFFFFFFE=deleted) */
|
|
71
|
+
dwBlockIndex: number;
|
|
72
|
+
}
|
|
73
|
+
/** Block table entry (16 bytes on disk) */
|
|
74
|
+
interface MpqBlockEntry {
|
|
75
|
+
/** File data offset relative to archive start */
|
|
76
|
+
dwFilePos: number;
|
|
77
|
+
/** Compressed file size */
|
|
78
|
+
dwCSize: number;
|
|
79
|
+
/** Uncompressed file size */
|
|
80
|
+
dwFSize: number;
|
|
81
|
+
/** File flags */
|
|
82
|
+
dwFlags: number;
|
|
83
|
+
}
|
|
84
|
+
/** Unified file entry (in-memory representation) */
|
|
85
|
+
interface FileEntry {
|
|
86
|
+
/** Jenkins hash (for BET table) */
|
|
87
|
+
fileNameHash: bigint;
|
|
88
|
+
/** File data offset relative to MPQ header */
|
|
89
|
+
byteOffset: bigint;
|
|
90
|
+
/** File time (FILETIME) from attributes */
|
|
91
|
+
fileTime: bigint;
|
|
92
|
+
/** Decompressed size */
|
|
93
|
+
fileSize: number;
|
|
94
|
+
/** Compressed size */
|
|
95
|
+
cmpSize: number;
|
|
96
|
+
/** File flags */
|
|
97
|
+
flags: number;
|
|
98
|
+
/** CRC32 from attributes */
|
|
99
|
+
crc32: number;
|
|
100
|
+
/** MD5 from attributes */
|
|
101
|
+
md5: Uint8Array;
|
|
102
|
+
/** File name (empty if unknown) */
|
|
103
|
+
fileName: string;
|
|
104
|
+
}
|
|
105
|
+
/** HET table header */
|
|
106
|
+
interface HetTableHeader {
|
|
107
|
+
signature: number;
|
|
108
|
+
version: number;
|
|
109
|
+
dataSize: number;
|
|
110
|
+
tableSize: number;
|
|
111
|
+
maxFileCount: number;
|
|
112
|
+
hashTableSize: number;
|
|
113
|
+
hashEntrySize: number;
|
|
114
|
+
totalIndexSize: number;
|
|
115
|
+
indexSizeExtra: number;
|
|
116
|
+
indexSize: number;
|
|
117
|
+
blockTableSize: number;
|
|
118
|
+
}
|
|
119
|
+
/** BET table header */
|
|
120
|
+
interface BetTableHeader {
|
|
121
|
+
signature: number;
|
|
122
|
+
version: number;
|
|
123
|
+
dataSize: number;
|
|
124
|
+
tableSize: number;
|
|
125
|
+
maxFileCount: number;
|
|
126
|
+
flagCount: number;
|
|
127
|
+
}
|
|
128
|
+
/** File search result */
|
|
129
|
+
interface FileFindData {
|
|
130
|
+
fileName: string;
|
|
131
|
+
plainName: string;
|
|
132
|
+
hashIndex: number;
|
|
133
|
+
blockIndex: number;
|
|
134
|
+
fileSize: number;
|
|
135
|
+
compSize: number;
|
|
136
|
+
fileFlags: number;
|
|
137
|
+
locale: number;
|
|
138
|
+
}
|
|
139
|
+
/** Open archive flags */
|
|
140
|
+
interface OpenArchiveOptions {
|
|
141
|
+
noListfile?: boolean;
|
|
142
|
+
noAttributes?: boolean;
|
|
143
|
+
noHeaderSearch?: boolean;
|
|
144
|
+
forceMpqV1?: boolean;
|
|
145
|
+
checkSectorCrc?: boolean;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* File stream abstraction for reading MPQ archives from disk.
|
|
150
|
+
* Uses Node.js fs module for synchronous positioned reads.
|
|
151
|
+
*/
|
|
152
|
+
declare class FileStream {
|
|
153
|
+
private fd;
|
|
154
|
+
private fileSize;
|
|
155
|
+
private filePath;
|
|
156
|
+
private constructor();
|
|
157
|
+
/**
|
|
158
|
+
* Open a file for reading.
|
|
159
|
+
*/
|
|
160
|
+
static open(path: string): FileStream;
|
|
161
|
+
/**
|
|
162
|
+
* Read bytes at a given offset.
|
|
163
|
+
*
|
|
164
|
+
* @param offset - Byte offset from the beginning of the file
|
|
165
|
+
* @param length - Number of bytes to read
|
|
166
|
+
* @returns Buffer containing the read data
|
|
167
|
+
*/
|
|
168
|
+
read(offset: bigint, length: number): Buffer;
|
|
169
|
+
/**
|
|
170
|
+
* Read bytes into an existing buffer.
|
|
171
|
+
*/
|
|
172
|
+
readInto(offset: bigint, buffer: Buffer, bufOffset: number, length: number): number;
|
|
173
|
+
/**
|
|
174
|
+
* Get the total file size.
|
|
175
|
+
*/
|
|
176
|
+
getSize(): bigint;
|
|
177
|
+
/**
|
|
178
|
+
* Get the file path.
|
|
179
|
+
*/
|
|
180
|
+
getPath(): string;
|
|
181
|
+
/**
|
|
182
|
+
* Close the file stream.
|
|
183
|
+
*/
|
|
184
|
+
close(): void;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* MpqFile represents an open file handle within an MPQ archive.
|
|
189
|
+
* It provides methods to read file data.
|
|
190
|
+
*/
|
|
191
|
+
|
|
192
|
+
declare class MpqFile {
|
|
193
|
+
private stream;
|
|
194
|
+
private archiveOffset;
|
|
195
|
+
private entry;
|
|
196
|
+
private sectorSize;
|
|
197
|
+
private fileKey;
|
|
198
|
+
private closed;
|
|
199
|
+
constructor(stream: FileStream, archiveOffset: bigint, entry: FileEntry, sectorSize: number);
|
|
200
|
+
/** Uncompressed file size */
|
|
201
|
+
get size(): number;
|
|
202
|
+
/** Compressed file size */
|
|
203
|
+
get compressedSize(): number;
|
|
204
|
+
/** File flags */
|
|
205
|
+
get flags(): number;
|
|
206
|
+
/** File name */
|
|
207
|
+
get name(): string;
|
|
208
|
+
/** The underlying file entry */
|
|
209
|
+
get fileEntry(): FileEntry;
|
|
210
|
+
/**
|
|
211
|
+
* Read the entire file contents.
|
|
212
|
+
*
|
|
213
|
+
* @returns Buffer containing the decompressed file data
|
|
214
|
+
*/
|
|
215
|
+
read(): Buffer;
|
|
216
|
+
/**
|
|
217
|
+
* Close the file handle.
|
|
218
|
+
*/
|
|
219
|
+
close(): void;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* MpqArchive - Main class for reading MPQ archives.
|
|
224
|
+
*
|
|
225
|
+
* This is the primary public API for opening and reading MPQ files.
|
|
226
|
+
*/
|
|
227
|
+
|
|
228
|
+
declare class MpqArchive {
|
|
229
|
+
private stream;
|
|
230
|
+
private header;
|
|
231
|
+
private archiveOffset;
|
|
232
|
+
private sectorSize;
|
|
233
|
+
private hashTable;
|
|
234
|
+
private blockTable;
|
|
235
|
+
private hiBlockTable;
|
|
236
|
+
private hetTable;
|
|
237
|
+
private betTable;
|
|
238
|
+
private fileEntries;
|
|
239
|
+
private attributes;
|
|
240
|
+
private closed;
|
|
241
|
+
private constructor();
|
|
242
|
+
/**
|
|
243
|
+
* Open an MPQ archive from a file path.
|
|
244
|
+
*
|
|
245
|
+
* @param path - Path to the MPQ file
|
|
246
|
+
* @param options - Open options
|
|
247
|
+
* @returns MpqArchive instance
|
|
248
|
+
*/
|
|
249
|
+
static open(path: string, options?: OpenArchiveOptions): MpqArchive;
|
|
250
|
+
/**
|
|
251
|
+
* Load all archive tables (hash, block, HET, BET).
|
|
252
|
+
*/
|
|
253
|
+
private loadTables;
|
|
254
|
+
/**
|
|
255
|
+
* Load and parse the (listfile) to populate file names.
|
|
256
|
+
*/
|
|
257
|
+
private loadListfile;
|
|
258
|
+
/**
|
|
259
|
+
* Load and parse the (attributes) file.
|
|
260
|
+
*/
|
|
261
|
+
private loadAttributes;
|
|
262
|
+
/**
|
|
263
|
+
* Internal: extract a file by name without relying on the listfile.
|
|
264
|
+
* Used for bootstrapping (listfile) and (attributes) loading.
|
|
265
|
+
*/
|
|
266
|
+
private extractFileByName;
|
|
267
|
+
/**
|
|
268
|
+
* Check if a file exists in the archive.
|
|
269
|
+
*/
|
|
270
|
+
hasFile(name: string): boolean;
|
|
271
|
+
/**
|
|
272
|
+
* Open a file within the archive for reading.
|
|
273
|
+
*
|
|
274
|
+
* @param name - File path within the archive
|
|
275
|
+
* @returns MpqFile handle
|
|
276
|
+
*/
|
|
277
|
+
openFile(name: string): MpqFile;
|
|
278
|
+
/**
|
|
279
|
+
* Extract a file's contents directly.
|
|
280
|
+
*
|
|
281
|
+
* @param name - File path within the archive
|
|
282
|
+
* @returns Buffer with the file contents
|
|
283
|
+
*/
|
|
284
|
+
extractFile(name: string): Buffer;
|
|
285
|
+
/**
|
|
286
|
+
* Get the list of all known file names (from listfile).
|
|
287
|
+
*/
|
|
288
|
+
getFileList(): string[];
|
|
289
|
+
/**
|
|
290
|
+
* Find files matching a wildcard pattern.
|
|
291
|
+
*
|
|
292
|
+
* @param mask - Wildcard pattern (supports '*' and '?')
|
|
293
|
+
* @returns Array of matching file entries
|
|
294
|
+
*/
|
|
295
|
+
findFiles(mask?: string): FileFindData[];
|
|
296
|
+
/**
|
|
297
|
+
* Apply an external list of file names to resolve unnamed entries.
|
|
298
|
+
* Equivalent to providing an external listfile to SFileFindFirstFile.
|
|
299
|
+
*
|
|
300
|
+
* @param names - Array of file names to try
|
|
301
|
+
* @returns Number of newly resolved names
|
|
302
|
+
*/
|
|
303
|
+
addListfile(names: string[]): number;
|
|
304
|
+
/**
|
|
305
|
+
* Enumerate all existing file entries, including those without names.
|
|
306
|
+
* Unnamed entries get a synthetic name like "File00001234.xxx".
|
|
307
|
+
*/
|
|
308
|
+
enumerateFiles(): FileFindData[];
|
|
309
|
+
/** Get the archive header */
|
|
310
|
+
getHeader(): Readonly<MpqHeader>;
|
|
311
|
+
/** Get the number of file entries */
|
|
312
|
+
get fileCount(): number;
|
|
313
|
+
/** Get the sector size */
|
|
314
|
+
getSectorSize(): number;
|
|
315
|
+
/**
|
|
316
|
+
* Close the archive and release resources.
|
|
317
|
+
*/
|
|
318
|
+
close(): void;
|
|
319
|
+
/**
|
|
320
|
+
* Resolve a file name to its FileEntry.
|
|
321
|
+
*/
|
|
322
|
+
private resolveFile;
|
|
323
|
+
private ensureOpen;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
declare class MpqError extends Error {
|
|
327
|
+
constructor(message: string);
|
|
328
|
+
}
|
|
329
|
+
declare class MpqNotFoundError extends MpqError {
|
|
330
|
+
constructor(message?: string);
|
|
331
|
+
}
|
|
332
|
+
declare class MpqCorruptError extends MpqError {
|
|
333
|
+
constructor(message?: string);
|
|
334
|
+
}
|
|
335
|
+
declare class MpqUnsupportedError extends MpqError {
|
|
336
|
+
constructor(message?: string);
|
|
337
|
+
}
|
|
338
|
+
declare class MpqEncryptionError extends MpqError {
|
|
339
|
+
constructor(message?: string);
|
|
340
|
+
}
|
|
341
|
+
declare class MpqCompressionError extends MpqError {
|
|
342
|
+
constructor(message?: string);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
declare const ID_MPQ = 441536589;
|
|
346
|
+
declare const ID_MPQ_USERDATA = 458313805;
|
|
347
|
+
declare const MPQ_FORMAT_VERSION_1 = 0;
|
|
348
|
+
declare const MPQ_FORMAT_VERSION_2 = 1;
|
|
349
|
+
declare const MPQ_FORMAT_VERSION_3 = 2;
|
|
350
|
+
declare const MPQ_FORMAT_VERSION_4 = 3;
|
|
351
|
+
declare const MPQ_FILE_IMPLODE = 256;
|
|
352
|
+
declare const MPQ_FILE_COMPRESS = 512;
|
|
353
|
+
declare const MPQ_FILE_ENCRYPTED = 65536;
|
|
354
|
+
declare const MPQ_FILE_KEY_V2 = 131072;
|
|
355
|
+
declare const MPQ_FILE_SINGLE_UNIT = 16777216;
|
|
356
|
+
declare const MPQ_FILE_EXISTS = 2147483648;
|
|
357
|
+
declare const MPQ_COMPRESSION_HUFFMANN = 1;
|
|
358
|
+
declare const MPQ_COMPRESSION_ZLIB = 2;
|
|
359
|
+
declare const MPQ_COMPRESSION_PKWARE = 8;
|
|
360
|
+
declare const MPQ_COMPRESSION_BZIP2 = 16;
|
|
361
|
+
declare const MPQ_COMPRESSION_LZMA = 18;
|
|
362
|
+
declare const MPQ_COMPRESSION_SPARSE = 32;
|
|
363
|
+
declare const MPQ_COMPRESSION_ADPCM_MONO = 64;
|
|
364
|
+
declare const MPQ_COMPRESSION_ADPCM_STEREO = 128;
|
|
365
|
+
declare const LISTFILE_NAME = "(listfile)";
|
|
366
|
+
declare const SIGNATURE_NAME = "(signature)";
|
|
367
|
+
declare const ATTRIBUTES_NAME = "(attributes)";
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Compute MPQ hash of a string. The hashType selects which section of the
|
|
371
|
+
* StormBuffer to use:
|
|
372
|
+
* - MPQ_HASH_TABLE_INDEX (0x000): hash table slot
|
|
373
|
+
* - MPQ_HASH_NAME_A (0x100): file name verification hash 1
|
|
374
|
+
* - MPQ_HASH_NAME_B (0x200): file name verification hash 2
|
|
375
|
+
* - MPQ_HASH_FILE_KEY (0x300): encryption key derivation
|
|
376
|
+
*/
|
|
377
|
+
declare function hashString(str: string, hashType: number): number;
|
|
378
|
+
/** Convenience: compute hash table index */
|
|
379
|
+
declare function hashTableIndex(fileName: string): number;
|
|
380
|
+
/** Convenience: compute name hash A */
|
|
381
|
+
declare function hashNameA(fileName: string): number;
|
|
382
|
+
/** Convenience: compute name hash B */
|
|
383
|
+
declare function hashNameB(fileName: string): number;
|
|
384
|
+
/** Convenience: compute file encryption key hash */
|
|
385
|
+
declare function hashFileKey(fileName: string): number;
|
|
386
|
+
/**
|
|
387
|
+
* Jenkins hashlittle2 - used for HET/BET tables (64-bit hash).
|
|
388
|
+
* Produces a 64-bit hash as a BigInt.
|
|
389
|
+
*/
|
|
390
|
+
declare function jenkinsHash(str: string): bigint;
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Decrypt an MPQ data block in-place.
|
|
394
|
+
* The buffer must be DWORD-aligned (length must be a multiple of 4).
|
|
395
|
+
*
|
|
396
|
+
* @param data - Buffer to decrypt (modified in place)
|
|
397
|
+
* @param key - Primary encryption key (DWORD)
|
|
398
|
+
*/
|
|
399
|
+
declare function decryptBlock(data: Buffer, key: number): void;
|
|
400
|
+
/**
|
|
401
|
+
* Encrypt an MPQ data block in-place.
|
|
402
|
+
*
|
|
403
|
+
* @param data - Buffer to encrypt (modified in place)
|
|
404
|
+
* @param key - Primary encryption key (DWORD)
|
|
405
|
+
*/
|
|
406
|
+
declare function encryptBlock(data: Buffer, key: number): void;
|
|
407
|
+
/**
|
|
408
|
+
* Calculate the decryption key for a file within an MPQ archive.
|
|
409
|
+
*
|
|
410
|
+
* @param fileName - Full path of the file within the archive
|
|
411
|
+
* @param byteOffset - File's offset within the archive (lower 32 bits)
|
|
412
|
+
* @param fileSize - Uncompressed file size
|
|
413
|
+
* @param flags - File flags from block table
|
|
414
|
+
* @returns The file decryption key
|
|
415
|
+
*/
|
|
416
|
+
declare function decryptFileKey(fileName: string, byteOffset: bigint, fileSize: number, flags: number): number;
|
|
417
|
+
|
|
418
|
+
declare function getStormBuffer(): Uint32Array;
|
|
419
|
+
|
|
420
|
+
export { ATTRIBUTES_NAME, type BetTableHeader, type FileEntry, type FileFindData, type HetTableHeader, ID_MPQ, ID_MPQ_USERDATA, LISTFILE_NAME, MPQ_COMPRESSION_ADPCM_MONO, MPQ_COMPRESSION_ADPCM_STEREO, MPQ_COMPRESSION_BZIP2, MPQ_COMPRESSION_HUFFMANN, MPQ_COMPRESSION_LZMA, MPQ_COMPRESSION_PKWARE, MPQ_COMPRESSION_SPARSE, MPQ_COMPRESSION_ZLIB, MPQ_FILE_COMPRESS, MPQ_FILE_ENCRYPTED, MPQ_FILE_EXISTS, MPQ_FILE_IMPLODE, MPQ_FILE_KEY_V2, MPQ_FILE_SINGLE_UNIT, MPQ_FORMAT_VERSION_1, MPQ_FORMAT_VERSION_2, MPQ_FORMAT_VERSION_3, MPQ_FORMAT_VERSION_4, MpqArchive, type MpqBlockEntry, MpqCompressionError, MpqCorruptError, MpqEncryptionError, MpqError, MpqFile, type MpqHashEntry, type MpqHeader, MpqNotFoundError, MpqUnsupportedError, type MpqUserData, type OpenArchiveOptions, SIGNATURE_NAME, decryptBlock, decryptFileKey, encryptBlock, getStormBuffer, hashFileKey, hashNameA, hashNameB, hashString, hashTableIndex, jenkinsHash };
|