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.

164 lines (163 loc) • 7.27 kB
import path from "node:path"; import { ProgressBarSymbol } from "../../console/progressBar.js"; import FileFactory from "../../factories/fileFactory.js"; import ArchiveEntry from "../../models/files/archives/archiveEntry.js"; import Chd from "../../models/files/archives/chd/chd.js"; import ZeroSizeFile from "../../models/files/zeroSizeFile.js"; import { FixExtension } from "../../models/options.js"; import OutputFactory from "../../modules/candidates/utils/outputFactory.js"; import ArrayUtil from "../../utils/arrayUtil.js"; import IntlUtil from "../../utils/intlUtil.js"; import Module from "../module.js"; class CandidateExtensionCorrector extends Module { options; fileFactory; readerSemaphore; constructor(options, progressBar, fileFactory, readerSemaphore) { super(progressBar, CandidateExtensionCorrector.name); this.options = options; this.fileFactory = fileFactory; this.readerSemaphore = readerSemaphore; } /** * Correct the file extensions. */ async correct(dat, candidates) { if (candidates.length === 0) { this.prefixedLogger.trace(`${dat.getName()}: no candidates to correct extensions for`); return candidates; } const romsThatNeedCorrecting = candidates.flatMap((candidate) => candidate.getRomsWithFiles()).filter((romWithFiles) => this.romNeedsCorrecting(romWithFiles)).length; if (romsThatNeedCorrecting === 0) { this.prefixedLogger.trace(`${dat.getName()}: no output files need their extension corrected`); return candidates; } this.prefixedLogger.trace( `${dat.getName()}: correcting ${IntlUtil.toLocaleString(romsThatNeedCorrecting)} output file extension${romsThatNeedCorrecting === 1 ? "" : "s"}` ); this.progressBar.setSymbol(ProgressBarSymbol.CANDIDATE_EXTENSION_CORRECTION); this.progressBar.resetProgress(romsThatNeedCorrecting); const correctedCandidates = await this.correctExtensions(dat, candidates); this.prefixedLogger.trace(`${dat.getName()}: done correcting output file extensions`); return correctedCandidates; } romNeedsCorrecting(romWithFiles) { if (romWithFiles.getInputFile() instanceof ZeroSizeFile) { return false; } if (romWithFiles.getRom().getName().trim() === "") { return true; } const inputFile = romWithFiles.getInputFile(); if (inputFile instanceof ArchiveEntry && inputFile.getArchive() instanceof Chd) { return false; } return this.options.getFixExtension() === FixExtension.ALWAYS || this.options.getFixExtension() === FixExtension.AUTO && !this.options.shouldDir2Dat() && (!this.options.usingDats() || romWithFiles.getRom().getName().trim() === ""); } async correctExtensions(dat, candidates) { return await Promise.all( candidates.map(async (candidate) => { const correctedRoms = (await Promise.all( candidate.getRomsWithFiles().map(async (romWithFiles) => { const correctedRom = await this.buildCorrectedRom(dat, candidate, romWithFiles); return romWithFiles.withRom(correctedRom); }) )).filter(ArrayUtil.filterUniqueMapped((romWithFiles) => romWithFiles.getRom().hashCode())); const correctedGame = candidate.getGame().withProps({ roms: correctedRoms.map((romWithFiles) => romWithFiles.getRom()) }); const correctedOutputPaths = correctedRoms.map((romWithFiles) => { const correctedOutputPath = OutputFactory.getPath( this.options, dat, correctedGame, romWithFiles.getRom(), romWithFiles.getInputFile() ); let correctedOutputFile = romWithFiles.getOutputFile().withFilePath(correctedOutputPath.format()); if (correctedOutputFile instanceof ArchiveEntry) { correctedOutputFile = correctedOutputFile.withEntryPath(correctedOutputPath.entryPath); } return romWithFiles.withOutputFile(correctedOutputFile); }); return candidate.withGame(correctedGame).withRomsWithFiles(correctedOutputPaths); }) ); } async buildCorrectedRom(dat, candidate, romWithFiles) { let correctedRom = romWithFiles.getRom(); if (correctedRom.getName().trim() === "") { const romWithFilesIdx = candidate.getRomsWithFiles().indexOf(romWithFiles); correctedRom = correctedRom.withName( `${candidate.getGame().getName()}${candidate.getRomsWithFiles().length > 1 ? ` (File ${romWithFilesIdx + 1})` : ""}.rom` ); } if (!this.romNeedsCorrecting(romWithFiles)) { return correctedRom; } await this.readerSemaphore.runExclusive(async () => { this.progressBar.incrementInProgress(); this.prefixedLogger.trace( `${dat.getName()}: ${candidate.getName()}: determining correct ROM extension for: ${romWithFiles.getInputFile().toString()}` ); const childBar = this.progressBar.addChildBar({ name: romWithFiles.getInputFile().toString() }); try { const correctedRomName = await this.correctFromFileSignature( dat, correctedRom, romWithFiles.getInputFile() ); if (correctedRomName === void 0) { this.prefixedLogger.trace( `${dat.getName()}: ${candidate.getName()}: didn't find correct ROM extension` ); } else if (correctedRomName !== correctedRom.getName()) { correctedRom = correctedRom.withName(correctedRomName); this.prefixedLogger.trace( `${dat.getName()}: ${candidate.getName()}: found correct ROM extension: ${path.posix.basename(correctedRomName)}` ); } } finally { childBar.delete(); } this.progressBar.incrementCompleted(); }); return correctedRom; } async correctFromFileSignature(dat, correctedRom, inputFile) { let fileSignature; try { fileSignature = await this.fileFactory.signatureFrom(inputFile); } catch (error) { this.prefixedLogger.error( `${dat.getName()}: failed to correct file extension for '${inputFile.toString()}': ${error}` ); } if (fileSignature !== void 0) { const extensionRegex = /\.[a-zA-Z0-9]+$/; const oldExtension = extensionRegex.exec(correctedRom.getName())?.at(0); let newExtension = fileSignature.getExtension(); if (oldExtension !== void 0 && /[A-Z]/.test(oldExtension) && !/[a-z]/.test(oldExtension)) { newExtension = newExtension.toUpperCase(); } return correctedRom.getName().replace(extensionRegex, "") + newExtension; } if (!(inputFile instanceof ArchiveEntry)) { const dotSplit = correctedRom.getName().split("."); const archiveIndex = dotSplit.findIndex( (_, idx) => FileFactory.isExtensionArchive(dotSplit.slice(0, idx + 1).join(".")) ); if (archiveIndex !== -1) { const archiveExtension = dotSplit.slice(archiveIndex).join("."); this.prefixedLogger.warn( `${dat.getName()}: ${inputFile.toString()}: file is not a ${archiveExtension} archive, but the correct extension isn't known` ); } } return void 0; } } export { CandidateExtensionCorrector as default }; //# sourceMappingURL=candidateExtensionCorrector.js.map