From 9b87fb9356fca0173a8022048d71e1c27d970374 Mon Sep 17 00:00:00 2001 From: mtkennerly Date: Wed, 15 Jul 2020 21:21:57 -0400 Subject: [PATCH] Fix handling for irregular paths, P vs Path template invocation, and variadic uses of P template --- data/manifest.yaml | 21 +++-- data/wiki-game-cache.yaml | 7 +- src/bin.ts | 2 + src/manifest.ts | 4 + src/wiki.ts | 176 ++++++++++++++++++++++++++------------ 5 files changed, 143 insertions(+), 67 deletions(-) diff --git a/data/manifest.yaml b/data/manifest.yaml index 8c2d0920..b6aa3b31 100644 --- a/data/manifest.yaml +++ b/data/manifest.yaml @@ -1620,7 +1620,7 @@ A Rite from the Stars: id: 792370 A Robot Named Fight!: files: - '/.config/unity3d/Matt Bitner/A Robot Named Fight/{{Refurl|url=https://steamcommunity.com/app/603530/discussions/1/1473096694440794319/#c3377008022026160641/|title=Where is my save file? :: A Robot Named Fight Bug Reporting|date=2019-05-28}}': + /.config/unity3d/Matt Bitner/A Robot Named Fight: tags: - save when: @@ -2522,7 +2522,12 @@ AI War 2: id: 438020 'AI: The Somnium Files': files: - '/SpikeChunsoft/AI The Somnium Files/PSYNCAUTOSAVE
/SpikeChunsoft/AI The Somnium Files/PSYNCDEF{{code|XX}}': + /SpikeChunsoft/AI The Somnium Files/PSYNCAUTOSAVE: + tags: + - save + when: + - os: windows + /SpikeChunsoft/AI The Somnium Files/PSYNCDEF*: tags: - save when: @@ -2534,6 +2539,10 @@ AI War 2: - os: windows installDir: AI The Somnium Files: {} + registry: + HKEY_CURRENT_USER/Software/SpikeChunsoft/AI_TheSomniumFiles: + tags: + - config steam: id: 948740 AIDS Simulator: @@ -96936,7 +96945,7 @@ Graveyard Keeper: - save when: - os: mac - '/Library/Preferences/{{file|unity.Lazy Bear Games.Graveyard Keeper.plist}}': + /Library/Preferences/unity.Lazy Bear Games.Graveyard Keeper.plist: tags: - config when: @@ -104793,13 +104802,9 @@ Her Story: when: - os: windows /Library/Application Support/unity.Sam Barlow.HerStory: - tags: - - save - when: - - os: mac - '/Library/Application Support/unity.Sam Barlow.HerStory/{{cn|Windows path was for save only, is this legit OS X versions config path?|date=January 15, 2016}}': tags: - config + - save when: - os: mac installDir: diff --git a/data/wiki-game-cache.yaml b/data/wiki-game-cache.yaml index d9e67929..0384f80f 100644 --- a/data/wiki-game-cache.yaml +++ b/data/wiki-game-cache.yaml @@ -1653,6 +1653,7 @@ A Rite from the Stars: pageId: 103285 revId: 841573 A Robot Named Fight!: + irregularPath: true pageId: 66273 revId: 934101 A Roll-Back Story: @@ -2109,8 +2110,9 @@ AI War 2: pageId: 44800 revId: 841706 'AI: The Somnium Files': + irregularPath: true pageId: 132809 - revId: 963156 + revId: 983332 AIDS Simulator: pageId: 96877 revId: 841708 @@ -47163,7 +47165,7 @@ Graveyard Defender: revId: 839908 Graveyard Keeper: pageId: 58471 - revId: 962811 + revId: 983342 Graveyard Shift: pageId: 51951 revId: 855328 @@ -50952,6 +50954,7 @@ Her Majesty's Ship: pageId: 88894 revId: 887258 Her Story: + irregularPath: true pageId: 26295 revId: 975631 Her War: diff --git a/src/bin.ts b/src/bin.ts index ddb1fc0e..78c33b66 100644 --- a/src/bin.ts +++ b/src/bin.ts @@ -15,6 +15,7 @@ interface Cli { unchecked?: boolean, unsupportedOs?: boolean, unsupportedPath?: boolean, + irregularPath?: boolean, tooBroad?: boolean, tooBroadUntagged?: boolean, game?: string, @@ -61,6 +62,7 @@ async function main() { unsupportedPath: args.unsupportedPath ?? false, tooBroad: args.tooBroad ?? false, tooBroadUntagged: args.tooBroadUntagged ?? false, + irregularPath: args.irregularPath ?? false, game: args.game, recent: args.recent, }, diff --git a/src/manifest.ts b/src/manifest.ts index ed193824..d6b6324b 100644 --- a/src/manifest.ts +++ b/src/manifest.ts @@ -51,6 +51,7 @@ export class ManifestFile extends YamlFile { unchecked: boolean, unsupportedOs: boolean, unsupportedPath: boolean, + irregularPath: boolean, tooBroad: boolean, tooBroadUntagged: boolean, game: string | undefined, @@ -80,6 +81,9 @@ export class ManifestFile extends YamlFile { if (filter.unsupportedPath && info.unsupportedPath) { check = true; } + if (filter.irregularPath && (wikiCache[title].irregularPath || Object.keys(this.data[title]?.files ?? []).some(x => x.includes("{{") || x.includes("") || x.includes("
")))) { + check = true; + } if (filter.game === title) { check = true; } diff --git a/src/wiki.ts b/src/wiki.ts index ffad4819..31fac6b7 100644 --- a/src/wiki.ts +++ b/src/wiki.ts @@ -15,6 +15,7 @@ export type WikiGameCache = { tooBroad?: boolean, recentlyChanged?: boolean, renamedFrom?: Array, + irregularPath?: boolean, }; }; @@ -176,7 +177,7 @@ const PATH_ARGS: { [arg: string]: { mapped: string, when?: Constraint, registry? } function makePathArgRegex(arg: string): RegExp { - const escaped = `{{P|${arg}}}` + const escaped = `{{P(ath)?|${arg}}}` .replace("\\", "\\\\") .replace("|", "\\|") .replace("{", "\\{") @@ -184,6 +185,48 @@ function makePathArgRegex(arg: string): RegExp { return new RegExp(escaped, "gi"); } +// Examples: +// [ [["p"], "linuxhome"], ".config" ] +// [ [["cn"], "Is this right?", "date=January 1, 2000"] ] +type PathSegment = string | [[string], ...Array]; + +function stringifyPathSegment(segment: PathSegment): [string, boolean] { + if (typeof segment === "string") { + return [segment, true]; + } + + const templateName = segment[0][0]; + switch (templateName.toLowerCase()) { + case "p": + case "path": + return [`{{${templateName}|${segment[1]}}}`, true]; + case "code": + case "file": + return ["*", false]; + case "localizedpath": + return [segment[1], false]; + default: + return ["", false]; + } +} + +function getRawPathFromCell(cell: string | Array | undefined): [string | undefined, boolean] { + let regular = true; + if (cell === undefined) { + return [undefined, regular]; + } else if (typeof cell === "string") { + return [cell.replace(/.*?<\ref>/, ""), regular]; + } else { + return [cell.map(x => { + const [stringified, segmentRegular] = stringifyPathSegment(x); + if (!segmentRegular) { + regular = false; + } + return stringified; + }).join("").replace(/.*?<\ref>/, ""), regular]; + } +} + /** * https://www.pcgamingwiki.com/wiki/Template:Path */ @@ -401,6 +444,7 @@ export async function getGame(pageTitle: string, cache: WikiGameCache): Promise< let unsupportedOs = 0; let unsupportedPath = 0; let tooBroad = 0; + let irregularPath = 0; page.parse().each("template", template => { if (template.name === "Infobox game") { const steamId = Number(template.parameters["steam appid"]); @@ -408,70 +452,82 @@ export async function getGame(pageTitle: string, cache: WikiGameCache): Promise< game.steam = { id: steamId }; } } else if (template.name === "Game data/saves" || template.name === "Game data/config") { - const rawPath = typeof template.parameters[2] === "string" ? template.parameters[2] : template.parameters[2]?.toString(); - if (rawPath === undefined || rawPath.length === 0) { - return; - } - try { - const [path, pathType] = parsePath(rawPath); - if (pathIsTooBroad(path)) { - tooBroad += 1; - return; + for (const cellKey of Object.getOwnPropertyNames(template.parameters)) { + if (cellKey === "0" || cellKey === "1") { + continue; } - if (pathType === PathType.FileSystem) { - const constraint = getConstraintFromSystem(template.parameters[1], rawPath); + const cell = template.parameters[cellKey]; + const [rawPath, regular] = getRawPathFromCell(cell); - if (!game.files.hasOwnProperty(path)) { - game.files[path] = { - when: [], - tags: [], - }; + if (!regular) { + irregularPath += 1; + } + + if (rawPath === undefined || rawPath.length === 0) { + continue; + } + + try { + const [path, pathType] = parsePath(rawPath); + if (pathIsTooBroad(path)) { + tooBroad += 1; + continue; } + if (pathType === PathType.FileSystem) { + const constraint = getConstraintFromSystem(template.parameters[1], rawPath); - if (!game.files[path].when.some(x => x.os === constraint.os && x.store === constraint.store)) { - if (constraint.os !== undefined && constraint.store !== undefined) { - game.files[path].when.push(constraint); - } else if (constraint.os !== undefined) { - game.files[path].when.push({ os: constraint.os }); - } else if (constraint.store !== undefined) { - game.files[path].when.push({ store: constraint.store }); + if (!game.files.hasOwnProperty(path)) { + game.files[path] = { + when: [], + tags: [], + }; + } + + if (!game.files[path].when.some(x => x.os === constraint.os && x.store === constraint.store)) { + if (constraint.os !== undefined && constraint.store !== undefined) { + game.files[path].when.push(constraint); + } else if (constraint.os !== undefined) { + game.files[path].when.push({ os: constraint.os }); + } else if (constraint.store !== undefined) { + game.files[path].when.push({ store: constraint.store }); + } + } + + const tag = getTagFromTemplate(template.name); + if (tag !== undefined && !game.files[path].tags.includes(tag)) { + game.files[path].tags.push(tag); + } + } else if (pathType === PathType.Registry) { + if (!game.registry.hasOwnProperty(path)) { + game.registry[path] = { + when: [], + tags: [], + }; + } + + const store = getStoreConstraintFromPath(rawPath); + if (store !== undefined && !game.registry[path].when.some(x => x.store === store)) { + game.registry[path].when.push({ store }); + } + + const tag = getTagFromTemplate(template.name); + if (tag !== undefined && !game.registry[path].tags.includes(tag)) { + game.registry[path].tags.push(tag); } } + } catch (e) { + console.log(` ${template.toString()}`); + console.log(` ${e}`); - const tag = getTagFromTemplate(template.name); - if (tag !== undefined && !game.files[path].tags.includes(tag)) { - game.files[path].tags.push(tag); + if (e instanceof UnsupportedOsError) { + unsupportedOs += 1; + continue; + } else if (e instanceof UnsupportedPathError) { + unsupportedPath += 1; + continue; + } else { + continue; } - } else if (pathType === PathType.Registry) { - if (!game.registry.hasOwnProperty(path)) { - game.registry[path] = { - when: [], - tags: [], - }; - } - - const store = getStoreConstraintFromPath(rawPath); - if (store !== undefined && !game.registry[path].when.some(x => x.store === store)) { - game.registry[path].when.push({ store }); - } - - const tag = getTagFromTemplate(template.name); - if (tag !== undefined && !game.registry[path].tags.includes(tag)) { - game.registry[path].tags.push(tag); - } - } - } catch (e) { - console.log(` ${template.toString()}`); - console.log(` ${e}`); - - if (e instanceof UnsupportedOsError) { - unsupportedOs += 1; - return; - } else if (e instanceof UnsupportedPathError) { - unsupportedPath += 1; - return; - } else { - return; } } } @@ -521,6 +577,12 @@ export async function getGame(pageTitle: string, cache: WikiGameCache): Promise< delete cache[pageTitle].tooBroad; } + if (irregularPath > 0) { + cache[pageTitle].irregularPath = true; + } else { + delete cache[pageTitle].irregularPath; + } + cache[pageTitle].revId = page.revisions?.[0]?.revid ?? 0; return game; }