Initial commit

This commit is contained in:
mtkennerly 2020-06-20 12:29:22 -04:00
commit cefc818922
15 changed files with 178912 additions and 0 deletions

10
.editorconfig Normal file
View file

@ -0,0 +1,10 @@
root = true
[*]
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
[*.{json,md,yaml,yml}]
indent_size = 2

1
.gitattributes vendored Normal file
View file

@ -0,0 +1 @@
* text=auto

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
node_modules/
out/
tmp/

27
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,27 @@
## Development
Requires Node.js.
* Add new games to wiki-game-cache.yaml (required in order to add them to the manifest):
* `npm run cache`
* Update the manifest with games from the cache (`--limit 25` is default at a time):
* All games in cache: `npm run manifest -- --all`
* Games missing from manifest: `npm run manifest -- --missing`
* Games already in the manifest: `npm run manifest -- --existing`
* Games that had an unknown OS: `npm run manifest -- --unsuportedOs`
* Games that had an unusable path: `npm run manifest -- --unsupportedPath`
* A specific game: `npm run manifest -- --game "Name of Game"`
* Validate the manifest against the schema:
* `npm run schema`
## API etiquette
When running or modifying the importer script, please be mindful not to
unnecessarily spam the PCGW or Steam APIs.
The [Mediawiki guidelines](https://www.mediawiki.org/wiki/API:Etiquette)
suggest that:
> Making your requests in series rather than in parallel, by waiting for one request
> to finish before sending a new request, should result in a safe request rate.
I am not sure about guidelines for the Steam API, but the cache file should mean
that we only ever need to reach out to Steam once per game.

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Matthew T. Kennerly (mtkennerly)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

113
README.md Normal file
View file

@ -0,0 +1,113 @@
# Ludusavi Manifest
The Ludusavi Manifest format is a YAML structure for defining the location of
game save data and other files that are of interest to back up. Although this
project was started for use by [Ludusavi](https://github.com/mtkennerly/ludusavi),
the goal is for the manifest format to be generic enough for any game backup tool
to implement, while leaving room for new fields and functionality over time.
This repository contains the [primary manifest](data/manifest.yaml), which is
compiled from data on [PCGamingWiki](https://www.pcgamingwiki.com/wiki/Home),
along with accessing the Steam API for game installation directory names.
If you find any data that is missing or incorrect, please contribute to the wiki,
and such changes will be incorporated into the primary manifest periodically.
There is also a list of [games without any info on what to back up](data/missing.md).
Game developers may include a secondary manifest (named `.ludusavi.yaml`) with
their games, so that backup tools can automatically detect and use it to discover
what files need to be backed up for save data and configuration.
## Format
For the schema, refer to [schema.yaml](data/schema.yaml). Note that the primary
manifest is validated with [schema.strict.yaml](data/schema.strict.yaml), which
additionally specifies enums for some fields. However, tools should implement
[schema.yaml](data/schema.yaml), so that new values in the manifest do not break
older tools.
Here is an example:
```yaml
An Example Game:
files:
<base>/saves:
tags:
- save
<base>/settings.json:
when:
- os: windows
- os: linux
tags:
- config
<base>/other:
when:
- os: mac
store: steam
installDir:
AnExampleGame: {}
registry:
<regHkcu>/Software/An Example Game:
tags:
- save
- config
steamId: 123
```
This means:
* `<base>/saves` will be backed up on any system.
* `<base>/settings.json` will be backed up if you're using Windows or Linux.
* `<base>/other` will be backed up if you're using Mac and Steam.
* On Windows, the registry path `<regHkcu>/Software/An Example Game` will be
backed up.
Paths can include these placeholders:
| placeholder | meaning |
|---------------------|---------------------------------------------------------------------------|
| `<root>` | a directory where games are installed (configured in backup tool) |
| `<game>` | an `installDir` (if defined) or the game's canonical name in the manifest |
| `<base>` | shorthand for `<root>/**/<game>` |
| `<home>` | current user's home directory in the OS (`~`) |
| `<storeUserId>` | current user's ID in the game store |
| `<osUserName>` | current user's name in the OS |
| `<winAppData>` | `%APPDATA%` on Windows |
| `<winLocalAppData>` | `%LOCALAPPDATA%` on Windows |
| `<winPublic>` | `%PUBLIC%` on Windows |
| `<winProgramData>` | `%PROGRAMDATA%` on Windows |
| `<winDir>` | `%WINDIR%` on Windows |
| `<xdgData>` | `$XDG_DATA_HOME` on Linux |
| `<xdgConfig>` | `$XDG_CONFIG_HOME` on Linux |
| `regHkcu` | `HKEY_CURRENT_USER` in the Windows registry |
| `regHklm` | `HKEY_LOCAL_MACHINE` in the Windows registry |
## Implementation
Tools must implement the following in addition to respecting the schema:
* For paths, first substitute the placeholders, then evaluate as a glob.
Because of treating paths as globs, a path may match multiple files or
directories.
* When a path identifies a folder, the backup includes all of its files
and subdirectories recursively.
* Relative paths must be resolved relative to the location of the manifest file.
This is important for secondary manifests to work correctly without
hard-coding their location.
* If a tool supports secondary manifests, they must be automatically detected
when they are named `.ludusavi.yaml` and exist anywhere within a configured
root.
Tools may also:
* Use store-specific logic to narrow down the `**` in `<base>`. For example,
with Steam, it would be `<root>/steamapps/common/<game>`.
The latest version of the primary manifest can be downloaded from
https://raw.githubusercontent.com/mtkennerly/ludusavi-manifest/master/data/manifest.yaml .
To check for updates:
* Store the value of the `ETag` header for the last downloaded version.
* Send a GET request to the URL with the `If-None-Match` header set to the
last known `ETag` value.
* If the response code is 304, then no update is needed.
* If the response code is 200, then store the new `ETag` value.
## Development
Please refer to [CONTRIBUTING.md](CONTRIBUTING.md).

904
data/manifest.yaml Normal file
View file

@ -0,0 +1,904 @@
'! That Bastard Is Trying To Steal Our Gold !':
installDir:
'! That Bastard Is Trying To Steal Our Gold !': {}
registry:
<regHkcu>/SOFTWARE/WTFOMGames/That Dick Trying To Steal Our Gold:
tags:
- config
- save
steamId: 449940
'!4RC4N01D!':
steamId: 777010
'!4RC4N01D! 2: Retro Edition':
steamId: 791550
'!4RC4N01D! 3: Cold Space':
steamId: 809370
'!4RC4N01D! 4: Kohbeep Edition':
steamId: 824660
'!Anyway!':
files:
<winLocalAppData>/anyway:
tags:
- save
when:
- os: windows
installDir:
Anyway: {}
steamId: 866510
'!BurnToDie!':
steamId: 928700
'!Dead Pixels Adventure!':
steamId: 873700
'!LABrpgUP!':
installDir:
'!LABrpgUP!': {}
steamId: 870990
'!Peace Phantom 2!':
steamId: 805170
$1 Ride:
installDir:
$1 Ride: {}
steamId: 508290
'''83':
steamId: 1059220
'''90s Football Stars':
installDir:
'''90s Football Stars': {}
steamId: 879310
'''n Verlore Verstand':
installDir:
'''n Verlore Verstand': {}
steamId: 439550
'***':
installDir:
SSS: {}
steamId: 1034230
'-KLAUS-':
installDir:
KLAUS: {}
steamId: 729370
.Age:
steamId: 638510
.EXE:
installDir:
.EXE: {}
steamId: 471640
.Projekt:
installDir:
projekt: {}
steamId: 759000
'.T.E.S.T: Expected Behaviour':
files:
<home>/AppData/LocalLow/Veslo Games/Test Expected Behaviour/default.profile:
tags:
- save
when:
- os: windows
<xdgConfig>/unity3d/Veslo Games/Test Expected Behaviour/default.profile:
tags:
- save
when:
- os: linux
<xdgConfig>/unity3d/Veslo Games/Test Expected Behaviour/pref:
tags:
- config
when:
- os: linux
installDir:
Test Expected Behaviour: {}
registry:
<regHkcu>/Software/Veslo Games/Test Expected Behaviour:
tags:
- config
steamId: 771710
.fall:
installDir:
fall: {}
steamId: 1087950
.hack//G.U. Last Recode:
files:
<root>/userdata/<storeUserId>/525480/remote/savedata:
tags:
- save
when:
- os: windows
store: steam
installDir:
hackGU: {}
steamId: 525480
0 A.D.:
files:
<home>/Documents/My Games/0ad:
tags:
- save
when:
- os: windows
<home>/Library/Application/ Support/0ad:
tags:
- save
when:
- os: mac
<winAppData>/0ad:
tags:
- config
when:
- os: windows
<xdgConfig>/0ad:
tags:
- config
when:
- os: linux
<xdgData>/0ad:
tags:
- save
when:
- os: linux
0 Day:
installDir:
0 Day: {}
steamId: 554920
'0000':
steamId: 639880
007 Legends:
files:
<home>/Documents/My Games/Activision/007 Legends:
tags:
- save
when:
- os: windows
<winLocalAppData>/Activision/007 Legends:
tags:
- config
when:
- os: windows
installDir:
007 Legends: {}
steamId: 211670
'007: Quantum of Solace':
files:
<winAppData>/Activision/Quantum of Solace/players/<storeUserId>/config.cfg:
tags:
- config
- save
when:
- os: windows
installDir:
Quantum of Solace: {}
steamId: 10080
03.04:
installDir:
03.04: {}
steamId: 952950
0Gravity:
installDir:
0Gravity: {}
steamId: 1101290
0rbitalis:
files:
<root>/userdata/<storeUserId>/278440/remote:
tags:
- config
- save
when:
- store: steam
<winAppData>/0RBITALIS:
tags:
- config
- save
when:
- os: windows
installDir:
0rbitalis: {}
steamId: 278440
0°N 0°W:
installDir:
0°N 0°W: {}
steamId: 670750
1 Hit Kill:
installDir:
1 HIT KILL: {}
steamId: 882750
'1 Moment of Time: Silentville':
files:
<winAppData>/2Monkeys/CrossMoT:
tags:
- save
when:
- os: windows
<winAppData>/2Monkeys/CrossMoT/Settings.sav:
tags:
- config
when:
- os: windows
installDir:
1 Moment Of Time Silentville: {}
steamId: 497400
1 Screen Platformer:
files:
<winLocalAppData>/OneScreenPlatformStaticGM14:
tags:
- config
- save
when:
- os: windows
installDir:
1 Screen Platformer: {}
steamId: 791180
1 ⛷ 1:
steamId: 932490
'1, 2, 3... Bruegel!':
installDir:
'1, 2, 3... Bruegel!': {}
steamId: 1071310
'1,000 Heads Among the Trees':
installDir:
1000 Heads Among the Trees: {}
steamId: 406730
1-2-Swift:
installDir:
1-2-Swift: {}
steamId: 583570
'1-Bit Revival: The Residuals of Null':
installDir:
1-Bit Revival The Residuals of Null: {}
steamId: 1025480
1... 2... 3... KICK IT! (Drop That Beat Like an Ugly Baby):
installDir:
123kickit: {}
registry:
<regHkcu>/Software/Dejobaan Games/Ugly Baby:
tags:
- config
steamId: 15540
1/4平方米的星空:
installDir:
A Piece of Wish upon the Stars: {}
steamId: 899190
10 Miles To Safety:
installDir:
10 Miles To Safety: {}
steamId: 1015140
10 Minute Barbarian:
installDir:
10 Minute Barbarian: {}
steamId: 389120
10 Minute Tower:
installDir:
10 Minute Tower: {}
steamId: 477010
10 Second Ninja:
files:
<home>/Library/Application Support/com.yoyogames.macyoyorunner:
tags:
- save
when:
- os: mac
<home>/Library/Application Support/com.yoyogames.macyoyorunner/settings.ini:
tags:
- config
when:
- os: mac
<winLocalAppData>/10_Second_Ninja:
tags:
- save
when:
- os: windows
<winLocalAppData>/10_Second_Ninja/settings.ini:
tags:
- config
when:
- os: windows
installDir:
10 Second Ninja: {}
steamId: 271670
10 Second Ninja X:
installDir:
10 Second Ninja X: {}
steamId: 435790
10 Second Shuriken:
installDir:
10 Second Shuriken: {}
steamId: 1130440
10 Seconds:
installDir:
10 seconds: {}
steamId: 852190
10 Years After:
installDir:
10 Years After: {}
registry:
<regHkcu>/SOFTWARE/Ten Tree Games/10 Years After:
tags:
- config
- save
steamId: 339240
'10,000,000':
files:
<xdgConfig>/unity3d/EightyEightGames/10000000:
tags:
- config
- save
when:
- os: linux
installDir:
'10000000': {}
registry:
<regHkcu>/Software/EightyEightGames/10000000:
tags:
- config
- save
steamId: 227580
10-4 Indirect Contact:
installDir:
10-4: {}
steamId: 1055140
100 Chests:
installDir:
100 Chests: {}
steamId: 856260
100 Seconds:
installDir:
100 Seconds: {}
steamId: 796580
100 Years' War:
installDir:
100 Years War: {}
steamId: 1025530
100$:
installDir:
100$: {}
steamId: 1016250
100% Orange Juice!:
files:
<base>/config.dat:
tags:
- config
when:
- os: windows
<base>/user.dat:
tags:
- save
when:
- os: windows
installDir:
100 Orange Juice: {}
steamId: 282800
1000 Amps:
files:
<base>:
tags:
- config
- save
when:
- store: steam
<home>/Library/Application Support/Brandon Brizzi/1000 Amps:
tags:
- config
- save
when:
- os: mac
installDir:
1000 Amps: {}
steamId: 205690
1000 Days to Escape:
installDir:
1000 days to escape: {}
steamId: 1103100
1000 Stages:
installDir:
1000 Stages: {}
steamId: 873180
1000$:
installDir:
1000$: {}
steamId: 1099840
1001 Hugs:
installDir:
1001Hugs: {}
steamId: 1157020
1001 Jigsaw Castles And Palaces:
installDir:
1001 Jigsaw Castles And Palaces: {}
steamId: 1158830
'1001 Jigsaw World Tour: Europe':
installDir:
1001 Jigsaw Europe: {}
steamId: 1128830
1001 Jigsaw. 6 Magic Elements:
installDir:
1001 Jigsaw. 6 Magic Elements: {}
steamId: 1095850
1001 Jigsaw. Earth Chronicles:
installDir:
1001 Jigsaw. Earth Chronicles: {}
steamId: 970870
1001 Jigsaw. Home Sweet Home:
installDir:
1001 Jigsaw. Home Sweet Home: {}
steamId: 970900
1001 Jigsaw. Myths of ancient Greece:
installDir:
1001 JIGSAW. MYTHS OF ANCIENT GREECE: {}
steamId: 1165430
'1001 Jigsaw. World Tour: Australian Puzzles':
installDir:
1001 Jigsaw. World Tour Australian Puzzles: {}
steamId: 970880
'1001 Jigsaw. World Tour: France':
installDir:
1001 Jigsaw. World Tour France: {}
steamId: 1095870
'1001 Jigsaw. World Tour: Great America':
installDir:
1001 Jigsaw. World Tour Great America: {}
steamId: 970910
'1001 Jigsaw. World Tour: London':
installDir:
1001 Jigsaw. World Tour London: {}
steamId: 970890
1001 Spikes:
files:
<home>/Documents/My Games/1001 Spikes:
tags:
- config
- save
when:
- os: windows
<home>/Library/Application Support/1001 Spikes:
tags:
- config
- save
when:
- os: mac
<xdgData>/1001 spikes:
tags:
- config
- save
when:
- os: linux
installDir:
1001 Spikes: {}
steamId: 260790
1001st Hyper Tower:
installDir:
1001stHyperTower: {}
steamId: 958050
100ft Robot Golf:
installDir:
100ft Robot Golf: {}
steamId: 368000
100nya:
installDir:
100nya: {}
steamId: 553830
101 Ways to Die:
installDir:
101 Ways to Die: {}
steamId: 413480
'1010':
installDir:
'1010': {}
steamId: 761190
'101010':
installDir:
'101010': {}
steamId: 1081800
'102 Dalmatians: Puppies to the Rescue':
files:
<base>/savegame.dat:
tags:
- save
when:
- os: windows
'103':
files:
<winLocalAppData>/OneZeroThree/Saved/SaveGames:
tags:
- config
- save
when:
- os: windows
installDir:
'103': {}
steamId: 913850
11-11 Memories Retold:
files:
<root>/userdata/<storeUserId>/735580/remote:
tags:
- save
when:
- store: steam
installDir:
RocketMan: {}
registry:
'<regHkcu>/Software/Bandai Namco/11-11: Memories Retold':
tags:
- config
steamId: 735580
112 Operator:
installDir:
112 Operator: {}
steamId: 793460
'1166':
installDir:
'1166': {}
steamId: 581810
11th Dream:
installDir:
11th Dream Game: {}
steamId: 949450
12 HOURS:
installDir:
12 HOURS: {}
steamId: 1063560
12 HOURS 2:
installDir:
12 HOURS 2: {}
steamId: 1126840
12 Labours of Hercules:
files:
<home>/.Saves/ZOG/Hercules:
tags:
- save
when:
- os: linux
<home>/.Saves/ZOG/Hercules/Settings.sav:
tags:
- config
when:
- os: linux
<home>/Library/Application Support/ZOG/Hercules:
tags:
- save
when:
- os: mac
<home>/Library/Application Support/ZOG/Hercules/Settings.sav:
tags:
- config
when:
- os: mac
<winAppData>/ZOG/Hercules:
tags:
- save
when:
- os: windows
<winAppData>/ZOG/Hercules/Settings.sav:
tags:
- config
when:
- os: windows
installDir:
12 Labours of Hercules: {}
steamId: 342580
'12 Labours of Hercules II: The Cretan Bull':
files:
<winAppData>/ZOG/Hercules2:
tags:
- save
when:
- os: windows
<winAppData>/ZOG/Hercules2/Settings.sav:
tags:
- config
when:
- os: windows
installDir:
12 Labours of Hercules II The Cretan Bull: {}
steamId: 360640
'12 Labours of Hercules III: Girl Power':
files:
<winAppData>/ZOG/Hercules3:
tags:
- save
when:
- os: windows
<winAppData>/ZOG/Hercules3/Settings.sav:
tags:
- config
when:
- os: windows
installDir:
12 Labours of Hercules III Girl Power: {}
steamId: 360650
'12 Labours of Hercules IV: Mother Nature':
installDir:
12 Labours of Hercules IV Mother Nature: {}
steamId: 396800
'12 Labours of Hercules IX: A Hero''s Moonwalk':
installDir:
12 Labours of Hercules IX A Hero's Moonwalk: {}
steamId: 1026070
'12 Labours of Hercules V: Kids of Hellas':
installDir:
12 Labours of Hercules V Kids of Hellas: {}
steamId: 491330
'12 Labours of Hercules VI: Race for Olympus':
files:
<home>/.Saves/ZOG/Hercules6CE:
tags:
- save
when:
- os: linux
<home>/.Saves/ZOG/Hercules6CE/Settings.sav:
tags:
- config
when:
- os: linux
<home>/Library/Application Support/ZOG/Hercules6CE:
tags:
- save
when:
- os: mac
<home>/Library/Application Support/ZOG/Hercules6CE/Settings.sav:
tags:
- config
when:
- os: mac
<winAppData>/ZOG/Hercules6CE:
tags:
- save
when:
- os: windows
<winAppData>/ZOG/Hercules6CE/Settings.sav:
tags:
- config
when:
- os: windows
installDir:
12 Labours of Hercules VI Race for Olympus: {}
steamId: 567800
'12 Labours of Hercules VII: Fleecing the Fleece':
installDir:
12 Labours of Hercules VII Fleecing the Fleece: {}
steamId: 663210
'12 Labours of Hercules VIII: How I Met Megara':
installDir:
12 Labours of Hercules VIII: {}
steamId: 938310
12 Orbits:
files:
<home>/.config/unity3d/Roman Uhlig/12 orbits:
tags:
- config
- save
when:
- os: mac
<home>/Library/Preferences/unity.Roman Uhlig.12 orbits.plist:
tags:
- config
- save
when:
- os: linux
installDir:
12 orbits: {}
registry:
<regHkcu>/Software/Roman Uhlig/12 orbits:
tags:
- config
- save
steamId: 529950
12 is Better Than 6:
files:
<winAppData>/_12ibt6/savedata.ini:
tags:
- save
when:
- os: windows
<winAppData>/_12ibt6/settings.ini:
tags:
- config
when:
- os: windows
installDir:
12 is Better Than 6: {}
steamId: 410110
123 Slaughter Me Street:
installDir:
123 Slaughter Me Street: {}
steamId: 405180
123 Slaughter Me Street 2:
installDir:
123 Slaughter Me Street 2: {}
steamId: 551190
'1248':
installDir:
'1248': {}
steamId: 814510
13 Cycles:
installDir:
13 Cycles: {}
steamId: 862790
'140':
files:
<home>/Library/Preferences/unity.JeppeCarlsen.140.plist:
tags:
- config
when:
- os: mac
<xdgConfig>/unity3d/JeppeCarlsen/140:
tags:
- config
when:
- os: linux
installDir:
'140': {}
registry:
<regHkcu>/SOFTWARE/JeppeCarlsen/140:
tags:
- save
steamId: 242820
'1406':
installDir:
'1406': {}
steamId: 1043350
'141':
installDir:
'141': {}
steamId: 1140110
15 Days:
installDir:
15 Days: {}
steamId: 342990
16bit Trader:
files:
<root>/userdata/<storeUserId>/375460/remote:
tags:
- save
when:
- store: steam
installDir:
16bittrader: {}
registry:
<regHkcu>/SOFTWARE/Forever Entertainment/16bit Trader:
tags:
- config
steamId: 375460
'1849':
files:
<winAppData>/com.somasim.fortynine/Local Store:
tags:
- config
- save
when:
- os: windows
installDir:
'1849': {}
steamId: 290970
'1982':
installDir:
'1982': {}
steamId: 639650
'2048':
installDir:
'2048': {}
steamId: 942050
'2084':
installDir:
'2084': {}
steamId: 987850
'21':
installDir:
'21': {}
steamId: 938520
'2100':
installDir:
'2100': {}
steamId: 1018090
'222':
installDir:
'222': {}
steamId: 1028160
'29':
steamId: 651490
'3079':
files:
<home>/3079Saves:
tags:
- config
- save
when:
- os: windows
- os: mac
- os: linux
installDir:
'3079': {}
steamId: 259620
'3089':
files:
<home>/3089:
tags:
- config
- save
when:
- os: windows
- os: mac
- os: linux
installDir:
'3089': {}
steamId: 263360
'428: Shibuya Scramble':
files:
<root>/userdata/<storeUserId>/648580/remote:
tags:
- save
when:
- store: steam
installDir:
428_shibuya_scramble_en: {}
steamId: 648580
'5089':
files:
<home>/5089:
tags:
- save
when:
- os: windows
installDir:
'5089': {}
steamId: 414510
60 Parsecs!:
files:
<root>/userdata/<storeUserId>/646270/remote:
tags:
- save
when:
- store: steam
<root>/userdata/<storeUserId>/646270/remote/settings:
tags:
- config
when:
- store: steam
installDir:
60 Parsecs!: {}
registry:
<regHkcu>/Software/Robot Gentleman/60 Parsecs:
tags:
- config
steamId: 646270
60 Seconds! Reatomized:
files:
<root>/userdata/<storeUserId>/1012880/remote:
tags:
- save
when:
- store: steam
<root>/userdata/<storeUserId>/1012880/remote/settings:
tags:
- config
when:
- store: steam
installDir:
60 Seconds! Reatomized: {}
steamId: 1012880
'6120':
installDir:
'6120': {}
steamId: 1063230
'69':
steamId: 854380
'7':
installDir:
7 Game: {}
steamId: 684210
8infinity:
files:
<root>/userdata/<storeUserId>/526540/remote:
tags:
- save
when:
- store: steam
installDir:
8infinity: {}
steamId: 526540
'900':
installDir:
'900': {}
steamId: 696860
'999':
installDir:
'999': {}
steamId: 876840
'99999':
installDir:
Gunkid99: {}
steamId: 906600

43944
data/missing.md Normal file

File diff suppressed because it is too large Load diff

67
data/schema.strict.yaml Normal file
View file

@ -0,0 +1,67 @@
definitions:
FileConstraint:
type: object
properties:
os:
$ref: "#/definitions/Os"
store:
$ref: "#/definitions/Store"
RegistryConstraint:
type: object
properties:
store:
$ref: "#/definitions/Store"
Os:
type: string
enum:
- windows
- linux
- mac
Store:
type: string
enum:
- steam
- epic
- discord
- origin
- uplay
Tag:
type: string
enum:
- save
- config
type: object
additionalProperties:
type: object
properties:
files:
type: object
additionalProperties:
type: object
properties:
tags:
type: array
items:
$ref: "#/definitions/Tag"
when:
type: array
items:
$ref: "#/definitions/FileConstraint"
installDir:
type: object
registry:
type: object
additionalProperties:
type: object
properties:
tags:
type: array
items:
$ref: "#/definitions/Tag"
when:
type: array
items:
$ref: "#/definitions/RegistryConstraint"
steamId:
type: integer

54
data/schema.yaml Normal file
View file

@ -0,0 +1,54 @@
definitions:
FileConstraint:
type: object
properties:
os:
$ref: "#/definitions/Os"
store:
$ref: "#/definitions/Store"
RegistryConstraint:
type: object
properties:
store:
$ref: "#/definitions/Store"
Os:
type: string
Store:
type: string
Tag:
type: string
type: object
additionalProperties:
type: object
properties:
files:
type: object
additionalProperties:
type: object
properties:
tags:
type: array
items:
$ref: "#/definitions/Tag"
when:
type: array
items:
$ref: "#/definitions/FileConstraint"
installDir:
type: object
registry:
type: object
additionalProperties:
type: object
properties:
tags:
type: array
items:
$ref: "#/definitions/Tag"
when:
type: array
items:
$ref: "#/definitions/RegistryConstraint"
steamId:
type: integer

224
data/steam-game-cache.yaml Normal file
View file

@ -0,0 +1,224 @@
'10080':
installDir: Quantum of Solace
'1012880':
installDir: 60 Seconds! Reatomized
'1015140':
installDir: 10 Miles To Safety
'1016250':
installDir: 100$
'1018090':
installDir: '2100'
'1025480':
installDir: 1-Bit Revival The Residuals of Null
'1025530':
installDir: 100 Years War
'1026070':
installDir: 12 Labours of Hercules IX A Hero's Moonwalk
'1028160':
installDir: '222'
'1034230':
installDir: SSS
'1043350':
installDir: '1406'
'1055140':
installDir: 10-4
'1063230':
installDir: '6120'
'1063560':
installDir: 12 HOURS
'1071310':
installDir: '1, 2, 3... Bruegel!'
'1081800':
installDir: '101010'
'1087950':
installDir: fall
'1095850':
installDir: 1001 Jigsaw. 6 Magic Elements
'1095870':
installDir: 1001 Jigsaw. World Tour France
'1099840':
installDir: 1000$
'1101290':
installDir: 0Gravity
'1103100':
installDir: 1000 days to escape
'1126840':
installDir: 12 HOURS 2
'1128830':
installDir: 1001 Jigsaw Europe
'1130440':
installDir: 10 Second Shuriken
'1140110':
installDir: '141'
'1157020':
installDir: 1001Hugs
'1158830':
installDir: 1001 Jigsaw Castles And Palaces
'1165430':
installDir: 1001 JIGSAW. MYTHS OF ANCIENT GREECE
'15540':
installDir: 123kickit
'205690':
installDir: 1000 Amps
'211670':
installDir: 007 Legends
'227580':
installDir: '10000000'
'242820':
installDir: '140'
'259620':
installDir: '3079'
'260790':
installDir: 1001 Spikes
'263360':
installDir: '3089'
'271670':
installDir: 10 Second Ninja
'278440':
installDir: 0rbitalis
'282800':
installDir: 100 Orange Juice
'290970':
installDir: '1849'
'339240':
installDir: 10 Years After
'342580':
installDir: 12 Labours of Hercules
'342990':
installDir: 15 Days
'360640':
installDir: 12 Labours of Hercules II The Cretan Bull
'360650':
installDir: 12 Labours of Hercules III Girl Power
'368000':
installDir: 100ft Robot Golf
'375460':
installDir: 16bittrader
'389120':
installDir: 10 Minute Barbarian
'396800':
installDir: 12 Labours of Hercules IV Mother Nature
'405180':
installDir: 123 Slaughter Me Street
'406730':
installDir: 1000 Heads Among the Trees
'410110':
installDir: 12 is Better Than 6
'413480':
installDir: 101 Ways to Die
'414510':
installDir: '5089'
'435790':
installDir: 10 Second Ninja X
'439550':
installDir: '''n Verlore Verstand'
'449940':
installDir: '! That Bastard Is Trying To Steal Our Gold !'
'471640':
installDir: .EXE
'477010':
installDir: 10 Minute Tower
'491330':
installDir: 12 Labours of Hercules V Kids of Hellas
'497400':
installDir: 1 Moment Of Time Silentville
'508290':
installDir: $1 Ride
'525480':
installDir: hackGU
'526540':
installDir: 8infinity
'529950':
installDir: 12 orbits
'551190':
installDir: 123 Slaughter Me Street 2
'553830':
installDir: 100nya
'554920':
installDir: 0 Day
'567800':
installDir: 12 Labours of Hercules VI Race for Olympus
'581810':
installDir: '1166'
'583570':
installDir: 1-2-Swift
'639650':
installDir: '1982'
'646270':
installDir: 60 Parsecs!
'648580':
installDir: 428_shibuya_scramble_en
'663210':
installDir: 12 Labours of Hercules VII Fleecing the Fleece
'670750':
installDir: 0°N 0°W
'684210':
installDir: 7 Game
'696860':
installDir: '900'
'729370':
installDir: KLAUS
'735580':
installDir: RocketMan
'759000':
installDir: projekt
'761190':
installDir: '1010'
'771710':
installDir: Test Expected Behaviour
'791180':
installDir: 1 Screen Platformer
'793460':
installDir: 112 Operator
'796580':
installDir: 100 Seconds
'814510':
installDir: '1248'
'852190':
installDir: 10 seconds
'856260':
installDir: 100 Chests
'862790':
installDir: 13 Cycles
'866510':
installDir: Anyway
'870990':
installDir: '!LABrpgUP!'
'873180':
installDir: 1000 Stages
'876840':
installDir: '999'
'879310':
installDir: '''90s Football Stars'
'882750':
installDir: 1 HIT KILL
'899190':
installDir: A Piece of Wish upon the Stars
'906600':
installDir: Gunkid99
'913850':
installDir: '103'
'938310':
installDir: 12 Labours of Hercules VIII
'938520':
installDir: '21'
'942050':
installDir: '2048'
'949450':
installDir: 11th Dream Game
'952950':
installDir: 03.04
'958050':
installDir: 1001stHyperTower
'970870':
installDir: 1001 Jigsaw. Earth Chronicles
'970880':
installDir: 1001 Jigsaw. World Tour Australian Puzzles
'970890':
installDir: 1001 Jigsaw. World Tour London
'970900':
installDir: 1001 Jigsaw. Home Sweet Home
'970910':
installDir: 1001 Jigsaw. World Tour Great America
'987850':
installDir: '2084'

131940
data/wiki-game-cache.yaml Normal file

File diff suppressed because it is too large Load diff

932
package-lock.json generated Normal file
View file

@ -0,0 +1,932 @@
{
"name": "ludusavi-manifest",
"version": "0.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@bbob/parser": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@bbob/parser/-/parser-2.5.6.tgz",
"integrity": "sha512-qBt88OQvjfkdHH7JhTzFSIU/zV6kwhiMchADnlFIkuRMWH8dSt6pmzPzjRQzS/v8NHGSwiKtTbC01AGGLOmmOA==",
"requires": {
"@bbob/plugin-helper": "^2.5.6"
}
},
"@bbob/plugin-helper": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@bbob/plugin-helper/-/plugin-helper-2.5.6.tgz",
"integrity": "sha512-JQRr5aughtj9S2SyzK9kmalwbTEEpnDeyv21NRpPateehHUxiNM70QPERx/7mm4dVk21sEizUGYiMd61xfFXQQ=="
},
"@doctormckay/stdlib": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/@doctormckay/stdlib/-/stdlib-1.11.0.tgz",
"integrity": "sha512-BEIkzKwxUqF/23nyuru0NqBkZXPjy37O0oMyHpY1gjD5vjUKMvCFI2GJt4OTsHVuo/Wi5TfYs1qq5wo+0Kf/IA=="
},
"@doctormckay/steam-crypto": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@doctormckay/steam-crypto/-/steam-crypto-1.2.0.tgz",
"integrity": "sha1-KxI8HpgDTzyMa5AQnjX8QnbghrA="
},
"@protobufjs/aspromise": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
"integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78="
},
"@protobufjs/base64": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
"integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
},
"@protobufjs/codegen": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
"integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
},
"@protobufjs/eventemitter": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
"integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A="
},
"@protobufjs/fetch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
"integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=",
"requires": {
"@protobufjs/aspromise": "^1.1.1",
"@protobufjs/inquire": "^1.1.0"
}
},
"@protobufjs/float": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
"integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E="
},
"@protobufjs/inquire": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
"integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik="
},
"@protobufjs/path": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
"integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0="
},
"@protobufjs/pool": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
"integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q="
},
"@protobufjs/utf8": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
"integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
},
"@types/js-yaml": {
"version": "3.12.4",
"resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.12.4.tgz",
"integrity": "sha512-fYMgzN+9e28R81weVN49inn/u798ruU91En1ZnGvSZzCRc5jXx9B2EDhlRaWmcO1RIxFHL8AajRXzxDuJu93+A==",
"dev": true
},
"@types/long": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
"integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w=="
},
"@types/minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=",
"dev": true
},
"@types/node": {
"version": "14.0.13",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.13.tgz",
"integrity": "sha512-rouEWBImiRaSJsVA+ITTFM6ZxibuAlTuNOCyxVbwreu6k6+ujs7DfnU9o+PShFhET78pMBl3eH+AGSI5eOTkPA==",
"dev": true
},
"abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"dev": true
},
"adm-zip": {
"version": "0.4.14",
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.14.tgz",
"integrity": "sha512-/9aQCnQHF+0IiCl0qhXoK7qs//SwYE7zX8lsr/DNk1BRAHYxeLZPL4pguwK29gUEqasYQjqPtEpDRSWEkdHn9g=="
},
"ajv": {
"version": "6.12.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz",
"integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"ajv-cli": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ajv-cli/-/ajv-cli-3.2.1.tgz",
"integrity": "sha512-EZW2fqkQVMvp3oPfVrUZnh4nl/sENKpSG1q+R5wHqaM71EU7JAxzxYzvkjcgIRer6Y4HFkY1uCEHT/DMWE5apw==",
"dev": true,
"requires": {
"ajv": "^6.7.0",
"ajv-pack": "^0.3.0",
"fast-json-patch": "^2.0.0",
"glob": "^7.1.0",
"js-yaml": "^3.13.1",
"json-schema-migrate": "^0.2.0",
"json5": "^2.1.3",
"minimist": "^1.2.0"
}
},
"ajv-pack": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/ajv-pack/-/ajv-pack-0.3.1.tgz",
"integrity": "sha1-tyxNQhnjko5ihC10Le2Tv1B5ZWA=",
"dev": true,
"requires": {
"js-beautify": "^1.6.4",
"require-from-string": "^1.2.0"
}
},
"appdirectory": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/appdirectory/-/appdirectory-0.1.0.tgz",
"integrity": "sha1-62yBYyDnsqsW9e2ZfyjYIF31Y3U="
},
"arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"dev": true
},
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"requires": {
"sprintf-js": "~1.0.2"
}
},
"array-filter": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz",
"integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM="
},
"available-typed-arrays": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz",
"integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==",
"requires": {
"array-filter": "^1.0.0"
}
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
},
"binarykvparser": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binarykvparser/-/binarykvparser-2.2.0.tgz",
"integrity": "sha512-mGBKngQF9ui53THcMjgjd0LrBH/HsI2Vywfjq52udSAmRGG87h0vjhkqun0kF+iC4rQ2jLZqldwJE7YN2ueiWw==",
"requires": {
"long": "^3.2.0"
},
"dependencies": {
"long": {
"version": "3.2.0",
"bundled": true
}
}
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"dev": true
},
"bytebuffer": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz",
"integrity": "sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=",
"requires": {
"long": "~3"
}
},
"cejs": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/cejs/-/cejs-3.7.0.tgz",
"integrity": "sha512-0CnkoGg7AK76v9D7ubYr3+XoZH9XbHvFjOaWlOA1KGbU3BcAY2ICkvkDR2KA9vgfP2vshBgcV7uzhWNlFEtfUQ=="
},
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
"dev": true
},
"commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
"config-chain": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz",
"integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==",
"dev": true,
"requires": {
"ini": "^1.3.4",
"proto-list": "~1.2.1"
}
},
"cuint": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz",
"integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs="
},
"define-properties": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
"integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
"requires": {
"object-keys": "^1.0.12"
}
},
"diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true
},
"editorconfig": {
"version": "0.15.3",
"resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz",
"integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==",
"dev": true,
"requires": {
"commander": "^2.19.0",
"lru-cache": "^4.1.5",
"semver": "^5.6.0",
"sigmund": "^1.0.1"
}
},
"es-abstract": {
"version": "1.17.6",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.0",
"is-regex": "^1.1.0",
"object-inspect": "^1.7.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.0",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
}
},
"es-to-primitive": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
"integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
"requires": {
"is-callable": "^1.1.4",
"is-date-object": "^1.0.1",
"is-symbol": "^1.0.2"
}
},
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
},
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
},
"fast-json-patch": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-2.2.1.tgz",
"integrity": "sha512-4j5uBaTnsYAV5ebkidvxiLUYOwjQ+JSFljeqfTxCrH9bDmlCQaOJFS84oDJ2rAXZq2yskmk3ORfoP9DCwqFNig==",
"dev": true,
"requires": {
"fast-deep-equal": "^2.0.1"
},
"dependencies": {
"fast-deep-equal": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
"dev": true
}
}
},
"fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true
},
"file-manager": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/file-manager/-/file-manager-2.0.0.tgz",
"integrity": "sha512-AX9jtqrrHK9JT4v3J7uMZGkDNiuuG4y4T6LoNm3lKzT/vReLCY8mnRWIpaG2ffNEpJHSkiwKejpu8x8THEYPzg=="
},
"foreach": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
"integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k="
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
"glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"requires": {
"function-bind": "^1.1.1"
}
},
"has-symbols": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
"integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dev": true,
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"ini": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"dev": true
},
"is-arguments": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz",
"integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA=="
},
"is-callable": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz",
"integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw=="
},
"is-date-object": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
"integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g=="
},
"is-generator-function": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.7.tgz",
"integrity": "sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw=="
},
"is-regex": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz",
"integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==",
"requires": {
"has-symbols": "^1.0.1"
}
},
"is-symbol": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
"integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
"requires": {
"has-symbols": "^1.0.1"
}
},
"is-typed-array": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.3.tgz",
"integrity": "sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ==",
"requires": {
"available-typed-arrays": "^1.0.0",
"es-abstract": "^1.17.4",
"foreach": "^2.0.5",
"has-symbols": "^1.0.1"
}
},
"js-beautify": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.11.0.tgz",
"integrity": "sha512-a26B+Cx7USQGSWnz9YxgJNMmML/QG2nqIaL7VVYPCXbqiKz8PN0waSNvroMtvAK6tY7g/wPdNWGEP+JTNIBr6A==",
"dev": true,
"requires": {
"config-chain": "^1.1.12",
"editorconfig": "^0.15.3",
"glob": "^7.1.3",
"mkdirp": "~1.0.3",
"nopt": "^4.0.3"
}
},
"js-yaml": {
"version": "3.14.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
"integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
},
"json-schema-migrate": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/json-schema-migrate/-/json-schema-migrate-0.2.0.tgz",
"integrity": "sha1-ukelsAcvxyOWRg4b1gtE1SF4u8Y=",
"dev": true,
"requires": {
"ajv": "^5.0.0"
},
"dependencies": {
"ajv": {
"version": "5.5.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
"dev": true,
"requires": {
"co": "^4.6.0",
"fast-deep-equal": "^1.0.0",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.3.0"
}
},
"fast-deep-equal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=",
"dev": true
},
"json-schema-traverse": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
"integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
"dev": true
}
}
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
"json5": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
"integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"long": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz",
"integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s="
},
"lru-cache": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
"dev": true,
"requires": {
"pseudomap": "^1.0.2",
"yallist": "^2.1.2"
}
},
"lzma": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/lzma/-/lzma-2.3.2.tgz",
"integrity": "sha1-N4OySFi5wOdHoN88vx+1/KqSxEE="
},
"make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"dev": true
},
"nopt": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
"integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
"dev": true,
"requires": {
"abbrev": "1",
"osenv": "^0.1.4"
}
},
"object-inspect": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
"integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw=="
},
"object-keys": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
},
"object.assign": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
"integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
"requires": {
"define-properties": "^1.1.2",
"function-bind": "^1.1.1",
"has-symbols": "^1.0.0",
"object-keys": "^1.0.11"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"requires": {
"wrappy": "1"
}
},
"os-homedir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
"dev": true
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
"dev": true
},
"osenv": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
"dev": true,
"requires": {
"os-homedir": "^1.0.0",
"os-tmpdir": "^1.0.0"
}
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
"permessage-deflate": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/permessage-deflate/-/permessage-deflate-0.1.7.tgz",
"integrity": "sha512-EUNi/RIsyJ1P1u9QHFwMOUWMYetqlE22ZgGbad7YP856WF4BFF0B7DuNy6vEGsgNNud6c/SkdWzkne71hH8MjA==",
"requires": {
"safe-buffer": "*"
}
},
"proto-list": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
"integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=",
"dev": true
},
"protobufjs": {
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.9.0.tgz",
"integrity": "sha512-LlGVfEWDXoI/STstRDdZZKb/qusoAWUnmLg9R8OLSO473mBLWHowx8clbX5/+mKDEI+v7GzjoK9tRPZMMcoTrg==",
"requires": {
"@protobufjs/aspromise": "^1.1.2",
"@protobufjs/base64": "^1.1.2",
"@protobufjs/codegen": "^2.0.4",
"@protobufjs/eventemitter": "^1.1.0",
"@protobufjs/fetch": "^1.1.0",
"@protobufjs/float": "^1.0.2",
"@protobufjs/inquire": "^1.1.0",
"@protobufjs/path": "^1.1.2",
"@protobufjs/pool": "^1.1.0",
"@protobufjs/utf8": "^1.1.0",
"@types/long": "^4.0.1",
"@types/node": "^13.7.0",
"long": "^4.0.0"
},
"dependencies": {
"@types/node": {
"version": "13.13.12",
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.12.tgz",
"integrity": "sha512-zWz/8NEPxoXNT9YyF2osqyA9WjssZukYpgI4UYZpOjcyqwIUqWGkcCionaEb9Ki+FULyPyvNFpg/329Kd2/pbw=="
},
"long": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
}
}
},
"pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
"dev": true
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true
},
"require-from-string": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz",
"integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=",
"dev": true
},
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
},
"sigmund": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
"integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=",
"dev": true
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
"source-map-support": {
"version": "0.5.19",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
"dev": true,
"requires": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"steam-appticket": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/steam-appticket/-/steam-appticket-1.0.1.tgz",
"integrity": "sha512-oYVInCvJlPPaQPYW1+iGcVP0N0ZvwtWiCDM1Z353XJ8l4DXQI/N+R5yyaRQcHRH5oQv3+BY6gPF40lu7gwEiJw==",
"requires": {
"@doctormckay/stdlib": "^1.6.0",
"@doctormckay/steam-crypto": "^1.2.0",
"bytebuffer": "^5.0.1",
"protobufjs": "^6.8.8",
"steamid": "^1.1.0"
}
},
"steam-totp": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/steam-totp/-/steam-totp-2.1.1.tgz",
"integrity": "sha512-d+tjnr3wwDkbrKFxjYZ0uK4CSF09oJwCmlGH8SdOlTDkbtBPuNhPKY0XzZxQVltZF6/JkEYj+uz+kBr6UrY7BQ=="
},
"steam-user": {
"version": "4.16.2",
"resolved": "https://registry.npmjs.org/steam-user/-/steam-user-4.16.2.tgz",
"integrity": "sha512-WZLtb3fqKRJNprKfnxyRL0w/iTdqZFUHiiuVpQ4xk3qVXME8e5KygV2tDZ/P1nDtJ/cLjkv5ksWHKHDiHe/K2w==",
"requires": {
"@bbob/parser": "^2.2.0",
"@doctormckay/stdlib": "^1.11.0",
"@doctormckay/steam-crypto": "^1.2.0",
"adm-zip": "^0.4.13",
"appdirectory": "^0.1.0",
"binarykvparser": "^2.2.0",
"bytebuffer": "^5.0.0",
"file-manager": "^2.0.0",
"lzma": "^2.3.2",
"protobufjs": "^6.8.8",
"steam-appticket": "^1.0.1",
"steam-totp": "^2.0.1",
"steamid": "^1.1.0",
"vdf": "^0.0.2",
"websocket13": "^2.1.3"
}
},
"steamid": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/steamid/-/steamid-1.1.3.tgz",
"integrity": "sha512-t86YjtP1LtPt8D+TaIARm6PtC9tBnF1FhxQeLFs6ohG7vDUfQuy/M8II14rx1TTUkVuYoWHP/7DlvTtoCGULcw==",
"requires": {
"cuint": "^0.2.1"
}
},
"string.prototype.trimend": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
"integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.5"
}
},
"string.prototype.trimstart": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
"integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.5"
}
},
"ts-node": {
"version": "8.10.2",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz",
"integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==",
"dev": true,
"requires": {
"arg": "^4.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"source-map-support": "^0.5.17",
"yn": "3.1.1"
}
},
"typescript": {
"version": "3.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.5.tgz",
"integrity": "sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==",
"dev": true
},
"uri-js": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
"dev": true,
"requires": {
"punycode": "^2.1.0"
}
},
"util": {
"version": "0.12.3",
"resolved": "https://registry.npmjs.org/util/-/util-0.12.3.tgz",
"integrity": "sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog==",
"requires": {
"inherits": "^2.0.3",
"is-arguments": "^1.0.4",
"is-generator-function": "^1.0.7",
"is-typed-array": "^1.1.3",
"safe-buffer": "^5.1.2",
"which-typed-array": "^1.1.2"
}
},
"vdf": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/vdf/-/vdf-0.0.2.tgz",
"integrity": "sha1-ve6nvN3sf6/IzcWMMq6ExyXCfhQ=",
"requires": {
"util": "*"
}
},
"websocket-extensions": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
"integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg=="
},
"websocket13": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/websocket13/-/websocket13-2.2.0.tgz",
"integrity": "sha512-m3aS0sLEM9dRM2+Cvgakdr/oLqyfAObdUlUqU3gdw3PuI81k1Hw3PWdwJsehvRRlScHolA13yYsx/X3OUzsTLA==",
"requires": {
"@doctormckay/stdlib": "^1.8.0",
"bytebuffer": "^5.0.1",
"permessage-deflate": "^0.1.6",
"websocket-extensions": "^0.1.2"
}
},
"which-typed-array": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.2.tgz",
"integrity": "sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ==",
"requires": {
"available-typed-arrays": "^1.0.2",
"es-abstract": "^1.17.5",
"foreach": "^2.0.5",
"function-bind": "^1.1.1",
"has-symbols": "^1.0.1",
"is-typed-array": "^1.1.3"
}
},
"wikiapi": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/wikiapi/-/wikiapi-1.10.0.tgz",
"integrity": "sha512-fhEbwsDZV1UHDwymDgaQ/J49GHfpFlFTe/kw4q9fk355Yo4Akc+iCqGifRWQbOBtNfExKnalIpVHyHA2iEeRrQ==",
"requires": {
"cejs": "^3.7.0"
}
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
},
"yallist": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
"dev": true
},
"yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"dev": true
}
}
}

28
package.json Normal file
View file

@ -0,0 +1,28 @@
{
"name": "ludusavi-manifest",
"version": "0.0.0",
"description": "Game data backup info",
"author": "Matthew T. Kennerly <mtkennerly@gmail.com>",
"license": "MIT",
"scripts": {
"cache": "ts-node ./src/importer.ts --cache",
"manifest": "ts-node ./src/importer.ts --manifest",
"schema": "npm run schema:normal && npm run schema:strict",
"schema:normal": "ajv validate -s ./data/schema.yaml -d ./data/manifest.yaml",
"schema:strict": "ajv validate -s ./data/schema.strict.yaml -d ./data/manifest.yaml"
},
"devDependencies": {
"@types/js-yaml": "^3.12.4",
"@types/minimist": "^1.2.0",
"@types/node": "^14.0.13",
"ajv-cli": "^3.2.1",
"ts-node": "^8.10.2",
"typescript": "^3.9.5"
},
"dependencies": {
"js-yaml": "^3.14.0",
"minimist": "^1.2.5",
"steam-user": "^4.16.2",
"wikiapi": "^1.10.0"
}
}

644
src/importer.ts Normal file
View file

@ -0,0 +1,644 @@
import * as Wikiapi from "wikiapi";
import * as fs from "fs";
import * as pathMod from "path";
import * as minimist from "minimist";
import * as yaml from "js-yaml";
import * as SteamUser from "steam-user";
import { resolve } from "path";
const REPO = pathMod.dirname(__dirname);
interface Cli {
cache?: boolean,
manifest?: boolean,
all?: boolean,
existing?: boolean,
missing?: boolean,
unsupportedOs?: boolean,
unsupportedPath?: boolean,
game?: string,
limit?: number,
}
class UnsupportedError extends Error {
constructor(message?: string) {
super(message);
Object.setPrototypeOf(this, new.target.prototype);
}
}
class UnsupportedOsError extends UnsupportedError {
constructor(message?: string) {
super(message);
Object.setPrototypeOf(this, new.target.prototype);
}
}
class UnsupportedPathError extends UnsupportedError {
constructor(message?: string) {
super(message);
Object.setPrototypeOf(this, new.target.prototype);
}
}
enum PathType {
FileSystem,
Registry,
}
type Os = "windows" | "linux" | "mac";
type Store = "steam" | "uplay";
type Tag = "save" | "config";
type GamePages = Array<{ pageid: number, title: string }>;
type WikiGameCache = {
[title: string]: {
pageId: number,
revId: number | null,
/** Whether an entry on the page failed because of an unsupported OS. */
unsupportedOs?: boolean,
/** Whether an entry on the page failed because of an unsupported Path template argument. */
unsupportedPath?: boolean,
};
};
type SteamGameCache = {
[appId: string]: {
installDir?: string,
};
};
// This defines how {{P|game}} and such are converted.
const PATH_ARGS: { [arg: string]: { mapped: string, when?: Constraint, registry?: boolean, ignored?: boolean } } = {
game: {
mapped: "<base>",
},
uid: {
mapped: "<storeUserId>",
},
steam: {
mapped: "<root>",
when: {
store: "steam",
},
},
uplay: {
mapped: "<root>",
when: {
store: "uplay"
},
},
hkcu: {
mapped: "<regHkcu>",
when: { os: "windows" },
registry: true,
},
hklm: {
mapped: "<regHklm>",
when: { os: "windows" },
registry: true,
},
wow64: {
mapped: "<regWow64>",
when: { os: "windows" },
registry: true,
ignored: true,
},
username: {
mapped: "<osUserName>",
when: { os: "windows" },
},
userprofile: {
mapped: "<home>",
when: { os: "windows" },
},
"userprofile\\documents": {
mapped: "<home>/Documents",
when: { os: "windows" },
},
appdata: {
mapped: "<winAppData>",
when: { os: "windows" },
},
localappdata: {
mapped: "<winLocalAppData>",
when: { os: "windows" },
},
public: {
mapped: "<winPublic>",
when: { os: "windows" },
},
allusersprofile: {
mapped: "<winProgramData>",
when: { os: "windows" },
},
programdata: {
mapped: "<winProgramData>",
when: { os: "windows" },
},
windir: {
mapped: "<winDir>",
when: { os: "windows" },
},
syswow64: {
mapped: "<winDir>/SysWOW64",
when: { os: "windows" },
},
osxhome: {
mapped: "<home>",
when: { os: "mac" },
},
linuxhome: {
mapped: "<home>",
when: { os: "linux" },
},
xdgdatahome: {
mapped: "<xdgData>",
when: { os: "linux" },
},
xdgconfighome: {
mapped: "<xdgConfig>",
when: { os: "linux" },
},
}
interface Manifest {
[game: string]: Game;
}
interface Game {
files?: {
[path: string]: {
when?: Array<Constraint>,
tags?: Array<Tag>,
}
};
installDir?: {
[name: string]: {}
};
registry?: {
[path: string]: {
when?: Array<Omit<Constraint, "os">>,
tags?: Array<Tag>,
}
};
steamId?: number;
}
interface Constraint {
os?: Os;
store?: Store;
}
function makePathArgRegex(arg: string): RegExp {
const escaped = `{{P|${arg}}}`
.replace("\\", "\\\\")
.replace("|", "\\|")
.replace("{", "\\{")
.replace("}", "\\}");
return new RegExp(escaped, "gi");
}
/**
* https://www.pcgamingwiki.com/wiki/Template:Path
*/
function parsePath(path: string): [string, PathType] {
const pathType = getPathType(path);
for (const [arg, info] of Object.entries(PATH_ARGS)) {
if (pathContainsArg(path, arg) && info.ignored) {
throw new UnsupportedPathError(`Unsupported path argument: ${arg}`);
}
let limit = 100;
let i = 0;
while (pathContainsArg(path, arg)) {
path = path.replace(makePathArgRegex(arg), info.mapped);
i++;
if (i >= limit) {
throw new UnsupportedPathError(`Unable to resolve path arguments in: ${path}`);
}
}
}
return [
path
.replace(/\\/g, "/")
.replace(/\/(?=$)/g, "")
.replace(/^~(?=($|\/))/, "<home>"),
pathType,
];
}
function pathContainsArg(path: string, arg: string): boolean {
return path.match(makePathArgRegex(arg)) !== null;
}
function getPathType(path: string): PathType {
for (const [arg, info] of Object.entries(PATH_ARGS)) {
if (info.registry && path.match(makePathArgRegex(arg)) !== null) {
return PathType.Registry;
}
}
return PathType.FileSystem;
}
function getOsConstraintFromPath(path: string): Os | undefined {
for (const [arg, info] of Object.entries(PATH_ARGS)) {
if (pathContainsArg(path, arg) && info?.when?.os) {
return info?.when?.os;
}
}
}
function getStoreConstraintFromPath(path: string): Store | undefined {
for (const [arg, info] of Object.entries(PATH_ARGS)) {
if (pathContainsArg(path, arg) && info?.when?.store) {
return info?.when?.store;
}
}
}
function getTagFromTemplate(template: string): Tag | undefined {
switch (template) {
case "Game data/saves":
return "save";
case "Game data/config":
return "config";
default:
return undefined;
}
}
function parseOs(os: string): Os {
switch (os) {
case "Windows":
return "windows";
case "OS X":
return "mac";
case "Linux":
return "linux";
default:
throw new UnsupportedOsError(`Unsupported OS: ${os}`);
}
}
function makeApiClient() {
return new Wikiapi("https://www.pcgamingwiki.com/w");
}
function saveMissingGames(cache: WikiGameCache, manifest: Manifest): void {
fs.writeFileSync(
`${REPO}/data/missing.md`,
Object.entries(cache)
.sort((x, y) => x[0].localeCompare(y[0]))
.filter(([k, _]) => (manifest[k]?.files ?? []).length === 0 && (manifest[k]?.registry ?? []).length === 0)
.map(([k, v]) => `* [${k}](https://www.pcgamingwiki.com/wiki/?curid=${v.pageId})`)
.join("\n") + "\n",
);
}
abstract class YamlFile<T = object> {
data: T;
abstract path: string;
abstract defaultData: T;
load(): void {
if (fs.existsSync(this.path)) {
this.data = yaml.safeLoad(fs.readFileSync(this.path, "utf8"));
} else {
this.data = this.defaultData;
}
}
save(): void {
fs.writeFileSync(
this.path,
yaml.safeDump(
this.data,
{
sortKeys: true,
indent: 2,
skipInvalid: true,
lineWidth: 120,
}
)
);
}
}
class WikiGameCacheFile extends YamlFile<WikiGameCache> {
path = `${REPO}/data/wiki-game-cache.yaml`;
defaultData = {};
async addNewGames(manifest: Manifest): Promise<void> {
const wiki = makeApiClient();
const pages: Array<{ pageid: number, title: string }> = JSON.parse(JSON.stringify(await wiki.categorymembers("Games")));
for (const page of pages) {
if (!this.data.hasOwnProperty(page.title)) {
this.data[page.title] = {
pageId: page.pageid,
revId: null,
};
}
};
}
}
class SteamGameCacheFile extends YamlFile<SteamGameCache> {
path = `${REPO}/data/steam-game-cache.yaml`;
defaultData = {};
constructor(public steamClient: SteamUser) {
super();
}
async getAppInstallDir(appId: number): Promise<string | undefined> {
const key = appId.toString();
if (this.data.hasOwnProperty(key)) {
return this.data[key].installDir;
} else {
const info: SteamProductInfoResponse = await this.steamClient.getProductInfo([appId], []);
const installDir = info.apps[key].appinfo.config?.installdir;
if (installDir !== undefined) {
this.data[key] = { installDir };
}
return installDir;
}
}
}
class ManifestFile extends YamlFile<Manifest> {
path = `${REPO}/data/manifest.yaml`;
defaultData = {};
async updateGames(
wikiCache: WikiGameCache,
filter: {
all: boolean,
existing: boolean,
missing: boolean,
unsupportedOs: boolean,
unsupportedPath: boolean,
game: string | undefined,
},
limit: number,
steamCache: SteamGameCacheFile,
): Promise<void> {
let i = 0;
for (const [title, info] of Object.entries(wikiCache)) {
let check = false;
if (filter.all) {
check = true;
}
if (filter.existing && this.data.hasOwnProperty(title)) {
check = true;
}
if (filter.missing && !this.data.hasOwnProperty(title)) {
check = true;
}
if (filter.unsupportedOs && info.unsupportedOs) {
check = true;
}
if (filter.unsupportedPath && info.unsupportedPath) {
check = true;
}
if (filter.game === title) {
check = true;
}
if (!check) {
continue;
}
i++;
if (i > limit) {
break;
}
const game = await getGame(title, wikiCache);
if (game.files === undefined && game.registry === undefined && game.steamId === undefined) {
delete this.data[title];
continue;
}
if (game.steamId !== undefined) {
const installDir = await steamCache.getAppInstallDir(game.steamId);
if (installDir !== undefined) {
if (game.installDir === undefined) {
game.installDir = {}
}
game.installDir[installDir] = {}
}
}
this.data[title] = game;
}
}
}
/**
* https://www.pcgamingwiki.com/wiki/Template:Game_data
*/
async function getGame(pageTitle: string, cache: WikiGameCache): Promise<Game> {
console.log(pageTitle);
const wiki = makeApiClient();
const page = await wiki.page(pageTitle, { rvprop: "ids|content" });
const game: Game = {
files: {},
registry: {},
};
let unsupportedOs = 0;
let unsupportedPath = 0;
page.parse().each("template", template => {
if (template.name === "Infobox game") {
const steamId = Number(template.parameters["steam appid"]);
if (!isNaN(steamId) && steamId > 0) {
game.steamId = 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.length === 0) {
return;
}
try {
const [path, pathType] = parsePath(rawPath);
if (pathType === PathType.FileSystem) {
if (!game.files.hasOwnProperty(path)) {
game.files[path] = {
when: [],
tags: [],
};
}
let os: Os | undefined = undefined;
let store: Store | undefined = undefined;
if ((template.parameters[1] as string).match(/steam/i)) {
store = "steam";
} else {
os = parseOs(template.parameters[1]);
store = getStoreConstraintFromPath(rawPath);
}
if (!game.files[path].when.some(x => x.os === os && x.store === store)) {
if (os !== undefined && store !== undefined) {
game.files[path].when.push({ os, store });
} else if (os !== undefined) {
game.files[path].when.push({ os });
} else if (store !== undefined) {
game.files[path].when.push({ 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}`);
if (e instanceof UnsupportedOsError) {
unsupportedOs += 1;
return;
} else if (e instanceof UnsupportedPathError) {
unsupportedPath += 1;
return;
} else {
return;
}
}
}
});
if (Object.keys(game.files).length === 0) {
delete game.files;
} else {
for (const path of Object.keys(game.files)) {
if (game.files[path].when.length === 0) {
delete game.files[path].when;
}
if (game.files[path].tags.length === 0) {
delete game.files[path].tags;
}
}
}
if (Object.keys(game.registry).length === 0) {
delete game.registry;
} else {
for (const path of Object.keys(game.registry)) {
if (game.registry[path].when.length === 0) {
delete game.registry[path].when;
}
if (game.registry[path].tags.length === 0) {
delete game.registry[path].tags;
}
}
}
if (unsupportedOs > 0) {
cache[pageTitle].unsupportedOs = true;
} else {
delete cache[pageTitle].unsupportedOs;
}
if (unsupportedPath > 0) {
cache[pageTitle].unsupportedPath = true;
} else {
delete cache[pageTitle].unsupportedPath;
}
cache[pageTitle].revId = page.revisions?.[0]?.revid ?? 0;
return game;
}
interface SteamProductInfoResponse {
apps: {
[appId: string]: {
appinfo: {
config?: {
installdir?: string
}
}
}
}
}
async function getSteamClient(): Promise<SteamUser> {
const client = new SteamUser();
client.logOn();
await new Promise(resolve => {
client.on("loggedOn", () => {
resolve();
});
});
return client;
}
async function main() {
const args = minimist<Cli>(process.argv.slice(2));
const wikiCache = new WikiGameCacheFile();
wikiCache.load();
const steamCache = new SteamGameCacheFile(await getSteamClient());
steamCache.load();
const manifest = new ManifestFile();
manifest.load();
try {
if (args.cache) {
await wikiCache.addNewGames(manifest.data);
}
if (args.manifest) {
await manifest.updateGames(
wikiCache.data,
{
all: args.all ?? false,
existing: args.existing ?? false,
missing: args.missing ?? false,
unsupportedOs: args.unsupportedOs ?? false,
unsupportedPath: args.unsupportedPath ?? false,
game: args.game,
},
args.limit ?? 25,
steamCache,
);
}
wikiCache.save();
steamCache.save();
manifest.save();
saveMissingGames(wikiCache.data, manifest.data);
steamCache.steamClient.logOff();
process.exit(0);
} catch (e) {
wikiCache.save();
steamCache.save();
manifest.save();
saveMissingGames(wikiCache.data, manifest.data);
steamCache.steamClient.logOff();
throw e;
}
}
main();