Add launch section to manifest

This commit is contained in:
mtkennerly 2022-06-03 06:41:45 +08:00
parent cb242f6433
commit a046c18ad4
No known key found for this signature in database
GPG key ID: E764BE00BE6E6408
7 changed files with 266804 additions and 373 deletions

File diff suppressed because it is too large Load diff

View file

@ -8096,6 +8096,7 @@
* [Demon Pit](https://www.pcgamingwiki.com/wiki/?curid=145079)
* [Demon Queen Melissa](https://www.pcgamingwiki.com/wiki/?curid=130672)
* [Demon Robot Runner](https://www.pcgamingwiki.com/wiki/?curid=88150)
* [Demon Slayer -Kimetsu no Yaiba- The Hinokami Chronicles](https://www.pcgamingwiki.com/wiki/?curid=166446)
* [Demon Truck](https://www.pcgamingwiki.com/wiki/?curid=41495)
* [Demon Turf](https://www.pcgamingwiki.com/wiki/?curid=161608)
* [Demon Turf: Neon Splash](https://www.pcgamingwiki.com/wiki/?curid=177760)
@ -9040,6 +9041,7 @@
* [Dragon Sinker](https://www.pcgamingwiki.com/wiki/?curid=77255)
* [Dragon Skies VR](https://www.pcgamingwiki.com/wiki/?curid=52622)
* [Dragon Slayer](https://www.pcgamingwiki.com/wiki/?curid=149592)
* [Dragon Slayer: The Legend of Heroes II](https://www.pcgamingwiki.com/wiki/?curid=174909)
* [Dragon Souls](https://www.pcgamingwiki.com/wiki/?curid=53491)
* [Dragon Spear](https://www.pcgamingwiki.com/wiki/?curid=108568)
* [Dragon Spirits](https://www.pcgamingwiki.com/wiki/?curid=154322)
@ -11247,6 +11249,7 @@
* [Final Dusk](https://www.pcgamingwiki.com/wiki/?curid=49125)
* [Final Fantasy Awakening](https://www.pcgamingwiki.com/wiki/?curid=26745)
* [Final Fantasy III](https://www.pcgamingwiki.com/wiki/?curid=169624)
* [Final Fantasy IV](https://www.pcgamingwiki.com/wiki/?curid=169625)
* [Final Fantasy XI](https://www.pcgamingwiki.com/wiki/?curid=788)
* [Final Fleet](https://www.pcgamingwiki.com/wiki/?curid=38791)
* [Final Hope](https://www.pcgamingwiki.com/wiki/?curid=110158)
@ -12526,6 +12529,7 @@
* [Gene](https://www.pcgamingwiki.com/wiki/?curid=48845)
* [Gene Rain](https://www.pcgamingwiki.com/wiki/?curid=100434)
* [Gene Rain: Wind Tower](https://www.pcgamingwiki.com/wiki/?curid=149099)
* [Geneforge](https://www.pcgamingwiki.com/wiki/?curid=10284)
* [General Coco](https://www.pcgamingwiki.com/wiki/?curid=149368)
* [General Horse and the Package of Doom](https://www.pcgamingwiki.com/wiki/?curid=113264)
* [General Practitioner](https://www.pcgamingwiki.com/wiki/?curid=114154)
@ -15872,6 +15876,7 @@
* [IStorm](https://www.pcgamingwiki.com/wiki/?curid=60281)
* [Istrolid](https://www.pcgamingwiki.com/wiki/?curid=38179)
* [Isyium](https://www.pcgamingwiki.com/wiki/?curid=54806)
* [It Came from Space and Ate Our Brains](https://www.pcgamingwiki.com/wiki/?curid=38329)
* [It Comes Around - A Kinetic Novel](https://www.pcgamingwiki.com/wiki/?curid=50737)
* [It comes from hell](https://www.pcgamingwiki.com/wiki/?curid=132678)
* [It Could Have Been Me](https://www.pcgamingwiki.com/wiki/?curid=135399)
@ -21169,6 +21174,7 @@
* [Nostradamus - The Four Horsemen of the Apocalypse](https://www.pcgamingwiki.com/wiki/?curid=80418)
* [Not a Prank](https://www.pcgamingwiki.com/wiki/?curid=139203)
* [Not Dying Today](https://www.pcgamingwiki.com/wiki/?curid=55764)
* [Not for Broadcast](https://www.pcgamingwiki.com/wiki/?curid=150852)
* [Not Heaven](https://www.pcgamingwiki.com/wiki/?curid=121145)
* [Not in Heaven](https://www.pcgamingwiki.com/wiki/?curid=121041)
* [Not My Day!](https://www.pcgamingwiki.com/wiki/?curid=120965)
@ -21613,6 +21619,7 @@
* [One Small Fire at a Time](https://www.pcgamingwiki.com/wiki/?curid=33858)
* [One Sole Purpose](https://www.pcgamingwiki.com/wiki/?curid=52634)
* [One Star](https://www.pcgamingwiki.com/wiki/?curid=60928)
* [One Step from Eden](https://www.pcgamingwiki.com/wiki/?curid=122848)
* [One Strike](https://www.pcgamingwiki.com/wiki/?curid=74195)
* [One Synth](https://www.pcgamingwiki.com/wiki/?curid=127726)
* [One Tank to Rule Them All](https://www.pcgamingwiki.com/wiki/?curid=82105)
@ -22212,6 +22219,7 @@
* [Paradise City VR](https://www.pcgamingwiki.com/wiki/?curid=124016)
* [Paradise Cleaning!](https://www.pcgamingwiki.com/wiki/?curid=146078)
* [Paradise Cracked](https://www.pcgamingwiki.com/wiki/?curid=57943)
* [Paradise Lost](https://www.pcgamingwiki.com/wiki/?curid=161803)
* [Paradise Lost (PolyAmorous)](https://www.pcgamingwiki.com/wiki/?curid=137402)
* [Paradise Lost: FPS Cosmic Horror Game](https://www.pcgamingwiki.com/wiki/?curid=76601)
* [Paradox Escape Route](https://www.pcgamingwiki.com/wiki/?curid=148613)
@ -22235,6 +22243,7 @@
* [PARANOIHELL](https://www.pcgamingwiki.com/wiki/?curid=150723)
* [Paranormal Detective: Escape from the 80's](https://www.pcgamingwiki.com/wiki/?curid=142066)
* [Paranormal Files: Hook Man's Legend](https://www.pcgamingwiki.com/wiki/?curid=149382)
* [Paranormal HK](https://www.pcgamingwiki.com/wiki/?curid=153800)
* [Paranormal Psychosis](https://www.pcgamingwiki.com/wiki/?curid=34527)
* [Paranormal Pursuit: The Gifted One](https://www.pcgamingwiki.com/wiki/?curid=55289)
* [Paranormal Teens](https://www.pcgamingwiki.com/wiki/?curid=55690)
@ -24150,6 +24159,7 @@
* [RACE Injection](https://www.pcgamingwiki.com/wiki/?curid=40857)
* [RACE On](https://www.pcgamingwiki.com/wiki/?curid=41218)
* [Race Race Racer](https://www.pcgamingwiki.com/wiki/?curid=152751)
* [Race to Mars](https://www.pcgamingwiki.com/wiki/?curid=15682)
* [Race with Ryan](https://www.pcgamingwiki.com/wiki/?curid=145134)
* [Race! Make 'm finish...](https://www.pcgamingwiki.com/wiki/?curid=155914)
* [Race.a.bit](https://www.pcgamingwiki.com/wiki/?curid=34099)
@ -28385,6 +28395,7 @@
* [Split Bullet](https://www.pcgamingwiki.com/wiki/?curid=51863)
* [Split or Steal](https://www.pcgamingwiki.com/wiki/?curid=150576)
* [Split Signal](https://www.pcgamingwiki.com/wiki/?curid=157323)
* [Splitgate](https://www.pcgamingwiki.com/wiki/?curid=130533)
* [Splitmind](https://www.pcgamingwiki.com/wiki/?curid=55610)
* [Splitter Critters](https://www.pcgamingwiki.com/wiki/?curid=81749)
* [Splody](https://www.pcgamingwiki.com/wiki/?curid=40169)
@ -29881,6 +29892,7 @@
* [Synthesis Universe -Episode 00-](https://www.pcgamingwiki.com/wiki/?curid=150505)
* [Synthetic Dreams](https://www.pcgamingwiki.com/wiki/?curid=66444)
* [Synthetic Love](https://www.pcgamingwiki.com/wiki/?curid=146136)
* [Synthetik: Legion Rising](https://www.pcgamingwiki.com/wiki/?curid=81934)
* [Synthrally](https://www.pcgamingwiki.com/wiki/?curid=91224)
* [Synthrun](https://www.pcgamingwiki.com/wiki/?curid=127359)
* [Synthwave Dream '85](https://www.pcgamingwiki.com/wiki/?curid=107712)

View file

@ -6,11 +6,25 @@ definitions:
$ref: "#/definitions/Os"
store:
$ref: "#/definitions/Store"
LaunchConstraint:
type: object
properties:
bit:
$ref: "#/definitions/Bit"
os:
$ref: "#/definitions/Os"
store:
$ref: "#/definitions/Store"
RegistryConstraint:
type: object
properties:
store:
$ref: "#/definitions/Store"
Bit:
type: integer
enum:
- 32
- 64
Os:
type: string
enum:
@ -53,6 +67,21 @@ additionalProperties:
$ref: "#/definitions/FileConstraint"
installDir:
type: object
launch:
type: object
additionalProperties:
type: array
items:
type: object
properties:
arguments:
type: string
workingDir:
type: string
when:
type: array
items:
$ref: "#/definitions/LaunchConstraint"
registry:
type: object
additionalProperties:

View file

@ -6,11 +6,22 @@ definitions:
$ref: "#/definitions/Os"
store:
$ref: "#/definitions/Store"
LaunchConstraint:
type: object
properties:
bit:
$ref: "#/definitions/Bit"
os:
$ref: "#/definitions/Os"
store:
$ref: "#/definitions/Store"
RegistryConstraint:
type: object
properties:
store:
$ref: "#/definitions/Store"
Bit:
type: integer
Os:
type: string
Store:
@ -37,6 +48,21 @@ additionalProperties:
$ref: "#/definitions/FileConstraint"
installDir:
type: object
launch:
type: object
additionalProperties:
type: array
items:
type: object
properties:
arguments:
type: string
workingDir:
type: string
when:
type: array
items:
$ref: "#/definitions/LaunchConstraint"
registry:
type: object
additionalProperties:

View file

@ -43,6 +43,7 @@ async function main() {
"tooBroad",
"tooBroadUntagged",
"steam",
"local",
],
string: [
"skipUntil",
@ -53,7 +54,7 @@ async function main() {
wikiCache.load();
const wikiMetaCache = new WikiMetaCacheFile();
wikiMetaCache.load();
const steamCache = new SteamGameCacheFile(await getSteamClient());
const steamCache = new SteamGameCacheFile(getSteamClient);
steamCache.load();
const manifest = new ManifestFile();
manifest.load();
@ -107,6 +108,7 @@ async function main() {
},
args.limit ?? 25,
steamCache,
args.local,
);
}
@ -115,7 +117,9 @@ async function main() {
steamCache.save();
manifest.save();
saveMissingGames(wikiCache.data, manifest.data);
steamCache.steamClient.logOff();
if (steamCache.steamClient) {
steamCache.steamClient.logOff();
}
process.exit(0);
} catch (e) {
wikiCache.save();
@ -123,7 +127,9 @@ async function main() {
steamCache.save();
manifest.save();
saveMissingGames(wikiCache.data, manifest.data);
steamCache.steamClient.logOff();
if (steamCache.steamClient) {
steamCache.steamClient.logOff();
}
throw e;
}
}

View file

@ -1,9 +1,11 @@
import { DELAY_BETWEEN_GAMES_MS, REPO, YamlFile } from ".";
import { SteamGameCacheFile } from "./steam";
import { SteamGameCache, SteamGameCacheFile } from "./steam";
import { WikiGameCache, getGame, pathIsTooBroad } from "./wiki";
export type Os = "dos" | "linux" | "mac" | "windows";
export type Bit = 32 | 64;
export type Store = "epic" | "gog" | "microsoft" | "steam" | "uplay" | "origin";
export type Tag = "config" | "save";
@ -15,16 +17,23 @@ export interface Manifest {
export interface Game {
files?: {
[path: string]: {
when?: Array<Constraint>,
when?: Array<Omit<Constraint, "bit">>,
tags?: Array<Tag>,
}
};
installDir?: {
[name: string]: {}
};
launch?: {
[path: string]: Array<{
arguments?: string;
workingDir?: string;
when?: Array<Constraint>,
}>
},
registry?: {
[path: string]: {
when?: Array<Omit<Constraint, "os">>,
when?: Array<Omit<Constraint, "bit" | "os">>,
tags?: Array<Tag>,
}
};
@ -35,9 +44,120 @@ export interface Game {
export interface Constraint {
os?: Os;
bit?: Bit;
store?: Store;
}
function normalizeLaunchPath(raw: string): string | undefined {
if (raw.includes("://")) {
return raw;
}
const standardized = raw
.replace(/\\/g, "/")
.replace(/\/\//g, "/")
.replace(/\/(?=$)/g, "")
.replace(/^.\//, "")
.replace(/^\/+/, "")
.trim();
if (standardized.length === 0 || standardized === ".") {
return undefined;
}
return `<base>/${standardized}`;
}
function doLaunchPathsMatch(fromSteam: string | undefined, fromManifest: string | undefined): boolean {
if (fromSteam === undefined) {
return fromManifest === undefined;
} else {
return normalizeLaunchPath(fromSteam) === fromManifest;
}
}
function integrateSteamData(game: Game, appInfo: SteamGameCache[""]) {
if (appInfo.installDir !== undefined) {
game.installDir = {[appInfo.installDir]: {}};
}
if (appInfo.launch !== undefined) {
delete game.launch;
for (const incoming of appInfo.launch) {
if (
incoming.executable === undefined ||
incoming.executable.includes("://") ||
![undefined, "default", "none"].includes(incoming.type) ||
incoming.config?.betakey !== undefined ||
incoming.config?.ownsdlc !== undefined
) {
continue;
}
const os: Os | undefined = {
"windows": "windows",
"macos": "mac",
"macosx": "mac",
"linux": "linux",
}[incoming.config?.oslist] as Os;
const bit: Bit | undefined = {
"32": 32,
"64": 64,
}[incoming.config?.osarch] as Bit;
const when: Constraint = {os, bit, store: "steam"};
if (when.os === undefined) {
delete when.os;
}
if (when.bit === undefined) {
delete when.bit;
}
let foundExisting = false;
for (const [existingExecutable, existingOptions] of Object.entries(game.launch ?? {})) {
for (const existing of existingOptions) {
if (
incoming.arguments === existing.arguments &&
doLaunchPathsMatch(incoming.executable, existingExecutable) &&
doLaunchPathsMatch(incoming.workingdir, existing.workingDir)
) {
foundExisting = true;
if (existing.when === undefined) {
existing.when = [];
}
if (existing.when.every(x => x.os !== os && x.bit !== bit && x.store !== "steam")) {
existing.when.push(when);
}
if (existing.when.length === 0) {
delete existing.when;
}
}
}
}
if (!foundExisting) {
const key = normalizeLaunchPath(incoming.executable);
if (key === undefined) {
continue;
}
const candidate: Game["launch"][""][0] = {when: [when]};
if (incoming.arguments !== undefined) {
candidate.arguments = incoming.arguments;
}
if (incoming.workingdir !== undefined) {
const workingDir = normalizeLaunchPath(incoming.workingdir);
if (workingDir !== undefined) {
candidate.workingDir = workingDir;
}
}
if (game.launch === undefined) {
game.launch = {};
}
if (game.launch[key] === undefined) {
game.launch[key] = [];
}
game.launch[key].push(candidate);
}
}
}
}
export class ManifestFile extends YamlFile<Manifest> {
path = `${REPO}/data/manifest.yaml`;
defaultData = {};
@ -62,6 +182,7 @@ export class ManifestFile extends YamlFile<Manifest> {
},
limit: number | undefined,
steamCache: SteamGameCacheFile,
local: boolean,
): Promise<void> {
let i = 0;
let foundSkipUntil = false;
@ -125,18 +246,20 @@ export class ManifestFile extends YamlFile<Manifest> {
continue;
}
i++;
if (limit > 0 && i > limit) {
break;
}
if (info.renamedFrom) {
for (const oldName of info.renamedFrom) {
delete this.data[oldName];
}
}
const [verifiedTitle, game] = await getGame(title, wikiCache);
let verifiedTitle: string;
let game: Game;
if (local) {
[verifiedTitle, game] = [title, this.data[title] ?? {}];
} else {
[verifiedTitle, game] = await getGame(title, wikiCache);
}
delete wikiCache[verifiedTitle].recentlyChanged;
if (verifiedTitle !== title) {
@ -149,16 +272,18 @@ export class ManifestFile extends YamlFile<Manifest> {
}
if (game.steam?.id !== undefined) {
const appInfo = await steamCache.getAppInfo(game.steam.id);
if (appInfo.installDir !== undefined) {
if (game.installDir === undefined) {
game.installDir = {}
}
game.installDir[appInfo.installDir] = {}
}
integrateSteamData(game, appInfo);
}
this.data[verifiedTitle] = game;
await new Promise(resolve => setTimeout(resolve, DELAY_BETWEEN_GAMES_MS));
if (!local) {
await new Promise(resolve => setTimeout(resolve, DELAY_BETWEEN_GAMES_MS));
}
i++;
if (limit > 0 && i > limit) {
break;
}
}
}
}

View file

@ -1,12 +1,23 @@
import { DELAY_BETWEEN_GAMES_MS, REPO, YamlFile } from ".";
import * as SteamUser from "steam-user";
type SteamGameCache = {
export type SteamGameCache = {
[appId: string]: {
installDir?: string,
unknown?: boolean,
nameLocalized?: Map<string, string>;
launch?: Array<object>;
launch?: Array<{
executable?: string;
arguments?: string;
workingdir?: string;
type?: string;
config?: {
betakey?: string;
osarch?: string;
oslist?: string;
ownsdlc?: string;
},
}>;
irregular?: boolean;
};
};
@ -14,8 +25,9 @@ type SteamGameCache = {
export class SteamGameCacheFile extends YamlFile<SteamGameCache> {
path = `${REPO}/data/steam-game-cache.yaml`;
defaultData = {};
steamClient: SteamUser | null = null;
constructor(public steamClient: SteamUser) {
constructor(private makeSteamClient: () => Promise<SteamUser>) {
super();
}
@ -33,6 +45,10 @@ export class SteamGameCacheFile extends YamlFile<SteamGameCache> {
return this.data[key];
}
if (this.steamClient === null) {
this.steamClient = await this.makeSteamClient();
}
const info: SteamProductInfoResponse = await this.steamClient.getProductInfo([appId], []);
if (info.unknownApps.includes(appId)) {