@chicowall/grf-loader 1.0.11 → 1.0.13
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 +248 -37
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +248 -3
- package/dist/index.d.ts +248 -3
- package/dist/index.global.js +6 -1
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +6 -2
package/dist/index.d.cts
CHANGED
|
@@ -6,25 +6,146 @@ interface TFileEntry {
|
|
|
6
6
|
realSize: number;
|
|
7
7
|
compressedSize: number;
|
|
8
8
|
lengthAligned: number;
|
|
9
|
+
/** Raw filename bytes for re-decoding if needed */
|
|
10
|
+
rawNameBytes?: Uint8Array;
|
|
11
|
+
}
|
|
12
|
+
/** Supported filename encodings */
|
|
13
|
+
type FilenameEncoding = 'utf-8' | 'euc-kr' | 'cp949' | 'latin1' | 'auto';
|
|
14
|
+
/** GRF loader options */
|
|
15
|
+
interface GrfOptions {
|
|
16
|
+
/** Encoding for filenames (default: 'auto') */
|
|
17
|
+
filenameEncoding?: FilenameEncoding;
|
|
18
|
+
/** Threshold for auto-detection: if % of U+FFFD exceeds this, try Korean encodings (default: 0.01 = 1%) */
|
|
19
|
+
autoDetectThreshold?: number;
|
|
20
|
+
/** Maximum uncompressed size per file in bytes (default: 256MB) */
|
|
21
|
+
maxFileUncompressedBytes?: number;
|
|
22
|
+
/** Maximum total entries allowed (default: 500000) */
|
|
23
|
+
maxEntries?: number;
|
|
24
|
+
}
|
|
25
|
+
/** Search/find options */
|
|
26
|
+
interface FindOptions {
|
|
27
|
+
/** Filter by file extension (without dot, e.g., 'spr', 'act') */
|
|
28
|
+
ext?: string;
|
|
29
|
+
/** Filter by substring in path */
|
|
30
|
+
contains?: string;
|
|
31
|
+
/** Filter by path ending */
|
|
32
|
+
endsWith?: string;
|
|
33
|
+
/** Filter by regex pattern */
|
|
34
|
+
regex?: RegExp;
|
|
35
|
+
/** Maximum results to return (default: unlimited) */
|
|
36
|
+
limit?: number;
|
|
37
|
+
}
|
|
38
|
+
/** Result of path resolution */
|
|
39
|
+
interface ResolveResult {
|
|
40
|
+
status: 'found' | 'not_found' | 'ambiguous';
|
|
41
|
+
/** The exact matched path (if found) */
|
|
42
|
+
matchedPath?: string;
|
|
43
|
+
/** All candidate paths (if ambiguous) */
|
|
44
|
+
candidates?: string[];
|
|
45
|
+
}
|
|
46
|
+
/** GRF statistics */
|
|
47
|
+
interface GrfStats {
|
|
48
|
+
/** Total file count */
|
|
49
|
+
fileCount: number;
|
|
50
|
+
/** Number of filenames with replacement character (U+FFFD) */
|
|
51
|
+
badNameCount: number;
|
|
52
|
+
/** Number of normalized key collisions */
|
|
53
|
+
collisionCount: number;
|
|
54
|
+
/** Extension statistics: ext -> count */
|
|
55
|
+
extensionStats: Map<string, number>;
|
|
56
|
+
/** Detected encoding used */
|
|
57
|
+
detectedEncoding: FilenameEncoding;
|
|
58
|
+
}
|
|
59
|
+
declare const GRF_ERROR_CODES: {
|
|
60
|
+
readonly INVALID_MAGIC: "GRF_INVALID_MAGIC";
|
|
61
|
+
readonly UNSUPPORTED_VERSION: "GRF_UNSUPPORTED_VERSION";
|
|
62
|
+
readonly NOT_LOADED: "GRF_NOT_LOADED";
|
|
63
|
+
readonly FILE_NOT_FOUND: "GRF_FILE_NOT_FOUND";
|
|
64
|
+
readonly AMBIGUOUS_PATH: "GRF_AMBIGUOUS_PATH";
|
|
65
|
+
readonly DECOMPRESS_FAIL: "GRF_DECOMPRESS_FAIL";
|
|
66
|
+
readonly CORRUPT_TABLE: "GRF_CORRUPT_TABLE";
|
|
67
|
+
readonly LIMIT_EXCEEDED: "GRF_LIMIT_EXCEEDED";
|
|
68
|
+
readonly INVALID_OFFSET: "GRF_INVALID_OFFSET";
|
|
69
|
+
readonly DECRYPT_REQUIRED: "GRF_DECRYPT_REQUIRED";
|
|
70
|
+
};
|
|
71
|
+
declare class GrfError extends Error {
|
|
72
|
+
code: keyof typeof GRF_ERROR_CODES;
|
|
73
|
+
context?: Record<string, unknown> | undefined;
|
|
74
|
+
constructor(code: keyof typeof GRF_ERROR_CODES, message: string, context?: Record<string, unknown> | undefined);
|
|
9
75
|
}
|
|
10
76
|
declare abstract class GrfBase<T> {
|
|
11
77
|
private fd;
|
|
12
78
|
version: number;
|
|
13
79
|
fileCount: number;
|
|
14
80
|
loaded: boolean;
|
|
81
|
+
/** Map of exact filename -> entry */
|
|
15
82
|
files: Map<string, TFileEntry>;
|
|
83
|
+
/** Map of normalized path -> array of exact filenames (supports collisions) */
|
|
84
|
+
private normalizedIndex;
|
|
85
|
+
/** Map of extension -> array of exact filenames (for fast extension lookup) */
|
|
86
|
+
private extensionIndex;
|
|
16
87
|
private fileTableOffset;
|
|
17
|
-
|
|
88
|
+
private cache;
|
|
89
|
+
private cacheMaxSize;
|
|
90
|
+
private cacheOrder;
|
|
91
|
+
protected options: Required<GrfOptions>;
|
|
92
|
+
private _stats;
|
|
93
|
+
constructor(fd: T, options?: GrfOptions);
|
|
18
94
|
abstract getStreamBuffer(fd: T, offset: number, length: number): Promise<Uint8Array>;
|
|
19
95
|
getStreamReader(offset: number, length: number): Promise<jDataview>;
|
|
20
96
|
load(): Promise<void>;
|
|
21
97
|
private parseHeader;
|
|
22
98
|
private parseFileList;
|
|
23
99
|
private decodeEntry;
|
|
100
|
+
private addToCache;
|
|
101
|
+
private getFromCache;
|
|
102
|
+
clearCache(): void;
|
|
24
103
|
getFile(filename: string): Promise<{
|
|
25
104
|
data: null | Uint8Array;
|
|
26
105
|
error: null | string;
|
|
27
106
|
}>;
|
|
107
|
+
/**
|
|
108
|
+
* Resolve a path to its exact filename in the GRF.
|
|
109
|
+
* Tries exact match first, then normalized (case-insensitive, slash-agnostic).
|
|
110
|
+
*/
|
|
111
|
+
resolvePath(query: string): ResolveResult;
|
|
112
|
+
/**
|
|
113
|
+
* Check if a file exists in the GRF.
|
|
114
|
+
*/
|
|
115
|
+
hasFile(filename: string): boolean;
|
|
116
|
+
/**
|
|
117
|
+
* Get file entry metadata without extracting the file.
|
|
118
|
+
*/
|
|
119
|
+
getEntry(filename: string): TFileEntry | null;
|
|
120
|
+
/**
|
|
121
|
+
* Find files matching the given criteria.
|
|
122
|
+
*/
|
|
123
|
+
find(options?: FindOptions): string[];
|
|
124
|
+
/**
|
|
125
|
+
* Get all files with a specific extension.
|
|
126
|
+
*/
|
|
127
|
+
getFilesByExtension(ext: string): string[];
|
|
128
|
+
/**
|
|
129
|
+
* List all unique extensions in the GRF.
|
|
130
|
+
*/
|
|
131
|
+
listExtensions(): string[];
|
|
132
|
+
/**
|
|
133
|
+
* List all files in the GRF.
|
|
134
|
+
*/
|
|
135
|
+
listFiles(): string[];
|
|
136
|
+
/**
|
|
137
|
+
* Get GRF statistics.
|
|
138
|
+
*/
|
|
139
|
+
getStats(): GrfStats;
|
|
140
|
+
/**
|
|
141
|
+
* Get the detected/configured encoding used for filenames.
|
|
142
|
+
*/
|
|
143
|
+
getDetectedEncoding(): FilenameEncoding;
|
|
144
|
+
/**
|
|
145
|
+
* Re-decode all filenames with a different encoding.
|
|
146
|
+
* Useful if auto-detection chose wrong or you want to try a specific encoding.
|
|
147
|
+
*/
|
|
148
|
+
reloadWithEncoding(encoding: FilenameEncoding): Promise<void>;
|
|
28
149
|
}
|
|
29
150
|
|
|
30
151
|
/**
|
|
@@ -33,12 +154,136 @@ declare abstract class GrfBase<T> {
|
|
|
33
154
|
* loading 2 gigas into memory
|
|
34
155
|
*/
|
|
35
156
|
declare class GrfBrowser extends GrfBase<File | Blob> {
|
|
157
|
+
constructor(file: File | Blob, options?: GrfOptions);
|
|
36
158
|
getStreamBuffer(buffer: File | Blob, offset: number, length: number): Promise<Uint8Array>;
|
|
37
159
|
}
|
|
38
160
|
|
|
161
|
+
/** Options for GrfNode */
|
|
162
|
+
interface GrfNodeOptions extends GrfOptions {
|
|
163
|
+
/** Use buffer pool for better performance (default: true) */
|
|
164
|
+
useBufferPool?: boolean;
|
|
165
|
+
}
|
|
39
166
|
declare class GrfNode extends GrfBase<number> {
|
|
40
|
-
|
|
167
|
+
private useBufferPool;
|
|
168
|
+
constructor(fd: number, options?: GrfNodeOptions);
|
|
41
169
|
getStreamBuffer(fd: number, offset: number, length: number): Promise<Uint8Array>;
|
|
42
170
|
}
|
|
43
171
|
|
|
44
|
-
|
|
172
|
+
/**
|
|
173
|
+
* Simple buffer pool for reducing GC pressure
|
|
174
|
+
* Pools buffers of common sizes for reuse
|
|
175
|
+
*/
|
|
176
|
+
declare class BufferPool {
|
|
177
|
+
private pools;
|
|
178
|
+
private maxPoolSize;
|
|
179
|
+
private readonly poolSizes;
|
|
180
|
+
constructor();
|
|
181
|
+
/**
|
|
182
|
+
* Get appropriate pool size for requested length
|
|
183
|
+
*/
|
|
184
|
+
private getPoolSize;
|
|
185
|
+
/**
|
|
186
|
+
* Acquire a buffer from the pool or create new one
|
|
187
|
+
*/
|
|
188
|
+
acquire(length: number): Buffer;
|
|
189
|
+
/**
|
|
190
|
+
* Release a buffer back to the pool
|
|
191
|
+
*/
|
|
192
|
+
release(buffer: Buffer): void;
|
|
193
|
+
/**
|
|
194
|
+
* Clear all pools
|
|
195
|
+
*/
|
|
196
|
+
clear(): void;
|
|
197
|
+
/**
|
|
198
|
+
* Get pool statistics
|
|
199
|
+
*/
|
|
200
|
+
stats(): {
|
|
201
|
+
size: number;
|
|
202
|
+
total: number;
|
|
203
|
+
inUse: number;
|
|
204
|
+
}[];
|
|
205
|
+
}
|
|
206
|
+
declare const bufferPool: BufferPool;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Korean encoding decoder module
|
|
210
|
+
*
|
|
211
|
+
* Uses iconv-lite in Node.js for proper CP949 support.
|
|
212
|
+
* Falls back to TextDecoder in browser (with limitations for CP949 extended chars).
|
|
213
|
+
*/
|
|
214
|
+
/**
|
|
215
|
+
* Check if we're in a Node.js environment with iconv-lite available
|
|
216
|
+
*/
|
|
217
|
+
declare function hasIconvLite(): boolean;
|
|
218
|
+
/**
|
|
219
|
+
* Count C1 control characters (U+0080-U+009F) in a string.
|
|
220
|
+
* These usually indicate incorrectly decoded Korean bytes.
|
|
221
|
+
* When EUC-KR decoder encounters CP949-extended bytes (0x80-0x9F range),
|
|
222
|
+
* they get decoded as C1 control characters instead of Korean characters.
|
|
223
|
+
*/
|
|
224
|
+
declare function countC1ControlChars(str: string): number;
|
|
225
|
+
/**
|
|
226
|
+
* Count replacement characters (U+FFFD) in a string
|
|
227
|
+
*/
|
|
228
|
+
declare function countReplacementChars(str: string): number;
|
|
229
|
+
/**
|
|
230
|
+
* Count total "bad" characters (replacement + C1 control)
|
|
231
|
+
*/
|
|
232
|
+
declare function countBadChars(str: string): number;
|
|
233
|
+
/**
|
|
234
|
+
* Check if a string looks like mojibake (CP949 bytes misread as Windows-1252).
|
|
235
|
+
*
|
|
236
|
+
* Mojibake occurs when:
|
|
237
|
+
* 1. Korean text is encoded as CP949 bytes
|
|
238
|
+
* 2. Those bytes are incorrectly decoded as Windows-1252/Latin-1
|
|
239
|
+
*
|
|
240
|
+
* Example: "유저인터페이스" → "À¯ÀúÀÎÅÍÆäÀ̽º"
|
|
241
|
+
*
|
|
242
|
+
* @param str - The string to check
|
|
243
|
+
* @returns true if the string appears to be mojibake
|
|
244
|
+
*/
|
|
245
|
+
declare function isMojibake(str: string): boolean;
|
|
246
|
+
/**
|
|
247
|
+
* Fix mojibake by re-encoding as Windows-1252 and decoding as CP949.
|
|
248
|
+
*
|
|
249
|
+
* This reverses the common encoding error where CP949 bytes were
|
|
250
|
+
* incorrectly interpreted as Windows-1252.
|
|
251
|
+
*
|
|
252
|
+
* Example: "À¯ÀúÀÎÅÍÆäÀ̽º" → "유저인터페이스"
|
|
253
|
+
*
|
|
254
|
+
* @param garbled - The mojibake string to fix
|
|
255
|
+
* @returns The corrected Korean string, or the original if unfixable
|
|
256
|
+
*/
|
|
257
|
+
declare function fixMojibake(garbled: string): string;
|
|
258
|
+
/**
|
|
259
|
+
* Convert Korean text to mojibake (for testing purposes).
|
|
260
|
+
*
|
|
261
|
+
* This simulates the encoding error where Korean text is encoded as CP949
|
|
262
|
+
* but decoded as Windows-1252.
|
|
263
|
+
*
|
|
264
|
+
* Example: "유저인터페이스" → "À¯ÀúÀÎÅÍÆäÀ̽º"
|
|
265
|
+
*
|
|
266
|
+
* @param korean - The Korean string to garble
|
|
267
|
+
* @returns The mojibake string
|
|
268
|
+
*/
|
|
269
|
+
declare function toMojibake(korean: string): string;
|
|
270
|
+
/**
|
|
271
|
+
* Normalize a filename by detecting and fixing encoding issues.
|
|
272
|
+
*
|
|
273
|
+
* This function:
|
|
274
|
+
* 1. Checks if the filename is mojibake and fixes it
|
|
275
|
+
* 2. Returns the normalized filename
|
|
276
|
+
*
|
|
277
|
+
* @param filename - The filename to normalize
|
|
278
|
+
* @returns The normalized filename
|
|
279
|
+
*/
|
|
280
|
+
declare function normalizeFilename(filename: string): string;
|
|
281
|
+
/**
|
|
282
|
+
* Normalize a path by fixing mojibake in each segment.
|
|
283
|
+
*
|
|
284
|
+
* @param filepath - The full path to normalize
|
|
285
|
+
* @returns The normalized path
|
|
286
|
+
*/
|
|
287
|
+
declare function normalizePath(filepath: string): string;
|
|
288
|
+
|
|
289
|
+
export { type FilenameEncoding, type FindOptions, GRF_ERROR_CODES, GrfBrowser, GrfError, GrfNode, type GrfNodeOptions, type GrfOptions, type GrfStats, type ResolveResult, type TFileEntry, bufferPool, countBadChars, countC1ControlChars, countReplacementChars, fixMojibake, hasIconvLite, isMojibake, normalizePath as normalizeEncodingPath, normalizeFilename, toMojibake };
|
package/dist/index.d.ts
CHANGED
|
@@ -6,25 +6,146 @@ interface TFileEntry {
|
|
|
6
6
|
realSize: number;
|
|
7
7
|
compressedSize: number;
|
|
8
8
|
lengthAligned: number;
|
|
9
|
+
/** Raw filename bytes for re-decoding if needed */
|
|
10
|
+
rawNameBytes?: Uint8Array;
|
|
11
|
+
}
|
|
12
|
+
/** Supported filename encodings */
|
|
13
|
+
type FilenameEncoding = 'utf-8' | 'euc-kr' | 'cp949' | 'latin1' | 'auto';
|
|
14
|
+
/** GRF loader options */
|
|
15
|
+
interface GrfOptions {
|
|
16
|
+
/** Encoding for filenames (default: 'auto') */
|
|
17
|
+
filenameEncoding?: FilenameEncoding;
|
|
18
|
+
/** Threshold for auto-detection: if % of U+FFFD exceeds this, try Korean encodings (default: 0.01 = 1%) */
|
|
19
|
+
autoDetectThreshold?: number;
|
|
20
|
+
/** Maximum uncompressed size per file in bytes (default: 256MB) */
|
|
21
|
+
maxFileUncompressedBytes?: number;
|
|
22
|
+
/** Maximum total entries allowed (default: 500000) */
|
|
23
|
+
maxEntries?: number;
|
|
24
|
+
}
|
|
25
|
+
/** Search/find options */
|
|
26
|
+
interface FindOptions {
|
|
27
|
+
/** Filter by file extension (without dot, e.g., 'spr', 'act') */
|
|
28
|
+
ext?: string;
|
|
29
|
+
/** Filter by substring in path */
|
|
30
|
+
contains?: string;
|
|
31
|
+
/** Filter by path ending */
|
|
32
|
+
endsWith?: string;
|
|
33
|
+
/** Filter by regex pattern */
|
|
34
|
+
regex?: RegExp;
|
|
35
|
+
/** Maximum results to return (default: unlimited) */
|
|
36
|
+
limit?: number;
|
|
37
|
+
}
|
|
38
|
+
/** Result of path resolution */
|
|
39
|
+
interface ResolveResult {
|
|
40
|
+
status: 'found' | 'not_found' | 'ambiguous';
|
|
41
|
+
/** The exact matched path (if found) */
|
|
42
|
+
matchedPath?: string;
|
|
43
|
+
/** All candidate paths (if ambiguous) */
|
|
44
|
+
candidates?: string[];
|
|
45
|
+
}
|
|
46
|
+
/** GRF statistics */
|
|
47
|
+
interface GrfStats {
|
|
48
|
+
/** Total file count */
|
|
49
|
+
fileCount: number;
|
|
50
|
+
/** Number of filenames with replacement character (U+FFFD) */
|
|
51
|
+
badNameCount: number;
|
|
52
|
+
/** Number of normalized key collisions */
|
|
53
|
+
collisionCount: number;
|
|
54
|
+
/** Extension statistics: ext -> count */
|
|
55
|
+
extensionStats: Map<string, number>;
|
|
56
|
+
/** Detected encoding used */
|
|
57
|
+
detectedEncoding: FilenameEncoding;
|
|
58
|
+
}
|
|
59
|
+
declare const GRF_ERROR_CODES: {
|
|
60
|
+
readonly INVALID_MAGIC: "GRF_INVALID_MAGIC";
|
|
61
|
+
readonly UNSUPPORTED_VERSION: "GRF_UNSUPPORTED_VERSION";
|
|
62
|
+
readonly NOT_LOADED: "GRF_NOT_LOADED";
|
|
63
|
+
readonly FILE_NOT_FOUND: "GRF_FILE_NOT_FOUND";
|
|
64
|
+
readonly AMBIGUOUS_PATH: "GRF_AMBIGUOUS_PATH";
|
|
65
|
+
readonly DECOMPRESS_FAIL: "GRF_DECOMPRESS_FAIL";
|
|
66
|
+
readonly CORRUPT_TABLE: "GRF_CORRUPT_TABLE";
|
|
67
|
+
readonly LIMIT_EXCEEDED: "GRF_LIMIT_EXCEEDED";
|
|
68
|
+
readonly INVALID_OFFSET: "GRF_INVALID_OFFSET";
|
|
69
|
+
readonly DECRYPT_REQUIRED: "GRF_DECRYPT_REQUIRED";
|
|
70
|
+
};
|
|
71
|
+
declare class GrfError extends Error {
|
|
72
|
+
code: keyof typeof GRF_ERROR_CODES;
|
|
73
|
+
context?: Record<string, unknown> | undefined;
|
|
74
|
+
constructor(code: keyof typeof GRF_ERROR_CODES, message: string, context?: Record<string, unknown> | undefined);
|
|
9
75
|
}
|
|
10
76
|
declare abstract class GrfBase<T> {
|
|
11
77
|
private fd;
|
|
12
78
|
version: number;
|
|
13
79
|
fileCount: number;
|
|
14
80
|
loaded: boolean;
|
|
81
|
+
/** Map of exact filename -> entry */
|
|
15
82
|
files: Map<string, TFileEntry>;
|
|
83
|
+
/** Map of normalized path -> array of exact filenames (supports collisions) */
|
|
84
|
+
private normalizedIndex;
|
|
85
|
+
/** Map of extension -> array of exact filenames (for fast extension lookup) */
|
|
86
|
+
private extensionIndex;
|
|
16
87
|
private fileTableOffset;
|
|
17
|
-
|
|
88
|
+
private cache;
|
|
89
|
+
private cacheMaxSize;
|
|
90
|
+
private cacheOrder;
|
|
91
|
+
protected options: Required<GrfOptions>;
|
|
92
|
+
private _stats;
|
|
93
|
+
constructor(fd: T, options?: GrfOptions);
|
|
18
94
|
abstract getStreamBuffer(fd: T, offset: number, length: number): Promise<Uint8Array>;
|
|
19
95
|
getStreamReader(offset: number, length: number): Promise<jDataview>;
|
|
20
96
|
load(): Promise<void>;
|
|
21
97
|
private parseHeader;
|
|
22
98
|
private parseFileList;
|
|
23
99
|
private decodeEntry;
|
|
100
|
+
private addToCache;
|
|
101
|
+
private getFromCache;
|
|
102
|
+
clearCache(): void;
|
|
24
103
|
getFile(filename: string): Promise<{
|
|
25
104
|
data: null | Uint8Array;
|
|
26
105
|
error: null | string;
|
|
27
106
|
}>;
|
|
107
|
+
/**
|
|
108
|
+
* Resolve a path to its exact filename in the GRF.
|
|
109
|
+
* Tries exact match first, then normalized (case-insensitive, slash-agnostic).
|
|
110
|
+
*/
|
|
111
|
+
resolvePath(query: string): ResolveResult;
|
|
112
|
+
/**
|
|
113
|
+
* Check if a file exists in the GRF.
|
|
114
|
+
*/
|
|
115
|
+
hasFile(filename: string): boolean;
|
|
116
|
+
/**
|
|
117
|
+
* Get file entry metadata without extracting the file.
|
|
118
|
+
*/
|
|
119
|
+
getEntry(filename: string): TFileEntry | null;
|
|
120
|
+
/**
|
|
121
|
+
* Find files matching the given criteria.
|
|
122
|
+
*/
|
|
123
|
+
find(options?: FindOptions): string[];
|
|
124
|
+
/**
|
|
125
|
+
* Get all files with a specific extension.
|
|
126
|
+
*/
|
|
127
|
+
getFilesByExtension(ext: string): string[];
|
|
128
|
+
/**
|
|
129
|
+
* List all unique extensions in the GRF.
|
|
130
|
+
*/
|
|
131
|
+
listExtensions(): string[];
|
|
132
|
+
/**
|
|
133
|
+
* List all files in the GRF.
|
|
134
|
+
*/
|
|
135
|
+
listFiles(): string[];
|
|
136
|
+
/**
|
|
137
|
+
* Get GRF statistics.
|
|
138
|
+
*/
|
|
139
|
+
getStats(): GrfStats;
|
|
140
|
+
/**
|
|
141
|
+
* Get the detected/configured encoding used for filenames.
|
|
142
|
+
*/
|
|
143
|
+
getDetectedEncoding(): FilenameEncoding;
|
|
144
|
+
/**
|
|
145
|
+
* Re-decode all filenames with a different encoding.
|
|
146
|
+
* Useful if auto-detection chose wrong or you want to try a specific encoding.
|
|
147
|
+
*/
|
|
148
|
+
reloadWithEncoding(encoding: FilenameEncoding): Promise<void>;
|
|
28
149
|
}
|
|
29
150
|
|
|
30
151
|
/**
|
|
@@ -33,12 +154,136 @@ declare abstract class GrfBase<T> {
|
|
|
33
154
|
* loading 2 gigas into memory
|
|
34
155
|
*/
|
|
35
156
|
declare class GrfBrowser extends GrfBase<File | Blob> {
|
|
157
|
+
constructor(file: File | Blob, options?: GrfOptions);
|
|
36
158
|
getStreamBuffer(buffer: File | Blob, offset: number, length: number): Promise<Uint8Array>;
|
|
37
159
|
}
|
|
38
160
|
|
|
161
|
+
/** Options for GrfNode */
|
|
162
|
+
interface GrfNodeOptions extends GrfOptions {
|
|
163
|
+
/** Use buffer pool for better performance (default: true) */
|
|
164
|
+
useBufferPool?: boolean;
|
|
165
|
+
}
|
|
39
166
|
declare class GrfNode extends GrfBase<number> {
|
|
40
|
-
|
|
167
|
+
private useBufferPool;
|
|
168
|
+
constructor(fd: number, options?: GrfNodeOptions);
|
|
41
169
|
getStreamBuffer(fd: number, offset: number, length: number): Promise<Uint8Array>;
|
|
42
170
|
}
|
|
43
171
|
|
|
44
|
-
|
|
172
|
+
/**
|
|
173
|
+
* Simple buffer pool for reducing GC pressure
|
|
174
|
+
* Pools buffers of common sizes for reuse
|
|
175
|
+
*/
|
|
176
|
+
declare class BufferPool {
|
|
177
|
+
private pools;
|
|
178
|
+
private maxPoolSize;
|
|
179
|
+
private readonly poolSizes;
|
|
180
|
+
constructor();
|
|
181
|
+
/**
|
|
182
|
+
* Get appropriate pool size for requested length
|
|
183
|
+
*/
|
|
184
|
+
private getPoolSize;
|
|
185
|
+
/**
|
|
186
|
+
* Acquire a buffer from the pool or create new one
|
|
187
|
+
*/
|
|
188
|
+
acquire(length: number): Buffer;
|
|
189
|
+
/**
|
|
190
|
+
* Release a buffer back to the pool
|
|
191
|
+
*/
|
|
192
|
+
release(buffer: Buffer): void;
|
|
193
|
+
/**
|
|
194
|
+
* Clear all pools
|
|
195
|
+
*/
|
|
196
|
+
clear(): void;
|
|
197
|
+
/**
|
|
198
|
+
* Get pool statistics
|
|
199
|
+
*/
|
|
200
|
+
stats(): {
|
|
201
|
+
size: number;
|
|
202
|
+
total: number;
|
|
203
|
+
inUse: number;
|
|
204
|
+
}[];
|
|
205
|
+
}
|
|
206
|
+
declare const bufferPool: BufferPool;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Korean encoding decoder module
|
|
210
|
+
*
|
|
211
|
+
* Uses iconv-lite in Node.js for proper CP949 support.
|
|
212
|
+
* Falls back to TextDecoder in browser (with limitations for CP949 extended chars).
|
|
213
|
+
*/
|
|
214
|
+
/**
|
|
215
|
+
* Check if we're in a Node.js environment with iconv-lite available
|
|
216
|
+
*/
|
|
217
|
+
declare function hasIconvLite(): boolean;
|
|
218
|
+
/**
|
|
219
|
+
* Count C1 control characters (U+0080-U+009F) in a string.
|
|
220
|
+
* These usually indicate incorrectly decoded Korean bytes.
|
|
221
|
+
* When EUC-KR decoder encounters CP949-extended bytes (0x80-0x9F range),
|
|
222
|
+
* they get decoded as C1 control characters instead of Korean characters.
|
|
223
|
+
*/
|
|
224
|
+
declare function countC1ControlChars(str: string): number;
|
|
225
|
+
/**
|
|
226
|
+
* Count replacement characters (U+FFFD) in a string
|
|
227
|
+
*/
|
|
228
|
+
declare function countReplacementChars(str: string): number;
|
|
229
|
+
/**
|
|
230
|
+
* Count total "bad" characters (replacement + C1 control)
|
|
231
|
+
*/
|
|
232
|
+
declare function countBadChars(str: string): number;
|
|
233
|
+
/**
|
|
234
|
+
* Check if a string looks like mojibake (CP949 bytes misread as Windows-1252).
|
|
235
|
+
*
|
|
236
|
+
* Mojibake occurs when:
|
|
237
|
+
* 1. Korean text is encoded as CP949 bytes
|
|
238
|
+
* 2. Those bytes are incorrectly decoded as Windows-1252/Latin-1
|
|
239
|
+
*
|
|
240
|
+
* Example: "유저인터페이스" → "À¯ÀúÀÎÅÍÆäÀ̽º"
|
|
241
|
+
*
|
|
242
|
+
* @param str - The string to check
|
|
243
|
+
* @returns true if the string appears to be mojibake
|
|
244
|
+
*/
|
|
245
|
+
declare function isMojibake(str: string): boolean;
|
|
246
|
+
/**
|
|
247
|
+
* Fix mojibake by re-encoding as Windows-1252 and decoding as CP949.
|
|
248
|
+
*
|
|
249
|
+
* This reverses the common encoding error where CP949 bytes were
|
|
250
|
+
* incorrectly interpreted as Windows-1252.
|
|
251
|
+
*
|
|
252
|
+
* Example: "À¯ÀúÀÎÅÍÆäÀ̽º" → "유저인터페이스"
|
|
253
|
+
*
|
|
254
|
+
* @param garbled - The mojibake string to fix
|
|
255
|
+
* @returns The corrected Korean string, or the original if unfixable
|
|
256
|
+
*/
|
|
257
|
+
declare function fixMojibake(garbled: string): string;
|
|
258
|
+
/**
|
|
259
|
+
* Convert Korean text to mojibake (for testing purposes).
|
|
260
|
+
*
|
|
261
|
+
* This simulates the encoding error where Korean text is encoded as CP949
|
|
262
|
+
* but decoded as Windows-1252.
|
|
263
|
+
*
|
|
264
|
+
* Example: "유저인터페이스" → "À¯ÀúÀÎÅÍÆäÀ̽º"
|
|
265
|
+
*
|
|
266
|
+
* @param korean - The Korean string to garble
|
|
267
|
+
* @returns The mojibake string
|
|
268
|
+
*/
|
|
269
|
+
declare function toMojibake(korean: string): string;
|
|
270
|
+
/**
|
|
271
|
+
* Normalize a filename by detecting and fixing encoding issues.
|
|
272
|
+
*
|
|
273
|
+
* This function:
|
|
274
|
+
* 1. Checks if the filename is mojibake and fixes it
|
|
275
|
+
* 2. Returns the normalized filename
|
|
276
|
+
*
|
|
277
|
+
* @param filename - The filename to normalize
|
|
278
|
+
* @returns The normalized filename
|
|
279
|
+
*/
|
|
280
|
+
declare function normalizeFilename(filename: string): string;
|
|
281
|
+
/**
|
|
282
|
+
* Normalize a path by fixing mojibake in each segment.
|
|
283
|
+
*
|
|
284
|
+
* @param filepath - The full path to normalize
|
|
285
|
+
* @returns The normalized path
|
|
286
|
+
*/
|
|
287
|
+
declare function normalizePath(filepath: string): string;
|
|
288
|
+
|
|
289
|
+
export { type FilenameEncoding, type FindOptions, GRF_ERROR_CODES, GrfBrowser, GrfError, GrfNode, type GrfNodeOptions, type GrfOptions, type GrfStats, type ResolveResult, type TFileEntry, bufferPool, countBadChars, countC1ControlChars, countReplacementChars, fixMojibake, hasIconvLite, isMojibake, normalizePath as normalizeEncodingPath, normalizeFilename, toMojibake };
|