emmercm/igir

View on GitHub
src/modules/dir2DatCreator.ts

Summary

Maintainability
A
2 hrs
Test Coverage
import path from 'node:path';

import ProgressBar, { ProgressBarSymbol } from '../console/progressBar.js';
import fsPoly from '../polyfill/fsPoly.js';
import DAT from '../types/dats/dat.js';
import Game from '../types/dats/game.js';
import LogiqxDAT from '../types/dats/logiqx/logiqxDat.js';
import Parent from '../types/dats/parent.js';
import Options from '../types/options.js';
import OutputFactory from '../types/outputFactory.js';
import ReleaseCandidate from '../types/releaseCandidate.js';
import Module from './module.js';

/**
 * Write a DAT that was generated by {@link DATGameInferrer} to disk.
 */
export default class Dir2DatCreator extends Module {
  private readonly options: Options;

  constructor(options: Options, progressBar: ProgressBar) {
    super(progressBar, Dir2DatCreator.name);
    this.options = options;
  }

  /**
   * Write the DAT.
   */
  async create(
    dat: DAT,
    parentsToCandidates: Map<Parent, ReleaseCandidate[]>,
  ): Promise<string | undefined> {
    if (!this.options.shouldDir2Dat()) {
      return undefined;
    }

    this.progressBar.logTrace(`${dat.getNameShort()}: writing dir2dat`);
    this.progressBar.setSymbol(ProgressBarSymbol.WRITING);
    this.progressBar.reset(1);

    const datDir = this.options.shouldWrite()
      ? OutputFactory.getDir(this.options, dat)
      : process.cwd();
    if (!(await fsPoly.exists(datDir))) {
      await fsPoly.mkdir(datDir, { recursive: true });
    }
    const datPath = path.join(datDir, dat.getFilename());

    // It is possible that the {@link ROM} embedded within {@link ReleaseCandidate}s has been
    // manipulated, such as from {@link CandidateExtensionCorrector}. Use the {@link Game}s and
    // {@link ROM}s from the {@link ReleaseCandidate}s instead of the original {@link DAT}.
    const gamesToCandidates = [...parentsToCandidates.values()]
      .flat()
      .reduce((map, releaseCandidate) => {
        const key = releaseCandidate.getGame();
        if (!map.has(key)) {
          map.set(key, [releaseCandidate]);
        } else {
          map.get(key)?.push(releaseCandidate);
        }
        return map;
      }, new Map<Game, ReleaseCandidate[]>());
    const gamesFromCandidates = [...gamesToCandidates.entries()].map(
      ([game, releaseCandidates]) => {
        const roms = releaseCandidates
          .at(0)
          ?.getRomsWithFiles()
          .map((romWithFiles) => romWithFiles.getRom());
        return game.withProps({ rom: roms });
      },
    );
    const datFromCandidates = new LogiqxDAT(dat.getHeader(), gamesFromCandidates);

    this.progressBar.logInfo(`${datFromCandidates.getNameShort()}: creating dir2dat '${datPath}'`);
    const datContents = datFromCandidates.toXmlDat();
    await fsPoly.writeFile(datPath, datContents);

    this.progressBar.logTrace(`${datFromCandidates.getNameShort()}: done writing dir2dat`);
    return datPath;
  }
}