UNPKG

igir

Version:

🕹 A zero-setup ROM collection manager that sorts, filters, extracts or archives, patches, and reports on collections of any size on any OS.

97 lines (96 loc) • 4.96 kB
import { ProgressBarSymbol } from '../../console/progressBar.js'; import FsPoly from '../../polyfill/fsPoly.js'; import ArchiveFile from '../../types/files/archives/archiveFile.js'; import Module from '../module.js'; /** * Calculate checksums for {@link ArchiveFile}s (which were skipped in {@link CandidateGenerator}). * This deferral is done to prevent calculating checksums for files that are filtered out by a * candidate filtering module. */ export default class CandidateArchiveFileHasher extends Module { options; fileFactory; driveSemaphore; constructor(options, progressBar, fileFactory, driveSemaphore) { super(progressBar, CandidateArchiveFileHasher.name); this.options = options; this.fileFactory = fileFactory; this.driveSemaphore = driveSemaphore; } /** * Hash the {@link ArchiveFile}s. */ async hash(dat, candidates) { if (candidates.length === 0) { this.progressBar.logTrace(`${dat.getName()}: no candidates to hash ArchiveFiles for`); return candidates; } if (!this.options.shouldTest() && !this.options.getOverwriteInvalid()) { this.progressBar.logTrace(`${dat.getName()}: not testing or overwriting invalid files, no need`); return candidates; } const archiveFileCount = candidates .flatMap((candidate) => candidate.getRomsWithFiles()) .filter((romWithFiles) => romWithFiles.getInputFile() instanceof ArchiveFile).length; if (archiveFileCount === 0) { this.progressBar.logTrace(`${dat.getName()}: no ArchiveFiles to hash`); return candidates; } this.progressBar.logTrace(`${dat.getName()}: generating ${archiveFileCount.toLocaleString()} hashed ArchiveFile candidate${archiveFileCount === 1 ? '' : 's'}`); this.progressBar.setSymbol(ProgressBarSymbol.CANDIDATE_HASHING); this.progressBar.resetProgress(archiveFileCount); const hashedCandidates = this.hashArchiveFiles(dat, candidates); this.progressBar.logTrace(`${dat.getName()}: done generating hashed ArchiveFile candidates`); return hashedCandidates; } async hashArchiveFiles(dat, candidates) { return Promise.all(candidates.map(async (candidate) => { const hashedRomsWithFiles = await Promise.all(candidate.getRomsWithFiles().map(async (romWithFiles) => { const inputFile = romWithFiles.getInputFile(); if (!(inputFile instanceof ArchiveFile)) { return romWithFiles; } const outputFile = romWithFiles.getOutputFile(); if (inputFile.equals(outputFile)) { /** * There's no need to calculate the checksum, {@link CandidateWriter} will skip * writing over itself */ return romWithFiles; } return this.driveSemaphore.runExclusive(inputFile, async () => { this.progressBar.incrementInProgress(); this.progressBar.logTrace(`${dat.getName()}: ${candidate.getName()}: calculating checksums for: ${inputFile.toString()}`); const childBar = this.progressBar.addChildBar({ name: inputFile.toString(), total: inputFile.getSize(), progressFormatter: FsPoly.sizeReadable, }); try { const hashedInputFile = await this.fileFactory.archiveFileFrom(inputFile.getArchive(), inputFile.getChecksumBitmask(), (progress) => { childBar.setCompleted(progress); }); // {@link CandidateGenerator} would have copied undefined values from the input // file, so we need to modify the expected output file as well for testing const hashedOutputFile = outputFile.withProps({ size: hashedInputFile.getSize(), crc32: hashedInputFile.getCrc32(), md5: hashedInputFile.getMd5(), sha1: hashedInputFile.getSha1(), sha256: hashedInputFile.getSha256(), }); const hashedRomWithFiles = romWithFiles .withInputFile(hashedInputFile) .withOutputFile(hashedOutputFile); this.progressBar.incrementCompleted(); return hashedRomWithFiles; } finally { childBar.delete(); } }); })); return candidate.withRomsWithFiles(hashedRomsWithFiles); })); } }