Skip to content

Commit

Permalink
Fix: respect output archive entry paths with subdirectories (#701)
Browse files Browse the repository at this point in the history
  • Loading branch information
emmercm authored Sep 27, 2023
1 parent c3659a2 commit 45cd8e3
Show file tree
Hide file tree
Showing 4 changed files with 1,001 additions and 61 deletions.
23 changes: 17 additions & 6 deletions src/modules/datMergerSplitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,22 @@ export default class DATMergerSplitter extends Module {
private mergeParent(parent: Parent, gameNamesToGames: Map<string, Game>): Game[] {
let games = parent.getGames();

// Get rid of duplicate ROMs. MAME will sometimes duplicate a file with the exact same name,
// size, and checksum but with a different "region" (e.g. neogeo).
const romNameFunc = (rom: ROM): string => rom.getName()
// Numeric sort will sort underscore before hyphens? ASCII says don't do that
.replace('-', '__');
const romSortFunc = (a: ROM, b: ROM): number => romNameFunc(a)
.localeCompare(romNameFunc(b), undefined, { numeric: true });

// Sanitization
games = games.map((game) => {
const romNames = game.getRoms().map((rom) => rom.getName());
return game.withProps({
rom: game.getRoms()
.filter((rom, idx) => romNames.indexOf(rom.getName()) === idx),
// Get rid of duplicate ROMs. MAME will sometimes duplicate a file with the exact same
// name, size, and checksum but with a different "region" (e.g. neogeo).
.filter((rom, idx) => romNames.indexOf(rom.getName()) === idx)
// Sort for easier debugging and testing
.sort(romSortFunc),
});
});

Expand All @@ -87,7 +96,7 @@ export default class DATMergerSplitter extends Module {
.filter(ArrayPoly.filterNotNullish)
.flatMap((deviceGame) => deviceGame.getRoms()),
...game.getRoms(),
],
].sort(romSortFunc),
});
});
}
Expand All @@ -101,7 +110,8 @@ export default class DATMergerSplitter extends Module {
}
return game.withProps({
rom: game.getRoms()
.filter((rom) => !rom.getBios()),
.filter((rom) => !rom.getBios())
.sort(romSortFunc),
});
});
}
Expand All @@ -117,7 +127,8 @@ export default class DATMergerSplitter extends Module {
) {
cloneGames = cloneGames
.map((childGame) => childGame.withProps({
rom: DATMergerSplitter.diffGameRoms(parentGame, childGame),
rom: DATMergerSplitter.diffGameRoms(parentGame, childGame)
.sort(romSortFunc),
}));
}

Expand Down
39 changes: 33 additions & 6 deletions src/types/outputFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export default class OutputFactory {
base: '',
name,
ext,
entryPath: path.basename(this.getRomBasename(options, dat, rom, inputFile)),
entryPath: this.getEntryPath(options, dat, game, rom, inputFile),
});
}

Expand Down Expand Up @@ -400,29 +400,56 @@ export default class OutputFactory {
rom: ROM,
inputFile: File,
): string {
const romPath = this.getRomBasename(options, dat, rom, inputFile);

// Determine the output path of the file
if (options.shouldZip(rom.getName())) {
// Should zip, generate the zip name from the game name
return `${game.getName()}.zip`;
}

const romBasename = this.getRomBasename(options, dat, rom, inputFile);

if (
!(inputFile instanceof ArchiveEntry || FileFactory.isArchive(inputFile.getFilePath()))
|| options.shouldExtract()
) {
// Should extract (if needed), generate the file name from the ROM name
return romPath;
return romBasename;
}

// Should leave archived, generate the archive name from the game name, but use the input
// file's extension
// file's extension
const extMatch = inputFile.getFilePath().match(/[^.]+((\.[a-zA-Z0-9]+)+)$/);
const ext = extMatch !== null ? extMatch[1] : '';
return game.getName() + ext;
}

private static getRomBasename(options: Options, dat: DAT, rom: ROM, inputFile: File): string {
private static getEntryPath(
options: Options,
dat: DAT,
game: Game,
rom: ROM,
inputFile: File,
): string {
const romBasename = this.getRomBasename(options, dat, rom, inputFile);
if (!options.shouldZip(rom.getName())) {
return romBasename;
}

// The file structure from HTGD SMDBs ends up in both the Game and ROM names. If we're
// zipping, then the Game name will end up in the filename, we don't need it duplicated in
// the entry path.
const gameNameSanitized = game.getName().replace(/[\\/]/g, path.sep);
return romBasename
.replace(/[\\/]/g, path.sep)
.replace(`${path.dirname(gameNameSanitized)}${path.sep}`, '');
}

private static getRomBasename(
options: Options,
dat: DAT,
rom: ROM,
inputFile: File,
): string {
let romNameSanitized = rom.getName();
if (!dat.getRomNamesContainDirectories()) {
romNameSanitized = romNameSanitized?.replace(/[\\/]/g, '_');
Expand Down
Loading

0 comments on commit 45cd8e3

Please sign in to comment.