"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GameAssembly = exports.isGameAssembly = void 0;
const tslib_1 = require("tslib");
const child_process_1 = require("child_process");
const util_1 = require("util");
const path_1 = tslib_1.__importDefault(require("path"));
const fs_1 = tslib_1.__importDefault(require("fs"));
const fetch_github_release_1 = tslib_1.__importDefault(require("@terascope/fetch-github-release"));
const chalk_1 = require("chalk");
const md5_1 = tslib_1.__importDefault(require("md5"));
const OutputFilter_1 = require("./OutputFilter");
const findNodeModules = require('find-node-modules');
function getCachePath(...subdirs) {
    const nodeModulesDir = path_1.default.resolve(findNodeModules()[0]);
    const cachePath = path_1.default.join(nodeModulesDir, '.cache', 'il2js', ...subdirs);
    if (!fs_1.default.existsSync(cachePath)) {
        fs_1.default.mkdirSync(cachePath, { recursive: true });
    }
    return cachePath;
}
async function getIl2CppDumperExePath() {
    const devConfig = path_1.default.join(__dirname, '../il2cpp.config.js');
    if (fs_1.default.existsSync(devConfig)) {
        // eslint-disable-next-line import/no-dynamic-require, global-require
        return require(devConfig);
    }
    const cachePath = getCachePath();
    const il2CppDumperExePath = path_1.default.join(cachePath, 'il2CppDumper.exe');
    if (fs_1.default.existsSync(il2CppDumperExePath)) {
        return il2CppDumperExePath;
    }
    // download il2cpp release
    await fetch_github_release_1.default('dnchattan', 'Il2CppDumper', cachePath, ((release) => !release.prerelease), (asset) => asset.name.endsWith('.zip'), false);
    return il2CppDumperExePath;
}
function isGameAssembly(value) {
    return !!value.gameAssemblyDllPath;
}
exports.isGameAssembly = isGameAssembly;
class GameAssembly {
    constructor(gameAssemblyDllPath, globalMetadataPath, version, filterOutputVisitor) {
        this.gameAssemblyDllPath = gameAssemblyDllPath;
        this.globalMetadataPath = globalMetadataPath;
        this.version = version;
        this.filterOutputVisitor = filterOutputVisitor;
        this.cached = false;
        this.outDir = getCachePath(version);
    }
    async load() {
        if (!this.structs) {
            await this.ensureDumpExists();
            this.structs = JSON.parse(await fs_1.default.promises.readFile(path_1.default.resolve(this.outDir, `structs.json`), 'utf8'));
            if (this.filterOutputVisitor) {
                this.structs.TypeInfoList = Array.from(new OutputFilter_1.OutputFilter(this.structs).include(this.filterOutputVisitor).typesList.values());
            }
        }
    }
    async ensureDumpExists() {
        const il2CppDumperExePath = await getIl2CppDumperExePath();
        const il2CppDumperDllPath = il2CppDumperExePath.replace('.exe', '.dll');
        const hashPath = `${il2CppDumperDllPath}.hash.txt`;
        const structsPath = path_1.default.join(this.outDir, 'structs.json');
        if (!fs_1.default.existsSync(this.outDir)) {
            fs_1.default.mkdirSync(this.outDir, { recursive: true });
        }
        const currentHash = md5_1.default(fs_1.default.readFileSync(il2CppDumperDllPath));
        if (fs_1.default.existsSync(structsPath)) {
            const lastHash = fs_1.default.existsSync(hashPath) ? fs_1.default.readFileSync(hashPath, 'utf8') : undefined;
            if (lastHash === currentHash) {
                console.log(`Re-using output (hash=${currentHash})`);
                this.cached = true;
                return;
            }
        }
        fs_1.default.writeFileSync(hashPath, currentHash, 'utf8');
        const cmd = [il2CppDumperExePath, this.gameAssemblyDllPath, this.globalMetadataPath, this.outDir]
            .map((part) => `"${part}"`)
            .join(' ');
        const result = await util_1.promisify(child_process_1.exec)(cmd);
        if (result.stderr) {
            console.error(chalk_1.stderr);
        }
        else {
            console.log(result.stdout);
        }
    }
}
exports.GameAssembly = GameAssembly;
//# sourceMappingURL=GameAssembly.js.map