Skip to content

Commit

Permalink
Feature: better handling of TOSEC naming conventions (#1123)
Browse files Browse the repository at this point in the history
  • Loading branch information
emmercm committed May 10, 2024
1 parent fe650c8 commit 6171249
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 28 deletions.
11 changes: 10 additions & 1 deletion src/modules/datParentInferrer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,16 @@ export default class DATParentInferrer extends Module {
.replace(/\[T[+-][^\]]+\]/, '')
.replace(/\[x\]/, '')
// ***** TOSEC *****
.replace(/\((AE|AL|AS|AT|AU|BA|BE|BG|BR|CA|CH|CL|CN|CS|CY|CZ|DE|DK|EE|EG|ES|EU|FI|FR|GB|GR|HK|HR|HU|ID|IE|IL|IN|IR|IS|IT|JO|JP|KR|LT|LU|LV|MN|MX|MY|NL|NO|NP|NZ|OM|PE|PH|PL|PT|QA|RO|RU|SE|SG|SI|SK|TH|TR|TW|US|VN|YU|ZA)\)/, '') // region
.replace(/\((ar|bg|bs|cs|cy|da|de|el|en|eo|es|et|fa|fi|fr|ga|gu|he|hi|hr|hu|is|it|ja|ko|lt|lv|ms|nl|no|pl|pt|ro|ru|sk|sl|sq|sr|sv|th|tr|ur|vi|yi|zh)\)/, '') // language
.replace(/\((demo|demo-kiosk|demo-playable|demo-rolling|demo-slideshow)\)/, '') // demo
.replace(/\([0-9x]{4}(-[0-9x]{2}(-[0-9x]{2})?)?\)/, '') // YYYY-MM-DD
.replace(/\((CGA|EGA|HGC|MCGA|MDA|NTSC|NTSC-PAL|PAL|PAL-60|PAL-NTSC|SVGA|VGA|XGA)\)/i, '') // video
.replace(/\(M[0-9]+\)/, '') // language
.replace(/\((CW|CW-R|FW|GW|GW-R|LW|PD|SW|SW-R)\)/i, '') // copyright
.replace(/\((alpha|beta|preview|pre-release|proto)\)/i, '') // development
.replace(/(\[(cr|f|h|m|p|t|tr|o|u|v|b|a|!)([0-9]+| [^\]]+)?\])+/i, '')
.replace(/(\W)v[0-9]+\.[0-9]+(\W)/i, '$1 $2')
// ***** Specific cases *****
.replace(/'([0-9][0-9])/, '$1') // year abbreviations
// ***** Console-specific *****
Expand All @@ -151,14 +154,20 @@ export default class DATParentInferrer extends Module {
.replace(/\(GameCube\)/i, '')
// Nintendo - Super Nintendo Entertainment System
.replace(/\(NP\)/i, '') // "Nintendo Power"
// Sega - Dreamcast
.replace(/\[[0-9]+S\]/, '') // boxcode
.replace(/\[[0-9]+MM?[0-9]+(, [0-9]+MM?[0-9]+)*\]/, '')
.replace(/for Dreamcast/i, '')
// Sega - Mega Drive / Genesis
.replace(/\(MP\)/i, '') // "MegaPlay version"
// Sega - Sega/Mega CD
.replace(/\(RE-?[0-9]*\)/, '')
// Sony - PlayStation 1
.replace(/\(EDC\)/i, '') // copy protection
.replace(/\(PSone Books\)/i, '')
.replace(/\((SCES|SCUS|SLES|SLUS)-[0-9]+\)/i, '')
.replace(/[(\]](SCES|SCUS|SLES|SLUS)-[0-9]+[(\]]/i, '')
// Sony - PlayStation Portable
.replace(/[(\]][UN][CLP][AEJKU][BFGHJMSXZ]-[0-9]+[(\]]/i, '')
// ***** Cleanup *****
.replace(/ +/g, ' ')
.trim();
Expand Down
2 changes: 1 addition & 1 deletion src/types/dats/dat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,11 @@ export default abstract class DAT {
.replace('Unofficial', '')
// Suffixes
.replace('Datfile', '')
.replace('Games', '')
.replace('(Deprecated)', '')
.replace(/\(Parent-Clone\)/g, '')
.replace('(WIP)', '')
// Cleanup
.replace(/-( +-)+/g, '- ')
.replace(/^[ -]+/, '')
.replace(/[ -]+$/, '')
.replace(/ +/g, ' ')
Expand Down
18 changes: 12 additions & 6 deletions src/types/dats/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,15 +206,21 @@ export default class Game implements GameProps {

getRevision(): number {
// Numeric revision
const numberMatches = this.getName().match(/\(Rev\s*([0-9.]+)\)/i);
if (numberMatches && numberMatches?.length >= 2 && !Number.isNaN(numberMatches[1])) {
return Number(numberMatches[1]);
const revNumberMatches = this.getName().match(/\(Rev\s*([0-9.]+)\)/i);
if (revNumberMatches && revNumberMatches?.length >= 2 && !Number.isNaN(revNumberMatches[1])) {
return Number(revNumberMatches[1]);
}

// Letter revision
const letterMatches = this.getName().match(/\(Rev\s*([A-Z])\)/i);
if (letterMatches && letterMatches?.length >= 2) {
return (letterMatches[1].toUpperCase().codePointAt(0) as number) - ('A'.codePointAt(0) as number) + 1;
const revLetterMatches = this.getName().match(/\(Rev\s*([A-Z])\)/i);
if (revLetterMatches && revLetterMatches?.length >= 2) {
return (revLetterMatches[1].toUpperCase().codePointAt(0) as number) - ('A'.codePointAt(0) as number) + 1;
}

// TOSEC versions
const versionMatches = this.getName().match(/\Wv([0-9]+\.[0-9]+)\W/i);
if (versionMatches && versionMatches?.length >= 2 && !Number.isNaN(versionMatches[1])) {
return Number(versionMatches[1]);
}

// Ring code revision
Expand Down
50 changes: 30 additions & 20 deletions src/types/internationalization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,31 @@ export default class Internationalization {
// Specific countries
{ region: 'ARG', long: 'Argentina', language: 'ES' },
{
region: 'AUS', long: 'Australia', language: 'EN', regex: /\(A\)/i,
region: 'AUS', long: 'Australia', language: 'EN', regex: /\((A|AU)\)/i,
},
{ region: 'BEL', long: 'Belgium', language: 'FR' },
{
region: 'BRA', long: 'Brazil', language: 'PT', regex: /\(B\)/i,
region: 'BEL', long: 'Belgium', language: 'FR', regex: /\(BE\)/i,
},
{ region: 'CAN', long: 'Canada', language: 'EN' },
{
region: 'CHN', long: 'China', language: 'ZH', regex: /\((C|CH)\)/i,
region: 'BRA', long: 'Brazil', language: 'PT', regex: /\((B|BR)\)/i,
},
{ region: 'DAN', long: 'Denmark', language: 'DA' },
{
region: 'FRA', long: 'France', language: 'FR', regex: /\(F\)/i,
region: 'CAN', long: 'Canada', language: 'EN', regex: /\(CA\)/i,
},
{
region: 'FYN', long: 'Finland', language: 'FI', regex: /\(FN\)/i,
region: 'CHN', long: 'China', language: 'ZH', regex: /\((C|CH|CN)\)/i,
},
{
region: 'GER', long: 'Germany', language: 'DE', regex: /\(G\)/i,
region: 'DAN', long: 'Denmark', language: 'DA', regex: /\(DK\)/i,
},
{
region: 'FRA', long: 'France', language: 'FR', regex: /\((F|FR)\)/i,
},
{
region: 'FYN', long: 'Finland', language: 'FI', regex: /\((FI|FN)\)/i,
},
{
region: 'GER', long: 'Germany', language: 'DE', regex: /\((DE|G)\)/i,
},
{
region: 'GRE', long: 'Greece', language: 'EL', regex: /\(Gr\)/i,
Expand All @@ -50,45 +56,49 @@ export default class Internationalization {
region: 'HOL', long: 'Netherlands', language: 'NL', regex: /\((D|H|NL)\)/i,
},
{
region: 'ITA', long: 'Italy', language: 'IT', regex: /\(I\)/i,
region: 'ITA', long: 'Italy', language: 'IT', regex: /\((I|IT)\)/i,
},
{
region: 'JPN', long: 'Japan', language: 'JA', regex: /\((1|J|JP)\)/i,
},
{
region: 'JPN', long: 'Japan', language: 'JA', regex: /\((1|J)\)/i,
region: 'KOR', long: 'Korea', language: 'KO', regex: /\((K|KR)\)/i,
},
{
region: 'KOR', long: 'Korea', language: 'KO', regex: /\(K\)/i,
region: 'MEX', long: 'Mexico', language: 'ES', regex: /\(MX\)/i,
},
{ region: 'MEX', long: 'Mexico', language: 'ES' },
{
region: 'NOR', long: 'Norway', language: 'NO', regex: /\(No\)/i,
},
{ region: 'NZ', long: 'New Zealand', language: 'EN' },
{ region: 'POR', long: 'Portugal', language: 'PT' },
{
region: 'RUS', long: 'Russia', language: 'RU', regex: /\(R\)/i,
region: 'RUS', long: 'Russia', language: 'RU', regex: /\((R|RU)\)/i,
},
{
region: 'SPA', long: 'Spain', language: 'ES', regex: /\((ES|S)\)/i,
},
{
region: 'SPA', long: 'Spain', language: 'ES', regex: /\(S\)/i,
region: 'SWE', long: 'Sweden', language: 'SV', regex: /\((SE|SW)\)/i,
},
{
region: 'SWE', long: 'Sweden', language: 'SV', regex: /\(Sw\)/i,
region: 'TAI', long: 'Taiwan', language: 'ZH', regex: /\(TW\)/i,
},
{ region: 'TAI', long: 'Taiwan', language: 'ZH' },
{
region: 'UK', long: 'United Kingdom', language: 'EN', regex: /\(UK\)/i,
region: 'UK', long: 'United Kingdom', language: 'EN', regex: /\((GB|UK)\)/i,
},
{
region: 'UNK', long: 'Unknown', language: 'EN', regex: /\(Unk\)/i,
},
{
region: 'USA', long: 'USA', language: 'EN', regex: /\((4|U)\)/i,
region: 'USA', long: 'USA', language: 'EN', regex: /\((4|U|US)\)/i,
},
// Regions
{
region: 'ASI', long: 'Asia', language: 'ZH', regex: /\(As\)/i,
},
{
region: 'EUR', long: 'Europe', language: 'EN', regex: /\(E\)/i,
region: 'EUR', long: 'Europe', language: 'EN', regex: /\((E|EU|PAL)\)/i,
},
{ region: '', long: 'Scandinavia', language: '' },
{
Expand Down
23 changes: 23 additions & 0 deletions test/modules/candidatePreferer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,24 @@ describe('sort', () => {
await buildReleaseCandidatesWithRegionLanguage(['four (Europe)', 'four (USA)'], undefined, undefined),
], ['one (USA) (EN)', 'two (USA) (ES)', 'three (USA) (EN)', 'four (USA)']);
});

test.each([
[[
'F1 World Grand Prix for Dreamcast v1.011 (1999)(Video System)(JP)(en)[!]',
'F1 World Grand Prix for Dreamcast v1.000 (1999)(Video System)(PAL)(M4)[!]',
'F1 World Grand Prix v1.006 (2000)(Video System)(US)(M4)[!]',
], 'F1 World Grand Prix v1.006 (2000)(Video System)(US)(M4)[!]'],
[[
'Fighting Vipers 2 v1.001 (2000)(Sega)(JP)(en)[!]',
'Fighting Vipers 2 v1.001 (2000)(Sega)(PAL)(M6)[!]',
], 'Fighting Vipers 2 v1.001 (2000)(Sega)(PAL)(M6)[!]'],
])('should return the first candidate when all matching by name: %s', async (names, expectedName) => {
await expectPreferredCandidates(
{ preferRegion: ['USA', 'EUR', 'JPN'], single: true },
[await buildReleaseCandidatesWithRegionLanguage(names)],
[expectedName],
);
});
});

describe('prefer revision newer', () => {
Expand Down Expand Up @@ -449,6 +467,11 @@ describe('sort', () => {
test.each([
[['one (Rev 1.1)', 'one (Rev 1.2)'], 'one (Rev 1.2)'],
[['two (Rev 13.37)'], 'two (Rev 13.37)'],
[[
'ChuChu Rocket! v1.003 (1999)(Sega)(JP)[!]',
'ChuChu Rocket! v1.007 (2000)(Sega)(US)(en-ja)[!]',
'ChuChu Rocket! v1.014 (2000)(Sega)(PAL)(M5)[!]',
], 'ChuChu Rocket! v1.014 (2000)(Sega)(PAL)(M5)[!]'],
])('should return the first candidate when all matching: %s', async (names, expectedName) => {
await expectPreferredCandidates(
{ preferRevisionNewer: true, single: true },
Expand Down
5 changes: 5 additions & 0 deletions test/modules/datParentInferrer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@ test.each([
'Legend of TOSEC, The (1986)(Devstudio)(US)[cr PDX][h TRSi]',
'Legend of TOSEC, The (1986)(Devstudio)(US)[h PDX - TRSi]',
], 'Legend of TOSEC, The (1986)(Devstudio)(US)'],
[[
'F1 World Grand Prix for Dreamcast v1.011 (1999)(Video System)(JP)(en)[!]',
'F1 World Grand Prix for Dreamcast v1.000 (1999)(Video System)(PAL)(M4)[!]',
'F1 World Grand Prix v1.006 (2000)(Video System)(US)(M4)[!]',
], 'F1 World Grand Prix for Dreamcast v1.011 (1999)(Video System)(JP)(en)[!]'],
])('should group similar games: %s', async (gameNames, expectedGameName) => {
const ungroupedDat = buildDat(gameNames);
const groupedDat = await new DATParentInferrer(new Options(), new ProgressBarFake())
Expand Down

0 comments on commit 6171249

Please sign in to comment.