"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.computeHashes = computeHashes; exports.computeHashesFromStream = computeHashesFromStream; /** * Servicio: checksumService * * Calcula sumas y metadatos de un fichero de forma eficiente usando streams. * Las funciones principales procesan el archivo en streaming para producir: * - `md5` (hex) * - `sha1` (hex) * - `crc32` (hex, 8 caracteres) * - `size` (bytes) * * `computeHashes(filePath)` devuelve un objeto con los valores anteriores y * está pensado para usarse durante la importación/normalización de ROMs. */ const fs_1 = __importDefault(require("fs")); const crypto_1 = require("crypto"); function makeCRCTable() { const table = new Uint32Array(256); for (let n = 0; n < 256; n++) { let c = n; for (let k = 0; k < 8; k++) { if (c & 1) c = 0xedb88320 ^ (c >>> 1); else c = c >>> 1; } table[n] = c >>> 0; } return table; } const CRC_TABLE = makeCRCTable(); function updateCrc(crc, buf) { let c = crc >>> 0; for (let i = 0; i < buf.length; i++) { c = (CRC_TABLE[(c ^ buf[i]) & 0xff] ^ (c >>> 8)) >>> 0; } return c >>> 0; } async function computeHashes(filePath) { return new Promise((resolve, reject) => { const md5 = (0, crypto_1.createHash)('md5'); const sha1 = (0, crypto_1.createHash)('sha1'); let size = 0; let crc = 0xffffffff >>> 0; const rs = fs_1.default.createReadStream(filePath); rs.on('error', (err) => reject(err)); rs.on('data', (chunk) => { md5.update(chunk); sha1.update(chunk); size += chunk.length; crc = updateCrc(crc, chunk); }); rs.on('end', () => { const md5sum = md5.digest('hex'); const sha1sum = sha1.digest('hex'); const final = (crc ^ 0xffffffff) >>> 0; const crcHex = final.toString(16).padStart(8, '0').toLowerCase(); resolve({ size, md5: md5sum, sha1: sha1sum, crc32: crcHex }); }); }); } async function computeHashesFromStream(rs) { return new Promise((resolve, reject) => { const md5 = (0, crypto_1.createHash)('md5'); const sha1 = (0, crypto_1.createHash)('sha1'); let size = 0; let crc = 0xffffffff >>> 0; let settled = false; const cleanup = () => { try { rs.removeListener('error', onError); rs.removeListener('data', onData); rs.removeListener('end', onEnd); rs.removeListener('close', onClose); } catch (e) { } }; const finalize = () => { if (settled) return; settled = true; cleanup(); const md5sum = md5.digest('hex'); const sha1sum = sha1.digest('hex'); const final = (crc ^ 0xffffffff) >>> 0; const crcHex = final.toString(16).padStart(8, '0').toLowerCase(); resolve({ size, md5: md5sum, sha1: sha1sum, crc32: crcHex }); }; const onError = (err) => { if (settled) return; settled = true; cleanup(); reject(err); }; const onData = (chunk) => { md5.update(chunk); sha1.update(chunk); size += chunk.length; crc = updateCrc(crc, chunk); }; const onEnd = () => finalize(); const onClose = () => finalize(); rs.on('error', onError); rs.on('data', onData); rs.on('end', onEnd); rs.on('close', onClose); }); } exports.default = computeHashes;