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.

94 lines (93 loc) • 4.39 kB
import async from 'async'; import { ProgressBarSymbol } from '../../console/progressBar.js'; import Defaults from '../../globals/defaults.js'; import ArchiveEntry from '../../types/files/archives/archiveEntry.js'; import ROMHeader from '../../types/files/romHeader.js'; import Module from '../module.js'; /** * For every input {@link File} file found, attempt to find a matching {@link Header} and resolve * its header-less checksums. */ export default class ROMHeaderProcessor extends Module { options; fileFactory; driveSemaphore; constructor(options, progressBar, fileFactory, driveSemaphore) { super(progressBar, ROMHeaderProcessor.name); this.options = options; this.fileFactory = fileFactory; this.driveSemaphore = driveSemaphore; } /** * Process each {@link File}, finding any {@link Header} present. */ async process(inputRomFiles) { if (inputRomFiles.length === 0) { return inputRomFiles; } const filesThatNeedProcessing = inputRomFiles.filter((inputFile) => this.fileNeedsProcessing(inputFile)).length; if (filesThatNeedProcessing === 0) { this.progressBar.logTrace('no ROMs need their header processed'); return inputRomFiles; } this.progressBar.logTrace(`processing headers in ${filesThatNeedProcessing.toLocaleString()} ROM${filesThatNeedProcessing === 1 ? '' : 's'}`); this.progressBar.setSymbol(ProgressBarSymbol.ROM_HEADER_DETECTION); this.progressBar.resetProgress(filesThatNeedProcessing); const parsedFiles = await async.mapLimit(inputRomFiles, Defaults.MAX_FS_THREADS, async (inputFile) => { if (!this.fileNeedsProcessing(inputFile)) { return inputFile; } return this.driveSemaphore.runExclusive(inputFile, async () => { this.progressBar.incrementInProgress(); const childBar = this.progressBar.addChildBar({ name: inputFile.toString(), }); let fileWithHeader; try { fileWithHeader = await this.getFileWithHeader(inputFile); } catch (error) { this.progressBar.logError(`${inputFile.toString()}: failed to process ROM header: ${error}`); fileWithHeader = inputFile; } finally { childBar.delete(); } this.progressBar.incrementCompleted(); return fileWithHeader; }); }); const headeredRomsCount = parsedFiles.filter((romFile) => romFile.getFileHeader() !== undefined).length; this.progressBar.logTrace(`found headers in ${headeredRomsCount.toLocaleString()} ROM${headeredRomsCount === 1 ? '' : 's'}`); this.progressBar.logTrace('done processing file headers'); return parsedFiles; } fileNeedsProcessing(inputFile) { /** * If the input file is from an archive, and we're not zipping or extracting, then we have no * chance to remove the header, so we shouldn't bother detecting one. * Matches {@link CandidateGenerator#buildCandidatesForGame} */ if (inputFile instanceof ArchiveEntry && !this.options.shouldZip() && !this.options.shouldExtract()) { return false; } if (inputFile.getSize() === 0) { // It can't have a header return false; } return (ROMHeader.headerFromFilename(inputFile.getExtractedFilePath()) !== undefined || this.options.shouldReadFileForHeader(inputFile.getExtractedFilePath())); } async getFileWithHeader(inputFile) { this.progressBar.logTrace(`${inputFile.toString()}: reading potentially headered file by file contents`); const headerForFileStream = await this.fileFactory.headerFrom(inputFile); if (headerForFileStream) { this.progressBar.logTrace(`${inputFile.toString()}: found header by file contents: ${headerForFileStream.getHeaderedFileExtension()}`); return inputFile.withFileHeader(headerForFileStream); } this.progressBar.logTrace(`${inputFile.toString()}: didn't find header by file contents`); return inputFile; } }