🚧 Add reader for .solidpkg files

This commit is contained in:
C-3PO 2018-06-21 22:39:15 +02:00
parent 1a37702b24
commit 90a090f6e3
Signed by: c3po
GPG key ID: 62993C4BB4D86F24
2 changed files with 87 additions and 0 deletions

View file

@ -1,6 +1,10 @@
import getSolidpkg from './getSolidpkg'; import getSolidpkg from './getSolidpkg';
import readSolidpkg from './readSolidpkg';
(async () => { (async () => {
const buffer = await getSolidpkg('assets_swtor_de_de', -1, 0); const buffer = await getSolidpkg('assets_swtor_de_de', -1, 0);
console.log(buffer.length, buffer); console.log(buffer.length, buffer);
const output = readSolidpkg(buffer);
console.log(output);
})(); })();

83
src/readSolidpkg.ts Normal file
View file

@ -0,0 +1,83 @@
const MAGIC_END_OF_CENTRAL_DIR = 0x06054b50;
const MAGIC_CENTRAL_DIR = 0x02014b50;
const COMPRESSION_DEFLATE = 8;
export default function readSolidpkg(buffer: Buffer) {
const dv = new DataView(buffer.buffer);
//--------------- READ END OF CENTRAL DIR ---------------
//Go to end of file
let pos = buffer.length - 22; //end of central dir is at least 22 bytes long
//Find end of central dir
while (pos >= 0 && dv.getUint32(pos, true) !== MAGIC_END_OF_CENTRAL_DIR) {
pos -= 1;
}
if (pos < 0) {
throw new Error('Could not find end of central dir.');
}
//skip 6 bytes
pos += 6;
/** Total number of entries in the central directory */
const numEntries = dv.getUint16(pos, true); pos += 2;
/** Size of the central directory */
const centralDirSize = dv.getUint32(pos, true); pos += 4;
/** Offset of start of central directory with respect to the starting disk number */
const centralDirOffset = dv.getUint32(pos, true); pos += 4;
if (numEntries !== 1) {
throw new Error(`Expected numEntries == 1 in end of central dir but it was "${numEntries}"`);
}
if (centralDirSize < 46 * numEntries) {
throw new Error('centralDirSize was smaller than expected in end of central dir.');
}
if (pos - centralDirSize < 0) {
throw new Error(`Central dir points before file start (0x${(pos - centralDirSize).toString(16)})`);
}
//--------------- READ CENTRAL DIR ---------------
//Go to start of central dir
pos -= -20 - centralDirSize;
{
const magicNum = dv.getUint32(pos, true); pos += 4;
if (magicNum !== MAGIC_CENTRAL_DIR) {
throw new Error(`Expected central dir signature but found "0x${magicNum.toString(16)}"`);
}
}
pos += 4; //skip version
/** The bitflag stores whether the file is encrypted or not. Most files are encrypted but there are
* some exceptions: assets_swtor_test_main_248to249.solidpkg, assets_swtor_test_en_us_270to271.solidpkg, and
* retailclient_publictest_246to247.solidpkg are not encrypted.
*/
const bitFlag = dv.getUint16(pos, true); pos += 2;
/** Type of compression, 8 = DEFLATE. Files are always compressed. */
const compression = dv.getUint16(pos, true); pos += 2;
if (compression !== COMPRESSION_DEFLATE) {
throw new Error(`File is not using DEFLATE compression but "${compression}"`);
}
const lastMod1 = dv.getUint16(pos, true); pos += 2;
const lastMod2 = dv.getUint16(pos, true); pos += 2;
const fileCrc = dv.getUint32(pos, true); pos += 4;
const comprSize = dv.getUint32(pos, true); pos += 4;
const uncomprSize = dv.getUint32(pos, true); pos += 4;
const fileNameLength = dv.getUint16(pos, true); pos += 2;
const extraFieldLength = dv.getUint16(pos, true); pos += 2;
const fileCommentLength = dv.getUint16(pos, true); pos += 2;
pos += 8; //skip disk num start and file attributes
const relOffset = dv.getUint32(pos, true); pos += 4;
pos += fileNameLength; //skip file name
//TODO: read password from extra field
//...
return { lastMod1, lastMod2, fileCrc, comprSize, uncomprSize, fileNameLength, extraFieldLength, fileCommentLength, relOffset };
}