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.

1,146 lines (1,145 loc) • 33.7 kB
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