Files
Tarun Kumar a49d6b441d chore: remove all external-project references, standalone README
Strip every mention of the external reference project from code comments,
scripts and workflows, and rewrite the README as a standalone project doc
(functionality, building, derived data, compatibility database, games
database, automation, deployment) with no outside references except the
KytyPS5 emulator itself.
2026-08-08 01:18:01 +05:30

63 lines
2.3 KiB
JavaScript

// Title-list transforms: regional-ID stripping, junk filtering, concept
// deduplication, and refresh-merging that preserves enrichment + overrides.
const REGION_RANK = { UP: 0, EP: 1, JP: 2 };
export function regionRank(region) {
return REGION_RANK[region] ?? 3;
}
/** Strip the regional "_NN" suffix from a title ID (PPSA01007_00 → PPSA01007). */
export function stripSuffix(titleId) {
return titleId.replace(/_\d{2}$/, "");
}
const JUNK_PATTERNS = [/entitlement/i, /\((?:prod|dev|qa|test|internal|beta)\)/i];
export function isJunk(row) {
const name = row.name ?? "";
if (name.trim() === "") return true;
return JUNK_PATTERNS.some((re) => re.test(name));
}
/** Deduplicate title rows by PS Store concept ID, preferring US/EU/JP regions. */
export function dedupeByConcept(rows) {
const byConcept = new Map();
for (const row of rows) {
if (isJunk(row)) continue;
if (!byConcept.has(row.conceptId)) byConcept.set(row.conceptId, []);
byConcept.get(row.conceptId).push(row);
}
const games = [];
for (const [conceptId, list] of byConcept) {
list.sort(
(a, b) => regionRank(a.region) - regionRank(b.region) || a.titleId.localeCompare(b.titleId),
);
const primary = list[0];
games.push({
conceptId,
titleId: stripSuffix(primary.titleId),
allTitleIds: [...new Set(list.map((r) => stripSuffix(r.titleId)))].sort(),
name: primary.name,
region: primary.region,
});
}
return games.sort((a, b) => a.titleId.localeCompare(b.titleId));
}
/** Preserve enrichment + manual overrides across database refreshes. */
export function mergeExisting(fresh, existing) {
const prev = new Map(existing.map((g) => [g.conceptId, g]));
return fresh.map((g) => {
const old = prev.get(g.conceptId);
if (!old) return g;
// Manual flags (commentsDisabled, noStore) survive refreshes even for
// entries that aren't enriched yet — including hand-completed entries.
const withOverrides =
old.commentsDisabled === undefined && old.noStore === undefined
? g
: { ...g, commentsDisabled: old.commentsDisabled, noStore: old.noStore };
if (!old.enriched) return withOverrides;
const { name, cover, publisher, releaseDate, genres, enriched, noStore } = old;
return { ...withOverrides, name, cover, publisher, releaseDate, genres, enriched, noStore };
});
}