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.
1,146 lines (1,145 loc) • 33.7 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __decorateClass = (decorators, target, key, kind) => {
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
for (var i = decorators.length - 1, decorator; i >= 0; i--)
if (decorator = decorators[i])
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
if (kind && result) __defProp(target, key, result);
return result;
};
import "reflect-metadata";
import os from "node:os";
import path from "node:path";
import { Expose, instanceToPlain, plainToInstance } from "class-transformer";
import fg from "fast-glob";
import micromatch from "micromatch";
import { LogLevel } from "../console/logLevel.js";
import IgirException from "../exceptions/igirException.js";
import Temp from "../globals/temp.js";
import ArrayUtil from "../utils/arrayUtil.js";
import DateUtil from "../utils/dateUtil.js";
import FsUtil, { WalkMode } from "../utils/fsUtil.js";
import URLUtil from "../utils/urlUtil.js";
import Disk from "./dats/disk.js";
import {
ChecksumBitmask
} from "./files/fileChecksums.js";
const InputChecksumArchivesMode = {
// Never calculate the checksum of archive files
NEVER: 1,
// Calculate the checksum of archive files if DATs reference archives
AUTO: 2,
// Always calculate the checksum of archive files
ALWAYS: 3
};
const InputChecksumArchivesModeInverted = Object.fromEntries(
Object.entries(InputChecksumArchivesMode).map(([key, value]) => [value, key])
);
const LinkMode = {
// Create hard links to the original files
HARDLINK: 1,
// Create symbolic links to the original files
SYMLINK: 2,
// Create copy-on-write links to the original files
REFLINK: 3
};
const LinkModeInverted = Object.fromEntries(
Object.entries(LinkMode).map(([key, value]) => [value, key])
);
const MergeMode = {
// Clones contain all parent ROMs, all games contain BIOS & device ROMs
FULLNONMERGED: 1,
// Clones contain all parent ROMs, BIOS & device ROMsets are separate
NONMERGED: 2,
// Clones exclude all parent ROMs, BIOS & device ROMsets are separate
SPLIT: 3,
// Clones are merged into parent, BIOS & device ROMsets are separate
MERGED: 4
};
const MergeModeInverted = Object.fromEntries(
Object.entries(MergeMode).map(([key, value]) => [value, key])
);
const GameSubdirMode = {
// Never add the Game name as a subdirectory
NEVER: 1,
// Add the Game name as a subdirectory if it has multiple output files
MULTIPLE: 2,
// Always add the Game name as a subdirectory
ALWAYS: 3
};
const GameSubdirModeInverted = Object.fromEntries(
Object.entries(GameSubdirMode).map(([key, value]) => [value, key])
);
const FixExtension = {
NEVER: 1,
AUTO: 2,
ALWAYS: 3
};
const FixExtensionInverted = Object.fromEntries(
Object.entries(FixExtension).map(([key, value]) => [value, key])
);
const MoveDeleteDirs = {
NEVER: 1,
AUTO: 2,
ALWAYS: 3
};
const MoveDeleteDirsInverted = Object.fromEntries(
Object.entries(MoveDeleteDirs).map(([key, value]) => [value, key])
);
const TrimScanFiles = {
// Never scan any files for trimming detection
NEVER: 1,
// Scan files with a known trimmable signature (default)
AUTO: 2,
// Scan all non-archive files regardless of signature
ALWAYS: 3
};
const TrimScanFilesInverted = Object.fromEntries(
Object.entries(TrimScanFiles).map(([key, value]) => [value, key])
);
const PreferRevision = {
OLDER: 1,
NEWER: 2
};
const PreferRevisionInverted = Object.fromEntries(
Object.entries(PreferRevision).map(([key, value]) => [value, key])
);
const PreferFiletype = {
PLAIN: 1,
ARCHIVE: 2
};
const PreferFiletypeInverted = Object.fromEntries(
Object.entries(PreferFiletype).map(([key, value]) => [value, key])
);
const PlaylistMode = {
MULTIPLE: 1,
ALWAYS: 2
};
const PlaylistModeInverted = Object.fromEntries(
Object.entries(PlaylistMode).map(([key, value]) => [value, key])
);
const ZipFormat = {
TORRENTZIP: "TORRENTZIP",
RVZSTD: "RVZSTD"
};
const ZipFormatInverted = Object.fromEntries(
Object.entries(ZipFormat).map(([key, value]) => [value, key])
);
const _Options = class _Options {
commands;
input;
inputExclude;
inputChecksumQuick;
inputChecksumMin;
inputChecksumMax;
inputChecksumArchives;
dat;
datExclude;
datNameRegex;
datNameRegexExclude;
datDescriptionRegex;
datDescriptionRegexExclude;
datCombine;
datIgnoreParentClone;
patch;
patchExclude;
patchOnly;
output;
dirMirror;
dirDatMirror;
dirDatName;
dirDatDescription;
dirLetter;
dirLetterCount;
dirLetterLimit;
dirLetterGroup;
dirGameSubdir;
outputConsoleTokens;
fixExtension;
overwrite;
overwriteInvalid;
writeRetry;
moveDeleteDirs;
cleanExclude;
cleanBackup;
cleanDryRun;
zipFormat;
zipExclude;
zipDatName;
linkMode;
symlinkRelative;
header;
// eslint-disable-next-line unicorn/no-non-function-verb-prefix
removeHeaders;
trimmedGlob;
trimScanFiles;
trimScanArchives;
trimAddPadding;
mergeRoms;
mergeDiscs;
excludeDisks;
allowExcessSets;
allowIncompleteSets;
filterRegex;
filterRegexExclude;
filterLanguage;
filterRegion;
filterCategoryRegex;
noBios;
onlyBios;
noDevice;
onlyDevice;
noUnlicensed;
onlyUnlicensed;
onlyRetail;
noDebug;
onlyDebug;
noDemo;
onlyDemo;
noBeta;
onlyBeta;
noSample;
onlySample;
noPrototype;
onlyPrototype;
noProgram;
onlyProgram;
noAftermarket;
onlyAftermarket;
noHomebrew;
onlyHomebrew;
noUnverified;
onlyUnverified;
noBad;
onlyBad;
single;
preferGameRegex;
preferRomRegex;
preferVerified;
preferGood;
preferLanguage;
preferRegion;
preferRevision;
preferRetail;
preferParent;
preferFiletype;
preferFilenameRegex;
playlistMode;
playlistExtensions;
dir2datOutput;
fixdatOutput;
reportOutput;
datThreads;
readerThreads;
writerThreads;
tempDir;
disableCache;
cachePath;
verbose;
debugLog;
help;
constructor(options) {
this.commands = options?.commands ?? [];
this.input = (options?.input ?? []).map((filePath) => filePath.replaceAll(/[\\/]/g, path.sep));
this.inputExclude = (options?.inputExclude ?? []).map(
(filePath) => filePath.replaceAll(/[\\/]/g, path.sep)
);
this.inputChecksumQuick = options?.inputChecksumQuick ?? false;
this.inputChecksumMin = options?.inputChecksumMin;
this.inputChecksumMax = options?.inputChecksumMax;
this.inputChecksumArchives = options?.inputChecksumArchives;
this.dat = (options?.dat ?? []).map(
(filePath) => URLUtil.canParse(filePath) ? filePath : filePath.replaceAll(/[\\/]/g, path.sep)
);
this.datExclude = (options?.datExclude ?? []).map(
(filePath) => filePath.replaceAll(/[\\/]/g, path.sep)
);
this.datNameRegex = options?.datNameRegex;
this.datNameRegexExclude = options?.datNameRegexExclude;
this.datDescriptionRegex = options?.datDescriptionRegex;
this.datDescriptionRegexExclude = options?.datDescriptionRegexExclude;
this.datCombine = options?.datCombine ?? false;
this.datIgnoreParentClone = options?.datIgnoreParentClone ?? false;
this.patch = (options?.patch ?? []).map((filePath) => filePath.replaceAll(/[\\/]/g, path.sep));
this.patchExclude = (options?.patchExclude ?? []).map(
(filePath) => filePath.replaceAll(/[\\/]/g, path.sep)
);
this.patchOnly = options?.patchOnly ?? false;
this.output = options?.output === void 0 ? void 0 : path.resolve(options.output);
this.dirMirror = options?.dirMirror ?? false;
this.dirDatMirror = options?.dirDatMirror ?? false;
this.dirDatName = options?.dirDatName ?? false;
this.dirDatDescription = options?.dirDatDescription ?? false;
this.dirLetter = options?.dirLetter ?? false;
this.dirLetterCount = options?.dirLetterCount ?? 0;
this.dirLetterLimit = options?.dirLetterLimit ?? 0;
this.dirLetterGroup = options?.dirLetterGroup ?? false;
this.dirGameSubdir = options?.dirGameSubdir;
this.outputConsoleTokens = options?.outputConsoleTokens;
this.fixExtension = options?.fixExtension;
this.overwrite = options?.overwrite ?? false;
this.overwriteInvalid = options?.overwriteInvalid ?? false;
this.writeRetry = Math.max(options?.writeRetry ?? 0, 0);
this.moveDeleteDirs = options?.moveDeleteDirs;
this.cleanExclude = (options?.cleanExclude ?? []).map(
(filePath) => filePath.replaceAll(/[\\/]/g, path.sep)
);
this.cleanBackup = options?.cleanBackup === void 0 ? void 0 : path.resolve(options.cleanBackup);
this.cleanDryRun = options?.cleanDryRun ?? false;
this.zipFormat = options?.zipFormat;
this.zipExclude = options?.zipExclude ?? "";
this.zipDatName = options?.zipDatName ?? false;
this.linkMode = options?.linkMode;
this.symlinkRelative = options?.symlinkRelative ?? false;
this.header = options?.header;
this.removeHeaders = options?.removeHeaders;
this.trimmedGlob = options?.trimmedGlob;
this.trimScanFiles = options?.trimScanFiles;
this.trimScanArchives = options?.trimScanArchives ?? false;
this.trimAddPadding = options?.trimAddPadding ?? false;
this.mergeRoms = options?.mergeRoms;
this.mergeDiscs = options?.mergeDiscs ?? false;
this.excludeDisks = options?.excludeDisks ?? false;
this.allowExcessSets = options?.allowExcessSets ?? false;
this.allowIncompleteSets = options?.allowIncompleteSets ?? false;
this.filterRegex = options?.filterRegex;
this.filterRegexExclude = options?.filterRegexExclude;
this.filterLanguage = options?.filterLanguage ?? [];
this.filterRegion = options?.filterRegion ?? [];
this.filterCategoryRegex = options?.filterCategoryRegex;
this.noBios = options?.noBios ?? false;
this.onlyBios = options?.onlyBios ?? false;
this.noDevice = options?.noDevice ?? false;
this.onlyDevice = options?.onlyDevice ?? false;
this.noUnlicensed = options?.noUnlicensed ?? false;
this.onlyUnlicensed = options?.onlyUnlicensed ?? false;
this.onlyRetail = options?.onlyRetail ?? false;
this.noDebug = options?.noDebug ?? false;
this.onlyDebug = options?.onlyDebug ?? false;
this.noDemo = options?.noDemo ?? false;
this.onlyDemo = options?.onlyDemo ?? false;
this.noBeta = options?.noBeta ?? false;
this.onlyBeta = options?.onlyBeta ?? false;
this.noSample = options?.noSample ?? false;
this.onlySample = options?.onlySample ?? false;
this.noPrototype = options?.noPrototype ?? false;
this.onlyPrototype = options?.onlyPrototype ?? false;
this.noProgram = options?.noProgram ?? false;
this.onlyProgram = options?.onlyProgram ?? false;
this.noAftermarket = options?.noAftermarket ?? false;
this.onlyAftermarket = options?.onlyAftermarket ?? false;
this.noHomebrew = options?.noHomebrew ?? false;
this.onlyHomebrew = options?.onlyHomebrew ?? false;
this.noUnverified = options?.noUnverified ?? false;
this.onlyUnverified = options?.onlyUnverified ?? false;
this.noBad = options?.noBad ?? false;
this.onlyBad = options?.onlyBad ?? false;
this.single = options?.single ?? false;
this.preferGameRegex = options?.preferGameRegex;
this.preferRomRegex = options?.preferRomRegex;
this.preferVerified = options?.preferVerified ?? false;
this.preferGood = options?.preferGood ?? false;
this.preferLanguage = options?.preferLanguage ?? [];
this.preferRegion = options?.preferRegion ?? [];
this.preferRevision = options?.preferRevision;
this.preferRetail = options?.preferRetail ?? false;
this.preferParent = options?.preferParent ?? false;
this.preferFiletype = options?.preferFiletype;
this.preferFilenameRegex = options?.preferFilenameRegex;
this.playlistMode = options?.playlistMode;
this.playlistExtensions = options?.playlistExtensions ?? [];
this.dir2datOutput = options?.dir2datOutput === void 0 ? void 0 : path.resolve(options.dir2datOutput);
this.fixdatOutput = options?.fixdatOutput === void 0 ? void 0 : path.resolve(options.fixdatOutput);
this.reportOutput = path.resolve(options?.reportOutput ?? process.cwd());
this.datThreads = Math.max(options?.datThreads ?? 0, 1);
this.readerThreads = Math.max(options?.readerThreads ?? 0, 1);
this.writerThreads = Math.max(options?.writerThreads ?? 0, 1);
this.tempDir = path.resolve(options?.tempDir ?? Temp.getTempDir());
this.disableCache = options?.disableCache ?? false;
this.cachePath = options?.cachePath;
this.verbose = options?.verbose ?? 0;
this.debugLog = options?.debugLog;
this.help = options?.help ?? false;
}
/**
* Construct a {@link Options} from a generic object, such as one from `yargs`.
*/
static fromObject(obj) {
return plainToInstance(this, obj, {
enableImplicitConversion: true
});
}
/**
* Return an object of all options.
*/
toObject() {
return instanceToPlain(this);
}
/**
* Return a JSON representation of all options.
*/
toString() {
return JSON.stringify(this.toObject());
}
// Helpers
static getRegex(pattern) {
if (!pattern?.trim()) {
return void 0;
}
return pattern.split(/\r?\n/).filter((line) => line.length > 0).map((line) => {
const flagsMatch = /^\/(.+)\/([a-z]*)$/.exec(line);
if (flagsMatch !== null) {
return new RegExp(flagsMatch[1], flagsMatch[2]);
}
return new RegExp(line);
});
}
// Commands
getCommands() {
return new Set(this.commands.map((c) => c.toLowerCase()));
}
/**
* Was any writing command provided?
*/
shouldWrite() {
return this.writeString() !== void 0;
}
/**
* The writing command that was specified.
*/
writeString() {
return ["copy", "move", "link"].find((command) => this.getCommands().has(command));
}
/**
* Was the `copy` command provided?
*/
shouldCopy() {
return this.getCommands().has("copy");
}
/**
* Was the `move` command provided?
*/
shouldMove() {
return this.getCommands().has("move");
}
/**
* Was the `link` command provided?
*/
shouldLink() {
return this.getCommands().has("link");
}
/**
* Was the `extract` command provided?
*/
shouldExtract() {
return this.getCommands().has("extract");
}
/**
* Should a given ROM be extracted?
*/
shouldExtractRom(rom) {
if (rom instanceof Disk) {
return false;
}
return this.shouldExtract();
}
/**
* Was the `zip` command provided?
*/
shouldZip() {
return this.getCommands().has("zip");
}
/**
* Should a given output file path be zipped?
*/
shouldZipRom(rom) {
if (rom instanceof Disk) {
return false;
}
return this.shouldZip() && (!this.getZipExclude() || !micromatch.isMatch(rom.getName().replace(/^.[\\/]/, ""), this.getZipExclude()));
}
/**
* Was the 'playlist' command provided?
*/
shouldPlaylist() {
return this.getCommands().has("playlist");
}
/**
* Was the 'dir2dat' command provided?
*/
shouldDir2Dat() {
return this.getCommands().has("dir2dat");
}
/**
* Was the 'fixdat' command provided?
*/
shouldFixdat() {
return this.getCommands().has("fixdat");
}
/**
* Was the `test` command provided?
*/
shouldTest() {
return this.getCommands().has("test");
}
/**
* Was the `clean` command provided?
*/
shouldClean() {
return this.getCommands().has("clean");
}
/**
* Was the `report` command provided?
*/
shouldReport() {
return this.getCommands().has("report");
}
// Options
getInputPaths() {
return this.input;
}
/**
* Scan for input files, and input files to exclude, and return the difference.
*/
async scanInputFilesWithoutExclusions(walkCallback) {
return await _Options.scanPathsWithoutExclusions(
this.input,
this.inputExclude,
WalkMode.FILES,
walkCallback,
this.shouldWrite() || !(this.shouldReport() || this.shouldFixdat())
);
}
/**
* Scan for subdirectories in the input paths.
*/
async scanInputSubdirectories(walkCallback) {
return await _Options.scanPaths(this.input, WalkMode.DIRECTORIES, walkCallback, false);
}
/**
* Scan for files or directories given some glob patterns.
*/
static async scanPaths(globPatterns, walkMode, walkCallback, shouldRequireFiles = true) {
const uniqueGlobPatterns = globPatterns.reduce(ArrayUtil.reduceUnique(), []);
const globbedPaths = [];
for (const uniqueGlobPattern of uniqueGlobPatterns) {
const paths = await this.globPath(uniqueGlobPattern, walkMode, walkCallback);
for (const globbedPath of paths) {
globbedPaths.push(globbedPath);
}
}
if (shouldRequireFiles && globbedPaths.length === 0) {
throw new IgirException(
`no files found in director${globPatterns.length === 1 ? "y" : "ies"}: ${globPatterns.map((p) => `'${p}'`).join(", ")}`
);
}
return globbedPaths.reduce(ArrayUtil.reduceUnique(), []);
}
static async scanPathsWithoutExclusions(includeGlobPatterns, excludeGlobPatterns, walkMode, walkCallback, shouldRequireIncludeFiles = true) {
const includePaths = await this.scanPaths(
includeGlobPatterns,
walkMode,
walkCallback,
shouldRequireIncludeFiles
);
const excludePaths = await this.scanPaths(excludeGlobPatterns, walkMode, void 0, false);
const excludePathsSet = new Set(excludePaths);
return includePaths.filter(
(filePath) => excludePathsSet.size === 0 || !excludePathsSet.has(filePath)
);
}
static async globPath(inputPath, walkMode, walkCallback) {
if (inputPath === os.devNull || inputPath.startsWith(os.devNull + path.sep)) {
return [];
}
if (await FsUtil.isDirectory(inputPath)) {
return (await FsUtil.walk(inputPath, walkMode, walkCallback)).map((p) => path.resolve(p));
}
if (await FsUtil.exists(inputPath)) {
if (walkCallback !== void 0) {
walkCallback(1);
}
return [path.resolve(inputPath)];
}
const inputPathEscaped = this.sanitizeGlobPattern(inputPath);
if (!inputPathEscaped) {
return [];
}
if (URLUtil.canParse(inputPath)) {
if (walkCallback !== void 0) {
walkCallback(1);
}
return [inputPath];
}
const globbedPaths = await fg(inputPathEscaped, {
onlyFiles: walkMode === WalkMode.FILES,
onlyDirectories: walkMode === WalkMode.DIRECTORIES
});
if (globbedPaths.length === 0) {
return [];
}
if (walkCallback !== void 0) {
walkCallback(globbedPaths.length);
}
return globbedPaths.map((globbedPath) => path.resolve(globbedPath));
}
/**
* Trying to use globs with directory names that resemble glob patterns (e.g. dirs that include
* parentheticals) is problematic. Most of the time globs are at the tail end of the path, so try
* to figure out what leading part of the pattern is just a path, and escape it appropriately,
* and then tack on the glob at the end.
* Example problematic paths:
* ./TOSEC - DAT Pack - Complete (3983) (TOSEC-v2023-07-10)/TOSEC-ISO/Sega*
* ./No-Intro/Nintendo - Nintendo 64 (BigEndian)*\/**
*/
static sanitizeGlobPattern(globPattern) {
return (
// fg only uses forward-slash path separators; convert back-slash characters if the user provided a purely Windows path
(process.platform === "win32" && !globPattern.includes("/") ? globPattern.replaceAll("\\", "/") : globPattern).replaceAll(/(^|[^?*+@!\\])\(([^|)]+)\)/g, "$1{\\(,}$2{\\),}").replaceAll(/\{([^.,}]+)\}/g, "{\\{,}$1{\\},}").replaceAll(/\[([^\]]+)\]/g, "{[$1],\\[$1\\]}")
);
}
getInputChecksumQuick() {
return this.inputChecksumQuick;
}
getInputChecksumMin() {
const checksumBitmask = Object.keys(ChecksumBitmask).find(
(bitmask) => bitmask.toUpperCase() === this.inputChecksumMin?.toUpperCase()
);
if (!checksumBitmask) {
return void 0;
}
return ChecksumBitmask[checksumBitmask];
}
getInputChecksumMax() {
const checksumBitmask = Object.keys(ChecksumBitmask).find(
(bitmask) => bitmask.toUpperCase() === this.inputChecksumMax?.toUpperCase()
);
if (!checksumBitmask) {
return void 0;
}
return ChecksumBitmask[checksumBitmask];
}
getInputChecksumArchives() {
const checksumMode = Object.keys(InputChecksumArchivesMode).find(
(mode) => mode.toLowerCase() === this.inputChecksumArchives?.toLowerCase()
);
if (!checksumMode) {
return void 0;
}
return InputChecksumArchivesMode[checksumMode];
}
/**
* Were any DAT paths provided?
*/
usingDats() {
return this.dat.length > 0;
}
getDatPaths() {
return this.dat;
}
/**
* Scan for DAT files, and DAT files to exclude, and return the difference.
*/
async scanDatFilesWithoutExclusions(walkCallback) {
return await _Options.scanPathsWithoutExclusions(
this.dat,
this.datExclude,
WalkMode.FILES,
walkCallback
);
}
getDatNameRegex() {
return _Options.getRegex(this.datNameRegex);
}
getDatNameRegexExclude() {
return _Options.getRegex(this.datNameRegexExclude);
}
getDatDescriptionRegex() {
return _Options.getRegex(this.datDescriptionRegex);
}
getDatDescriptionRegexExclude() {
return _Options.getRegex(this.datDescriptionRegexExclude);
}
getDatCombine() {
return this.datCombine;
}
getDatIgnoreParentClone() {
return this.datIgnoreParentClone;
}
getPatchFileCount() {
return this.patch.length;
}
/**
* Scan for patch files, and patch files to exclude, and return the difference.
*/
async scanPatchFilesWithoutExclusions(walkCallback) {
return await _Options.scanPathsWithoutExclusions(
this.patch,
this.patchExclude,
WalkMode.FILES,
walkCallback
);
}
getPatchOnly() {
return this.patchOnly;
}
getOutput() {
return this.output ?? (this.shouldWrite() ? "" : this.getTempDir());
}
/**
* Get the "root" sub-path of the output dir, the sub-path up until the first replaceable token.
*/
getOutputDirRoot() {
const resolvedOutput = path.resolve(this.getOutput());
const outputSplit = resolvedOutput.split(path.sep);
for (let i = 0; i < outputSplit.length; i += 1) {
if (/{[a-zA-Z]+}/.test(outputSplit[i])) {
return outputSplit.slice(0, i).join(path.sep) || path.sep;
}
}
return resolvedOutput;
}
getDirMirror() {
return this.dirMirror;
}
getDirDatMirror() {
return this.dirDatMirror;
}
getDirDatName() {
return this.dirDatName;
}
getDirDatDescription() {
return this.dirDatDescription;
}
getDirLetter() {
return this.dirLetter;
}
getDirLetterCount() {
return this.dirLetterCount;
}
getDirLetterLimit() {
return this.dirLetterLimit;
}
getDirLetterGroup() {
return this.dirLetterGroup;
}
getDirGameSubdir() {
const subdirMode = Object.keys(GameSubdirMode).find(
(mode) => mode.toLowerCase() === this.dirGameSubdir?.toLowerCase()
);
if (!subdirMode) {
return void 0;
}
return GameSubdirMode[subdirMode];
}
getOutputConsoleTokens() {
return this.outputConsoleTokens;
}
getFixExtension() {
const fixExtensionMode = Object.keys(FixExtension).find(
(mode) => mode.toLowerCase() === this.fixExtension?.toLowerCase()
);
if (!fixExtensionMode) {
return void 0;
}
return FixExtension[fixExtensionMode];
}
getOverwrite() {
return this.overwrite;
}
getOverwriteInvalid() {
return this.overwriteInvalid;
}
getWriteRetry() {
return this.writeRetry;
}
getMoveDeleteDirs() {
const moveDeleteDirsMode = Object.keys(MoveDeleteDirs).find(
(mode) => mode.toLowerCase() === this.moveDeleteDirs?.toLowerCase()
);
if (!moveDeleteDirsMode) {
return void 0;
}
return MoveDeleteDirs[moveDeleteDirsMode];
}
async scanCleanExcludeFiles() {
return await _Options.scanPaths(this.cleanExclude, WalkMode.FILES, void 0, false);
}
/**
* Scan for output files, and output files to exclude from cleaning, and return the difference.
*/
async scanOutputFilesWithoutCleanExclusions(outputDirs, writtenFiles, walkCallback) {
const writtenFilesNormalized = new Set(writtenFiles.map((file) => file.getFilePath()));
const cleanExcludedFilesNormalized = new Set(await this.scanCleanExcludeFiles());
return (await _Options.scanPaths(outputDirs, WalkMode.FILES, walkCallback, false)).filter(
(filePath) => !writtenFilesNormalized.has(filePath) && !cleanExcludedFilesNormalized.has(filePath)
).toSorted((a, b) => a.localeCompare(b));
}
getCleanBackup() {
return this.cleanBackup;
}
getCleanDryRun() {
return this.cleanDryRun;
}
getZipFormat() {
const zipFormat = Object.keys(ZipFormat).find(
(mode) => mode.toLowerCase() === this.zipFormat?.toLowerCase()
);
if (!zipFormat) {
return void 0;
}
return ZipFormat[zipFormat];
}
getZipExclude() {
return this.zipExclude;
}
getZipDatName() {
return this.zipDatName;
}
getLinkMode() {
const linkMode = Object.keys(LinkMode).find(
(mode) => mode.toLowerCase() === this.linkMode?.toLowerCase()
);
if (!linkMode) {
return void 0;
}
return LinkMode[linkMode];
}
getSymlinkRelative() {
return this.symlinkRelative;
}
/**
* Should a file have its contents read to detect any {@link Header}?
*/
shouldReadFileForHeader(filePath) {
return this.header !== void 0 && this.header.length > 0 && micromatch.isMatch(filePath.replace(/^.[\\/]/, ""), this.header);
}
/**
* Should a file have its contents read to detect any {@link ROMPadding}?
*/
shouldReadFileForTrimming(filePath) {
return this.trimmedGlob !== void 0 && this.trimmedGlob.length > 0 && micromatch.isMatch(filePath.replace(/^.[\\/]/, ""), this.trimmedGlob);
}
getTrimScanFiles() {
const trimScanFilesMode = Object.keys(TrimScanFiles).find(
(mode) => mode.toLowerCase() === this.trimScanFiles?.toLowerCase()
);
if (!trimScanFilesMode) {
return void 0;
}
return TrimScanFiles[trimScanFilesMode];
}
getTrimScanArchives() {
return this.trimScanArchives;
}
getTrimAddPadding() {
return this.trimAddPadding;
}
/**
* Can the {@link Header} be removed for a {@link extension} during writing?
*/
canRemoveHeader(extension) {
if (this.removeHeaders === void 0) {
return false;
}
if (this.removeHeaders.length === 1 && this.removeHeaders[0] === "") {
return true;
}
return this.removeHeaders.some(
// eslint-disable-next-line unicorn/no-non-function-verb-prefix
(removeHeader) => removeHeader.toLowerCase() === extension.toLowerCase()
);
}
getMergeRoms() {
const mergeMode = Object.keys(MergeMode).find(
(mode) => mode.toLowerCase() === this.mergeRoms?.toLowerCase()
);
if (!mergeMode) {
return void 0;
}
return MergeMode[mergeMode];
}
getMergeDiscs() {
return this.mergeDiscs;
}
getExcludeDisks() {
return this.excludeDisks;
}
getAllowExcessSets() {
return this.allowExcessSets;
}
getAllowIncompleteSets() {
return this.allowIncompleteSets;
}
getFilterRegex() {
return _Options.getRegex(this.filterRegex);
}
getFilterRegexExclude() {
return _Options.getRegex(this.filterRegexExclude);
}
getFilterLanguage() {
if (this.filterLanguage.length > 0) {
return new Set(_Options.filterUniqueUpper(this.filterLanguage));
}
return /* @__PURE__ */ new Set();
}
getFilterRegion() {
if (this.filterRegion.length > 0) {
return new Set(_Options.filterUniqueUpper(this.filterRegion));
}
return /* @__PURE__ */ new Set();
}
getFilterCategoryRegex() {
return _Options.getRegex(this.filterCategoryRegex);
}
getNoBios() {
return this.noBios;
}
getOnlyBios() {
return this.onlyBios;
}
getNoDevice() {
return this.noDevice;
}
getOnlyDevice() {
return this.onlyDevice;
}
getNoUnlicensed() {
return this.noUnlicensed;
}
getOnlyUnlicensed() {
return this.onlyUnlicensed;
}
getOnlyRetail() {
return this.onlyRetail;
}
getNoDebug() {
return this.noDebug;
}
getOnlyDebug() {
return this.onlyDebug;
}
getNoDemo() {
return this.noDemo;
}
getOnlyDemo() {
return this.onlyDemo;
}
getNoBeta() {
return this.noBeta;
}
getOnlyBeta() {
return this.onlyBeta;
}
getNoSample() {
return this.noSample;
}
getOnlySample() {
return this.onlySample;
}
getNoPrototype() {
return this.noPrototype;
}
getOnlyPrototype() {
return this.onlyPrototype;
}
getNoProgram() {
return this.noProgram;
}
getOnlyProgram() {
return this.onlyProgram;
}
getNoAftermarket() {
return this.noAftermarket;
}
getOnlyAftermarket() {
return this.onlyAftermarket;
}
getNoHomebrew() {
return this.noHomebrew;
}
getOnlyHomebrew() {
return this.onlyHomebrew;
}
getNoUnverified() {
return this.noUnverified;
}
getOnlyUnverified() {
return this.onlyUnverified;
}
getNoBad() {
return this.noBad;
}
getOnlyBad() {
return this.onlyBad;
}
getSingle() {
return this.single;
}
getPreferGameRegex() {
return _Options.getRegex(this.preferGameRegex);
}
getPreferRomRegex() {
return _Options.getRegex(this.preferRomRegex);
}
getPreferVerified() {
return this.preferVerified;
}
getPreferGood() {
return this.preferGood;
}
getPreferLanguages() {
return _Options.filterUniqueUpper(this.preferLanguage);
}
getPreferRegions() {
return _Options.filterUniqueUpper(this.preferRegion);
}
getPreferRevision() {
const preferRevision = Object.keys(PreferRevision).find(
(mode) => mode.toLowerCase() === this.preferRevision?.toLowerCase()
);
if (!preferRevision) {
return void 0;
}
return PreferRevision[preferRevision];
}
getPreferRetail() {
return this.preferRetail;
}
getPreferParent() {
return this.preferParent;
}
getPreferFiletype() {
const preferFiletype = Object.keys(PreferFiletype).find(
(mode) => mode.toLowerCase() === this.preferFiletype?.toLowerCase()
);
if (!preferFiletype) {
return void 0;
}
return PreferFiletype[preferFiletype];
}
getPreferFilenameRegex() {
return _Options.getRegex(this.preferFilenameRegex);
}
getPlaylistMode() {
const playlistMode = Object.keys(PlaylistMode).find(
(mode) => mode.toLowerCase() === this.playlistMode?.toLowerCase()
);
if (!playlistMode) {
return void 0;
}
return PlaylistMode[playlistMode];
}
getPlaylistExtensions() {
return this.playlistExtensions;
}
getDir2DatOutput() {
return FsUtil.makeLegal(
this.dir2datOutput ?? (this.shouldWrite() ? this.getOutputDirRoot() : process.cwd())
);
}
getFixdatOutput() {
return FsUtil.makeLegal(
this.fixdatOutput ?? (this.shouldWrite() ? this.getOutputDirRoot() : process.cwd())
);
}
getReportOutput() {
let { reportOutput } = this;
const symbolMatches = reportOutput.match(/%([a-zA-Z])(\1|o)*/g);
if (symbolMatches) {
const now = /* @__PURE__ */ new Date();
const uniqueMatches = symbolMatches.reduce(ArrayUtil.reduceUnique(), []);
for (const match of uniqueMatches) {
const val = DateUtil.format(match.replace(/^%/, ""), now);
reportOutput = reportOutput.replace(match, val);
}
}
return FsUtil.makeLegal(reportOutput);
}
getDatThreads() {
return this.datThreads;
}
getReaderThreads() {
return this.readerThreads;
}
getWriterThreads() {
return this.writerThreads;
}
getTempDir() {
return this.tempDir;
}
getDisableCache() {
return this.disableCache;
}
getCachePath() {
return this.cachePath;
}
getLogLevel() {
if (this.verbose === 1) {
return LogLevel.INFO;
}
if (this.verbose === 2) {
return LogLevel.DEBUG;
}
if (this.verbose >= 3) {
return LogLevel.TRACE;
}
return LogLevel.WARN;
}
getDebugLog() {
if (this.debugLog === void 0) {
return void 0;
}
let { debugLog } = this;
if (debugLog === "") {
debugLog = "igir_%YYYY-%MM-%DDT%HH:%mm:%ss.log";
}
const symbolMatches = debugLog.match(/%([a-zA-Z])(\1|o)*/g);
if (symbolMatches) {
const now = /* @__PURE__ */ new Date();
const uniqueMatches = symbolMatches.reduce(ArrayUtil.reduceUnique(), []);
for (const match of uniqueMatches) {
const val = DateUtil.format(match.replace(/^%/, ""), now);
debugLog = debugLog.replace(match, val);
}
}
return FsUtil.makeLegal(debugLog);
}
getHelp() {
return this.help;
}
static filterUniqueUpper(array) {
return array.map((value) => value.toUpperCase()).reduce(ArrayUtil.reduceUnique(), []);
}
};
__decorateClass([
Expose({ name: "_" })
], _Options.prototype, "commands", 2);
let Options = _Options;
export {
FixExtension,
FixExtensionInverted,
GameSubdirMode,
GameSubdirModeInverted,
InputChecksumArchivesMode,
InputChecksumArchivesModeInverted,
LinkMode,
LinkModeInverted,
MergeMode,
MergeModeInverted,
MoveDeleteDirs,
MoveDeleteDirsInverted,
PlaylistMode,
PlaylistModeInverted,
PreferFiletype,
PreferFiletypeInverted,
PreferRevision,
PreferRevisionInverted,
TrimScanFiles,
TrimScanFilesInverted,
ZipFormat,
ZipFormatInverted,
Options as default
};
//# sourceMappingURL=options.js.map