Vehicles
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
// IMG archive reader.
|
||||
//
|
||||
// San Andreas ships VER2 archives: the 'VER2' tag, a u32 entry count, then one
|
||||
// 32 byte directory entry per file -
|
||||
// u32 offset, in 2048 byte sectors
|
||||
// u16 streaming size, u16 size in archive (zero on retail, so prefer the former)
|
||||
// char name[24], null padded
|
||||
// Bodies follow the directory, each starting on a sector boundary.
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
const SECTOR = 2048;
|
||||
|
||||
class Img {
|
||||
constructor(file) {
|
||||
this.fd = fs.openSync(file, 'r');
|
||||
this.path = file;
|
||||
|
||||
const header = Buffer.alloc(8);
|
||||
fs.readSync(this.fd, header, 0, 8, 0);
|
||||
if (header.toString('ascii', 0, 4) !== 'VER2') {
|
||||
throw new Error(`${file} is not a VER2 img archive`);
|
||||
}
|
||||
const count = header.readUInt32LE(4);
|
||||
|
||||
const dir = Buffer.alloc(count * 32);
|
||||
fs.readSync(this.fd, dir, 0, dir.length, 8);
|
||||
|
||||
// Lower cased, because the ide names and the archive names disagree on case.
|
||||
this.entries = new Map();
|
||||
for (let i = 0; i < count; i++) {
|
||||
const at = i * 32;
|
||||
const name = dir.toString('ascii', at + 8, at + 32).split('\0')[0];
|
||||
const sectors = dir.readUInt16LE(at + 4) || dir.readUInt16LE(at + 6);
|
||||
this.entries.set(name.toLowerCase(), {
|
||||
name,
|
||||
offset: dir.readUInt32LE(at) * SECTOR,
|
||||
size: sectors * SECTOR,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
has(name) {
|
||||
return this.entries.has(name.toLowerCase());
|
||||
}
|
||||
|
||||
// Entry bodies are padded out to the sector size, and every reader downstream
|
||||
// walks chunk headers rather than trusting the length, so the padding is
|
||||
// harmless and gets written as-is.
|
||||
read(name) {
|
||||
const entry = this.entries.get(name.toLowerCase());
|
||||
if (!entry) return null;
|
||||
const buffer = Buffer.alloc(entry.size);
|
||||
fs.readSync(this.fd, buffer, 0, entry.size, entry.offset);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
close() {
|
||||
fs.closeSync(this.fd);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { Img };
|
||||
Reference in New Issue
Block a user