ludusavi 0.18.0

Game save backup tool
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
# ![Logo]assets/icon.svg Ludusavi
Ludusavi is a tool for backing up your PC video game save data,
written in [Rust](https://www.rust-lang.org).
It is cross-platform and supports multiple game stores.

## Features
* Ability to back up data from more than 10,000 games plus your own custom entries.
* Backup and restore for Steam as well as other game libraries.
* Both a graphical interface and command line interface for scripting.
  Tab completion is available for Bash, Fish, Zsh, PowerShell, and Elvish.
* Support for:
  * Saves that are stored as files and in the Windows registry.
  * Proton saves with Steam.
  * Steam screenshots.
* Available as a [Playnite]https://playnite.link extension:
  https://github.com/mtkennerly/ludusavi-playnite
* Works on the Steam Deck.

This tool uses the [Ludusavi Manifest](https://github.com/mtkennerly/ludusavi-manifest)
for info on what to back up, and it will automatically download the latest version of
the primary manifest. The data is ultimately sourced from [PCGamingWiki](https://www.pcgamingwiki.com/wiki/Home),
so please contribute any new or fixed data back to the wiki itself, and your
improvements will be incorporated into Ludusavi's data as well.

If you'd like to help translate Ludusavi into other languages,
[check out the Crowdin project](https://crowdin.com/project/ludusavi).

## Demo
### GUI
> ![GUI demo of previewing a backup]docs/demo-gui.gif

### CLI
> ![CLI demo of previewing a backup]docs/demo-cli.gif

## Installation
### Requirements
* Ludusavi is available for Windows, Linux, and Mac.
* Your system must support OpenGL.

### Methods
You can install Ludusavi one of these ways:

* Download the executable for your operating system from the
  [releases page]https://github.com/mtkennerly/ludusavi/releases.
  It's portable, so you can simply download it and put it anywhere
  on your system.
  **If you're unsure, choose this option.**

* On Windows, you can use [Winget]https://github.com/microsoft/winget-cli.

  * To install: `winget install -e --id mtkennerly.ludusavi`
  * To update: `winget upgrade -e --id mtkennerly.ludusavi`

* On Windows, you can use [Scoop]https://scoop.sh.

  * To install: `scoop bucket add extras && scoop install ludusavi`
  * To update: `scoop update && scoop update ludusavi`

* For Linux, Ludusavi is available on [Flathub]https://flathub.org/apps/details/com.github.mtkennerly.ludusavi.
  Note that it has limited file system access by default (`~` and `/run/media`).
  If you'd like to enable broader access, [see here]https://github.com/flathub/com.github.mtkennerly.ludusavi/blob/master/README.md.

* If you have [Rust]https://www.rust-lang.org, you can use Cargo.

  * To install or update: `cargo install ludusavi`

  On Linux, this requires the following system packages, or their equivalents
  for your distribution:

  * Ubuntu: `sudo apt-get install -y gcc cmake libx11-dev libxcb-composite0-dev libfreetype6-dev libexpat1-dev libfontconfig1-dev`

### Notes
If you are on Windows:

* When you first run Ludusavi, you may see a popup that says
  "Windows protected your PC", because Windows does not recognize the program's
  publisher. Click "more info" and then "run anyway" to start the program.

If you are on Mac:

* When you first run Ludusavi, you may see a popup that says
  "Ludusavi can't be opened because it is from an unidentified developer".
  To allow Ludusavi to run, please refer to [this article]https://support.apple.com/en-us/HT202491,
  specifically the section on `How to open an app [...] from an unidentified developer`.

## Usage
### Roots
Roots are folders that Ludusavi can check for additional game data. When you
first run Ludusavi, it will try to find some common roots on your system, but
you may end up without any configured. These are listed on the "other" screen,
where you can use the plus button in the roots section to configure as many as you need,
along with the root's type:

* For a Steam root, this should be the folder containing the `steamapps` and
  `userdata` subdirectories. Here are some common/standard locations:
  * Windows: `C:/Program Files (x86)/Steam`
  * Linux: `~/.steam/steam`

  On Linux, for games that use Proton, Ludusavi will back up the `*.reg` files
  if the game is known to have registry-based saves.

  On Linux, if you've used Steam's "add a non-Steam game" feature,
  then Ludusavi will also back up any Proton save data for those games.
  This requires the shortcut name in Steam to match the title by which Ludusavi knows the game
  (i.e., the title of its PCGamingWiki article).
* For a Heroic root, this should be the folder containing the `gog_store`
  and `GamesConfig` subdirectories.

  Ludusavi can find GOG/Epic saves in Heroic's game install folders.
  On Linux, Ludusavi can also find saves in Heroic's Wine, Proton, and Lutris prefixes.

  When using Wine prefixes with Heroic, Ludusavi will back up the `*.reg` files
  if the game is known to have registry-based saves.
* For a Lutris root, this should be the folder containing the `games` subdirectory.
* For the "other" root type and the remaining store-specific roots,
  this should be a folder whose direct children are individual games.
  For example, in the Epic Games store, this would be what you choose as the
  "install location" for your games (e.g., if you choose `D:/Epic` and it
  creates a subfolder for `D:/Epic/Celeste`, then the root would be `D:/Epic`).
* For a home folder root, you may specify any folder. Whenever Ludusavi
  normally checks your standard home folder (Windows: `%USERPROFILE%`,
  Linux/Mac: `~`), it will additionally check this root. This is useful if
  you set a custom `HOME` to manipulate the location of save data.
* For a Wine prefix root, this should be the folder containing `drive_c`.
  Currently, Ludusavi does not back up registry-based saves from the prefix,
  but will back up any file-based saves.

You may use [globs] in root paths to identify multiple roots at once.
If you have a folder name that contains a special glob character,
you can escape it by wrapping it in brackets (e.g., `[` becomes `[[]`).

### Backup retention
You can configure how many backups to keep by pressing the gear icon on the backup screen.
A full backup contains all save data for a game,
while a differential backup contains just the data that has changed since the last full backup.

When Ludusavi makes a new backup for a game, it will also remove any excess backups for that specific game.
When a full backup is deleted, its associated differential backups are deleted as well.

For example, if you configure a retention limit of 2 full and 2 differential,
then Ludusavi will create 2 differential backups for each full backup, like so:

* Backup #1: full
  * Backup #2: differential
  * Backup #3: differential
* Backup #4: full
  * Backup #5: differential
  * Backup #6: differential

When backup #7 is created, because the full retention is set to 2,
Ludusavi will delete backups 1 through 3.

If your full retention is only 1 and your differential retention is 1+,
then Ludusavi will keep the full backup and just delete the oldest differential as needed.

On the restore screen, you can use the three-dot menu next to a game to lock any of its backups.
Locked backups do not count toward the retention limits and are retained indefinitely.

### Cloud backup
Ludusavi integrates with [Rclone](https://rclone.org) to provide cloud backups.
You can configure this on the "other" screen.
Any Rclone remote is supported, but Ludusavi can help you configure some of the more common ones:
Google Drive, OneDrive, Dropbox, Box, FTP servers, SMB servers, and WebDAV servers.
Support is verified for Rclone 1.62.2, but other versions should work as well.

If you turn on automtic synchronization,
then Ludusavi will check if your local and cloud saves are already in sync at the start of a backup.
If so, then any changes will be uploaded once the backup is done.
If they weren't in sync to begin with, then Ludusavi will warn you about the conflict and leave the cloud data alone.
You can perform an upload or download at any time on the "other" screen to resolve such a conflict.

Bear in mind that many factors can affect cloud sync performance,
including network speed, outages on the cloud side, and any limitations of Rclone itself.
You can try setting custom Rclone arguments if you find that it is too slow.
For example, `--fast-list` and/or `--ignore-checksum` can speed things up,
while `--transfers=1` can help to avoid rate-limiting but may slow things down.
The "other" screen has a field to configure custom arguments,
and you can find documentation for them here: https://rclone.org/flags

You can also use other cloud backup tools of your choice,
as long as they can make the storage available as what looks like a normal folder.
For example:

* If you use something like [Google Drive for Desktop]https://www.google.com/drive/download,
  which creates a special drive (`G:`) to stream from/to the cloud,
  then you can configure Ludusavi to use a folder in that drive.
* If you use something like [Syncthing]https://syncthing.net,
  which continuously synchronizes a local folder across systems,
  then you can configure Ludusavi to use that local folder.
* If you use Rclone's mounting functionality,
  then you can configure Ludusavi to use the mount folder.

### Selective scanning
Once you've done at least one full scan (via the preview/backup buttons),
Ludusavi will remember the games it found and show them to you the next time you run the program.
That way, you can selectively preview or back up a single game without doing a full scan.
Use the three-dot menu next to each game's title to operate on just that one game.

You can also use keyboard shortcuts to swap the three-dot menu with some specific buttons:

* preview: shift
* backup/restore: ctrl (Mac: cmd)
* backup/restore without confirmation: ctrl + alt (Mac: cmd + option)

### Backup structure
* Within the target folder, for every game with data to back up, a subfolder
  will be created based on the game's name, where some invalid characters are
  replaced by `_`. In rare cases, if the whole name is invalid characters,
  then it will be renamed to `ludusavi-renamed-<ENCODED_NAME>`.
* Within each game's subfolder, there will be a `mapping.yaml` file that
  Ludusavi needs to identify the game.

  When using the simple backup format, there will be some drive folders
  (e.g., `drive-C` on Windows or `drive-0` on Linux and Mac) containing the
  backup files, matching the normal file locations on your computer.
  When using the zip backup format, there will be zip files instead.
* If the game has save data in the registry and you are using Windows, then
  the game's subfolder will also contain a `registry.yaml` file (or it will
  be placed in each backup's zip file).
  If you are using Steam and Proton instead of Windows, then the Proton `*.reg`
  files will be backed up along with the other game files instead.

During a restore, Ludusavi only considers folders with a `mapping.yaml` file.

### Filter
You can click the filter icon at the top of the backup/restore screens to use some filters.
Note that this only affects which games you see in the list,
but Ludusavi will still back up the full set of games.

You can apply filters for the following:

* Whether the game title matches a search.
* Whether multiple games have the same save files:
  * `Unique` (no conflicts)
  * `Duplicated` (conflicts exist)
* Whether all save files for a game are enabled for processing:
  * `Complete` (all saves enabled)
  * `Partial` (some saves disabled)
* Whether the game itself is enabled for processing:
  * `Enabled` (checkbox next to game is checked)
  * `Disabled` (checkbox next to game is unchecked)

### Duplicates
You may see a "duplicates" badge next to some games. This means that some of
the same files were also backed up for another game. That could be intentional
(e.g., an HD remaster may reuse the original save locations), but it could
also be a sign of an issue in the manifest data. You can expand the game's
file list to see which exact entries are duplicated.

You can resolve conflicts by disabling certain save files from being backed up.
Once a conflict is resolved, the badge will become faded.
You can also click on the badge to view just the conflicting games.

### Redirects
You can use redirects to back up or restore to a different location than the original file.
These are listed on the "other" screen, where you can click the plus button to add more
and then enter both the old and new location.
For example, if you backed up some saves from `C:/Games`, but then you moved it to `D:/Games`,
then you could create a restore redirect with `C:/Games` as the source and `D:/Games` as the target.

Tip: As you're editing your redirects, try running a preview and expanding some
games' file lists. This will show you what effect your redirects
will have when you perform the restore for real.

### Custom games
You can create your own game save definitions on the `custom games` screen.
If the game name exactly matches a known game, then your custom entry will override it.

For file paths, you can click the browse button to quickly select a folder.
The path can be a file too, but the browse button only lets you choose
folders at this time. You can just type in the file name afterwards.
You can also use [globs]
(e.g., `C:/example/*.txt` selects all TXT files in that folder)
and the placeholders defined in the
[Ludusavi Manifest format](https://github.com/mtkennerly/ludusavi-manifest).
If you have a folder name that contains a special glob character,
you can escape it by wrapping it in brackets (e.g., `[` becomes `[[]`).

### Backup exclusions
Backup exclusions let you set paths and registry keys to completely ignore
from all games. They will not be shown at all during backup scans.

Configure exclusions on the "other" screen.

### Command line
Run `ludusavi --help` for the CLI usage information.
You can also view info for specific subcommands, such as `ludusavi manifest update --help`.

### Configuration
Ludusavi stores its configuration in the following locations:

* Windows: `%APPDATA%/ludusavi`
* Linux: `$XDG_CONFIG_HOME/ludusavi` or `~/.config/ludusavi`
  * Flatpak: `~/.var/app/com.github.mtkennerly.ludusavi/config/ludusavi`
* Mac: `~/Library/Application Support/ludusavi`

Alternatively, if you'd like Ludusavi to store its configuration in the same
place as the executable, then simply create a file called `ludusavi.portable`
in the directory that contains the executable file. You might want to do that
if you're going to run Ludusavi from a flash drive on multiple computers.

If you're using the GUI, then it will automatically update the config file
as needed, so you don't need to worry about its content. However, if you're
using the CLI exclusively, then you'll need to edit `config.yaml` yourself.

Ludusavi also stores `manifest.yaml` (info on what to back up) here.
You should not modify that file, because Ludusavi will overwrite your changes
whenever it downloads a new copy.

### Logging
Log files are stored in the config folder (see above).
By default, only warnings and errors are logged,
but you can customize this by setting the `RUST_LOG` environment variable
(e.g., `RUST_LOG=ludusavi=debug`).
The most recent 5 log files are kept, rotating on app launch or when a log reaches 10 MiB.

## Interfaces
### CLI API
CLI mode defaults to a human-readable format, but you can switch to a
machine-readable JSON format with the `--api` flag.

<details>
<summary>Click to expand</summary>

For the `backup`/`restore` commands:

* `errors` (optional, map):
  * `someGamesFailed` (optional, boolean): Whether any games failed.
  * `unknownGames` (optional, list of strings): Names of unknown games, if any.
  * `cloudConflict` (optional, empty map): When this field is present,
    Ludusavi could not automatically synchronize with the cloud because of conflicting data.
  * `cloudSyncFailed` (optional, empty map): When this field is present,
    Ludusavi tried and failed to automatically synchronize with the cloud.
* `overall` (map):
  * `totalGames` (number): How many games were found.
  * `totalBytes` (number): How many bytes are used by files associated with
    found games.
  * `processedGames` (number): How many games were processed.
    This excludes ignored, failed, and cancelled games.
  * `processedBytes` (number): How many bytes were processed.
    This excludes ignored, failed, and cancelled games.
  * `changedGames` (object): Total count of `new`, `same`, and `different` games.
* `games` (map):
  * Each key is the name of a game, and the value is a map with these fields:
    * `decision` (string): How Ludusavi decided to handle this game.

      Possible values:
      * `Processed`
      * `Ignored`
      * `Cancelled`
    * `change` (string): How this game compares to its previous backup (if doing a new backup)
      or how its previous backup compares to the current system state (if doing a restore).

      Possible values:
      * `New`
      * `Same`
      * `Different`
    * `files` (map):
      * Each key is a file path, and each value is a map with these fields:
        * `failed` (optional, boolean): Whether this entry failed to process.
        * `change` (string): Same as game-level field, but for a specific backup item.
        * `ignored` (optional, boolean): Whether this entry was ignored.
        * `bytes` (number): Size of the file.
        * `originalPath` (optional, string): If the file was restored to a
          redirected location, then this is its original path.
        * `duplicatedBy` (optional, array of strings): Any other games that
          also have the same file path.
    * `registry` (map):
      * Each key is a registry path, and each value is a map with these fields:
        * `failed` (optional, boolean): Whether this entry failed to process.
        * `change` (string): Same as game-level field, but for a specific backup item.
        * `ignored` (optional, boolean): Whether this entry was ignored.
        * `duplicatedBy` (optional, array of strings): Any other games that
          also have the same registry path.
        * `values` (optional, map): Any registry values inside of the registry key.
          * `change` (string): Same as game-level field, but for a specific backup item.
          * `ignored` (optional, boolean): Whether this entry was ignored.
          * `duplicatedBy` (optional, array of strings): Any other games that
            also have the same registry key+value.

The `backups` command is similar, but without `overall`, and with each game containing
`{"backups": [ {"name": <string>, "when": <string>, "comment": <string>} ]}`.
The `find` command also does not have `overall`, and each game object is empty.

For the `cloud upload` and `cloud download` commands:

* `cloud` (map):
  * Each key is the path of a file relative to the cloud folder,
    and the value is a map with these fields:
    * `change` (string): Same as the `change` fields for the `backup` command.

Note that, in some error conditions, there may not be any JSON output,
so you should check if stdout was blank before trying to parse it.
If the command line input cannot be parsed, then the output will not be
in a stable format.

API output goes on stdout, but stderr may still be used for human-readable warnings/errors.
If stderr is not empty, you may want to log it,
since not all human-readable warnings have an API equivalent.

Example:

```json
{
  "errors": {
    "someGamesFailed": true,
  },
  "overall": {
    "totalGames": 2,
    "totalBytes": 150,
    "processedGames": 1,
    "processedBytes": 100,
  },
  "games": {
    "Game 1": {
      "decision": "Processed",
      "files": {
        "/games/game1/save.json": {
          "bytes": 100
        }
      },
      "registry": {
        "HKEY_CURRENT_USER/Software/Game1": {
          "failed": true
        }
      }
    },
    "Game 2": {
      "decision": "Ignored",
      "files": {
        "/games/game2/save.json": {
          "bytes": 50
        }
      },
      "registry": {}
    }
  }
}
```

</details>

### Configuration file
Here are the available settings in `config.yaml` (all are required unless otherwise noted):

<details>
<summary>Click to expand</summary>

* `runtime` (map):
  * `threads` (integer): How many threads to use for parallel scanning.
    Must be greater than 0.
* `manifest` (map):
  * `url` (string): Where to download the primary manifest.
* `language` (string, optional): Display language. Valid options:
  `en-US` (English, default),
  `de-DE` (German),
  `eo-UY` (Esperanto),
  `es-ES` (Spanish),
  `fil-PH` (Filipino),
  `fr-FR` (French),
  `it-IT` (Italian),
  `nl-NL` (Dutch),
  `pl-PL` (Polish),
  `pt-BR` (Brazilian Portuguese),
  `ru-RU` (Russian),
  `uk-UA` (Ukrainian).

  Experimental options that currently have graphical display issues:
  `ar-SA` (Arabic),
  `ja-JP` (Japanese),
  `ko-KR` (Korean),
  `zh-Hans` (Simplified Chinese).
* `theme` (string, optional): Visual theme. Valid options:
  `light` (default), `dark`.
* `roots` (list):
  * Each entry in the list should be a map with these fields:
    * `path` (string): Where the root is located on your system.
    * `store` (string): Game store associated with the root. Valid options:
      `ea`, `epic`, `gog`, `gogGalaxy`, `heroic`, `lutris`, `microsoft`, `origin`, `prime`,
      `steam`, `uplay`, `otherHome`, `otherWine`, `other`
* `redirects` (optional, list):
  * Each entry in the list should be a map with these fields:
    * `kind` (string): When and how to apply the redirect.

      Possible values:
      * `backup`
      * `restore`
      * `bidirectional`
    * `source` (string): The original location when the backup was performed.
    * `target` (string): The new location.
* `backup` (map):
  * `path` (string): Full path to a directory in which to save backups.
    This can be overridden in the CLI with `--path`.
  * `ignoredGames` (optional, array of strings): Names of games to skip when backing up.
    This can be overridden in the CLI by passing a list of games.
  * `filter` (optional, map):
    * `excludeStoreScreenshots` (optional, boolean): If true, then the backup
      should exclude screenshots from stores like Steam. Default: false.
    * `ignoredPaths` (list of strings): Globally ignored paths.
    * `ignoredRegistry` (list of strings): Globally ignored registry keys.
  * `toggledPaths` (map): Paths overridden for inclusion/exclusion in the backup.
    Each key is a game name, and the value is another map. In the inner map,
    each key is a path, and the value is a boolean (true = included).
    Settings on child paths override settings on parent paths.
  * `toggledRegistry` (map): Registry overridden for inclusion/exclusion in the backup.
    Each map key is a game name, and the map value is another map. In the inner map,
    each key is a path, and the value is a boolean (true = included).
    each map key is a registry key path, and the map value is boolean (true = included).
    Instead of a plain boolean, you can specify `{ key: boolean, values: { value_name: boolean } }`
    to control individual registry values as well.
    Settings on child paths override settings on parent paths.
  * `sort` (map):
    * `key` (string): One of `name`, `size`, `status`.
    * `reversed` (boolean): If true, sort reverse alphabetical or from the largest size.
  * `retention` (map):
    * `full` (integer): Full backups to keep. Range: 1-255.
    * `differential` (integer): Full backups to keep. Range: 0-255.
  * `format` (map):
    * `chosen` (string): One of `simple`, `zip`.
    * `zip` (map): Settings for the zip format.
      * `compression` (string): One of `none`, `deflate`, `bzip2`, `zstd`.
    * `compression` (map): Settings for specific compression methods.
      In compression levels, higher numbers are slower, but save more space.
      * `deflate` (object):
        * `level` (integer): 1 to 9.
      * `bzip2` (object):
        * `level` (integer): 1 to 9.
      * `zstd` (object):
        * `level` (integer): -7 to 22.
* `restore` (map):
  * `path` (string): Full path to a directory from which to restore data.
    This can be overridden in the CLI with `--path`.
  * `ignoredGames` (optional, list of strings): Names of games to skip when restoring.
    This can be overridden in the CLI by passing a list of games.
  * `sort` (map):
    * `key` (string): One of `name`, `size`.
    * `reversed` (boolean): If true, sort reverse alphabetical or from the largest size.
* `scan` (map):
  * `showDeselectedGames` (boolean): In the GUI, show games that have been deselected.
  * `showUnchangedGames` (boolean): In the GUI, show games that have been scanned, but do not have any changed saves.
  * `showUnscannedGames` (boolean): In the GUI, show recent games that have not been scanned yet.
* `cloud` (map):
  * `remote`: Rclone remote.
    You should use the GUI or the `cloud set` command to modify this,
    since any changes need to be synchronized with Rclone to take effect.
  * `path` (string): Cloud folder to use for backups.
  * `synchronize` (boolean): If true, upload changes automatically after backing up,
    as long as there aren't any conflicts.
* `apps` (map):
  * `rclone` (map):
    * `path` (string): Path to `rclone.exe`.
    * `arguments` (string): Any global flags (space-separated) to include in Rclone commands.
* `customGames` (optional, list):
  * Each entry in the list should be a map with these fields:
    * `name` (string): Name of the game.
    * `files` (optional, list of strings): Any files or directories you want
      to back up.
    * `registry` (optional, list of strings): Any registry keys you want to back up.

Example:

```yaml
manifest:
  url: "https://raw.githubusercontent.com/mtkennerly/ludusavi-manifest/master/data/manifest.yaml"
roots:
  - path: "D:/Steam"
    store: steam
backup:
  path: ~/ludusavi-backup
restore:
  path: ~/ludusavi-backup
```

</details>

### Environment variables
Environment variables can be used to tweak some additional behavior:

<details>
<summary>Click to expand</summary>

* `RUST_LOG`: Configure logging.
  Example: `RUST_LOG=ludusavi=debug`
* `LUDUSAVI_DEBUG`: If this is set to any value,
  then Ludusavi will not detach from the console on Windows in GUI mode.
  It will also print some debug messages in certain cases.
  Example: `LUDUSAVI_DEBUG=1`
* `LUDUSAVI_THREADS`: Overrive the `runtime.threads` value from the config file.
  Example: `LUDUSAVI_THREADS=8`

</details>

## Comparison with other tools
There are other excellent backup tools available, but not a singular
cross-platform and cross-store solution:

* [GameSave Manager](https://www.gamesave-manager.com) (as of v3.1.512.0):
  * Only supports Windows.
  * Much slower than Ludusavi. On the same hardware and with default settings,
    an initial scan of the whole system takes 2 minutes in GSM versus 10 seconds in Ludusavi.
    Performing a backup immediately after that scan takes 4 minutes 16 seconds in GSM versus 4.5 seconds in Ludusavi.
    In this test, GSM found 257 games with 2.84 GB, and Ludusavi found 297 games with 2.95 GiB.
  * Closed source, so the community cannot contribute improvements.
  * Interface can be slow or unresponsive.
    For example, when clicking "select all / de-select all", each checkbox has to individually toggle itself.
    With 257 games, this means you end up having to wait around 42 seconds.
  * Minimal command line interface.
  * Can create symlinks for games and game data.
    Ludusavi does not support this.
* [Game Backup Monitor](https://mikemaximus.github.io/gbm-web) (as of v1.2.2):
  * Does not support Mac.
  * Database only covers 577 games (as of 2022-11-16), although it can also import
    the Ludusavi manifest starting in 1.3.1.
  * No command line interface.
  * Can automatically back up saves for a game after you play it.
    Ludusavi can only do that in conjunction with a launcher like Playnite.
* [Gaming Backup Multitool for Linux](https://supremesonicbrazil.gitlab.io/gbml-web) (as of v1.4.0.0):
  * Only supports Linux and Steam.
  * Database is not actively updated. As of 2022-11-16, the last update was 2018-06-05.
  * No command line interface.

## Troubleshooting
* The window content is way too big and goes off screen.
  * Try setting the `WINIT_X11_SCALE_FACTOR` environment variable to `1`.
    Flatpak installs will have this set automatically.
* The file/folder picker doesn't work.
  * **Linux:** Make sure that you have Zenity or kdialog installed and available on the `PATH`.
    The `DISPLAY` environment variable must also be set.
  * **Steam Deck:** Use desktop mode instead of game mode.
  * **Flatpak:** The `DISPLAY` environment variable may not be getting passed through to the container.
    This has been observed on GNOME systems.
    Try running `flatpak run --nosocket=fallback-x11 --socket=x11 com.github.mtkennerly.ludusavi`.

## Development
Please refer to [CONTRIBUTING.md](./CONTRIBUTING.md).

[globs]: https://en.wikipedia.org/wiki/Glob_(programming)