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.

102 lines (101 loc) • 4.41 kB
import async from 'async'; import { ProgressBarSymbol } from '../../console/progressBar.js'; import Defaults from '../../globals/defaults.js'; import FsPoly from '../../polyfill/fsPoly.js'; import ArchiveEntry from '../../types/files/archives/archiveEntry.js'; import Module from '../module.js'; /** * For every input {@link File} found, attempt to find any {@link ROMPadding} present and resolve * its padded checksums. */ export default class ROMTrimProcessor extends Module { options; fileFactory; driveSemaphore; constructor(options, progressBar, fileFactory, driveSemaphore) { super(progressBar, ROMTrimProcessor.name); this.options = options; this.fileFactory = fileFactory; this.driveSemaphore = driveSemaphore; } /** * Process each {@link File}, finding any {@link ROMPadding} present. */ async process(inputRomFiles) { if (inputRomFiles.length === 0) { return inputRomFiles; } if (!this.options.usingDats()) { // We don't care about detecting trimming if we're not matching to a DAT return inputRomFiles; } const filesThatNeedProcessing = inputRomFiles.filter((inputFile) => this.fileNeedsProcessing(inputFile)).length; if (filesThatNeedProcessing === 0) { this.progressBar.logTrace('no ROMs can be trimmed'); return inputRomFiles; } this.progressBar.logTrace(`processing trimming in ${inputRomFiles.length.toLocaleString()} ROM${inputRomFiles.length === 1 ? '' : 's'}`); this.progressBar.setSymbol(ProgressBarSymbol.ROM_TRIMMING_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(), total: inputFile.getSize(), progressFormatter: FsPoly.sizeReadable, }); let fileWithTrimming; try { fileWithTrimming = await this.getFile(inputFile, childBar); } catch (error) { this.progressBar.logError(`${inputFile.toString()}: failed to process ROM trimming: ${error}`); fileWithTrimming = inputFile; } finally { childBar.delete(); } this.progressBar.incrementCompleted(); return fileWithTrimming; }); }); const trimmedRomsCount = parsedFiles.filter((romFile) => romFile.getPaddings().length > 0).length; this.progressBar.logTrace(`found ${trimmedRomsCount.toLocaleString()} trimmed ROM${trimmedRomsCount === 1 ? '' : 's'}`); this.progressBar.logTrace('done processing file trimming'); return parsedFiles; } fileNeedsProcessing(inputFile) { if (this.options.shouldReadFileForTrimming(inputFile.getFilePath())) { return true; } if (inputFile instanceof ArchiveEntry && !this.options.getTrimScanArchives()) { return false; } if (inputFile.getSize() === 0 || (inputFile.getSize() & (inputFile.getSize() - 1)) === 0) { // Is a power of two, so it isn't trimmed return false; } return true; } async getFile(inputFile, progressBar) { if (!this.options.shouldReadFileForTrimming(inputFile.getFilePath())) { const fileSignature = await this.fileFactory.signatureFrom(inputFile); if (!fileSignature?.canBeTrimmed()) { // This file isn't known to be trimmable return inputFile; } } const paddings = await this.fileFactory.paddingsFrom(inputFile, (progress) => { progressBar.setCompleted(progress); }); if (paddings.length === 0) { // This file isn't trimmed return inputFile; } return inputFile.withPaddings(paddings); } }