Skip to content

Commit 63e029c

Browse files
author
Flenarn
committed
chore: Abstract and cleanup pack/unpack/dump logic.
1 parent d3bd9da commit 63e029c

File tree

8 files changed

+490
-618
lines changed

8 files changed

+490
-618
lines changed

tools/cache/lib/configSource.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
export type SourceField = {
2+
key: string;
3+
value: string;
4+
line: number;
5+
};
6+
7+
export type SourceSection = {
8+
name: string;
9+
line: number;
10+
fields: SourceField[];
11+
};
12+
13+
export function parseBracketedConfigSource(content: string): SourceSection[] {
14+
const lines = content.split('\n');
15+
const sections: SourceSection[] = [];
16+
let current: SourceSection | null = null;
17+
18+
for (let i = 0; i < lines.length; i++) {
19+
const raw = lines[i];
20+
const line = raw.endsWith('\r') ? raw.slice(0, -1) : raw;
21+
const trimmed = line.trim();
22+
23+
if (trimmed.length === 0 || trimmed.startsWith('//')) {
24+
continue;
25+
}
26+
27+
if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
28+
current = {
29+
name: trimmed.substring(1, trimmed.length - 1),
30+
line: i + 1,
31+
fields: []
32+
};
33+
sections.push(current);
34+
continue;
35+
}
36+
37+
if (!current) {
38+
continue;
39+
}
40+
41+
const eq = line.indexOf('=');
42+
if (eq === -1) {
43+
continue;
44+
}
45+
46+
const key = line.substring(0, eq).trim();
47+
const value = line.substring(eq + 1).trim();
48+
if (key.length === 0) {
49+
continue;
50+
}
51+
52+
current.fields.push({ key, value, line: i + 1 });
53+
}
54+
55+
return sections;
56+
}
57+
58+
export function resolveSectionId(name: string, nameToId: Map<string, number>, fallbackPrefix: string): number | null {
59+
const mapped = nameToId.get(name);
60+
if (mapped !== undefined) {
61+
return mapped;
62+
}
63+
64+
if (name.startsWith(fallbackPrefix)) {
65+
const parsed = parseInt(name.substring(fallbackPrefix.length));
66+
if (!isNaN(parsed)) {
67+
return parsed;
68+
}
69+
}
70+
71+
return null;
72+
}
73+
74+
export function parseConfigBoolean(value: string): boolean {
75+
const normalized = value.trim().toLowerCase();
76+
return normalized === 'yes' || normalized === 'true' || normalized === '1';
77+
}
78+
79+
export function parseConfigInteger(value: string): number {
80+
if (value.startsWith('0x') || value.startsWith('0X')) {
81+
const parsedHex = parseInt(value, 16);
82+
return isNaN(parsedHex) ? 0 : parsedHex;
83+
}
84+
85+
const parsed = parseInt(value);
86+
return isNaN(parsed) ? 0 : parsed;
87+
}

tools/cache/lib/dumpTools.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import fs from 'fs';
2+
import path from 'path';
3+
4+
import { ensureDir } from '#tools/cache/lib/js5Tools.js';
5+
6+
export function writeTextDump(outPath: string, lines: string[]): void {
7+
ensureDir(path.dirname(outPath));
8+
fs.writeFileSync(outPath, lines.join('\n'), 'utf-8');
9+
}
10+
11+
export function writeBinaryDump(outPath: string, bytes: Uint8Array): void {
12+
ensureDir(path.dirname(outPath));
13+
fs.writeFileSync(outPath, bytes);
14+
}
15+
16+
export function formatRgbHex(value: number): string {
17+
return `0x${value.toString(16).toUpperCase().padStart(6, '0')}`;
18+
}

tools/cache/lib/js5Tools.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Packet from '#/io/Packet.js';
44
import { getGroup } from '#/util/OpenRS2.js';
55
import { unpackJs5Group } from '#/io/Js5Group.js';
66
import { CompressionType } from '#/io/CompressionType.js';
7+
import { parseJs5ArchiveIndex as parseJs5ArchiveIndexCore, splitGroupFiles } from '#/io/Js5ArchiveIndex.js';
78

89
export function readJs5Id(packet: Packet, format: number): number {
910
if (format >= 7) {
@@ -117,6 +118,14 @@ export async function readGroupBytes(
117118
}
118119

119120
export function ensureDir(dirPath: string): void {
121+
if (fs.existsSync(dirPath)) {
122+
const stat = fs.statSync(dirPath);
123+
if (stat.isDirectory()) {
124+
return;
125+
}
126+
throw new Error(`Path exists and is not a directory: ${dirPath}`);
127+
}
128+
120129
fs.mkdirSync(dirPath, { recursive: true });
121130
}
122131

@@ -150,11 +159,85 @@ export function parsePackFile(packPath: string): Map<string, number> {
150159
return nameToId;
151160
}
152161

162+
export function parsePackFileById(packPath: string): Map<number, string> {
163+
if (!fs.existsSync(packPath)) {
164+
return new Map();
165+
}
166+
167+
const content = fs.readFileSync(packPath, 'utf-8');
168+
const idToName = new Map<number, string>();
169+
170+
for (const line of content.split('\n')) {
171+
const trimmed = line.trim();
172+
if (trimmed.length === 0 || trimmed.startsWith('#')) {
173+
continue;
174+
}
175+
176+
const eq = trimmed.indexOf('=');
177+
if (eq === -1) {
178+
continue;
179+
}
180+
181+
const id = parseInt(trimmed.substring(0, eq));
182+
const name = trimmed.substring(eq + 1);
183+
184+
if (!isNaN(id) && name.length > 0) {
185+
idToName.set(id, name);
186+
}
187+
}
188+
189+
return idToName;
190+
}
191+
153192
export type Js5ArchiveIndex = {
154193
groupIds: number[];
155194
fileIdsByGroup: Map<number, number[]>;
156195
};
157196

197+
export async function loadArchiveFileIds(indexArchive: number, archive: number, openrs2: boolean = true): Promise<number[]> {
198+
const indexPacked = await loadIndexPacked(indexArchive, `data/cache/255/${indexArchive}.dat`, openrs2);
199+
const indexData = unpackJs5Group(indexPacked);
200+
const { fileIdsByGroup } = parseJs5ArchiveIndexCore(indexData);
201+
const fileIds = fileIdsByGroup.get(archive);
202+
203+
if (!fileIds) {
204+
throw new Error(`Archive ${archive} not found in index ${indexArchive}`);
205+
}
206+
207+
return fileIds;
208+
}
209+
210+
export type LoadedArchiveGroup = {
211+
fileIds: number[];
212+
groupPacked: Uint8Array;
213+
groupUnpacked: Uint8Array;
214+
files: Map<number, Uint8Array>;
215+
};
216+
217+
export async function loadArchiveGroupFiles(
218+
indexArchive: number,
219+
archive: number,
220+
groupsDir: string,
221+
openrs2: boolean = true
222+
): Promise<LoadedArchiveGroup> {
223+
const fileIds = await loadArchiveFileIds(indexArchive, archive, openrs2);
224+
const groupPacked = await readGroupBytes(indexArchive, archive, groupsDir, openrs2);
225+
226+
if (!groupPacked) {
227+
throw new Error(`Missing group data for index ${indexArchive}, archive ${archive}`);
228+
}
229+
230+
const groupUnpacked = unpackJs5Group(groupPacked);
231+
const files = splitGroupFiles(groupUnpacked, fileIds);
232+
233+
return {
234+
fileIds,
235+
groupPacked,
236+
groupUnpacked,
237+
files
238+
};
239+
}
240+
158241
export function parseJs5ArchiveIndex(indexData: Uint8Array): Js5ArchiveIndex {
159242
const packet = new Packet(indexData);
160243

0 commit comments

Comments
 (0)