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
JavaScript
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`);
}
}
}
}