ayunWebEPK/ayunEPKDecompiler.js

244 lines
4.4 KiB
JavaScript
Raw Normal View History

window.decompileEPK = function () {
let currentOffset = 0;
let numFiles = 0;
function detectOldHeader(epkData) {
const oldHeader = "EAGPKG!!";
for (let i = 0; i < oldHeader.length; i++) {
if (epkData[i] != oldHeader.charCodeAt(i)) return false;
}
return true;
}
function readASCII(epkData) {
const len = read(epkData);
let str = "";
for (let i = 0; i < len; i++) {
str += String.fromCharCode(read(epkData));
}
return str;
}
function read(epkData) {
return epkData[currentOffset++];
}
function skip(num) {
currentOffset += num;
}
function loadShort(epkData) {
return (read(epkData) << 8) | read(epkData);
}
function loadInt(epkData) {
return (read(epkData) << 24) | (read(epkData) << 16) | (read(epkData) << 8) | read(epkData);
}
function readUTF(epkData) {
const len = loadShort(epkData);
let str = "";
for (let i = 0; i < len; i++) {
str += String.fromCharCode(read(epkData));
}
return str;
}
// https://stackoverflow.com/a/18639903/6917520
const crc32 = (function() {
let table = new Uint32Array(256);
for (let i = 256; i--;) {
let tmp = i;
for (let k = 8; k--;) {
tmp = tmp & 1 ? 3988292384 ^ tmp >>> 1 : tmp >>> 1;
}
table[i] = tmp;
}
return function(data) {
let crc = -1;
for (let i = 0, l = data.length; i < l; i++) {
crc = crc >>> 8 ^ table[crc & 255 ^ data[i]];
}
return (crc ^ -1) >>> 0;
};
})();
function decompileOld(epkData) {
readUTF(epkData);
try {
let zData = pako.inflate(epkData.slice(currentOffset));
currentOffset = 0;
return readFilesOld(zData);
} catch (err) {
return null;
}
}
function decompileNew(epkData) {
const vers = readASCII(epkData);
if (!vers.startsWith("ver2.")) {
return null;
}
skip(read(epkData));
skip(loadShort(epkData));
skip(8);
numFiles = loadInt(epkData);
const compressionType = String.fromCharCode(read(epkData));
let zData = epkData.slice(currentOffset);
if (compressionType == "Z" || compressionType == "G") {
try {
zData = pako.inflate(zData);
} catch (err) {
return null;
}
} else if (compressionType == "0") {
// do nothing
} else {
return null;
}
currentOffset = 0;
return readFilesNew(zData);
}
function readFilesOld(data) {
let files = [];
let file;
while ((file = readFileOld(data)) != null) {
if (file == -1) return null;
files.push(file);
}
return files;
}
function readFileOld(data) {
const s = readUTF(data);
if (s == " end") {
return null;
} else if (s != "<file>") {
return -1;
}
const path = readUTF(data);
skip(20);
const len = loadInt(data);
const blob = new Blob([data.slice(currentOffset, currentOffset + len)]);
skip(len);
if (readUTF(data) != "</file>") {
return -1;
}
return {
type: "FILE",
name: path,
data: blob
};
}
function readFilesNew(data) {
let files = [];
let file;
while ((file = readFileNew(data)) != null) {
if (file == -1) return null;
files.push(file);
}
return files;
}
function readFileNew(data) {
const type = String.fromCharCode(read(data), read(data), read(data), read(data));
if (numFiles == 0) {
if (type != "END$") {
return -1;
}
return null;
}
if (type == "END$") {
return -1;
}
const name = readASCII(data);
const len = loadInt(data);
let blob = null;
if (type == "FILE") {
if (len < 5) {
return -1;
}
const crc = loadInt(data);
const blobBuffer = data.slice(currentOffset, currentOffset + len - 5);
skip(len - 5);
blob = new Blob([blobBuffer]);
if (crc != (crc32(blobBuffer) | 0)) {
return -1;
}
if (read(data) != 58) {
return -1;
}
} else {
blob = new Blob([data.slice(currentOffset, currentOffset + len)]);
skip(len);
}
if (read(data) != 62) {
return -1;
}
numFiles--;
return {
type: type,
name: name,
data: blob
};
}
return async function(rawBuffer) {
let epkData = new Uint8Array(rawBuffer);
if (detectOldHeader(epkData)) {
epkData = epkData.slice(8);
return decompileOld(epkData);
}
const header = "EAGPKG$$";
for (let i = 0; i < header.length; i++) {
if (epkData[i] != header.charCodeAt(i)) return null;
}
const endCode = ":::YEE:>";
for (let i = 0; i < endCode.length; i++) {
if (epkData[epkData.length - 8 + i] != endCode.charCodeAt(i)) return null;
}
epkData = epkData.slice(8, -8);
return decompileNew(epkData);
};
};