"use strict";
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2024-2026 Gracker (Chris)
// This file is part of SmartPerfetto. See LICENSE for details.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.detectHostPlatformKey = detectHostPlatformKey;
exports.resolveAdbTool = resolveAdbTool;
exports.resolveTraceboxTool = resolveTraceboxTool;
exports.androidTraceboxPlatformKey = androidTraceboxPlatformKey;
exports.assertToolUsable = assertToolUsable;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const crypto = __importStar(require("crypto"));
const child_process_1 = require("child_process");
function detectHostPlatformKey() {
    if (process.platform === 'darwin' && process.arch === 'arm64')
        return 'darwin-arm64';
    if (process.platform === 'darwin' && process.arch === 'x64')
        return 'darwin-x64';
    if (process.platform === 'linux' && process.arch === 'x64')
        return 'linux-x64';
    if (process.platform === 'linux' && process.arch === 'arm64')
        return 'linux-arm64';
    if (process.platform === 'win32' && process.arch === 'x64')
        return 'win32-x64';
    return undefined;
}
function resolveAdbTool(opts = {}) {
    const env = opts.env ?? process.env;
    const platformKey = detectHostPlatformKey();
    const executableName = process.platform === 'win32' ? 'adb.exe' : 'adb';
    const envPath = env.ADB_PATH?.trim();
    if (envPath) {
        return buildResolution({
            name: 'adb',
            source: 'env',
            path: path.resolve(envPath),
            platformKey,
            hint: 'ADB_PATH is set but does not point to an executable adb binary.',
        });
    }
    if (platformKey) {
        const bundledPath = path.join(opts.backendRoot ?? process.cwd(), 'prebuilts', 'android-platform-tools', platformKey, executableName);
        const bundled = buildResolution({
            name: 'adb',
            source: 'bundled',
            path: bundledPath,
            platformKey,
            hint: 'Bundled ADB is not installed in this package. Set ADB_PATH or put adb on PATH.',
        });
        if (bundled.exists && bundled.executable)
            return bundled;
    }
    return buildPathResolution({
        name: 'adb',
        executableName,
        platformKey,
        hint: 'adb was not found. Set ADB_PATH or install Android SDK Platform-Tools.',
    });
}
function resolveTraceboxTool(overridePath, platformKey = detectHostPlatformKey(), opts = {}) {
    const executableName = platformKey?.startsWith('win32') ? 'tracebox.exe' : 'tracebox';
    if (overridePath?.trim()) {
        return buildResolution({
            name: 'tracebox',
            source: 'override',
            path: path.resolve(overridePath),
            platformKey,
            hint: '--tracebox does not point to an executable tracebox binary.',
        });
    }
    if (platformKey) {
        return buildResolution({
            name: 'tracebox',
            source: 'bundled',
            path: path.join(opts.backendRoot ?? process.cwd(), 'prebuilts', 'perfetto-recording-tools', platformKey, executableName),
            platformKey,
            hint: [
                'No approved bundled tracebox binary is available for this platform.',
                'Use --tracebox /path/to/tracebox, or capture on Android Q/API 29+ without --sideload.',
            ].join(' '),
        });
    }
    return {
        name: 'tracebox',
        source: 'missing',
        path: executableName,
        exists: false,
        executable: false,
        hint: 'Unsupported host platform for bundled tracebox. Use --tracebox /path/to/tracebox.',
    };
}
function androidTraceboxPlatformKey(abi) {
    switch (abi) {
        case 'arm64-v8a':
            return 'android-arm64';
        case 'armeabi-v7a':
        case 'armeabi':
            return 'android-arm';
        case 'x86_64':
            return 'android-x64';
        case 'x86':
            return 'android-x86';
        default:
            return undefined;
    }
}
function assertToolUsable(tool) {
    if (tool.exists && tool.executable) {
        if (tool.name === 'tracebox' && tool.source === 'bundled') {
            assertBundledTraceboxSha(tool);
        }
        return;
    }
    throw new Error(`${tool.name} is not available at ${tool.path}. ${tool.hint ?? ''}`.trim());
}
function assertBundledTraceboxSha(tool) {
    const platformKey = tool.platformKey;
    const pinKey = platformKey ? traceboxShaKey(platformKey) : undefined;
    const pinFile = findRecordingToolsPinFile();
    if (!pinKey || !pinFile) {
        throw new Error(`bundled tracebox exists but no SHA256 pin can be resolved for ${platformKey ?? 'unknown-platform'}`);
    }
    const pins = parseEnvFile(pinFile);
    const expected = pins[pinKey]?.trim();
    if (!expected) {
        throw new Error(`bundled tracebox exists but ${pinKey} is empty in ${pinFile}`);
    }
    const actual = crypto.createHash('sha256').update(fs.readFileSync(tool.path)).digest('hex');
    if (actual !== expected) {
        throw new Error(`bundled tracebox failed SHA256 verification: expected ${expected}, got ${actual}`);
    }
}
function traceboxShaKey(platformKey) {
    switch (platformKey) {
        case 'linux-x64':
            return 'PERFETTO_TRACEBOX_SHA256_LINUX_X64';
        case 'linux-arm64':
            return 'PERFETTO_TRACEBOX_SHA256_LINUX_ARM64';
        case 'darwin-arm64':
            return 'PERFETTO_TRACEBOX_SHA256_DARWIN_ARM64';
        case 'darwin-x64':
            return 'PERFETTO_TRACEBOX_SHA256_DARWIN_X64';
        case 'android-arm64':
            return 'PERFETTO_TRACEBOX_SHA256_ANDROID_ARM64';
        case 'android-arm':
            return 'PERFETTO_TRACEBOX_SHA256_ANDROID_ARM';
        case 'android-x64':
            return 'PERFETTO_TRACEBOX_SHA256_ANDROID_X64';
        case 'android-x86':
            return 'PERFETTO_TRACEBOX_SHA256_ANDROID_X86';
        default:
            return undefined;
    }
}
function findRecordingToolsPinFile() {
    const candidates = [
        path.join(process.cwd(), 'dist', 'perfetto-recording-tools-pin.env'),
        path.resolve(process.cwd(), '..', 'scripts', 'perfetto-recording-tools-pin.env'),
    ];
    return candidates.find((candidate) => fs.existsSync(candidate));
}
function parseEnvFile(filePath) {
    const values = {};
    for (const line of fs.readFileSync(filePath, 'utf-8').split(/\r?\n/)) {
        const trimmed = line.trim();
        if (!trimmed || trimmed.startsWith('#'))
            continue;
        const idx = trimmed.indexOf('=');
        if (idx <= 0)
            continue;
        values[trimmed.slice(0, idx)] = trimmed.slice(idx + 1);
    }
    return values;
}
function buildResolution(input) {
    const exists = fs.existsSync(input.path);
    return {
        name: input.name,
        source: input.source,
        path: input.path,
        exists,
        executable: exists && isExecutable(input.path),
        platformKey: input.platformKey,
        hint: input.hint,
    };
}
function buildPathResolution(input) {
    const probe = (0, child_process_1.spawnSync)(input.executableName, ['version'], {
        encoding: 'utf-8',
        timeout: 3000,
        stdio: 'ignore',
    });
    const usable = !probe.error && probe.status === 0;
    return {
        name: input.name,
        source: usable ? 'path' : 'missing',
        path: input.executableName,
        exists: usable,
        executable: usable,
        platformKey: input.platformKey,
        hint: input.hint,
    };
}
function isExecutable(filePath) {
    try {
        fs.accessSync(filePath, fs.constants.X_OK);
        return true;
    }
    catch {
        return false;
    }
}
//# sourceMappingURL=captureTools.js.map