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.
63 lines (62 loc) • 3.31 kB
JavaScript
import path from 'node:path';
import FsPoly from '../polyfill/fsPoly.js';
import DATStatus, { GameStatus } from '../types/datStatus.js';
import Module from './module.js';
/**
* Generate a single report file with information about every DAT processed.
*/
export default class ReportGenerator extends Module {
options;
constructor(options, progressBar) {
super(progressBar, ReportGenerator.name);
this.options = options;
}
/**
* Generate the report.
*/
async generate(scannedRomFiles, cleanedOutputFiles, datStatuses) {
this.progressBar.logTrace('generating report');
const reportPath = this.options.getReportOutput();
const anyGamesFoundAtAll = datStatuses.some((datStatus) => datStatus.anyGamesFound(this.options));
const matchedFileCsvs = (await Promise.all(datStatuses
.filter((datStatus) => datStatus.anyGamesFound(this.options) || !anyGamesFoundAtAll)
.sort((a, b) => a.getDATName().localeCompare(b.getDATName()))
.map(async (datsStatus) => datsStatus.toCsv(this.options))))
.filter((csv) => csv.length > 0)
.map((csv, idx) => {
// Strip the CSV header from everything except the first file
if (idx === 0) {
return csv;
}
return csv.split('\n').slice(1).join('\n');
});
const usedFilePaths = new Set(datStatuses
.flatMap((datStatus) => datStatus.getInputFiles())
.map((file) => file.getFilePath()));
const usedHashes = new Set(datStatuses.flatMap((datStatus) => datStatus.getInputFiles()).map((file) => file.hashCode()));
const duplicateFilePaths = scannedRomFiles
.filter((inputFile) => !usedFilePaths.has(inputFile.getFilePath()) && usedHashes.has(inputFile.hashCode()))
.map((inputFile) => inputFile.getFilePath())
.filter((inputFile) => !usedFilePaths.has(inputFile))
.sort();
const duplicateCsv = await DATStatus.filesToCsv(duplicateFilePaths, GameStatus.DUPLICATE);
const unusedFilePaths = scannedRomFiles
.filter((inputFile) => !usedFilePaths.has(inputFile.getFilePath()) && !usedHashes.has(inputFile.hashCode()))
.map((inputFile) => inputFile.getFilePath())
.filter((inputFile) => !usedFilePaths.has(inputFile))
.sort();
const unusedCsv = await DATStatus.filesToCsv(unusedFilePaths, GameStatus.UNUSED);
const cleanedCsv = await DATStatus.filesToCsv(cleanedOutputFiles, GameStatus.DELETED);
this.progressBar.logInfo(`writing report '${reportPath}'`);
const reportPathDir = path.dirname(reportPath);
if (!(await FsPoly.exists(reportPathDir))) {
await FsPoly.mkdir(reportPathDir, { recursive: true });
}
const rows = [...matchedFileCsvs, duplicateCsv, unusedCsv, cleanedCsv].filter((csv) => csv.length > 0);
await FsPoly.writeFile(reportPath, rows.join('\n'));
this.progressBar.logTrace(`wrote ${datStatuses.length.toLocaleString()} CSV row${datStatuses.length === 1 ? '' : 's'}: ${reportPath}`);
this.progressBar.logTrace('done generating report');
this.progressBar.finish(reportPath);
this.progressBar.freeze();
}
}