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.

99 lines (98 loc) • 4.69 kB
import { CHDType } from 'chdman'; import ArrayPoly from '../polyfill/arrayPoly.js'; import FsPoly from '../polyfill/fsPoly.js'; import ArchiveEntry from '../types/files/archives/archiveEntry.js'; import Chd from '../types/files/archives/chd/chd.js'; import Gzip from '../types/files/archives/sevenZip/gzip.js'; import Tar from '../types/files/archives/tar.js'; import { ChecksumBitmask } from '../types/files/fileChecksums.js'; import Module from './module.js'; /** * The base class for every input file scanner class. */ export default class Scanner extends Module { options; driveSemaphore; fileFactory; constructor(options, progressBar, fileFactory, driveSemaphore, loggerPrefix) { super(progressBar, loggerPrefix); this.options = options; this.driveSemaphore = driveSemaphore; this.fileFactory = fileFactory; } async getFilesFromPaths(filePaths, checksumBitmask, checksumArchives = false) { return (await this.driveSemaphore.map(filePaths, async (inputFile) => { this.progressBar.incrementInProgress(); const childBar = this.progressBar.addChildBar({ name: inputFile, }); let files; try { files = await this.getFilesFromPath(inputFile, checksumBitmask, checksumArchives); await this.logWarnings(files); } finally { childBar.delete(); } this.progressBar.incrementCompleted(); return files; })).flat(); } async getUniqueFilesFromPaths(filePaths, checksumBitmask) { if (checksumBitmask === ChecksumBitmask.NONE) { throw new Error('must provide ChecksumBitmask when getting unique files'); } const foundFiles = await this.getFilesFromPaths(filePaths, checksumBitmask); return foundFiles.filter(ArrayPoly.filterUniqueMapped((file) => file.hashCode())); } async getFilesFromPath(filePath, checksumBitmask, checksumArchives = false) { try { if (await FsPoly.isSymlink(filePath)) { const realFilePath = await FsPoly.readlinkResolved(filePath); if (!(await FsPoly.exists(realFilePath))) { this.progressBar.logWarn(`${filePath}: broken symlink, '${realFilePath}' doesn't exist`); return []; } } const filesFromPath = await this.fileFactory.filesFrom(filePath, checksumBitmask, this.options.getInputChecksumQuick() ? ChecksumBitmask.NONE : checksumBitmask); const fileIsArchive = filesFromPath.some((file) => file instanceof ArchiveEntry); if (checksumArchives && fileIsArchive) { filesFromPath.push(await this.fileFactory.fileFrom(filePath, checksumBitmask)); } if (filesFromPath.length === 0) { this.progressBar.logWarn(`${filePath}: found no files in path`); } return filesFromPath; } catch (error) { this.progressBar.logError(`${filePath}: failed to parse file: ${error}`); return []; } } async logWarnings(files) { if (this.options.getInputChecksumQuick()) { const archiveWithoutChecksums = files .filter((file) => file instanceof ArchiveEntry) .map((archiveEntry) => archiveEntry.getArchive()) .find((archive) => archive instanceof Gzip || archive instanceof Tar); if (archiveWithoutChecksums !== undefined) { this.progressBar.logWarn(`${archiveWithoutChecksums.getFilePath()}: quick checksums will skip ${archiveWithoutChecksums.getExtension()} files`); return; } const chdInfos = await Promise.all(files .filter((file) => file instanceof ArchiveEntry) .map((archiveEntry) => archiveEntry.getArchive()) .filter((archive) => archive instanceof Chd) .map(async (chd) => [chd, await chd.getInfo()])); const cdRom = chdInfos.find(([, info]) => info.type === CHDType.CD_ROM); if (cdRom !== undefined) { this.progressBar.logWarn(`${cdRom[0].getFilePath()}: quick checksums will skip .cue/.bin files in CD-ROM CHDs`); return; } const gdRom = chdInfos.find(([, info]) => info.type === CHDType.GD_ROM); if (gdRom !== undefined) { this.progressBar.logWarn(`${gdRom[0].getFilePath()}: quick checksums will skip .gdi/.bin/.raw files in GD-ROM CHDs`); } } } }